@robota-sdk/agent-cli 3.0.0-beta.85 → 3.0.0-beta.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -48
- package/README.md +265 -39
- package/bin/{robota.cjs → agent.cjs} +1 -1
- package/dist/node/bin.js +23 -23
- package/dist/node/{bounded-output-Bgx2Cgkx-28em7iHq.js → bounded-output-BdWv9SX3-28em7iHq.js} +1 -1
- package/dist/node/{bounded-output-Bgx2Cgkx-28em7iHq.js.map → bounded-output-BdWv9SX3-28em7iHq.js.map} +1 -1
- package/dist/node/cli-core-6GIkeGqT.js +2911 -0
- package/dist/node/{command-executor-d6DdfUTD-DhFSDHvb.js → command-executor-D_UBZHxh-CVZipoOV.js} +2 -2
- package/dist/node/{command-executor-d6DdfUTD-DhFSDHvb.js.map → command-executor-D_UBZHxh-CVZipoOV.js.map} +1 -1
- package/dist/node/{command-executor-d6DdfUTD-3KEnsTnN.js → command-executor-D_UBZHxh-D7UrdAlN.js} +1 -1
- package/dist/node/headless.js +2 -2
- package/dist/node/index.d.ts +2423 -166
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +605 -581
- package/dist/node/index.js.map +1 -1
- package/dist/node/node-BUERQysz.js +15 -0
- package/dist/node/node-BWNobqRD.js +15 -0
- package/dist/node/node-BWNobqRD.js.map +1 -0
- package/dist/node/{node-CpH9vRQt.js → node-BbADaLn-.js} +1 -1
- package/dist/node/node-C4_FOvnk.js.map +1 -1
- package/dist/node/node-Cjb1v2Jw.js +165 -0
- package/dist/node/node-DENLEmMT.js.map +1 -1
- package/dist/node/node-DGvY0f9e.js +165 -0
- package/dist/node/node-DGvY0f9e.js.map +1 -0
- package/dist/web/assets/index-BSxTg2Py.css +1 -0
- package/dist/web/assets/index-BpCxNRj_.js +309 -0
- package/dist/web/index.html +3 -3
- package/package.json +46 -44
- package/dist/node/cli-core-DjVnz1-_.js +0 -2891
- package/dist/node/node-B48couEE.js +0 -14
- package/dist/node/node-B48couEE.js.map +0 -1
- package/dist/node/node-CgLYctGm.js +0 -67
- package/dist/node/node-CgLYctGm.js.map +0 -1
- package/dist/node/node-DAOCaE6N.js +0 -67
- package/dist/node/node-DlLm_kxn.js +0 -14
- package/dist/web/assets/index-DSLs7oij.js +0 -309
- package/dist/web/assets/index-OqCBtMJ2.css +0 -1
- /package/dist/node/{bounded-output-Bgx2Cgkx-C4ucXwLR.js → bounded-output-BdWv9SX3-C4ucXwLR.js} +0 -0
package/README.md
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
# @robota-sdk/agent-cli
|
|
4
4
|
|
|
5
|
-
`robota` is
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
`robota` is the configurable terminal interface for Robota, assembled from the
|
|
6
|
+
same TypeScript libraries you can embed. It is a coding-capable agent within the environment whose
|
|
7
|
+
direction is [our agents developing and advancing our agents](../../VISION.md). Autonomous
|
|
8
|
+
self-evolution remains an ambition; the behavior below describes the current CLI. It reads your
|
|
9
|
+
project, edits files and runs commands under a permission system you control, and works with
|
|
10
|
+
Anthropic, OpenAI, Gemini, DeepSeek, Qwen and local OpenAI-compatible models.
|
|
9
11
|
|
|
10
12
|
The CLI is assembled from the same packages you can use in your own app: `@robota-sdk/agent-framework`
|
|
11
13
|
for the session, `@robota-sdk/agent-ui-terminal` for the terminal UI, and one package per model
|
|
@@ -15,8 +17,6 @@ provider. To build your own agent rather than use this one, start with the
|
|
|
15
17
|
> **Beta.** Behavior may change before the stable release. Please
|
|
16
18
|
> [report issues](https://github.com/woojubb/robota/issues).
|
|
17
19
|
|
|
18
|
-

|
|
19
|
-
|
|
20
20
|
## Install
|
|
21
21
|
|
|
22
22
|
Requires Node.js 22.12.0 or later.
|
|
@@ -35,7 +35,7 @@ Run `robota` inside a Git repository. It first asks whether to trust the folder:
|
|
|
35
35
|
workspace can load the project's own settings, hooks, skills, plugins and MCP servers. Answer no and
|
|
36
36
|
the session starts **Restricted**, with your user settings and the built-in tools only. The first
|
|
37
37
|
time, it then walks you through choosing a provider and filling in its fields (model, base URL, API
|
|
38
|
-
key), and saves the profile to
|
|
38
|
+
key), and saves the profile to `<configured-user-state-dir>/settings.json`.
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
cd my-project
|
|
@@ -53,9 +53,9 @@ robota --configure-provider anthropic --type anthropic --model claude-sonnet-4-6
|
|
|
53
53
|
robota trust --yes
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
`robota init` writes a starter `AGENTS.md` and
|
|
56
|
+
`robota init` writes a starter `AGENTS.md` and `<configured-project-state-dir>/settings.json` for the current project.
|
|
57
57
|
`robota --configure` reruns the interactive provider setup, and `robota --reset` deletes
|
|
58
|
-
|
|
58
|
+
`<configured-user-state-dir>/settings.json`.
|
|
59
59
|
|
|
60
60
|
## What you can do
|
|
61
61
|
|
|
@@ -63,7 +63,7 @@ robota trust --yes
|
|
|
63
63
|
|
|
64
64
|
`robota` starts an interactive session. Type a request, or `/` to open the command menu (`/help`
|
|
65
65
|
lists every command). `Esc` stops the current response, `Ctrl+R` searches the prompts you have typed
|
|
66
|
-
before, and every key can be rebound in
|
|
66
|
+
before, and every key can be rebound in `<configured-user-state-dir>/keybindings.json` (see the
|
|
67
67
|
[keybindings guide](../../content/guide/keybindings.md)).
|
|
68
68
|
|
|
69
69
|
The agent works with file and shell tools (`Read`, `Write`, `Edit`, `Glob`, `Grep`, `Bash`),
|
|
@@ -154,7 +154,7 @@ any settings file:
|
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
An `ask` rule asks even in `bypassPermissions`, and a few actions are never approved automatically
|
|
157
|
-
in any mode: `rm` on the root, home or working directory, and writes into `.git`,
|
|
157
|
+
in any mode: `rm` on the root, home or working directory, and writes into `.git`, `<configured-project-state-dir>`,
|
|
158
158
|
`.claude`, `.agents` or shell and tool configuration files.
|
|
159
159
|
|
|
160
160
|
Shell commands can also run inside an OS sandbox (bubblewrap on Linux, Seatbelt on macOS): `/sandbox`
|
|
@@ -162,7 +162,7 @@ switches between `auto-allow`, `regular` and `off`, and the `sandbox` settings k
|
|
|
162
162
|
When something misbehaves, `robota --safe-mode` starts with instruction files, skills, plugins, hooks
|
|
163
163
|
and MCP servers all off.
|
|
164
164
|
|
|
165
|
-
Workspace trust is granted per Git worktree and kept in
|
|
165
|
+
Workspace trust is granted per Git worktree and kept in `<configured-user-state-dir>/workspace-trust.json`:
|
|
166
166
|
|
|
167
167
|
```bash
|
|
168
168
|
robota trust status # is this workspace trusted, and what would trust load? (--json: one line)
|
|
@@ -207,11 +207,11 @@ when the first is overloaded, `--effort <level>` sets model effort, and `--advis
|
|
|
207
207
|
lets the model consult a second model. See [Providers](../../content/guide/providers.md) and
|
|
208
208
|
[Local LLM setup](../../content/guide/local-llm.md).
|
|
209
209
|
|
|
210
|
-
### Connect MCP servers, or serve
|
|
210
|
+
### Connect MCP servers, or serve the session over MCP
|
|
211
211
|
|
|
212
212
|
Declare remote MCP servers under `mcpServers` in a settings file. A declared server is not connected
|
|
213
213
|
until you approve it: `/mcp` shows each server's state, and `/mcp approve <server>` connects it in
|
|
214
|
-
the running session and keeps the approval in
|
|
214
|
+
the running session and keeps the approval in `<configured-user-state-dir>/mcp-approvals.json` for later starts. A
|
|
215
215
|
server that uses OAuth also needs a sign-in — `/mcp login <server>` in a session, or
|
|
216
216
|
`robota mcp login <server>` in a terminal — and connects once you sign in.
|
|
217
217
|
|
|
@@ -223,7 +223,44 @@ server that uses OAuth also needs a sign-in — `/mcp login <server>` in a sessi
|
|
|
223
223
|
}
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
An HTTP or stdio definition can explicitly select `"protocolVersion": "2026-07-28"` for a stateless
|
|
227
|
+
peer. Omit that field for the legacy handshake. Changing it requires approval again; unsupported values
|
|
228
|
+
are refused rather than silently ignored. The selected version is visible in the definition details.
|
|
229
|
+
Startup reports unavailable client-input, subscription and extension capabilities. Selecting this
|
|
230
|
+
version does not enable distributed Skills or give the server any additional authority. Add
|
|
231
|
+
`"skills": true` to explicitly opt that definition into a peer's advertised Skills extension;
|
|
232
|
+
changing this selection requires fresh server approval. `/mcp skill-list <server>` reads metadata
|
|
233
|
+
only. A local terminal or app user can review `/mcp skill-inspect <server> <uri>`, then copy its
|
|
234
|
+
`/mcp skill-approve <server> <uri> <fingerprint>` command to consent to those exact instructions and
|
|
235
|
+
frontmatter. `/mcp skill-withdraw <server> <uri>` withdraws that consent. JSON string arrays also
|
|
236
|
+
accept opaque names and URIs containing spaces. Content decisions persist separately in
|
|
237
|
+
`<configured-user-state-dir>/mcp-skill-approvals.json`, bound to the host origin, workspace and URI.
|
|
238
|
+
Observed changed or removed manifests withdraw consent even if the peer later reverts them.
|
|
239
|
+
Discovery returns an exact invocation name for each supported skill, or a reason its profile is
|
|
240
|
+
unavailable. Invoke that name through the session's skill command. Instructions load only when
|
|
241
|
+
the actual turn starts, under current content consent; ending or cancelling that turn closes its
|
|
242
|
+
activation. A queued or cancelled request does not load instructions into another turn. Remote
|
|
243
|
+
shell expressions remain instruction text and do not execute as local preprocessing. During an
|
|
244
|
+
active execution, `/skill-read ["<invocation-name>", "<resource-uri>"]` reads a listed supporting
|
|
245
|
+
file with fresh verification. A fork can read only files from its own activations; a nested SKILL.md remains
|
|
246
|
+
supporting data until separately approved and activated.
|
|
247
|
+
|
|
248
|
+
MCP calls recheck approval and the current source immediately before dispatch. Removing or
|
|
249
|
+
replacing a definition, revoking approval, changing required workspace trust, or closing the
|
|
250
|
+
composition refuses new calls through retained tools. Calls already dispatched keep their real
|
|
251
|
+
outcome; revocation is not rollback. A changed configuration needs a fresh startup and approval
|
|
252
|
+
rather than silently substituting a new connection into an existing tool chain. The startup host
|
|
253
|
+
also exposes declarative contribution descriptors bound to configuration fingerprints, without
|
|
254
|
+
credential values or an invented server version.
|
|
255
|
+
|
|
256
|
+
| Surface | Verified coverage | Limit |
|
|
257
|
+
| ------------------------------------------ | -------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
258
|
+
| Linux interactive, serve and print startup | Real admitted stdio calls; changed settings refused before another call | Shared startup composition; not a rendered app UI trial |
|
|
259
|
+
| Installed Linux bundle in serve startup | Disable, uninstall and selected-revision update; durable approval rechecked at restart | Already dispatched calls settle; no rollback claim |
|
|
260
|
+
| Remote HTTP | Composition, transport admission and call routing regressions | No live account benchmark |
|
|
261
|
+
| macOS and Windows | Repository CI build routes | No external-plugin pilot on those OSes |
|
|
262
|
+
|
|
263
|
+
`robota mcp serve` does the reverse: it serves one agent session to an MCP host over stdio (or over
|
|
227
264
|
authenticated HTTP with the `--http-*` and `--oauth-*` flags). Trust the project first, and give the
|
|
228
265
|
host the absolute path of `robota` and the project directory:
|
|
229
266
|
|
|
@@ -243,10 +280,10 @@ See [MCP](../../content/guide/mcp.md).
|
|
|
243
280
|
|
|
244
281
|
### Add skills, commands, agents and plugins
|
|
245
282
|
|
|
246
|
-
Skills and commands are Markdown files the CLI finds in
|
|
283
|
+
Skills and commands are Markdown files the CLI finds in `<configured-project-state-dir>/skills/`, `.claude/skills/`,
|
|
247
284
|
`.claude/commands/` and `.agents/skills/` — in a trusted project and under your home directory. Each
|
|
248
285
|
one becomes a slash command (`/<name>`), and `/skills` lists them. Agent definitions are read from
|
|
249
|
-
|
|
286
|
+
`<configured-project-state-dir>/agents/`, `.agents/agents/` and `.claude/agents/`. Plugins bundle these together with hooks,
|
|
250
287
|
themes and MCP servers:
|
|
251
288
|
|
|
252
289
|
```text
|
|
@@ -259,7 +296,7 @@ See the [CLI guide](../../content/guide/cli.md) for skill frontmatter and plugin
|
|
|
259
296
|
|
|
260
297
|
### Use the graphical interface
|
|
261
298
|
|
|
262
|
-
`robota --serve --open` starts a headless runtime for the current workspace, serves the
|
|
299
|
+
`robota --serve --open` starts a headless runtime for the current workspace, serves the configured GUI
|
|
263
300
|
on `127.0.0.1` and opens it in your browser. The Electron desktop app in this repository
|
|
264
301
|
([`apps/agent-app`](../../apps/agent-app/docs/README.md)) shows the same GUI over the workspace
|
|
265
302
|
daemon; it is not published to npm. See [The GUI and the Desktop App](../../content/guide/gui.md).
|
|
@@ -289,26 +326,26 @@ robota eval <definition> # run an evals-as-code definition; exits 1 on a metric
|
|
|
289
326
|
Settings are merged from these files, lowest priority first. The two user files always apply; the
|
|
290
327
|
four project files apply only in a trusted workspace.
|
|
291
328
|
|
|
292
|
-
| File
|
|
293
|
-
|
|
|
294
|
-
|
|
|
295
|
-
| `~/.claude/settings.json`
|
|
296
|
-
|
|
|
297
|
-
|
|
|
298
|
-
| `.claude/settings.json`
|
|
299
|
-
| `.claude/settings.local.json`
|
|
300
|
-
|
|
301
|
-
Other files the CLI keeps under
|
|
302
|
-
|
|
303
|
-
| Path | Contents
|
|
304
|
-
| ---------------------- |
|
|
305
|
-
| `workspace-trust.json` | workspaces you have trusted
|
|
306
|
-
| `sessions/` | saved sessions (a trusted project may keep them in
|
|
307
|
-
| `history.jsonl` | prompts you typed, for `Ctrl+R` (`"promptHistory": false` turns it off)
|
|
308
|
-
| `keybindings.json` | key bindings
|
|
309
|
-
| `themes/` | your own `/theme` themes
|
|
310
|
-
| `plugins/` | installed plugins (a project may have its own
|
|
311
|
-
| `mcp-credentials/` | OAuth tokens for MCP servers, readable by you only
|
|
329
|
+
| File | Scope |
|
|
330
|
+
| ---------------------------------------------------- | --------------------------------------- |
|
|
331
|
+
| `<configured-user-state-dir>/settings.json` | user |
|
|
332
|
+
| `~/.claude/settings.json` | user (Claude Code-compatible) |
|
|
333
|
+
| `<configured-project-state-dir>/settings.json` | project, committed |
|
|
334
|
+
| `<configured-project-state-dir>/settings.local.json` | project, local to this machine |
|
|
335
|
+
| `.claude/settings.json` | project (Claude Code-compatible) |
|
|
336
|
+
| `.claude/settings.local.json` | project, local (Claude Code-compatible) |
|
|
337
|
+
|
|
338
|
+
Other files the CLI keeps under `<configured-user-state-dir>/`:
|
|
339
|
+
|
|
340
|
+
| Path | Contents |
|
|
341
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------- |
|
|
342
|
+
| `workspace-trust.json` | workspaces you have trusted |
|
|
343
|
+
| `sessions/` | saved sessions (a trusted project may keep them in `<configured-project-state-dir>/sessions/`) |
|
|
344
|
+
| `history.jsonl` | prompts you typed, for `Ctrl+R` (`"promptHistory": false` turns it off) |
|
|
345
|
+
| `keybindings.json` | key bindings |
|
|
346
|
+
| `themes/` | your own `/theme` themes |
|
|
347
|
+
| `plugins/` | installed plugins (a project may have its own `<configured-project-state-dir>/plugins/`) |
|
|
348
|
+
| `mcp-credentials/` | OAuth tokens for MCP servers, readable by you only |
|
|
312
349
|
|
|
313
350
|
## Use it from code
|
|
314
351
|
|
|
@@ -320,6 +357,195 @@ A subagent runs in a child process that starts your entry script again with a wo
|
|
|
320
357
|
settles (the worker ends the process). So call `startCli()` from the script Node started, and put
|
|
321
358
|
nothing before it that must not run once per subagent.
|
|
322
359
|
|
|
360
|
+
### Hosted task-worker execution
|
|
361
|
+
|
|
362
|
+
The library also exports `HostedOrganizationControl` and `createHostedOrganizationGateway` for
|
|
363
|
+
the owner-hosted company backend. This composition requires an anchored `OrganizationLedger`,
|
|
364
|
+
an `OrganizationAudit`, provider-backed worker inventory and named detection, containment,
|
|
365
|
+
assessment and recovery owners. The gateway serves the configured admission endpoints and
|
|
366
|
+
OpenAI model endpoints; its worker ingress has no policy, approval or recovery routes.
|
|
367
|
+
Each model-only grant has a distinct owner-issued task token and runtime binding. The logical task
|
|
368
|
+
root survives grant rotation; provider inventory and gateway admission pin its current binding. Issuer signing
|
|
369
|
+
capabilities, upstream credentials, policy, audit and private payload storage stay outside workers.
|
|
370
|
+
Sign task access with the exported `hostedWorkerAccessBytes` and provision E2B workers with an
|
|
371
|
+
`organization` binding; `createE2BOrganizationInventory` enumerates all pages, including paused
|
|
372
|
+
workers, and rechecks ownership before deletion. Owner stop methods persist withdrawal first,
|
|
373
|
+
abort affected broker calls, and wait for descendant provider cleanup; unresolved cleanup is
|
|
374
|
+
reported to the incident owners.
|
|
375
|
+
|
|
376
|
+
`HostedOrganizationPayloads` stores bounded request/response bytes in an owner-only directory;
|
|
377
|
+
the durable journal and audit retain digests rather than these bodies. Install
|
|
378
|
+
`createHostedOrganizationModelAction` with a fixed provider endpoint/model, owner-selected
|
|
379
|
+
reservation, input/output bounds and explicit upstream idempotency support. A mandatory trusted
|
|
380
|
+
`countInputTokens` port counts the exact immutable upstream input before dispatch; input plus the
|
|
381
|
+
owner-selected output cap must fit the token and conservative cost reservation.
|
|
382
|
+
`createHostedOrganizationInputTokenCounter` connects a fixed private counting endpoint through an
|
|
383
|
+
owner-selected encoder. A Responses counter does not establish exact Chat token counts; select a
|
|
384
|
+
verified counter for that surface. The counting service must be read-only and covered by the
|
|
385
|
+
owner's pricing assumptions. The gateway buffers
|
|
386
|
+
the upstream result until durable settlement, then returns JSON or OpenAI stream events.
|
|
387
|
+
Configure the worker's OpenAI profile with `options: { durableOperations: true }`: one operation
|
|
388
|
+
identity survives SDK retries, and missing identities are refused. Unknown provider outcomes
|
|
389
|
+
retain reservations and require asset-owner reconciliation; this adapter never retries upstream.
|
|
390
|
+
The private payload store retains digest-bound operation claims for recovery. Owner code may
|
|
391
|
+
load those claims with `operation` and call `reconcileExternalOperation` with confirmed asset-owner
|
|
392
|
+
evidence. Reconciliation charges the entire retained reservation and cannot restore revoked
|
|
393
|
+
authority; the worker gateway exposes no reconciliation route.
|
|
394
|
+
Broker supervisory token/cost observations report settled usage; pending reservations remain
|
|
395
|
+
enforced by the hierarchical ledger. Local process and wire tests establish these software paths,
|
|
396
|
+
while physical containment, provider billing and independent remote custody require deployment
|
|
397
|
+
measurements.
|
|
398
|
+
|
|
399
|
+
Hosted posture requires current signed admission for a separate task worker and company broker.
|
|
400
|
+
The installed E2B executor runs the stock CLI inside that worker. The runtime forwards arguments,
|
|
401
|
+
input and output and owns cancellation, credential expiry and provider deletion; it does not run
|
|
402
|
+
task tools against its own filesystem. Direct `--api-key` configuration is refused.
|
|
403
|
+
|
|
404
|
+
`PRODUCT_HOSTED_RUNTIME_CONFIG` selects an owner-controlled version-3 deployment file. It retains
|
|
405
|
+
`identity`, `epoch`, `worker`, `broker`, `snapshot`, `lifetimeMs` and `probeTimeoutMs`, and requires
|
|
406
|
+
`limits: { sessions, modelCalls, modelTokens, costMicros }` with nonnegative safe integer ceilings.
|
|
407
|
+
Version-2 deployments and proofs are refused because they cannot attest current accounting.
|
|
408
|
+
The worker and broker answer version-3 admission challenges with the configured limits; worker
|
|
409
|
+
proofs carry `usage: null`, while broker proofs carry cumulative usage in that same four-counter
|
|
410
|
+
shape. The broker measures registered sessions and provider calls outside the worker for the
|
|
411
|
+
current identity and epoch. Counts and micro-cost units come from its trusted instrumentation,
|
|
412
|
+
not CLI output, model messages or a checkpoint. Both attestations sign the fixed tuple returned by
|
|
413
|
+
the exported `hostedAdmissionBytes`, binding limits and accounting to the current challenge,
|
|
414
|
+
identity, root, epoch and checkpoint. The issuer must validate requested limits against owner policy
|
|
415
|
+
before attesting them.
|
|
416
|
+
|
|
417
|
+
`HostedRuntimeController.status()` exposes the latest verified observations to the owner. The
|
|
418
|
+
controller checks them before executor allocation, during admission refresh and after short
|
|
419
|
+
executions before reporting success. Invalid, regressing, unavailable or over-limit observations
|
|
420
|
+
refuse admission or stop and release owned execution. Runtime ceilings supervise observed totals;
|
|
421
|
+
the broker's per-request reservations must bound new model calls and their maximum token/cost
|
|
422
|
+
usage before dispatch. Supervisory polling does not replace those reservations or establish an
|
|
423
|
+
invoice hard cap. The broker must retain cumulative accounting across controller restart/resume;
|
|
424
|
+
recovered task content cannot reset it. Actual provider charges and cleanup remain separately
|
|
425
|
+
measured operational evidence.
|
|
426
|
+
|
|
427
|
+
The operator supplies `PRODUCT_E2B_API_KEY` to the runtime and sets
|
|
428
|
+
`PRODUCT_HOSTED_WORKER_EXECUTION_CONFIG` to an absolute owner-only JSON file. Its version-1 shape is:
|
|
429
|
+
|
|
430
|
+
```json
|
|
431
|
+
{
|
|
432
|
+
"version": 1,
|
|
433
|
+
"templateId": "operator-pinned-template",
|
|
434
|
+
"nodeExecutable": "/usr/local/bin/node",
|
|
435
|
+
"workspaceRoot": "/workspace",
|
|
436
|
+
"entrypoint": { "path": "/opt/agent/cli.mjs", "digest": "<sha256>" },
|
|
437
|
+
"productConfig": { "path": "/opt/agent/product.env", "digest": "<sha256>" },
|
|
438
|
+
"access": {
|
|
439
|
+
"version": 1,
|
|
440
|
+
"identity": {
|
|
441
|
+
"tenant": "...",
|
|
442
|
+
"task": "...",
|
|
443
|
+
"rootTask": "...",
|
|
444
|
+
"actor": "...",
|
|
445
|
+
"runtime": "..."
|
|
446
|
+
},
|
|
447
|
+
"worker": "...",
|
|
448
|
+
"broker": "...",
|
|
449
|
+
"epoch": 1,
|
|
450
|
+
"endpoint": "https://company-broker.example/v1",
|
|
451
|
+
"token": "<task-scoped-broker-token>",
|
|
452
|
+
"issuedAt": 0,
|
|
453
|
+
"expiresAt": 0,
|
|
454
|
+
"signature": "<broker-ed25519-base64url-signature>"
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
The broker signs the fixed tuple encoded by `hostedWorkerAccessBytes` in the implementation.
|
|
460
|
+
Access must match the admission's identity, resources and epoch, use the admitted broker origin,
|
|
461
|
+
and expire within both admission and sixty seconds of issuance. This executor ends execution at
|
|
462
|
+
expiry; it does not renew credentials. The template's CLI and product environment artifacts must
|
|
463
|
+
match their pinned digests. The product artifact accepts only CLI product metadata variables.
|
|
464
|
+
Provider metadata must match task ownership and the template, with public traffic disabled and
|
|
465
|
+
outbound access limited to the broker hostname and no attached volumes. The resource must already
|
|
466
|
+
be running, with timeout deletion and auto-resume disabled; connecting a paused memory image is
|
|
467
|
+
refused. These checks do not establish measured cloud containment.
|
|
468
|
+
|
|
469
|
+
An owner backend can call `provisionE2BTaskWorker` before issuing admission. It creates the
|
|
470
|
+
operator-selected clean template with closed networking, no worker credentials or volumes, and
|
|
471
|
+
an exclusive new workspace. It returns the resource, workspace root, operation ID, checkpoint
|
|
472
|
+
reference, optional conversation artifact receipt and an idempotent `release()` operation. The management capability stays in this
|
|
473
|
+
owner-side SDK composition. After the receipt, the owner signs current worker/broker admission
|
|
474
|
+
and task access for the returned resource and configures that workspace in the execution input.
|
|
475
|
+
|
|
476
|
+
For recovery, supply an owner-approved `{ id, digest }` reference and the matching UTF-8 JSON bytes
|
|
477
|
+
in `checkpoint`. The manifest has `version: 1`, `id`, the source `identity`, `epoch`, `worker`, and
|
|
478
|
+
`files: [{ path, base64 }]`. Paths are relative regular workspace files; private HOME/TMPDIR and link or memory records
|
|
479
|
+
are not accepted. Owner authority journals stay outside this workspace. The source must belong
|
|
480
|
+
to the same tenant/task/root and precede the current epoch and runtime. File bytes are read back
|
|
481
|
+
and compared before the owner receives a successful result. Treat recovered project content as
|
|
482
|
+
untrusted; current profile, policy and credentials come from the clean operator composition.
|
|
483
|
+
For conversation recovery, use manifest `version: 2` with the same fields and `session` containing
|
|
484
|
+
an existing versioned session-record envelope (`{ schemaVersion, record }`). Only the conversation,
|
|
485
|
+
name, selector and timestamps survive. Saved system messages/prompts, message metadata, tool schemas,
|
|
486
|
+
VM state, goals, background jobs, loops, memory references and branch pointers do not restore;
|
|
487
|
+
current composition rebuilds execution and authority. Display history is rebuilt from the projected
|
|
488
|
+
conversation; saved events are not imported. The record's cwd becomes the fresh workspace.
|
|
489
|
+
Pass the returned `resumeSession: { id, path, digest }` in the private worker execution input and
|
|
490
|
+
invoke `--resume <id>` (or `--continue`, optionally with `--fork-session`). The default executor
|
|
491
|
+
verifies the receipt and copies the projected record into the current product artifact's selected
|
|
492
|
+
private user session store before CLI startup. It refuses an existing destination, mismatched
|
|
493
|
+
selector/digest, malformed record or a user state root inside recovered project files. No saved
|
|
494
|
+
settings, trust decision or credential is imported. A resume without this receipt refuses execution.
|
|
495
|
+
|
|
496
|
+
The default executor accepts this filesystem checkpoint only when signed current admission and
|
|
497
|
+
provider metadata agree on its digest, fresh identity/epoch and source resource. It does not
|
|
498
|
+
restore provider memory snapshots or reconnect the checkpoint source. Setup failures delete
|
|
499
|
+
verified allocations; unknown creation/ownership and unresolved deletion return
|
|
500
|
+
`E2BWorkerProvisioningError` with reconciliation identifiers. The external owner must retain and
|
|
501
|
+
reconcile those outcomes rather than retry or report cleanup as successful.
|
|
502
|
+
|
|
503
|
+
The worker receives a closed environment with private HOME/TMPDIR, product configuration and the
|
|
504
|
+
scoped broker token through the OpenAI-compatible provider interface. Management and upstream
|
|
505
|
+
credentials stay outside the worker. Interactive input uses a PTY; input EOF requests the stock
|
|
506
|
+
CLI's Ctrl-C shutdown. For `--serve`, include `serve: { port, workerPort, token }` in the private
|
|
507
|
+
execution input. `port` selects the runtime's IPv4 loopback ingress (`0` assigns a free port);
|
|
508
|
+
`workerPort` selects the worker daemon port. Generate a fresh, unpredictable owner client `token`
|
|
509
|
+
of 32–256 URL-safe characters and connect to the printed runtime URL with `?token=<token>`.
|
|
510
|
+
The runtime validates the client token, Host and Origin, then carries binary WebSocket traffic
|
|
511
|
+
through an owned provider command to the pinned worker loopback listener. It replaces client
|
|
512
|
+
headers and credentials with a separate per-launch worker token. The owner token and provider
|
|
513
|
+
management capability stay in the runtime. Stopping execution closes the listener and clients
|
|
514
|
+
and deletes the worker. This local ingress does not provide remote desktop pairing or TLS.
|
|
515
|
+
Credential renewal requires broker integration; this executor still ends at its signed access expiry.
|
|
516
|
+
|
|
517
|
+
Embedders can override the installed executor with `hostedRuntimeExecutorFactory`. It receives
|
|
518
|
+
`(admission, signal, invocation)` with immutable captured arguments and returns `run`, `stop` and
|
|
519
|
+
`release`. The adapter owns authenticated communication, input/output and trusted usage reporting;
|
|
520
|
+
stop and release settle only after cleanup. Missing or invalid authority refuses execution without
|
|
521
|
+
entering local host adapters. CLI flags request behavior and never confer company authority.
|
|
522
|
+
|
|
523
|
+
## Remote desktop owner composition
|
|
524
|
+
|
|
525
|
+
Use `HostedDesktopWorkerPort.connect({ endpoint, token, worker, control })` with the owner's private
|
|
526
|
+
runtime loopback ingress. It discovers and pins the actual worker session; retain this carrier across
|
|
527
|
+
desktop reconnects. Create `HostedDesktopAuthorization` with the public HTTPS URL, pinned issuer,
|
|
528
|
+
current `HostedOrganizationControl`, and owner-selected bindings containing `id`, `user`, `client`,
|
|
529
|
+
the discovered `session`, and the same `worker`. Give `HostedDesktopGateway` the owner's HTTPS server,
|
|
530
|
+
that authorization, a `HostedDesktopAuthorityStore` and a binding-ID-to-worker-port map. The store uses
|
|
531
|
+
a private existing directory and an independently held organization ledger anchor; `create: true` is only
|
|
532
|
+
for first creation. Store loss, rollback or an uncertain anchor acknowledgment refuses access.
|
|
533
|
+
|
|
534
|
+
The issuer signs a short-lived asymmetric JWT with exact `sub`, `client_id`, `tenant`, `task`, `session`,
|
|
535
|
+
`workload` and `epoch` binding claims, unique `jti`, `iat`, and `exp`. Ordinary credentials have the public
|
|
536
|
+
URL as audience and `desktop:pair desktop:drive` scopes. A separate operator credential has
|
|
537
|
+
`<publicUrl>/approval` as audience and `desktop:approve` scope. Credentials expire within two minutes;
|
|
538
|
+
pairing grants at most one minute and never extends current company authority. Reconnect uses new JWTs,
|
|
539
|
+
withdraws the previous connection and preserves the pinned worker session. Issuer/JWK outages and current
|
|
540
|
+
grant withdrawal close active access. `gateway.revoke(bindingId)` is an owner-only withdrawal API.
|
|
541
|
+
|
|
542
|
+
Remote traffic requires TLS and the pinned Host/Origin. TLS termination requires explicitly selected
|
|
543
|
+
proxy IPs with matching HTTPS forwarding headers; arbitrary proxy headers are refused. The renderer
|
|
544
|
+
cannot switch tasks/sessions, execute slash commands, change settings, or approve permissions. The
|
|
545
|
+
main-process operator channel binds Allow once/Deny to the currently pending operation digest and
|
|
546
|
+
rejects replay. Run all authority stores and gateway/control composition outside the task worker.
|
|
547
|
+
These local integration checks do not establish deployed cloud containment or operational latency.
|
|
548
|
+
|
|
323
549
|
## Work on the CLI in this repository
|
|
324
550
|
|
|
325
551
|
```bash
|
|
@@ -340,5 +566,5 @@ pnpm cli:trust # trust this repository for the source CLI
|
|
|
340
566
|
|
|
341
567
|
## License
|
|
342
568
|
|
|
343
|
-
|
|
569
|
+
This package is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a
|
|
344
570
|
[commercial license](../../COMMERCIAL.md). See [LICENSING.md](../../LICENSING.md).
|
|
@@ -8,7 +8,7 @@ const REQUIRED_NODE_MAJOR = 22;
|
|
|
8
8
|
const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
|
|
9
9
|
if (nodeMajor < REQUIRED_NODE_MAJOR) {
|
|
10
10
|
process.stderr.write(
|
|
11
|
-
'\n
|
|
11
|
+
'\n This CLI requires Node.js ' +
|
|
12
12
|
REQUIRED_NODE_MAJOR +
|
|
13
13
|
' or higher.\n' +
|
|
14
14
|
' Current version: ' +
|