See also: Dev CLI Wishlist
TODO:
- ideas to expand the browser api:
- make process info available (i.e., categorized list of processes spawned by the project shown in processes)
- make the project log available, so agent knows recent/all activity by users
- make sure to delete the whole "CURRENTLY NOT USED" thing in db-schema/projects.ts
- browser automation policy hardening: see
browser-automation-policy.md - implement "host stop".
- Projects: name versus title. Right now we're using the project title (which can have any characters in it and is easy to change at any time) for naming things, which isn't great. There's also a "name" field associated to a project, which is much more constrained. We should accept that for "-w" and also change the whole UI to strongly suggest choosing a name when making a project.
- When making a project or host, show the corresponding cocalc cli command, like GCP does. See http://localhost:7000/projects/00000000-1000-4000-8000-000000000000/files/home/wstein/build/cocalc-lite4/lite4.chat#chat=1771453636305
- We really need some good documentation for all this... in a form that is extremely AI Agent friendly(!). E.g., it's so important to point out that this is genuine full ssh, and fully supports port forwarding, X11 forwarding. It's not just a half-broken "ssh gateway", but the real deal.
- client and server versioning...
- rewrite all the code in the cli to benefit from typescript for the cocalc api -- http://localhost:7000/projects/00000000-1000-4000-8000-000000000000/files/home/wstein/build/cocalc-lite4/lite4.chat#chat=1771572607303
- cloudflare ssh integration.
- get logs from host (i.e., /mnt/cocalc/data/log), similar to how that works with kubernetes
- upgrade host (or set host to a specific version)
- ssh to host (or something like "kubectl exec bash").
| Area | Status | Notes |
|---|---|---|
browser session list/use/clear |
done | Browser heartbeat + active session targeting is in place. |
browser open/close (multiple files) |
done | Multi-file open/close in one command. |
browser exec |
done | Inline JS, --file, --stdin, configurable timeout. |
browser exec LRO |
done | start/get/wait/cancel implemented and exposed in CLI. |
browser exec-api |
done | Prints TypeScript API declaration + usage snippets. |
api.notify |
done | In-browser notifications (show/info/success/warning/error). |
api.fs |
done | Node-like async fs + find/fd/ripgrep/dust wrappers. |
api.bash |
done | Blocking + async job lifecycle (run/start/get/wait). |
api.notebook |
MVP | listCells, runCells, setCells. |
api.terminal |
MVP | list/openSplit/spawn/write/history/state/cwd/resize/destroy. |
api.timetravel |
MVP+ | patchflow/snapshots/backups/git list/get primitives. |
| syncdoc handling | done | Direct syncdoc open path + refcounted reuse + cleanup. |
| lite-mode browser support | done | Browser CLI flow works with lite hub surface. |
| extension runtime | MVP | Session-scoped api.extensions with hello-world editor demo. |
- Harden lite-mode conat auth defaults and agent-scope token model.
- Expand notebook editing API beyond set/run/list.
- Add richer timetravel helpers (
restore/search/summarize) at unified layer. - Extend extension runtime from hello-world MVP to generic bundle/manifest install.
- Add policy/approval hooks directly in browser exec API methods.
This is direct feedback from a real debugging session against the lite dev env while reproducing Jupyter keyboard-routing issues with floating agent windows.
browser action clickbrowser action typebrowser screenshotbrowser logs tail- raw
browser execfor DOM and runtime inspection
- after a reload, a second active browser session appeared for the same live browser tab
- unpinned commands then targeted the newer session implicitly
- this is dangerous for debugging because the agent can think it is operating on the user's exact tab when it is not
Requested improvement:
- once a browser session is selected, strongly prefer strict target pinning by default
- make any fallback to a different browser id very explicit
- one command reported a
target_session_urlof project home while the actual runtimepage_urland DOM state were on the notebook tab - this makes it harder to trust CLI output during debugging
Requested improvement:
- ensure
target_browser_id,target_session_url, and runtimepage_urlalways agree, or emit a clear mismatch warning
- trying to click the notebook underneath an overlapping floating dock hit a dock button instead
- this made it awkward to force the notebook into command mode when debugging keyboard ownership under overlays
Requested improvement:
- add better selector-driven focus/click helpers
- consider higher-level notebook/editor actions for "focus selected cell", "enter command mode", etc.
browser action typeappended text correctly in the floating agent editorbrowser action presson the same contenteditable selector did not insert text, even though it did preserve focus and was useful for shortcut leakage checks
Requested improvement:
- document this distinction more clearly
- add richer diagnostics for
press, including before/after active element and whetherkeydownandinputreached the intended target
- opening the
...menu worked fine - selecting a menu item by label still required raw DOM inspection to find the generated selector
Requested improvement:
- add actions such as
click-text,click-menu-item, orselect-menu-item
- browser action plus raw DOM inspection was reliable
- some higher-level helpers like file opening and notebook cell listing were more likely to stall during a busy live-session workflow
Requested improvement:
- harden those helpers for live sessions
- add better timeout/error reporting so the caller can distinguish "slow" from "stuck"
- Provide a single, scriptable CLI for CoCalc Launchpad and related products.
- Use user-facing terminology:
projectinstead ofworkspace. - Cover both major control-plane entities:
- projects
- hosts
- Match the practical ergonomics of Sprites CLI (auth, list/create/use, exec/console, proxy URL workflows).
- Make CoCalc AI-agent-friendly by exposing stable, scriptable primitives for files, search, notebooks, and browser-session automation.
- Keep implementation SEA-friendly:
- JS/TS only
- no native compiled dependencies in the core CLI path
- single-file bundle for SEA without extraction of native addons
- Replacing every internal admin operation on day one.
- Building a full TUI dashboard.
- Supporting every edge-case interactive flow before scriptable flows are stable.
- Binary name:
cocalc - Primary noun:
project - Keep internal code references to "project" in adapters only.
Examples:
cocalc project list
cocalc project create my-notebook
cocalc project exec my-notebook -- uname -a- API-first, script-first:
- every command supports stable machine output via
--json - deterministic exit codes
- every command supports stable machine output via
- Human mode should remain concise and readable.
- Strongly typed command adapters so TypeScript catches API drift during build.
- Resource targeting should be flexible:
- by id
- by name
- by active context (
use)
- Consistent verb structure:
list,get,create,update,deletestart,stop,restartmove,copy,restore
All commands support:
--jsonmachine-readable output--output table|json|yaml(defaulttablein human mode, using ascii-table3)--quiet--verbose--org <org>or equivalent context selector--profile <name>config profile--api <url>override base API URL--timeout <duration>for blocking operations--waitfor asynchronous operations that can be waited on
- Command parser framework:
commander(required) to avoid ad hoc argument parsing drift. - Human tables:
ascii-table3(required) for compact, stable terminal formatting. - Keep command and output wiring centralized so global options (
--json,--wait,--timeout) behave identically everywhere.
Proposed standardized exit codes:
0success1usage/validation error2auth error3permission denied4not found5conflict/precondition failed6timeout7network/transport error8server/internal error
Config file:
~/.config/cocalc/config.json(Linux/macOS)%APPDATA%/cocalc/config.json(Windows)
Context layers:
- command flags
- env vars
- active profile config
- defaults
Suggested env vars:
COCALC_API_URLCOCALC_TOKENCOCALC_PROFILECOCALC_ORGCOCALC_OUTPUT
Local directory context files:
.cocalc-project(active project id/name for current directory).cocalc-org(optional org affinity)
cocalc auth login [--api <url>] [--org <org>] [--token <token>]
cocalc auth setup --token <token>
cocalc auth status
cocalc auth logout [--all] [--force]
cocalc auth list
cocalc auth use <profile-or-org>Phase strategy:
- Default encrypted-at-rest file token storage using Node crypto (no native deps).
- Optional keyring integration via subprocess wrappers (not native Node addons):
- macOS
security - Linux
secret-toolorpass - Windows
cmdkey/Credential Manager bridge
- macOS
- Explicit command toggles:
cocalc auth keyring enable
cocalc auth keyring disableThis preserves single-file SEA reliability while leaving room for OS-native storage later.
cocalc project create [name] [--host <host>] [--json]
cocalc project list [--prefix <name>] [--state <state>] [--json]
cocalc project get <project>
cocalc project use <project>
cocalc project unuse
cocalc project rename <project> <new-name>
cocalc project delete <project> [--force]
cocalc project start <project> [--wait]
cocalc project stop <project> [--wait]
cocalc project restart <project> [--wait]Notes:
<project>accepts id or name.usewrites local.cocalc-project.
cocalc project exec <project> -- <cmd...>
cocalc project ssh <project> [ssh-args...]
cocalc project console <project>
cocalc project terminal <project>
cocalc project sync up <project> --local <dir> --remote <path> [--watch]
cocalc project sync down <project> --remote <path> --local <dir>
cocalc project sync bidir <project> --local <dir> --remote <path> [--watch]Notes:
project syncis planned to usereflect-syncunder the hood for fast SSH-based incremental sync.
cocalc project file list <project> [path]
cocalc project file cat <project> <path>
cocalc project file put <project> <src> <dest>
cocalc project file get <project> <path> <local-dest>
cocalc project file rm <project> <path>
cocalc project file rg <project> <pattern> [path] [-- <rg-args...>]
cocalc project file fd <project> [pattern] [path] [-- <fd-args...>]Notes:
- Project file operations should work even when the project is stopped, whenever possible, by routing through project-host file services instead of requiring runtime startup.
rg/fdsupport is explicitly included for AI-agent productivity.
cocalc project move <project> --host <host> [--wait]
cocalc project copy-path \
--src-project <project> --src <path> \
--dest-project <project> --dest <path> [--wait]
cocalc project placement <project>cocalc project snapshot create <project>
cocalc project snapshot list <project>
cocalc project snapshot info <project> <snapshot>
cocalc project snapshot delete <project> <snapshot>
cocalc project snapshot restore-path <project> --snapshot <id> --path <src> [--dest <dest>] [--wait]
cocalc project snapshot restore-all <project> --snapshot <id> [--wait]
cocalc project backup create <project> [--wait]
cocalc project backup list <project>
cocalc project backup files <project> --backup <id> [path]
cocalc project backup restore-path <project> --backup <id> --path <src> [--dest <dest>] [--wait]
cocalc project backup restore-all <project> --backup <id> [--wait]Notes:
snapshotis the canonical CLI term for CoCalc btrfs snapshots (faster than backup/checkpoint systems that scale with data size).backupis for rustic/off-host durability and is slower.- Current backend capability is path restore;
restore-allis intentionally included in the interface plan as a target capability to implement. - Optional compatibility aliases can be provided later:
checkpoint->snapshot.
cocalc project proxy url <project> --port <port> [--host <host>] [--open]
cocalc project proxy token issue <project> --host <host> [--ttl <seconds>]
cocalc project proxy curl <project> --port <port> [--host <host>] [--path <path>]Rationale:
- This aligns with your smoke-test need to verify denied access without token and allowed access with token.
- It can compose existing host-connection and token issuance APIs.
cocalc host list [--all] [--json]
cocalc host get <host>
cocalc host create <name> [--region ...] [--size ...]
cocalc host start <host> [--wait]
cocalc host stop <host> [--wait]
cocalc host restart <host> [--wait]
cocalc host deprovision <host> [--wait] [--force]
cocalc host software upgrade <host> [--target all|project-host|project]
cocalc host connector upgrade <host> [--version <v>]
cocalc host logs <host> [--follow]
cocalc host resolve-connection <host>
cocalc host issue-http-token --host <host> [--project <project>] [--ttl <seconds>]cocalc op list [--scope project|host] [--id <id>]
cocalc op get <op-id>
cocalc op wait <op-id> [--timeout 5m]
cocalc op cancel <op-id>cocalc doctor
cocalc config get [key]
cocalc config set <key> <value>
cocalc config list
cocalc versioncocalc browser session list
cocalc browser use <session-id>
cocalc browser files
cocalc browser open <project> <path...>
cocalc browser close <project> <path...>
cocalc browser exec-api
cocalc browser exec <project> [code...]
cocalc browser exec <project> --file <script.js>
cocalc browser exec <project> --stdin
cocalc browser exec --async --wait ...
cocalc browser exec-get <exec-id>
cocalc browser exec-wait <exec-id>
cocalc browser exec-cancel <exec-id>Notes:
- This is for agent/human collaboration workflows where a turn is associated with a browser session id.
browser execshould run in a constrained frontend API sandbox (e.g., restricted Redux helpers with approval boundaries).- The API should be expanded systematically by capability domain so agents can compose reliable automations.
Goal:
- Make browser automation powerful enough that a user can ask for real end-to-end help ("create notebook, run, explain, notify me"), and an agent can do it with deterministic scriptable primitives.
Design rule:
- Prefer a small number of composable primitives over many one-off commands.
- Every state-changing primitive should support approval hooks and clear audit events.
- Return data should be stable, typed, and easy for LLMs to consume.
- Design for multiple CoCalc products/environments, not Launchpad-only behavior.
The browser exec architecture must be mode-aware:
launchpadmode:- rich host-backed functionality
- timetravel providers:
patchflow,snapshots,backups,git
litemode (cocalc-plus):- no host backup/snapshot infrastructure
- timetravel providers: typically
patchflow, optionallygit
Agent-facing implication:
- Never hardcode provider assumptions in scripts.
- Scripts should discover capabilities first, then branch.
Required primitives:
api.system.getCapabilities():{ product_mode: "launchpad" | "lite" | "unknown", features: {...} }
api.timetravel.listProviders(path):- authoritative provider list for a file in the current environment
api.exec.getApiVersion():- compatibility/version negotiation for script portability
CLI implication:
cocalc browser exec-apishould include:- API version
- product-mode notes
- capability discovery snippet
api.session
getInfo()browser/session/project context metadatalistOpenProjects()listOpenFiles({ projectId? })focusProject(projectId)focusFile(projectId, path)
api.files
list({ path, depth?, hidden? })readText(path, { start?, end? })writeText(path, content, { create?, overwrite? })patchText(path, edits)mkdir(path, { parents? })remove(path, { recursive?, trash? })move(src, dest, { overwrite? })copy(src, dest, { overwrite? })
api.fs (node-compatible filesystem API + safe power tools)
- Expose the existing async node-style filesystem surface from src/packages/conat/files/fs.ts as directly as possible.
- This gives agents a familiar API from training data, including binary-safe reads/writes.
- Key methods:
readFile(path, encoding?)writeFile(path, data)readdir(path, opts?)stat(path),lstat(path),exists(path)mkdir,rm,rename,copyFile,cp,movewatch,find,fd,ripgrep,dust
- Important behavior:
- Works even when project runtime is not running (through file service backend).
- Supports
Bufferpayloads for binary workflows. - Includes resource-limited, argument-whitelisted command wrappers for safety.
- Return normalization guidance:
- Keep raw
stdout/stderrbuffers available. - Provide optional helper decoding for agent ergonomics (
utf8, JSON lines parsing).
- Keep raw
api.editor
open(paths, opts)close(paths)getSelection(path?)setSelection(path, range)reveal(path, { line, column })save(path?)saveAll()
api.notebook
listCells(path)getCells(path, { ids?, includeOutputs? })setCells(path, updates)insertCells(path, inserts)deleteCells(path, ids)moveCells(path, moves)runCells(path, ids?)runAll(path)interruptKernel(path)restartKernel(path, { runAll? })getKernelStatus(path)
api.timetravel (unified history API across providers)
listProviders(path)->["patchflow", "snapshots", "backups", "git"]subset per filelistVersions(path, opts)whereoptsincludes:provider?: "patchflow" | "snapshots" | "backups" | "git"from_ms?,to_ms?limit?,order?query?(provider-specific search text / metadata filter)
getVersion(path, { provider, version_id })getVersionText(path, { provider, version_id })diffVersions(path, { from, to, provider? })restoreVersion(path, { provider, version_id, dest_path?, mode? })search(path, opts)unified search over one or more providerssummarize(path, opts)compact activity summary over a time range
Provider helpers for agent ergonomics:
api.timetravel.patchflow.*api.timetravel.snapshots.*api.timetravel.backups.*api.timetravel.git.*
Notes:
- Keep one top-level
api.timetravelnamespace for composability. - Expose provider-specific helpers underneath it, rather than four disconnected top-level APIs.
- This matches how users think ("history of this file"), while still allowing explicit source control.
api.bash (project command execution with async/streaming control)
- Expose a focused bash execution API in browser exec for composability inside larger scripts.
- Back it directly by the existing execute-code contract in src/packages/util/types/execute-code.ts.
- Core methods:
run(opts)blocking execution (returns stdout/stderr/exit_code)start(opts)async execution (returns job metadata quickly)get(jobId, opts?)poll job status/outputawait(jobId, opts?)wait until completionkill(jobId)terminate running jobstream(jobId, onEvent)optional realtime events (stdout,stderr,stats,done,error)
- Key options (aligned with existing execute-code options):
command,args?,bash?,cwd?,path?env?,timeout?,max_output?err_on_exit?,ulimit_timeout?,filesystem?
- Why this belongs in browser exec API:
- agents can call bash in the middle of a larger browser-side workflow without extra RPC round trips
- enables one-shot workflows like search -> transform -> open -> notify in a single script
- leverages tooling agents are extremely strong at
api.terminal (high-value, carefully gated)
listSessions()openSession(opts)exec(command, opts)non-interactive conveniencewrite(sessionId, input)read(sessionId, { maxBytes? })interrupt(sessionId)close(sessionId)
api.chatroom
listThreads(path)createThread(path, opts)pinThread(path, threadId)listMessages(path, opts)postMessage(path, opts)deleteMessages(path, opts)destructive + approval
api.ui
notify.show/info/success/warning/error(...)(already started)confirm(prompt, opts)explicit user prompt/approvalmodal(opts)rich interaction surfacestatus(text)transient progress indicatorcopyToClipboard(text)
api.search
rg(pattern, opts)scoped ripgrep abstractionfindFiles(glob, opts)findSymbols(query, opts)where available
api.project
start(projectId?)stop(projectId?)restart(projectId?)setTitle(projectId, title)openInNewTab(projectId, path?)
"Summarize files I have open"
- Needs
api.session.listOpenFiles+api.files.readText.
"Open notebooks mentioning elliptic curves, newest first"
- Needs
api.fs.ripgrep+api.fs.stat+api.editor.open.
"Open matching notebooks and run all cells so they are ready"
- Needs
api.fs.ripgrep+api.editor.open+api.notebook.runAll.
"Convert notebooks to .py and combine into a library"
- Needs
api.fs.find/fd+ notebook conversion helpers +api.fs.writeFile.
"Run project-level transformations in the middle of a browser script"
- Needs
api.bash.run/start/get/await+api.fs+api.editor/api.notebook.
"Create a notebook to analyze X and run all cells"
- Needs
api.notebook.insert/set/runAll+api.ui.notify.
"Close all markdown files"
- Needs
api.session.listOpenFiles+api.editor.close.
"Fix this project and show me what changed"
- Needs
api.search,api.files,api.terminal.exec(or backend tools),api.ui.modal/notify.
"Notify me when done"
- Needs
api.ui.notify+ long-running exec lifecycle.
"Restore the file a.tex from the last backup"
- Needs
api.timetravel.listVersions({ provider: "backups" })+api.timetravel.restoreVersion.
"Search snapshots of a.ipynb for the last version that ran without errors and copy it to a-working.ipynb"
- Needs
api.timetravel.listVersions({ provider: "snapshots" })+api.timetravel.getVersion+api.notebook.listCells/output checks +api.timetravel.restoreVersion({ dest_path }).
"Summarize activity on a.txt over the last two days"
- Needs
api.timetravel.search+api.timetravel.summarize.
Policy levels:
read: metadata + text readswrite: file/editor/notebook editsexec: terminal command executionbash_exec: project bash command executiondestructive: deletes/resets/kernel restartsui_prompt: user-facing modal/confirm interactions
Mutation safety policy (snapshot-first by default):
- Default in Launchpad:
snapshot_before_mutationfor anywrite,bash_exec, ordestructiveaction. - Optional stricter policy:
snapshot_before_turn(one snapshot at turn start) plus per-mutation checkpoints for high-risk flows. - Optional recovery policy:
snapshot_and_auto_rollback_on_failurefor scripted workflows. - Opt-out policy for advanced users:
no_auto_snapshotwith explicit warning. - For lite mode (no host snapshot provider), degrade gracefully:
requiredsnapshot policy blocks mutation with a clear error.preferredsnapshot policy warns and continues.
Policy hooks:
api.safety.beginTurn({ snapshot: "if_available" | "required" | "none" })api.safety.beforeMutation({ reason, paths? })api.safety.endTurn({ summarize_changes?: boolean })
Execution flow:
- Preflight permission plan from script (or dynamic prompts).
- Per-call approval where policy requires it.
- Every mutating call logged with timestamp, actor, project, args summary.
- Support dry-run for destructive families when feasible.
The typed browser action API now includes:
navigate- fields:
url,replace?,wait_for_url_ms?
- fields:
scroll_by- fields:
dx?,dy?,behavior?
- fields:
scroll_to- selector mode:
selector,block?,inline?,timeout_ms?,poll_ms? - absolute mode:
top?,left?,behavior?
- selector mode:
batch- fields:
actions: BrowserAtomicActionRequest[],continue_on_error? - executes multiple typed steps in one RPC call to reduce latency/races
- fields:
CLI coverage:
cocalc browser action navigate <url> ...cocalc browser action scroll-by <dy> [dx] ...cocalc browser action scroll-to [--selector ... | --top ... --left ...] ...cocalc browser action batch --file <actions.json> ...
Batch file format:
{
"continue_on_error": false,
"actions": [
{ "name": "navigate", "url": "http://localhost:7003/..." },
{ "name": "scroll_to", "selector": ".cell", "block": "center" },
{ "name": "wait_for_selector", "selector": ".plot-container", "state": "visible" },
{ "name": "click", "selector": ".run-button" }
]
}Current practical guidance for notebook/virtualized UIs:
- Prefer
scroll_tobefore interaction when DOM virtualization is present. - For iframe-heavy outputs (e.g., plotly inside iframe), top-document coordinate actions can miss inner targets; this is a known limitation and motivates frame-aware routing.
- Paths are absolute everywhere.
- IDs are stable (
project_id,cell.id,thread.id, etc.). - Internal compatibility note: browser API can map
project_idto backendproject_idin adapters. - Optional output simplification knobs:
- notebook outputs:
raw|summary|text - terminal output: bounded windows
- notebook outputs:
- Return objects should include:
okchangedcountswarnings- deterministic identifiers
- Keep current async LRO-style exec (
start/get/wait/cancel) as the primary control plane. - Add optional event streaming later (
exec-stream) for progress and approvals. - Add script source options (inline/file/stdin) for robust agent invocation.
- Keep
exec-apias canonical discoverability endpoint (TS declaration output).
Phase A (near-term)
- Make
cocalc browser ...work in lite mode by wiring against src/packages/lite/hub (parallel lightweight API surface to server conat API), so browser automation can be used during ongoing development. - Expand
api.session,api.files(read/write text),api.editorsave/focus. (in progress) - Expose
api.fsnode-compatible baseline (readFile/writeFile/readdir/stat/rm/mkdir/rename) plus saferipgrep/find/fd. - Harden
api.notebookwith insert/delete/move + kernel status. (in progress) - Add
api.ui.confirmand richer notify variants. (in progress) - Add
api.timetravelMVP (listProviders,listVersions,getVersionText,restoreVersion) withpatchflow+snapshotsfirst. (in progress)
Phase B
- Add
api.chatroomprimitives. - Add
api.searchprimitives. - Add better notebook output modes and size controls.
- Extend
api.timetravelwithbackups+gitproviders and unifiedsearch/summarize.
Phase C
- Add
api.terminalsafe subset with approval gating. - Add
api.bashwith async job lifecycle + optional stream events. - Add event stream support for long interactive automations.
Phase D
- Advanced UI automation:
- modal forms
- guided workflows
- richer progress/status surfaces.
Purpose:
- Let agents deliver repeatable, user-scoped frontend functionality by shipping extension bundles, not one-off ad hoc scripts.
- Reuse CoCalc's runtime editor registration model in a controlled, capability-gated way.
High-level model:
- Agent builds extension bundle (single JS file + manifest).
- Agent installs extension via browser API (
api.extensions.install). - Extension activates in current browser session and can register editors/panels/actions.
- User can open extension-backed files (e.g.,
browser.gitview) or panels.
TypeScript-style manifest:
export type ExtensionCapability =
| "read_files"
| "write_files"
| "read_timetravel"
| "restore_timetravel"
| "read_git"
| "exec_terminal"
| "bash_exec"
| "ui_notify"
| "ui_modal"
| "network_fetch";
export type ExtensionManifest = {
id: string; // e.g. "com.cocalc.gitview"
name: string;
version: string; // semver
description?: string;
entry: string; // entry module symbol/path in bundle
capabilities: ExtensionCapability[];
compatibility?: {
api_min?: string;
api_max?: string;
product_modes?: ("launchpad" | "lite")[];
};
};Browser API surface:
api.extensions = {
list(): Promise<ExtensionSummary[]>;
get(id: string): Promise<ExtensionDetails>;
install(opts: {
manifest: ExtensionManifest;
bundle_js: string;
replace?: boolean;
scope?: "session" | "user" | "project";
}): Promise<{ ok: true; id: string; version: string }>;
enable(id: string): Promise<{ ok: true }>;
disable(id: string): Promise<{ ok: true }>;
uninstall(id: string): Promise<{ ok: true }>;
invoke(id: string, command: string, args?: unknown): Promise<unknown>;
};Extension runtime context:
export type ExtensionContext = {
extension: { id: string; version: string };
api: {
session: ...;
files: ...;
notebook: ...;
timetravel: ...;
ui: ...;
};
registerEditor(spec: {
id: string;
name: string;
file_extensions: string[]; // e.g. [".gitview"]
can_open?: (path: string) => boolean;
open: (path: string, opts?: unknown) => Promise<EditorHandle>;
}): () => void;
registerPanel(spec: {
id: string;
name: string;
open: (opts?: unknown) => Promise<PanelHandle>;
}): () => void;
registerAction(spec: {
id: string;
label: string;
run: (args?: unknown) => Promise<unknown>;
}): () => void;
onDispose(fn: () => void): void;
};
export async function activate(ctx: ExtensionContext): Promise<void>;
export async function deactivate?(): Promise<void>;Editor registration behavior:
- Extension editors are user-scoped and hot-loadable.
- Registration is reversible (returns disposer).
- Opening a matching file extension dispatches to extension editor implementation.
- Extensions must render via approved UI host primitives (no unrestricted DOM ownership).
Capability + approval policy:
- Install requires explicit approval when capabilities include
write_files,exec_terminal,bash_exec,network_fetch, or destructive history restore. - Runtime calls are capability-checked per API method.
- All extension-originated mutating actions are audit logged with extension id + version.
Persistence and sharing:
- Scope options:
session: current browser session onlyuser: persists for user accountproject: stored in project config (shareable with collaborators)
- Initial implementation can support
sessionfirst, thenuser, thenproject.
Suggested CLI surface:
cocalc browser ext list
cocalc browser ext install --manifest ./ext.json --bundle ./ext.js [--scope session|user|project]
cocalc browser ext enable <id>
cocalc browser ext disable <id>
cocalc browser ext uninstall <id>
cocalc browser ext invoke <id> <command> [--arg-json ...]Target example:
com.cocalc.gitviewextension registers.gitvieweditor.- Opening
browser.gitviewshows recent commits/diffs for current repo. - Agent iterates quickly by replacing bundle version via
install --replace.
Mode compatibility guidance:
- Extensions should branch on
api.system.getCapabilities()+api.timetravel.listProviders(path). - In lite mode, hide snapshot/backup UI paths automatically when those providers are unavailable.
Success metrics:
- Median user request can be resolved with <=2 browser exec calls.
-
90% of common notebook/file/chat tasks handled without bespoke backend changes.
- Low timeout/cancellation failure rates on async exec operations.
cocalc project jupyter --path <ipynb> run <cell-id...>
cocalc project jupyter --path <ipynb> add --after <cell-id> [--type code|markdown] [--source <text>]
cocalc project jupyter --path <ipynb> delete <cell-id...>
cocalc project jupyter --path <ipynb> move <cell-id> --before <cell-id>
cocalc project jupyter --path <ipynb> list-cellsNotes:
- Cell-id-addressable operations are preferred for deterministic agent workflows.
- Follow-up can add execution result streaming and kernel state inspection.
cocalc project chatroom --path <chat-path> thread list
cocalc project chatroom --path <chat-path> thread create --title <title> [--type codex|general]
cocalc project chatroom --path <chat-path> thread pin <thread-title-or-id>
cocalc project chatroom --path <chat-path> thread unpin <thread-title-or-id>
cocalc project chatroom --path <chat-path> message list [--thread <thread>] [--limit <n>]
cocalc project chatroom --path <chat-path> message delete --older-than 7d [--thread <thread>] [--yes]Notes:
- The command surface should support common automation flows such as:
- pinning a thread (e.g., "Todo list")
- creating a new Codex thread
- deleting messages older than a retention threshold
- Destructive operations (e.g., message deletion) should require explicit confirmation flags (
--yes) and support dry-run previews where possible. - Chatroom APIs should be exposed in a way that supports both human workflows and agent automation.
cocalc plus [args...]
cocalc launchpad [args...]Behavior:
- If
cocalc-plus/cocalc-launchpadis not installed, prompt and install it via the corresponding installer. - Then forward all remaining arguments to the installed binary.
- Provide non-interactive flags for automation:
--install-if-missing--yes--channel latest|stable|...
Rationale:
- A user can install only
cocalcfirst, discover additional products viacocalc --help, and immediately launch UI products with minimal friction. - This improves first-run conversion from CLI users to Plus/Launchpad usage.
High-level parity mapping:
sprite create->cocalc project createsprite list->cocalc project listsprite use->cocalc project usesprite exec->cocalc project execsprite console->cocalc project consolesprite proxy/sprite url->cocalc project proxy ...sprite checkpoint ...->cocalc project snapshot ...sprite restore->cocalc project backup restore-path/snapshot restore-path(withrestore-allplanned)sprite org/auth->cocalc auth ...andcocalc org ...(if needed)
CoCalc-specific extension:
- Explicit first-class
hostcommand tree - Explicit project placement/move/copy workflows across hosts
- Noun-first:
cocalc <noun> <verb> ... - Use
projectconsistently. - Avoid ambiguous synonyms at launch.
- Resource identifiers:
- accept id or exact name
- require explicit
--idin scripts when ambiguity exists
--json success envelope:
{
"ok": true,
"command": "project start",
"data": {"project_id": "...", "op_id": "...", "status": "..."},
"meta": {"api": "...", "org": "...", "duration_ms": 1234}
}--json error envelope:
{
"ok": false,
"command": "project start",
"error": {
"code": "permission_denied",
"message": "...",
"details": {}
},
"meta": {"request_id": "..."}
}Proposed package structure:
src/packages/cli/
package.json
tsconfig.json
src/
bin/cocalc.ts
command-registry.ts
core/
api-client.ts
context.ts
config.ts
output.ts
errors.ts
wait.ts
commands/
auth.ts
project.ts
project-files.ts
project-snapshots.ts
project-backups.ts
host.ts
op.ts
browser.ts
jupyter.ts
chatroom.ts
config.ts
doctor.ts
sea/
build-static.sh
build-bundle.sh
build-sea.sh
Build approach:
- Follow
src/packages/plusSEA model for consistency. - Use static bundling (
nccor esbuild) to one JS entrypoint. - Ensure all dynamic imports and file references are SEA-safe.
Dependency constraints:
- allowed: pure JS packages (
commander,ascii-table3,picocolors,yaml) - avoid: native addons (
node-pty,keytar,sqlite3, etc.)
- CLI command adapters must import and use typed request/response contracts from Conat/hub APIs where available.
- No untyped stringly-typed JSON plumbing in command handlers unless explicitly wrapped and validated.
- Add compile-time checks in CLI package build to fail on API signature drift.
- Add a small
contractslayer in src/packages/cli to isolate naming translation (projectCLI <->projectbackend).
- CLI should use a thin adapter layer over existing Conat hub APIs.
- Keep protocol-specific details out of command handlers.
- Add minimal new server endpoints only when a workflow cannot be composed safely.
For your proxy smoke use-case, existing APIs appear sufficient:
- resolve host connection URL
- issue project-host HTTP auth token
- compose
/{project_id}/proxy/{port}/
Implement only these first:
cocalc project createcocalc project start --waitcocalc project execcocalc project sshcocalc project move --host --waitcocalc project copy-path --waitcocalc project snapshot createcocalc project snapshot listcocalc host resolve-connectioncocalc host issue-http-tokencocalc project proxy urlcocalc project proxy curl
This subset is enough to cleanly express the current smoke flows:
- two-host move verification
- cross-project copy verification
- HTTP proxy deny/allow checks with token bootstrap
- SSH and exec checks via one CLI surface
- snapshot smoke checks with fast btrfs semantics
WS1=$(cocalc project create smoke-a --json | jq -r '.data.project_id')
WS2=$(cocalc project create smoke-b --json | jq -r '.data.project_id')
cocalc project start "$WS1" --wait
cocalc project start "$WS2" --wait
cocalc project exec "$WS1" -- bash -lc 'mkdir -p smoke && echo hello > smoke/a.txt'
cocalc project move "$WS1" --host "$HOST2" --wait
cocalc project copy-path \
--src-project "$WS1" --src smoke/a.txt \
--dest-project "$WS2" --dest smoke/copied.txt --wait
cocalc project exec "$WS2" -- cat smoke/copied.txt
URL=$(cocalc project proxy url "$WS1" --port 8000 --json | jq -r '.data.url')
TOKEN=$(cocalc host issue-http-token --host "$HOST2" --project "$WS1" --json | jq -r '.data.token')
cocalc project proxy curl "$WS1" --port 8000 --expect denied
cocalc project proxy curl "$WS1" --port 8000 --token "$TOKEN" --expect okPhase 0 (smoke-only):
- Implement MVP commands above.
- Lock JSON schemas and exit codes.
- Add CI smoke scripts using CLI.
Phase 1 (general operator usability):
- add list/get/status/stop/delete command completeness
- add full snapshot/backup command completeness (including restore-all when backend support lands)
- add
op waitand streaming progress
Phase 2 (polish and broader parity):
- profile/org UX polish
- optional keyring backend
- project sync commands backed by
reflect-sync - browser session, Jupyter, and chatroom command families
- autocompletion (bash/zsh/fish)
- improved table output and paging
- Risk: command naming drift between code (
project) and UX (project)- Mitigation: adapter layer and strict naming tests on CLI help output.
- Risk: SEA bundle regressions from dynamic module patterns
- Mitigation: single static entry, minimal dependencies, SEA integration tests.
- Risk: flaky wait logic around LRO status races
- Mitigation: support
--waitwith postcondition verification where needed.
- Mitigation: support
Proceed with src/packages/cli and implement Phase 0 only first.
This gives immediate value for smoke tests while preserving a coherent long-term command model aligned with Sprites-style workflows and CoCalc’s host/project architecture.
Status (implemented first slice):
browser exec --posture prodnow defaults to a QuickJS-WASM sandbox whenpolicy.allow_raw_execis not set totrue.- Raw
new Function(...)evaluation remains available only when explicitly opted in viaallow_raw_exec=true.
Sandbox execution model:
- Agent script runs inside QuickJS (isolated from page JS globals).
- A constrained bridge exposes
globalThis.apihelpers:api.action(name, payload)api.navigate(...),api.click(...),api.clickAt(...),api.drag(...)api.type(...),api.press(...)api.scrollBy(...),api.scrollTo(...)api.waitForSelector(...),api.waitForUrl(...)
- Script emits planned actions; host executes them sequentially using existing typed action machinery and policy checks.
Important constraints of this first slice:
- Runtime uses the Asyncify-capable QuickJS variant
(
@jitl/quickjs-wasmfile-release-asyncifyviaquickjs-emscripten-core). - Sandbox
api.*calls execute immediately through policy-gated host actions and return structured results, so scripts can compose multi-step logic. - Determinism and safety still come from typed action allowlists, per-action policy checks, and strict action-count / payload-size caps.
Security posture:
- In prod posture, this gives a safer default than raw page-context JS.
- Policy checks still apply per action (project/origin/action allowlists).
- Additional approvals/guards for destructive actions are still required for full production hardening.