Skip to main content

crewship craft

This experimental human CLI uses the separate native profile. The server flag is off by default. Existing documents are never converted. Agent mandates, proposals are separate surfaces. Whole-document actions operate only on an existing explicit native Page and its qualified startup profile.
preview reads only GET /api/v1/pages/{slug}/craft/preview under ordinary human authentication, without opening a native admission or acquiring a lease. A missing shared version or redirect is an error; it never falls back to private latest bytes. availability reads GET /api/v1/pages/{slug}/craft/availability. connect creates one session with POST /api/v1/pages/{slug}/craft/sessions, prints only its public session_id, then reads one JSON action per stdin line. The private session token stays in process memory and is attached as X-Craft-Session. Responses default to escaped NDJSON. With -f yaml, each response is a YAML document separated by ---; stdin actions remain NDJSON. Availability honors the same output format flag. Authentication and workspace selection use the normal CLI context. A refusal prints the server’s error text with the private session token removed, and exits with the usual classified code: 5 for a 409, 2 for 400/422, 7 for a 5xx, 8 when the request never got a response. Under --format json the error envelope carries the status and the redacted body under error.extensions. A 409 on a lease action (acquire, renew, release, request-handoff, handoff) is followed by one status read so the message names the current holder, for example (lease held by human "Jana", generation 12); the lease document is added to error.extensions.lease. A refusal whose body carries a proposal adds its state, so a stale proposal reads (proposal state: stale) while an already decided one keeps the server’s “already decided” text. The native agent task commands keep their status-only messages (native HTTP 503), because their refusal bodies can describe a private runtime, but exit with the same codes. A Read grantee can open connect, but only status, watch and reauthorize answer: they report who holds the lease, never content. read, versions, proposals and proposal need edit rights or the workspace owner or admin read rule and otherwise answer 403 (exit code 4). Comment alone cannot open connect. Read the shared version with crewship craft preview <page>. share needs the Page’s owner. Edit rights and the workspace owner or admin role are not enough: they answer 403 (exit code 4) with only the page owner may share a version, even while holding the lease. The lease row’s can_share says whether this connection may share. | accept-proposal | POST /api/v1/pages/{slug}/craft/proposals/{proposal_id}/accept | | reject-proposal | POST /api/v1/pages/{slug}/craft/proposals/{proposal_id}/reject |

Version history

crewship craft versions <page> opens one native admission (POST /api/v1/pages/{slug}/craft/sessions), reads GET /api/v1/pages/{slug}/craft/document/versions under it and exits. It never acquires the lease and never writes; the private session token is not printed. Inside connect, the versions action reads the same listing on the open connection. The listing needs the same rights as read: edit rights, or the workspace owner or admin read rule. A Read or Comment grantee or a non-member gets the same refusal as read (403, exit code 4); crewship craft preview <page> is how a Read or Comment grantee reads the document. While the workspace feature flag craft_version_history is off (the default), the route answers 404 (exit code 3) to anyone who has those rights.
Versions are newest first; current marks the head. At most 200 entries are returned; truncated: true means older versions exist and remain restorable. Entries carry no document bytes.

Read one saved version

crewship craft version <page> <version> opens the same read-only admission, reads GET /api/v1/pages/{slug}/craft/document/versions/{version} and exits. It returns that version’s complete bytes (base64 in bytes), its time and author, whether it is the current version, and the editor profile needed to open it read-only. Use it to preview or diagnose an old version without touching the document: it never acquires the lease, never writes and never changes the shared version. With --output FILE the decoded document is written to FILE (mode 0600; - writes it to stdout) and only the metadata is printed. The version must be a canonical positive decimal integer within the signed 64-bit range. A non-positive, non-canonical or overflowing version returns validation exit code 2 before authentication, admission or any HTTP request; machine-readable errors also carry error.exit_code: 2. For a negative positional value, place it after -- (for example, crewship craft version @PAGE_ID -- -1) so it is checked as a version rather than parsed as a flag.
It needs the same rights as read and the same craft_version_history flag as the listing. A Read or Comment grantee or a non-member gets 403 (exit code 4) for every version, the shared one included; a missing version, or the flag off, gets 404 (exit code 3), and only someone who may read learns that a version does not exist. A workspace owner or admin reading another member’s document is audited exactly like read (artifact.private_owner_accessed or artifact.workspace_admin_accessed); if the audit entry cannot be written, nothing is returned. The listing is audited the same way. To restore, use the restore action inside connect while holding the lease. It is the fenced write a save uses (current generation, baseVersion equal to the latest version) and appends baseVersion + 1 with the target’s content; nothing is deleted:
A stale generation, a moved-on baseVersion or a missing lease answers 409 (exit code 5); without edit rights the answer is 403. For the lease holder, a targetVersion that does not exist answers 404 (exit code 3).

Agent proposals

When a person holds the lease, an agent task does not wait or steal it: its craft_propose tool records a complete candidate over the exact version it read and leaves the document unchanged. proposals lists them (author, base version, summary, state); proposal returns the candidate bytes for comparison. accept-proposal saves the candidate as this connection’s own write: it needs the current generation of this connection’s lease and re-checks Page rights, the lease and that the document is still at the proposal’s base version, all in one transaction. If the document changed meanwhile, the proposal becomes stale and nothing is applied. reject-proposal needs write rights but no lease. Decisions are final. Use the actual generation returned by the server; it is a canonical positive int64 decimal string. Handoff targets an opaque server-issued request ID, never an actor supplied by the CLI. Unknown fields, including a different Page or session, are rejected. Lease/control action lines are limited to 16 KiB. Document mutation lines use {"action":"save","snapshot":{...}} (or import/restore/reconcile) with the exact whole-document binding; the decoded native bytes are bounded to 32 MiB and the encoded NDJSON line to45MiB. A document read response may also use 45 MiB; other responses and SSE events remain bounded to 64 KiB. A connection accepts at most 4096 actions. Native bytes are base64, never HTML exports. The snapshot public session ID must match this connection; private admission tokens never belong in the snapshot. No automatic mutation retry occurs after a conflict or unknown ACK.

Confirm a save after a lost reply

Give each save a fresh attemptId in its snapshot: 8 to 64 of [A-Za-z0-9_-], chosen by you. import takes it too; share and restore refuse it. If the reply is lost but the connect process is still running, send reconcile on that same connection with the original snapshot fields, without bytes, and the same attemptId: Reconcile is bound to the original admission. The server compares the session in the snapshot with the session that committed, and the admission token lives only in the connect process, so another process can never confirm the save.

Recover after losing the connect process

There is no reconcile from a new process. The original snapshot is refused before any request with invalid native snapshot binding (exit 1), because its sessionId is not the new connection’s. With sessionId rewritten, the server answers 409 (exit 5) even when the save committed. A 410 from a different session says nothing about the old attempt either: it answers for that session’s own attempts, and the old save may still have committed. Do not use reconcile here. Check the history instead:
  1. Keep the local draft and note the baseVersion of the lost save.
  2. Open a new connect and send acquire. The old lease lapses about 15 seconds after its process stops, and acquire is refused with 409 (exit 5) until then, which ends that connection, so start a new one and try again. The new generation fences the old session: its save can no longer publish.
  3. Send read and compare version with the lost baseVersion.
  4. If version still equals baseVersion, nothing committed. Save again with a fresh editorRevision and attemptId.
  5. If version is higher, inspect baseVersion + 1 with crewship craft versions <page> and crewship craft version <page> <version> --output candidate.json. Without the craft_version_history flag these answer 404 (exit 3); read then shows only the head.
  6. If the sha256 of candidate.json equals your contentHash and the author is you, the save committed: do not save again. Otherwise someone else wrote that version: merge your draft onto the head and save at the current version.
watch consumes the server’s live SSE stream until cancellation or stream end; it does not process later stdin actions. Only a valid session_retired event is a confirmed clean end; unexpected EOF reports an unconfirmed disconnect. The server heartbeat renews the current holder while connected. Other actions have the normal CLI request timeout. A failed mutation stops processing without an automatic retry or reacquisition. EOF does not save content or release/reacquire a lease; send an explicit release when wanted. Reauthorization uses the current CLI credential and cannot revive an expired admission or replace its original authority.

Initialize one owned Word or Sheet Page

crewship craft initialize <page> [--application word|sheet] --file <native.json> calls POST /api/v1/pages/{slug}/craft/initialize with native bytes (at most 32 MiB). --application defaults to word; any other value than word or sheet exits 2 before a request. The Page must already exist, be empty, and be personally owned by the current human with pages:write. The server chooses all qualified pins and atomically creates its initial native version and lease. This does not convert legacy content or create a Page. The command retains the issued private admission in its process and prints only public session, Page, application, generation and version strings. Subsequent NDJSON actions use that admission exactly as connect; there is no implicit second session or lease acquisition. EOF does not release or commit content. Use an explicit release action when required. --file - consumes stdin as raw native bytes and ends without subsequent NDJSON actions.

crewship craft task

These human commands require the separate default-off native agent flag and current Page write rights. They use normal CLI authentication and workspace selection; they do not create an editor session or borrow an agent identity.

crewship craft task instruction

Opens a new private chat with the agent and saves the instruction there without an agent turn, the same way the editor’s “Upravit agentem” does. The text reaches no ordinary run, agent log or journal entry, and a later message in the chat cannot repeat it. Prints chat_id and message_id for task start. The command fails if the server does not confirm the save.

crewship craft task start

--chat-id selects the registered agent chat, --message-id selects your own instruction saved by craft task instruction and --whole-document supplies explicit consent for this one task. All three are required; consent defaults to false and --whole-document=false is refused before any request. An ordinary chat message or a group chat message answers 403. Acceptance means queued, not executed or saved. Start never automatically retries or follows redirects. The agent’s crew must be on the enabled agent artifact policy; otherwise start answers 403 and nothing is queued. Removing the crew or disabling the policy stops a running task at its next step: any agent tool call (including reads), dispatch or confirmation.

crewship craft task status

Status reads the actual bounded host timeline and the last confirmed receipt. That receipt is historical; an uncertain or in-flight later attempt is not Saved.

crewship craft task stop

Stop withdraws your current agent mandate for that Page and requests process termination. The response distinguishes revoked custody from a process whose stop is still pending. It cannot force a human holder and does not replay a failed mutation. Existing document content and versions remain available.

Publish a native preview from the current connection

Use the share NDJSON action inside crewship craft connect <page> after acquiring the current lease and confirming the saved native version. Its snapshot contains only workspaceId, pageId, applicationId, enginePin, public sessionId, generation, baseVersion and targetVersion. Versions and generations are canonical positive decimal strings. Do not include native bytes, editorRevision or contentHash: publishing selects an existing committed version and cannot save a pending buffer. The server checks current edit rights, lease generation, latest-version CAS and an atomic publication audit.
The opaque admission bearer stays in the existing connect process and its HTTP header. It is never an argument or printed response field. Publication is sent once; a refusal, redirect or lost response never triggers a replay. The phase: "shared" receipt confirms only publication of that version, not an unsaved native buffer.