CLI

Run and save notebooks with the CLI

Insert and run live notebook cells, inspect outputs, and recover detached runs.

Work with a live notebook

The project Jupyter commands inspect and edit the live notebook without requiring an open browser tab. Use them instead of rewriting .ipynb JSON while CoCalc is editing the document. This recipe inserts one cell in an existing scratch notebook, runs it, inspects its output, and saves it.

Step 1: Select the notebook and confirm its kernel

In Bash, use the profile from the quickstart, the full project ID, and an existing scratch notebook with a Python kernel:

export CLI_PROFILE=cocalc-ai
export PROJECT_ID='REPLACE_WITH_FULL_PROJECT_ID'
export NOTEBOOK_PATH='/home/user/scratch/cli-demo.ipynb'

cocalc --profile "$CLI_PROFILE" project get --project "$PROJECT_ID"
cocalc --profile "$CLI_PROFILE" --json project jupyter kernel \
  --project "$PROJECT_ID" --path "$NOTEBOOK_PATH"
cocalc --profile "$CLI_PROFILE" --json project jupyter cells \
  --project "$PROJECT_ID" --path "$NOTEBOOK_PATH"

Use the absolute remote notebook path, adjusting /home/user if needed; relative notebook paths resolve against the CLI computer's home directory. Confirm the project, path, kernel, and existing cells. cells includes each cell's full input. Use project jupyter --help to inspect your version's commands.

Step 2: Insert and run one cell

Run this insertion once:

cocalc --profile "$CLI_PROFILE" --json project jupyter insert \
  --project "$PROJECT_ID" --path "$NOTEBOOK_PATH" \
  --at-end --type code --input 'print(2 + 3)'

Copy data.cell.id into CELL_ID, then run that cell:

export CELL_ID='REPLACE_WITH_RETURNED_CELL_ID'

cocalc --profile "$CLI_PROFILE" --json project jupyter run \
  --project "$PROJECT_ID" --path "$NOTEBOOK_PATH" \
  --cell-id "$CELL_ID"

Require successful command completion, ok:true, and data.error_count:0. This command follows execution by default. --jsonl is a different, streaming output format; do not parse it as one JSON result.

For a noisy cell, add --limit 40 to that same run command to limit output initially streamed by the backend. The limit must be a positive integer; it is not a row count, a calculation limit, or a bound on the saved result file. In a followed JSON run, data.more_output_count counts additional-output notifications, not omitted rows. Inspect the retained cell output and independently validate complete files as described in Keep complete results when output is limited. A zero error count alone does not establish scientific correctness or completeness.

Step 3: Inspect output and save

cocalc --profile "$CLI_PROFILE" --json project jupyter outputs \
  --project "$PROJECT_ID" --path "$NOTEBOOK_PATH" \
  --cell-id "$CELL_ID"

cocalc --profile "$CLI_PROFILE" --json project jupyter save \
  --project "$PROJECT_ID" --path "$NOTEBOOK_PATH"

The selected cell's output should contain 5; saving should report data.saved:true. outputs --cell-id returns that cell's input, output, and metadata. Inspect this read-back rather than inferring notebook contents from successful command submission alone.

Prefer cell IDs when retaining selections across commands. Cell indexes are zero-based and can shift when collaborators insert or move cells. IDs identify cells but do not lock their contents.

Follow a long run after disconnecting

For long-running code, replace the run command in step 2 with a detached submission; do not execute both unless you intend to run the cell twice:

cocalc --profile "$CLI_PROFILE" --json project jupyter run \
  --project "$PROJECT_ID" --path "$NOTEBOOK_PATH" \
  --cell-id "$CELL_ID" --detach

Retain data.run_id as RUN_ID. Detach waits for a backend acknowledgment, not completion. Follow that exact run:

export RUN_ID='REPLACE_WITH_RETURNED_RUN_ID'

cocalc --profile "$CLI_PROFILE" --json project jupyter live \
  --project "$PROJECT_ID" --path "$NOTEBOOK_PATH" \
  --run-id "$RUN_ID" --timeout 20m

Require the matching data.run_id, data.follow:true, successful command completion, and data.error_count:0; then inspect outputs and save. The live command can return cell errors without setting a failing process exit code. --no-follow only retrieves the available snapshot and cannot establish completion.

If following times out or disconnects, inspect the retained run before submitting another one. Without an explicit ID, selection prefers a currently running run, then the most recently updated retained run; it may not be the run you started.

--allow-errors relaxes the following run command's error-exit rule. It does not make erroneous output successful or guarantee that later cells execute. Detached or noninteractive code should not depend on answering kernel input prompts.

For JavaScript notebook scripts, inspect project jupyter exec --help. Consume the run's output iterator, check errors, and release its client handle with run.close() in finally. Closing a handle does not interrupt the kernel. Use project jupyter interrupt deliberately to interrupt work; it affects the notebook kernel, not just one local CLI waiter.