Skip to main content
This page is a task guide for three experimental command groups that edit artifacts from a terminal:
  • crewship page design edits a Design artifact (a canvas of frames, shapes, text and images) and manages its versions, shared version and comments.
  • crewship craft opens a human edit session on a native Craft document (Word), takes the lease, reads and saves whole-document versions and starts agent tasks.
  • crewship mooring connect exchanges the original protocol frames of a Mooring collaboration session over a WebSocket.
Design commands sit under the page root for historical reasons; the objects they create are Artifacts and appear in the Artifacts library when the web library is enabled. Per-flag details for every subcommand stay in crewship page, crewship craft and crewship mooring. All examples use a human CLI login (crewship login) and the workspace selected with --workspace or CREWSHIP_WORKSPACE. Outputs were recorded against a test workspace and shortened; IDs are replaced with placeholders.

Before you start

Everything on this page is off by default. Check the current workspace flags with crewship feature-flag list. Instance switches are described in Environment variables.

Output and exit codes

page design commands always print the server’s JSON object; the default format, -f json and -f table give the same JSON, and -f yaml prints YAML. craft commands print one JSON line per response (NDJSON) or YAML documents separated by --- with -f yaml; craft availability prints a single object. page design watch and mooring connect refuse -f quiet; craft has no quiet mode and prints NDJSON under -f quiet, including the watch stream. Errors go to stderr. With the default format they are one line of text; with -f json, -f ndjson or -f yaml they use the standard CLI error envelope. For page design the envelope carries the HTTP status and server text, and the exit code follows the status:
Craft HTTP refusals use the standard classified codes: 400/422 → 2, 401/403 → 4, 404 → 3, 409 → 5, 429 → 6, and 5xx → 7. A connection failure before a response uses 8. Human native operations include the HTTP status and server error body in the envelope, with the private session token removed. Lease and proposal refusals can add context; see Craft errors. Native agent task commands keep status-only messages because their refusal bodies can describe a private runtime. Their envelope carries only message and the classified exit_code, for example:
Use the classified exit code when branching in scripts. craft version <page> <version> requires a canonical positive decimal integer within the signed 64-bit range. Invalid versions (such as 0, 01, +1, a negative value or an overflowing number) exit with validation code 2 before authentication or any HTTP request; the error envelope also carries exit_code: 2. Pass a negative positional value after --, for example crewship craft version @<artifact-id> -- -1, so it is checked as a version instead of parsed as a flag. The local page design create check for a missing --name and the page design restore/share checks for an invalid --version still return unclassified code 1 before any request. These specific checks do not define the exit code for other commands’ argument validation.

Design artifacts

Permissions

Create a Design and add content

The slug must be unused in the workspace. A taken slug currently returns 409 Design changed; reload and reapply. Edits are a JSON object with operations (1 to 500) and an optional message. An upsert carries a whole node: id, type (frame, rect, text or image), name, x, y, width, height, fill and optionally parentId, text, src or linkTo. linkTo is the ID of the screen that a click on the layer opens in prototype playback; "" removes it. A text layer also takes the text style fields fontFamily (sans, serif, mono), fontWeight (100 to 900 in steps of 100), fontSize (1 to 1000), lineHeight (0.5 to 5), textAlign (left, center, right), textWrap (none, wrap) and overflow (visible, clip); other values are refused. Any layer also takes hidden and locked (true or false): a hidden layer is not drawn or exported, and a locked one cannot be picked or moved on the canvas. Rectangles and images also take radius (0 to 10000), stroke (#rgb or #rrggbb) and strokeWidth (0 to 1000). Delete uses {"op":"delete","id":"<node-id>"} and reorder uses {"op":"reorder","order":["<id>", …]}. Keep the IDs of existing nodes. The file may be at most 1 MiB; other top-level keys are refused before the request (unknown design field "extra").
hero.json
apply changes the live canvas seen by browsers authorized for live reads, and works even when no browser has it open. Shared-only readers keep seeing their shared snapshot. Changing the shared pin does not push replacement document state to an already-open shared-only socket; read again or reopen. Every successful apply records a new version. Re-applying the same file is not a no-op: upserting the same nodes again still updates the canvas state and records another version. Use --file - to read stdin. An unknown node type or malformed node returns 400 invalid Design update.

Read the canvas

--version 0 (the default) reads the latest canvas for callers with current Edit permission plus both CLI pages:read and pages:write scopes (or pages:*, *, or an unscoped full-role token), and separately authorized workspace OWNER/ADMIN readers. Ordinary Read grantees receive only the shared version, and 404 until one is shared. A positive --version N is limited to that shared pin for shared-only readers. Non-admin Page owners and Edit grantees narrowed to pages:read credentials are shared-only too. Foreign administrator access is audited; a read-scoped administrator credential can read the latest canvas and history but cannot write. With a selected retained version, the version and latest_version fields both report that version; use page design versions for the latest version visible to the caller. can_share is true only for the Page’s owner with write rights, the same rule page design share enforces.

Save and list versions

snapshot (alias save) records the live canvas as a new version even when it is unchanged since the last one, and it has no idempotency key: a retry after a lost reply adds a second version with the same content, so read versions before sending it again. Take latest_version from versions whenever a command below needs the latest version.

Share a version

Only the owner can change the shared version. A version that does not exist returns 404 design not found.

Comment on a node

Use the latest version from page design versions (3 in this walkthrough):
--version must be the latest version, and --node must exist in it; --quote, when given, must equal the node’s name or text. Otherwise the server returns 409 Design changed; reload and reapply. comments returns {"comments":[…]} with up to 500 comments. With --send-to-agent, the comment is queued for the agent connected through page design agent and its status is pending. The agent’s crew must be allowed by the workspace artifact policy. Otherwise delivery is refused with This agent's crew is not allowed to create artifact projects in this workspace. The check applies to Design as well, even though the message names projects. Add --wait to also deliver the task to that connected chat and wait for the agent’s answer, as the editor does. --wait-timeout (default 15m) bounds the wait; the task stays queued when it runs out. Without a connected chat, --wait exits with code 2 and names the page design agent command. See page artifact comment --wait for the full behavior.

Restore a version

restore never rewrites history: it copies the selected version into the live canvas and records it as a new version. Version 1 is the empty canvas from create, so restoring it removes every node from the live canvas (get then returns "nodes": []) and makes it the latest version. Comments on title-1 now fail with 409 because the node no longer exists. To continue, apply hero.json again (this records version 5) and use the new latest version.

Connect an agent chat

The chat must be private, created by you and attached to an agent. Any other chat ID returns 403 current Design edit permission required. get then reports the same origin to the owner.

Watch the permitted view

Each line is one binary Yjs sync or awareness frame, base64-encoded. --count 0 (the default) watches until interrupted. Current Edit permission plus both CLI pages:read and pages:write scopes (or pages:*, *, or an unscoped full-role token), and separately authorized workspace OWNER/ADMIN access permit live document updates. Foreign administrator access is audited. Non-admin Page owners/Edit grantees narrowed to pages:read are shared-only: the shared snapshot and presence, without live document updates. Before a shared pin exists, the socket can upgrade and deliver presence, but its initial sync closes with 1008 Design permissions changed; watch reports that close as an error if it reaches it; --count can finish on presence frames before observing the close. A live agent run reads Design through the design_get tool instead; with CREWSHIP_AGENT_TOKEN set, watch is refused.

Native Craft documents

Native Craft keeps a Word document as whole-document versions. One holder at a time owns the edit lease; saves need the current lease generation.

Check availability

availability never fails for a missing artifact; it reports false values. Run it first. enabled is false when the instance switch is off (the other human commands then report HTTP 503 and the server error text), and also when the artifact is not a native document or you cannot read it (then expect HTTP 403; connect reports API error (403): native Craft access denied).

Read the shared preview

Returns the shared version without opening a session or taking the lease. If no version is shared the command fails; it never falls back to the latest private content. This is the only way a Read or Comment grantee reads a native document: craft version, craft versions and the read action of craft connect need edit rights or the workspace owner or admin read rule, and otherwise answer 403.

Edit with a lease: acquire, read, save, release

craft connect opens one human session, prints its public session_id, then reads one JSON action per stdin line and prints one response per action. The private session token stays in the process, and the session ends when the command exits. Several action fields come from earlier responses, so feed the actions one at a time: start the command in a terminal, type one line, read the response, then type the next line. A script does the same by keeping the command’s stdin open (for example a shell coprocess or a named pipe) and writing each line only after it has read the previous response. A file written in advance works only for actions that need no response values, such as status.
  1. Type {"action":"acquire"}. The lease view comes back: holder, mine, generation, expires_at, can_edit, can_write and any pending handoff_requested_by. Note generation.
  2. Type {"action":"read"}. The response holds version, bytes (the document, base64), applicationId, enginePin, editor bundle fields and quota (usedBytes, limitBytes, warning, retention). Note version, applicationId and enginePin.
  3. Edit the decoded document, then type one save line:
  4. Type {"action":"release","generation":"<generation>"} to give the lease back. End of input does not save or release.
Where each snapshot field comes from: Other actions: status (lease view), acquire-explicit, renew (needs generation), import, restore and reconcile (snapshot actions), share (publish an existing version as the preview), reauthorize and watch (follow the live event stream; later input lines are ignored). See crewship craft for the route of each action and the snapshot fields. Generations and versions are positive decimal strings. Unknown fields are refused, control lines are limited to 16 KiB and one connection accepts at most 4096 actions. A failed action stops the command; nothing is retried automatically.

Hand off the lease

The person who wants the lease sends {"action":"request-handoff"} and gets a request_id. The current holder then sends:
The target is always the server-issued request, never a user named on the command line.

Initialize an empty artifact as Word or Sheet

The artifact must exist, be empty and be owned by you, and you need pages:write. The native file is not a .docx or .xlsx file: it is the editor’s native document, a single JSON object (1 byte to 32 MiB) that the instance’s profile for that application validates; anything else is refused. --application is word (the default, wordcraft) or sheet (gridcraft); any other value exits 2 before a request. The command prints session_id, application_id, page_id, generation and version ("1"), holds the new lease, and then reads actions from stdin one line at a time exactly like connect; use the printed generation, page_id and session_id in them. With --file - the document is read from stdin and no actions follow. An empty file fails before any request.

Start, check and stop an agent task

instruction saves your text in a new private chat without an agent turn and prints the chat_id and message_id for start. start refuses an ordinary chat message or a group chat message with 403, and needs explicit --whole-document consent; without it the command stops with native agent start requires explicit --whole-document=true consent. Success returns {"work_id":"<work-id>","status":"queued"}: queued, not saved. status reads the task’s recorded outcomes. stop revokes the agent’s mandate on the artifact and returns grant_revoked, lease_retired, cancel_requested, stopped and stop_pending. Stop cannot take the lease from a human holder.

Craft errors

Human native operations preserve the HTTP status and server error text after redacting the private session token. A typical refusal is API error (503): native Craft unavailable. The structured error envelope can include status, detail and extensions; lease conflicts can add the current holder, and proposal conflicts can add their state. Exact detail depends on the refused operation. The table describes the HTTP refusal, rather than promising a fixed status-only message: Native agent task refusals deliberately omit their potentially private server body. These commands keep native HTTP <status> with the classified exit code; for example, native HTTP 503 exits with 7. This status-only form is separate from the human operations above. The local initialize --file size check reports native document must contain 1 to32MiB of native bytes when the file is empty or larger than 32 MiB, before native initialization.

Mooring connect

The transport-specific errors listed below remain unclassified exit code 1. Their structured stderr envelope contains only message and exit_code; a refused WebSocket upgrade does not expose its HTTP status or server body. Earlier login, workspace or token-host checks can still return their own classified errors. Do not infer an HTTP status from Mooring connection rejected. crewship mooring connect joins a Mooring collaboration session that crewship mooring session admitted. It needs a human login; an agent token is refused before any request.
Pass the returned connection_ref with --connection. --file holds one original JSON frame per line (- reads stdin); the first is the original session_join with its one-use ticket. Each frame is limited to 256 KiB. Server frames print as JSON lines by default and with -f json or -f ndjson, or as YAML documents with -f yaml. A pending frame is not a saved change, and the command creates no receipts. Keep your original frames to retry them. The transport contract is in Mooring session transport.