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.
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.
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:
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: itscraft_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 eachsave 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:
- Keep the local draft and note the
baseVersionof the lost save. - Open a new
connectand sendacquire. The old lease lapses about 15 seconds after its process stops, andacquireis refused with409(exit5) 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. - Send
readand compareversionwith the lostbaseVersion. - If
versionstill equalsbaseVersion, nothing committed. Save again with a fresheditorRevisionandattemptId. - If
versionis higher, inspectbaseVersion + 1withcrewship craft versions <page>andcrewship craft version <page> <version> --output candidate.json. Without thecraft_version_historyflag these answer404(exit3);readthen shows only the head. - If the sha256 of
candidate.jsonequals yourcontentHashand 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
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
crewship craft task stop
Publish a native preview from the current connection
Use theshare 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.
phase: "shared"
receipt confirms only publication of that version, not an unsaved native buffer.