Duet CLI
This workspace composes the duet command: local agent work, terminal UI, and workspace/file/sync operations under one login. The npm identity remains @duetso/agent.
Copyright (c) 2026 dzhng, LLC. Licensed under AGPL-3.0-only; bundled dependencies retain their licenses and notices.
External agents use the consumer skill to install and operate the released command from their own computer. The root agent setup owns skill installation.
Install Bun 1.3.14 and Node 22 or newer before installing the public command:
npm install -g @duetso/agent
duet login
The command runs on Bun; npm supplies the external package. duet login saves
one human credential for workspace commands and Mac sync. Use duet workspace use <slug> to choose a default workspace and duet --help for the command surface.
Harness runtime and protocol ownership belong to the private agent workspace. The CLI consumes that owner directly; it owns dispatch and user configuration. Local memory and training remain local unless --workspace explicitly selects a remote operation. A saved default selects remote operation targets and model billing; it never changes execution mode. Explicit file addresses and workspace flags must agree.
Named API operations consume the shared customer bindings.
Their help derives from those bindings. Complete body input comes from a file
or stdin; query and declared header inputs stay separate. JSON results retain
API fields on stdout, while HTTP status and headers are NDJSON metadata on stderr.
Named binary writes stream a local file with --in, or stdin with --in -.
CLI JSON body files also accept the shared
binary request representations
for arbitrary bytes. Declared content type and conditional headers are retained.
Empty conditional/optional reads preserve HTTP metadata without replacing a
local destination. Binary reads use the existing src/file-transfers.ts streaming owner for stdout
or a local destination. With --json, failures retain the complete upstream body
inside apiError; a non-scope refusal keeps its own code. Local file handling and process exit codes remain CLI-owned.
Service calls use named vendor operations rather than an arbitrary Duet REST caller. The API retains route admission, vendor credentials, funding and accounting. Calls preserve repeated query/form values, complete JSON and raw bytes; HTTP status and all returned headers go to stderr. An output file can contain a vendor refusal, so callers must retain the exit status and metadata. Installed help owns the input flags.
Named model operations use the gateway's separate origin and existing billing
workspace. Their complete native bodies, protocol choices and headers are
advertised by command help; the gateway owns admission and exact model IDs.
Native streams require a protocol ending. Media bytes use ordinary local files,
and machine results retain surrounding native metadata. A delivery failure keeps
that result and any successfully saved files on stdout, and exits nonzero.
With --json, stderr includes indexed delivery errors. Generation is never
repeated for recovery.
Detached video receipts and raw status outcomes remain resumable without another
client job system.
The prompt convenience and native commands share SDK media and output owners.
Remote conversations use durable input outcomes from the shared
session contract. Submission writes its recovery request
identity to stderr before HTTP, then returns a terminal result or resumable handle.
A pending result does not cancel work; result/wait only observe that input.
Exit 0 means successful completion, 1 means failed/cancelled, and 2 requires
inspecting the body. Re-wait only on status: "pending". A terminal ask needs
an answer with a new input handle; other terminal outcomes, including sleep
and interrupted, remain final for their handle. Transport closure cannot
establish completion.
duet app credentials manages external runtime keys for an already registered
app using the same saved human login or process-provided VM key. Workspace and
app selections are explicit. Export returns the complete API response on stdout, including the one-time
plaintext key and deployment URLs; HTTP metadata goes to stderr. Capture the
key in memory and pass it through stdin to the host's server-side secret store.
The command never saves exported keys or retries an ambiguous mint. Inspect the
live inventory and revoke unwanted entries before explicitly exporting again.
Rotation uses a second export, host deployment and verification, then revocation
of the prior credential. duet app credentials --help owns the command surface.
The saved human login and default API/workspace live in config.json. Other provider keys remain in provider env files, and connected subscriptions keep their own store. Login approval identifies the person; access follows current workspace memberships. Mac mirroring has its own non-secret machine identity, so removing a Mac does not revoke the login.
Both repository and public builds keep native packages external through
scripts/native-packages.ts, preserving their import-relative assets. Public
packaging also inlines private workspace code and preserves the Pi patch. npm is an external distribution channel. VM commands use the
managed runtime release, including interactive shell invocation.
Use the package scripts for builds, checks and focused disposable-container tests. This CLI remains Bun-native. The root lockfile owns dependencies, and the root repository owns publication and VM release delivery.
Mac sync keeps one computer identity across separate workspace transfer instances, with account removal independent of user login.