@sanlabs/sanbox-cli 0.0.10 → 0.0.11
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/README.md +58 -9
- package/dist/api.js +12 -0
- package/dist/cli.js +114 -11
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,9 +15,11 @@ installed_cli_version="$(sanbox --version)"
|
|
|
15
15
|
test "$installed_cli_version" = "$latest_cli_version"
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Always use the latest published CLI. CLI 0.0.
|
|
19
|
-
|
|
20
|
-
CLI 0.0.
|
|
18
|
+
Always use the latest published CLI. CLI 0.0.11 adds OpenCode Computer templates and short-lived
|
|
19
|
+
native SDK connections for HTTP, SSE streaming, and interactive steering.
|
|
20
|
+
CLI 0.0.10 added per-run Hermes email and Telegram channels, Supabase user authorization, and
|
|
21
|
+
removed the retired run-chat commands.
|
|
22
|
+
CLI 0.0.9 added user login for private SSH access to supported running sandboxes.
|
|
21
23
|
CLI 0.0.6 added Anthropic self-hosted environment inspection and administration.
|
|
22
24
|
|
|
23
25
|
## Configure
|
|
@@ -82,8 +84,20 @@ sanbox doctor --json
|
|
|
82
84
|
|
|
83
85
|
Model IDs are provider-scoped. The CLI never guesses or silently substitutes a provider, model, or template.
|
|
84
86
|
For waited task runs, choose a template with `runnable: true`, `template_type: "runner"`, and
|
|
85
|
-
`runner_config.harness: "opencode"` or `"browser-use"`.
|
|
86
|
-
|
|
87
|
+
`runner_config.harness: "opencode"` or `"browser-use"`. OpenCode remains the default one-shot mode,
|
|
88
|
+
while OpenCode Computer runs a retained private OpenCode server:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
sanbox templates create \
|
|
92
|
+
--name "OpenCode Computer" \
|
|
93
|
+
--harness opencode \
|
|
94
|
+
--mode computer \
|
|
95
|
+
--model-provider openai \
|
|
96
|
+
--model '<model-id>'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Existing templates keep their current names, IDs, slugs, and task behavior. Browser Use is for one-shot
|
|
100
|
+
web tasks whose target domains are already approved. Hermes service templates are always-on. A Hermes template
|
|
87
101
|
declares which optional channels its runs may activate:
|
|
88
102
|
|
|
89
103
|
```bash
|
|
@@ -244,22 +258,57 @@ access until Pause creates the next snapshot generation. Before resuming, requir
|
|
|
244
258
|
`sandbox_state: "paused"` and a positive `snapshot_generation`. Do not submit agent work while that
|
|
245
259
|
lease is active. Paused state has no retention TTL and remains available until explicitly deleted.
|
|
246
260
|
|
|
247
|
-
## SSH Into A
|
|
261
|
+
## SSH Into A Running Sandbox
|
|
248
262
|
|
|
249
263
|
```bash
|
|
250
264
|
sanbox login
|
|
251
265
|
sanbox ssh <run-id>
|
|
252
266
|
```
|
|
253
267
|
|
|
254
|
-
The run must be a currently running Hermes Computer created from
|
|
268
|
+
The run must be a currently running OpenCode run or Hermes Computer created from an SSH-capable
|
|
269
|
+
artifact.
|
|
255
270
|
The signed-in user must own the run or be an organization admin. The command uses your local OpenSSH
|
|
256
271
|
client, but it does not expose the microVM on a public IP or port. The CLI generates a temporary
|
|
257
272
|
Ed25519 identity, pins the microVM's runtime host key, and tunnels the SSH stream over a one-time
|
|
258
273
|
authenticated WebSocket. The temporary key is deleted when the connection closes, and the saved
|
|
259
274
|
user token is not passed to the OpenSSH child process.
|
|
260
275
|
|
|
261
|
-
OpenCode and Browser Use runs are one-shot agent executions.
|
|
262
|
-
|
|
276
|
+
OpenCode and Browser Use runs are one-shot agent executions. OpenCode Computer keeps its private
|
|
277
|
+
server running after the initial task so it can accept later steering. A persisted task sandbox can
|
|
278
|
+
still be resumed manually for inspection when a snapshot exists.
|
|
279
|
+
|
|
280
|
+
## Connect The OpenCode SDK
|
|
281
|
+
|
|
282
|
+
Create a short-lived connection for a currently running OpenCode Computer:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
sanbox opencode connect <run-id> --expires 1h --json
|
|
286
|
+
sanbox opencode connections list <run-id> --json
|
|
287
|
+
sanbox opencode connections revoke <run-id> <connection-id> --json
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The create response contains `url`, `access_token`, and connection metadata. The plaintext token is
|
|
291
|
+
returned only once, but it can authenticate all OpenCode SDK requests and SSE reconnects until it
|
|
292
|
+
expires, is revoked, or the runtime session ends. Multiple connections can coexist. Organization API
|
|
293
|
+
keys use their existing organization role and require member access or higher; no additional API-key
|
|
294
|
+
or connection-token scopes are introduced.
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { createOpencodeClient } from "@opencode-ai/sdk"
|
|
298
|
+
|
|
299
|
+
const client = createOpencodeClient({
|
|
300
|
+
baseUrl: connection.url,
|
|
301
|
+
headers: { Authorization: `Bearer ${connection.access_token}` },
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
const events = await client.event.subscribe()
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Sanbox proxies the native HTTP and SSE API through its private Firecracker management path. The
|
|
308
|
+
microVM never exposes an inbound port. Only security-sensitive OpenCode configuration,
|
|
309
|
+
authentication, credential mutation, server-administration, raw PTY, and public-share routes are
|
|
310
|
+
denied; core session, event, file, command, and session-shell APIs remain native. See
|
|
311
|
+
[OpenCode SDK access](../docs/opencode-sdk-access.md) for the API and lifecycle contract.
|
|
263
312
|
|
|
264
313
|
## Batch Work
|
|
265
314
|
|
package/dist/api.js
CHANGED
|
@@ -231,6 +231,18 @@ export class SanboxClient {
|
|
|
231
231
|
async revokeFileAccessPoint(runId, accessPointId) {
|
|
232
232
|
return this.request(await this.orgPath(`/runs/${encodeURIComponent(runId)}/file-access-points/${encodeURIComponent(accessPointId)}`), { method: "DELETE" });
|
|
233
233
|
}
|
|
234
|
+
async listOpenCodeConnections(runId) {
|
|
235
|
+
return this.request(await this.orgPath(`/runs/${encodeURIComponent(runId)}/opencode-connections`));
|
|
236
|
+
}
|
|
237
|
+
async createOpenCodeConnection(runId, expiresInSeconds) {
|
|
238
|
+
return this.request(await this.orgPath(`/runs/${encodeURIComponent(runId)}/opencode-connections`), {
|
|
239
|
+
method: "POST",
|
|
240
|
+
body: JSON.stringify({ expires_in_seconds: expiresInSeconds })
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async revokeOpenCodeConnection(runId, connectionId) {
|
|
244
|
+
return this.request(await this.orgPath(`/runs/${encodeURIComponent(runId)}/opencode-connections/${encodeURIComponent(connectionId)}`), { method: "DELETE" });
|
|
245
|
+
}
|
|
234
246
|
async downloadArtifact(runId, artifactPath) {
|
|
235
247
|
return this.rawRequest(`${await this.orgPath(`/runs/${encodeURIComponent(runId)}/artifacts`)}?path=${encodeURIComponent(artifactPath)}`);
|
|
236
248
|
}
|
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ Commands:
|
|
|
40
40
|
sanbox templates list [--json]
|
|
41
41
|
sanbox templates get <template-id> [--json]
|
|
42
42
|
sanbox templates validate <template-id> [--json]
|
|
43
|
-
sanbox templates create --name "..." --model-provider <provider-id> --model <model-id> [--harness opencode|hermes|browser-use] [--channel email|telegram] [--browser-domain <hostname>] [--llm-budget-usd <amount>] [--json]
|
|
43
|
+
sanbox templates create --name "..." --model-provider <provider-id> --model <model-id> [--harness opencode|hermes|browser-use] [--mode task|computer] [--channel email|telegram] [--browser-domain <hostname>] [--llm-budget-usd <amount>] [--json]
|
|
44
44
|
sanbox run "task" --template <template-id> [--email-address <address>] [--telegram-bot-token <token>] [--telegram-allowed-user <id>] [--input <path>] [--wait | --watch] [--json | --jsonl]
|
|
45
45
|
sanbox run --task "..." --template <template-id> [--email-address <address>] [--telegram-bot-token <token>] [--telegram-allowed-user <id>] [--input <path>] [--wait | --watch] [--json | --jsonl]
|
|
46
46
|
sanbox batch --tasks tasks.json --template <template-id> [--input <path>] [--max-parallel 5] [--wait] [--json]
|
|
@@ -57,6 +57,9 @@ Commands:
|
|
|
57
57
|
sanbox runs resume <run-id> [--wait] [--json]
|
|
58
58
|
sanbox runs pause <run-id> [--wait] [--json]
|
|
59
59
|
sanbox runs supabase authorize <run-id> [--open] [--return-url <https-url>] [--json]
|
|
60
|
+
sanbox opencode connect <run-id> [--expires 1h] [--json]
|
|
61
|
+
sanbox opencode connections list <run-id> [--json]
|
|
62
|
+
sanbox opencode connections revoke <run-id> <connection-id> [--json]
|
|
60
63
|
sanbox ssh <run-id>
|
|
61
64
|
sanbox init [--force]
|
|
62
65
|
sanbox init agent [--write]
|
|
@@ -66,12 +69,24 @@ const sshHelp = `Sanbox SSH
|
|
|
66
69
|
Usage:
|
|
67
70
|
sanbox ssh <run-id>
|
|
68
71
|
|
|
69
|
-
Opens
|
|
70
|
-
Ed25519 key, authorizes it for one connection, verifies the microVM host key, and tunnels
|
|
71
|
-
the authenticated private management path. Run sanbox login first. The
|
|
72
|
+
Opens a running OpenCode run or Hermes Computer with your local OpenSSH client. Sanbox creates an
|
|
73
|
+
ephemeral Ed25519 key, authorizes it for one connection, verifies the microVM host key, and tunnels
|
|
74
|
+
SSH over the authenticated private management path. Run sanbox login first. The run owner and
|
|
72
75
|
organization admins can connect; organization API keys cannot open interactive sessions. The guest
|
|
73
76
|
does not expose port 22 publicly.
|
|
74
77
|
`;
|
|
78
|
+
const openCodeHelp = `Sanbox OpenCode SDK access
|
|
79
|
+
|
|
80
|
+
Usage:
|
|
81
|
+
sanbox opencode connect <run-id> [--expires 1h] [--json]
|
|
82
|
+
sanbox opencode connections list <run-id> [--json]
|
|
83
|
+
sanbox opencode connections revoke <run-id> <connection-id> [--json]
|
|
84
|
+
|
|
85
|
+
Creates short-lived bearer credentials for the native OpenCode SDK. The token is bound to one
|
|
86
|
+
running OpenCode Computer and its current runtime session. Its plaintext value is returned only
|
|
87
|
+
when created. Multiple connections may be active at once; each expires and can be revoked
|
|
88
|
+
independently. Organization API keys require member access or higher.
|
|
89
|
+
`;
|
|
75
90
|
const runHelp = `Sanbox run
|
|
76
91
|
|
|
77
92
|
Usage:
|
|
@@ -144,10 +159,12 @@ Usage:
|
|
|
144
159
|
sanbox templates list [--json]
|
|
145
160
|
sanbox templates get <template-id> [--json]
|
|
146
161
|
sanbox templates validate <template-id> [--json]
|
|
147
|
-
sanbox templates create --name "Code review" --model-provider <provider-id> --model <model-id> [--harness opencode|hermes|browser-use] [--browser-domain <hostname>] [--llm-budget-usd <amount>] [--json]
|
|
162
|
+
sanbox templates create --name "Code review" --model-provider <provider-id> --model <model-id> [--harness opencode|hermes|browser-use] [--mode task|computer] [--browser-domain <hostname>] [--llm-budget-usd <amount>] [--json]
|
|
148
163
|
|
|
149
164
|
Template creation requires an exact provider id and that provider's exact model id.
|
|
150
165
|
LiteLLM budgets are optional USD amounts and apply separately to each run.
|
|
166
|
+
OpenCode defaults to --mode task. Use --mode computer for a retained private OpenCode server;
|
|
167
|
+
per-run LiteLLM budgets are not available for that retained mode yet.
|
|
151
168
|
Hermes templates are always-on computers. Use repeatable --channel email|telegram to select which
|
|
152
169
|
channels runs may activate. Email and Telegram credentials are supplied only when creating a run.
|
|
153
170
|
Browser Use templates run local headless Chromium inside a one-shot sandbox. They require OpenAI
|
|
@@ -435,6 +452,7 @@ const flagSets = {
|
|
|
435
452
|
"model-provider",
|
|
436
453
|
"model",
|
|
437
454
|
"harness",
|
|
455
|
+
"mode",
|
|
438
456
|
"llm-budget-usd",
|
|
439
457
|
"web-access",
|
|
440
458
|
"channel",
|
|
@@ -473,6 +491,8 @@ const flagSets = {
|
|
|
473
491
|
"runs.resume": [...commonFlags, "wait", "poll-interval-ms", "timeout-seconds"],
|
|
474
492
|
"runs.pause": [...commonFlags, "wait", "poll-interval-ms", "timeout-seconds"],
|
|
475
493
|
"runs.supabase": [...commonFlags, "open", "return-url"],
|
|
494
|
+
"opencode.connect": [...commonFlags, "expires"],
|
|
495
|
+
"opencode.connections": commonFlags,
|
|
476
496
|
ssh: ["api-url", "help"],
|
|
477
497
|
"ssh-proxy": [],
|
|
478
498
|
login: ["api-url", "json", "no-browser", "help"],
|
|
@@ -490,10 +510,13 @@ const commandKey = (command) => {
|
|
|
490
510
|
return `auth.${command[1] || ""}`;
|
|
491
511
|
if (command[0] === "runs" && command[1] === "supabase")
|
|
492
512
|
return "runs.supabase";
|
|
513
|
+
if (command[0] === "opencode" && command[1] === "connections")
|
|
514
|
+
return "opencode.connections";
|
|
493
515
|
if (command[0] === "model-providers" ||
|
|
494
516
|
command[0] === "anthropic-environments" ||
|
|
495
517
|
command[0] === "templates" ||
|
|
496
|
-
command[0] === "runs"
|
|
518
|
+
command[0] === "runs" ||
|
|
519
|
+
command[0] === "opencode") {
|
|
497
520
|
return `${command[0]}.${command[1] || ""}`;
|
|
498
521
|
}
|
|
499
522
|
if (command[0] === "init" && command[1] === "agent")
|
|
@@ -506,7 +529,7 @@ const requiredValueFlags = new Set([
|
|
|
506
529
|
"telegram-bot-token", "telegram-allowed-user", "channel",
|
|
507
530
|
"tasks", "max-parallel", "batch-id", "poll-interval-ms",
|
|
508
531
|
"event-page-size", "timeout-seconds", "view", "after-event-id", "limit", "name",
|
|
509
|
-
"model-provider", "model", "harness", "llm-budget-usd",
|
|
532
|
+
"model-provider", "model", "harness", "mode", "llm-budget-usd",
|
|
510
533
|
"browser-domain", "browser-max-steps", "browser-step-timeout-seconds",
|
|
511
534
|
"browser-vision-mode", "browser-viewport", "browser-download-policy",
|
|
512
535
|
"browser-additional-instructions", "expires", "output", "artifact"
|
|
@@ -548,7 +571,7 @@ const validateFlags = (command, flags) => {
|
|
|
548
571
|
}
|
|
549
572
|
const knownRoots = new Set([
|
|
550
573
|
"auth", "context", "doctor", "model-providers", "anthropic-environments", "templates",
|
|
551
|
-
"run", "batch", "runs", "ssh", "ssh-proxy", "login", "logout", "init", "version"
|
|
574
|
+
"run", "batch", "runs", "opencode", "ssh", "ssh-proxy", "login", "logout", "init", "version"
|
|
552
575
|
]);
|
|
553
576
|
const allowed = flagSets[key] ?? (knownRoots.has(command[0] || "") ? commonFlags : null);
|
|
554
577
|
if (!allowed)
|
|
@@ -590,6 +613,8 @@ const validatePositionals = (command, flags) => {
|
|
|
590
613
|
"runs.resume": 3,
|
|
591
614
|
"runs.pause": 3,
|
|
592
615
|
"runs.supabase": 4,
|
|
616
|
+
"opencode.connect": 3,
|
|
617
|
+
"opencode.connections": 5,
|
|
593
618
|
ssh: 2,
|
|
594
619
|
"ssh-proxy": 1,
|
|
595
620
|
login: 1,
|
|
@@ -1144,6 +1169,14 @@ const commandTemplates = async (command, flags) => {
|
|
|
1144
1169
|
if (harness !== "opencode" && harness !== "hermes" && harness !== "browser-use") {
|
|
1145
1170
|
throw new CliError("invalid_harness", "templates create --harness must be opencode, hermes, or browser-use.");
|
|
1146
1171
|
}
|
|
1172
|
+
const modeFlag = flagString(flags, "mode");
|
|
1173
|
+
const mode = modeFlag || "task";
|
|
1174
|
+
if (mode !== "task" && mode !== "computer") {
|
|
1175
|
+
throw new CliError("invalid_opencode_mode", "templates create --mode must be task or computer.");
|
|
1176
|
+
}
|
|
1177
|
+
if (harness !== "opencode" && modeFlag) {
|
|
1178
|
+
throw new CliError("opencode_mode_not_supported", "templates create --mode requires --harness opencode.");
|
|
1179
|
+
}
|
|
1147
1180
|
const budgetRaw = flagString(flags, "llm-budget-usd");
|
|
1148
1181
|
const parsedBudgetUsd = budgetRaw ? Number(budgetRaw) : undefined;
|
|
1149
1182
|
const llmBudgetUsd = parsedBudgetUsd === undefined
|
|
@@ -1156,6 +1189,9 @@ const commandTemplates = async (command, flags) => {
|
|
|
1156
1189
|
if (harness === "hermes" && llmBudgetUsd !== undefined) {
|
|
1157
1190
|
throw new CliError("hermes_budget_unsupported", "Always-on Hermes templates do not support --llm-budget-usd yet.");
|
|
1158
1191
|
}
|
|
1192
|
+
if (harness === "opencode" && mode === "computer" && llmBudgetUsd !== undefined) {
|
|
1193
|
+
throw new CliError("opencode_computer_budget_unsupported", "OpenCode Computer templates do not support --llm-budget-usd yet.");
|
|
1194
|
+
}
|
|
1159
1195
|
const channels = [...new Set(flagList(flags, "channel").map((value) => value.trim()).filter(Boolean))];
|
|
1160
1196
|
if (channels.some((channel) => channel !== "email" && channel !== "telegram")) {
|
|
1161
1197
|
throw new CliError("invalid_hermes_channels", "--channel must be email or telegram.");
|
|
@@ -1194,6 +1230,7 @@ const commandTemplates = async (command, flags) => {
|
|
|
1194
1230
|
name,
|
|
1195
1231
|
provider_id: modelProvider,
|
|
1196
1232
|
model_id: model,
|
|
1233
|
+
...(harness === "opencode" && modeFlag ? { mode: mode } : {}),
|
|
1197
1234
|
...(llmBudgetUsd === undefined ? {} : { llm_budget_usd: llmBudgetUsd }),
|
|
1198
1235
|
...(harness === "browser-use" ? {
|
|
1199
1236
|
harness,
|
|
@@ -1224,11 +1261,17 @@ const commandTemplates = async (command, flags) => {
|
|
|
1224
1261
|
commandAction([
|
|
1225
1262
|
"sanbox",
|
|
1226
1263
|
"run",
|
|
1227
|
-
harness === "hermes"
|
|
1264
|
+
harness === "hermes"
|
|
1265
|
+
? "<agent role and instructions>"
|
|
1266
|
+
: harness === "opencode" && mode === "computer"
|
|
1267
|
+
? "<computer role and instructions>"
|
|
1268
|
+
: "<task>",
|
|
1228
1269
|
"--template",
|
|
1229
1270
|
payload.template.id
|
|
1230
|
-
], harness === "hermes"
|
|
1231
|
-
?
|
|
1271
|
+
], harness === "hermes" || (harness === "opencode" && mode === "computer")
|
|
1272
|
+
? harness === "hermes"
|
|
1273
|
+
? "Start the always-on computer and activate only the channels this run needs."
|
|
1274
|
+
: "Start the retained OpenCode computer."
|
|
1232
1275
|
: "Create a run with the new template.")
|
|
1233
1276
|
]);
|
|
1234
1277
|
return;
|
|
@@ -1427,6 +1470,57 @@ const commandRuns = async (command, flags) => {
|
|
|
1427
1470
|
}
|
|
1428
1471
|
throw new Error(`Unknown runs action: ${action}`);
|
|
1429
1472
|
};
|
|
1473
|
+
const commandOpenCode = async (command, flags) => {
|
|
1474
|
+
const client = makeClient(flags);
|
|
1475
|
+
const action = command[1];
|
|
1476
|
+
if (action === "connect") {
|
|
1477
|
+
const runId = requiredPositional(command[2], "run_id_required", "opencode connect requires a run id.");
|
|
1478
|
+
const expiresInSeconds = parseFileAccessExpiry(flagString(flags, "expires", "1h"));
|
|
1479
|
+
if (expiresInSeconds > 24 * 60 * 60) {
|
|
1480
|
+
throw new CliError("invalid_opencode_connection_expiry", "OpenCode connection expiry must be between 1 minute and 1 day.");
|
|
1481
|
+
}
|
|
1482
|
+
const payload = await client.createOpenCodeConnection(runId, expiresInSeconds);
|
|
1483
|
+
if (hasFlag(flags, "json")) {
|
|
1484
|
+
printSuccess("opencode.connect", payload, jsonContext(client), [
|
|
1485
|
+
commandAction(["sanbox", "opencode", "connections", "revoke", runId, payload.connection.id, "--json"], "Revoke this OpenCode SDK connection.")
|
|
1486
|
+
]);
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
process.stdout.write(`URL ${payload.url}\nTOKEN ${payload.access_token}\nEXPIRES ${payload.connection.expires_at}\n`);
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
if (action === "connections") {
|
|
1493
|
+
const connectionAction = command[2];
|
|
1494
|
+
if (connectionAction === "list") {
|
|
1495
|
+
const runId = requiredPositional(command[3], "run_id_required", "opencode connections list requires a run id.");
|
|
1496
|
+
if (command[4]) {
|
|
1497
|
+
throw new CliError("unexpected_argument", `Unexpected positional argument: ${command[4]}`);
|
|
1498
|
+
}
|
|
1499
|
+
const payload = await client.listOpenCodeConnections(runId);
|
|
1500
|
+
if (hasFlag(flags, "json")) {
|
|
1501
|
+
printSuccess("opencode.connections.list", payload, jsonContext(client));
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
for (const connection of payload.connections) {
|
|
1505
|
+
process.stdout.write(`${connection.id}\t${connection.status}\t${connection.expires_at}\t${connection.token_prefix}\n`);
|
|
1506
|
+
}
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
if (connectionAction === "revoke") {
|
|
1510
|
+
const runId = requiredPositional(command[3], "run_id_required", "opencode connections revoke requires a run id.");
|
|
1511
|
+
const connectionId = requiredPositional(command[4], "opencode_connection_id_required", "opencode connections revoke requires a connection id.");
|
|
1512
|
+
const payload = await client.revokeOpenCodeConnection(runId, connectionId);
|
|
1513
|
+
if (hasFlag(flags, "json")) {
|
|
1514
|
+
printSuccess("opencode.connections.revoke", payload, jsonContext(client));
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
process.stdout.write(`revoked ${payload.connection.id}\n`);
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
throw new CliError("opencode_connections_action_required", "opencode connections requires an action: list or revoke.");
|
|
1521
|
+
}
|
|
1522
|
+
throw new CliError("opencode_action_required", "opencode requires an action: connect or connections.");
|
|
1523
|
+
};
|
|
1430
1524
|
const commandDoctor = async (flags) => {
|
|
1431
1525
|
const localConfig = readLocalConfig();
|
|
1432
1526
|
const apiUrl = String(flags["api-url"] || process.env.SANBOX_API_URL || localConfig.api_url || defaultApiUrl).replace(/\/+$/, "");
|
|
@@ -1674,6 +1768,8 @@ const helpFor = (command) => {
|
|
|
1674
1768
|
return anthropicEnvironmentsHelp;
|
|
1675
1769
|
if (command[0] === "templates")
|
|
1676
1770
|
return templatesHelp;
|
|
1771
|
+
if (command[0] === "opencode")
|
|
1772
|
+
return openCodeHelp;
|
|
1677
1773
|
if (command[0] === "ssh")
|
|
1678
1774
|
return sshHelp;
|
|
1679
1775
|
return help;
|
|
@@ -1701,6 +1797,11 @@ const commandId = (command) => {
|
|
|
1701
1797
|
}
|
|
1702
1798
|
if (command[0] === "runs")
|
|
1703
1799
|
return `runs.${command[1] || "unknown"}`;
|
|
1800
|
+
if (command[0] === "opencode" && command[1] === "connections") {
|
|
1801
|
+
return `opencode.connections.${command[2] || "unknown"}`;
|
|
1802
|
+
}
|
|
1803
|
+
if (command[0] === "opencode")
|
|
1804
|
+
return `opencode.${command[1] || "unknown"}`;
|
|
1704
1805
|
if (command[0] === "ssh" || command[0] === "ssh-proxy")
|
|
1705
1806
|
return command[0];
|
|
1706
1807
|
return command[0] || "help";
|
|
@@ -1748,6 +1849,8 @@ const main = async (command, flags) => {
|
|
|
1748
1849
|
return commandBatch(flags);
|
|
1749
1850
|
if (command[0] === "runs")
|
|
1750
1851
|
return commandRuns(command, flags);
|
|
1852
|
+
if (command[0] === "opencode")
|
|
1853
|
+
return commandOpenCode(command, flags);
|
|
1751
1854
|
if (command[0] === "ssh")
|
|
1752
1855
|
return commandSSH(command, flags);
|
|
1753
1856
|
if (command[0] === "ssh-proxy") {
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "0.0.
|
|
1
|
+
export const version = "0.0.11";
|