@boxcompute/cli 0.2.2 → 0.2.4
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 +40 -0
- package/README.md +28 -0
- package/dist/cli.js +63 -5
- package/dist/client.d.ts +22 -0
- package/dist/client.js +15 -0
- package/package.json +6 -4
- package/skills/boxcompute-sandbox/SKILL.md +5 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
This file records user-visible changes to the BoxCompute CLI. GitHub Releases
|
|
4
|
+
use the same notes and bind them to the exact source commit.
|
|
5
|
+
|
|
6
|
+
## [0.2.4] - 2026-09-09
|
|
7
|
+
|
|
8
|
+
`bxc sandbox exec` now leaves arguments after `--` entirely to the program
|
|
9
|
+
running inside the sandbox.
|
|
10
|
+
|
|
11
|
+
### CLI users
|
|
12
|
+
|
|
13
|
+
- Commands such as `bxc sandbox exec SANDBOX_ID -- python --version --json`
|
|
14
|
+
now pass `--version` and `--json` to Python instead of treating them as
|
|
15
|
+
global `bxc` options.
|
|
16
|
+
- No configuration changes are required. Upgrade normally with `bxc update`.
|
|
17
|
+
|
|
18
|
+
### Security
|
|
19
|
+
|
|
20
|
+
No security-relevant changes.
|
|
21
|
+
|
|
22
|
+
## [0.2.3] - 2026-09-06
|
|
23
|
+
|
|
24
|
+
- Added access to retained sandbox logs after runtime deletion.
|
|
25
|
+
|
|
26
|
+
## [0.2.2] - 2026-09-05
|
|
27
|
+
|
|
28
|
+
- Added `bxc update` and its `bxc up` alias.
|
|
29
|
+
|
|
30
|
+
## [0.2.1] - 2026-09-05
|
|
31
|
+
|
|
32
|
+
- Improved release validation and managed skill updates.
|
|
33
|
+
|
|
34
|
+
## [0.2.0] - 2026-09-04
|
|
35
|
+
|
|
36
|
+
- Added the multi-instance Sandbox API v2 commands.
|
|
37
|
+
|
|
38
|
+
## [0.1.1] - 2026-09-04
|
|
39
|
+
|
|
40
|
+
- Initial public npm release with browser login and managed agent skills.
|
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ bxc workspaces
|
|
|
48
48
|
bxc sandboxes
|
|
49
49
|
bxc sandbox start WORKSPACE_ID
|
|
50
50
|
# Use the returned sandbox instance ID for later commands.
|
|
51
|
+
bxc sandbox logs SANDBOX_ID --source execute
|
|
51
52
|
bxc sandbox exec SANDBOX_ID -- python -m pytest
|
|
52
53
|
```
|
|
53
54
|
|
|
@@ -69,3 +70,30 @@ bxc update
|
|
|
69
70
|
saved BoxCompute credential and refreshes untouched managed skills before the
|
|
70
71
|
client's next agent session. The new CLI uses v2 for multi-instance sandboxes
|
|
71
72
|
and gives a server-first upgrade message if it reaches an older deployment.
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
bun install --frozen-lockfile
|
|
78
|
+
bun run typecheck
|
|
79
|
+
bun test
|
|
80
|
+
bun run lint
|
|
81
|
+
bun run build
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The application-side authentication and customer Sandbox API implementations
|
|
85
|
+
live in the private `boxcompute/web-agent` repository. Changes to either side of
|
|
86
|
+
that contract must remain backward compatible during rollout: deploy the server
|
|
87
|
+
first, then publish the CLI.
|
|
88
|
+
|
|
89
|
+
## Releases
|
|
90
|
+
|
|
91
|
+
Every user-visible change is recorded in [CHANGELOG.md](CHANGELOG.md). Releases
|
|
92
|
+
use semantic `vMAJOR.MINOR.PATCH` tags and are published from `main` by the
|
|
93
|
+
protected `Publish BoxCompute CLI` workflow. npm trusted publishing supplies a
|
|
94
|
+
short-lived release credential; the repository stores no npm token.
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
Copyright © 2026 BoxCompute. All rights reserved. The source is publicly
|
|
99
|
+
visible, but it is not offered under an open-source license.
|
package/dist/cli.js
CHANGED
|
@@ -26,6 +26,7 @@ Commands:
|
|
|
26
26
|
sandbox Manage isolated BoxCompute sandboxes
|
|
27
27
|
start Create and start a workspace sandbox
|
|
28
28
|
status Inspect one sandbox
|
|
29
|
+
logs Read current or retained sandbox logs
|
|
29
30
|
exec Execute a program inside a sandbox
|
|
30
31
|
delete [alias: rm] Destroy the runtime; the workspace remains
|
|
31
32
|
skill [alias: skills] Manage coding-harness skills
|
|
@@ -54,6 +55,7 @@ Examples:
|
|
|
54
55
|
$ bxc skill install
|
|
55
56
|
$ bxc workspaces
|
|
56
57
|
$ bxc sandbox start WORKSPACE_ID
|
|
58
|
+
$ bxc sandbox logs SANDBOX_ID --source execute
|
|
57
59
|
$ bxc sandbox exec SANDBOX_ID -- python -m pytest
|
|
58
60
|
|
|
59
61
|
Compatibility:
|
|
@@ -68,6 +70,7 @@ Commands:
|
|
|
68
70
|
|
|
69
71
|
start WORKSPACE_ID Create and start a new sandbox instance
|
|
70
72
|
status SANDBOX_ID Inspect one sandbox
|
|
73
|
+
logs SANDBOX_ID [options] Read logs without starting the runtime
|
|
71
74
|
exec SANDBOX_ID [options] -- PROGRAM [ARG...]
|
|
72
75
|
Execute a program inside a sandbox
|
|
73
76
|
delete SANDBOX_ID --yes [alias: rm] Destroy the runtime; keep the workspace
|
|
@@ -78,6 +81,15 @@ Exec options:
|
|
|
78
81
|
--env KEY=VALUE Set an environment variable; repeatable
|
|
79
82
|
--timeout SECONDS Command timeout
|
|
80
83
|
--max-output-bytes BYTES Maximum captured output
|
|
84
|
+
|
|
85
|
+
Log options:
|
|
86
|
+
|
|
87
|
+
--since TIMESTAMP Include entries at or after an RFC 3339 time
|
|
88
|
+
--until TIMESTAMP Include entries before an RFC 3339 time
|
|
89
|
+
--stream stdout|stderr Filter by output stream
|
|
90
|
+
--source workload|execute|process
|
|
91
|
+
Filter by log source
|
|
92
|
+
--limit ENTRIES Maximum entries (default: 1000, max: 5000)
|
|
81
93
|
`;
|
|
82
94
|
const skillHelp = `Manage coding-harness skills
|
|
83
95
|
|
|
@@ -228,8 +240,10 @@ function flag(tokens, name) {
|
|
|
228
240
|
tokens.splice(index, 1);
|
|
229
241
|
return true;
|
|
230
242
|
}
|
|
231
|
-
function
|
|
232
|
-
const
|
|
243
|
+
function globalFlag(tokens, ...names) {
|
|
244
|
+
const separator = tokens.indexOf("--");
|
|
245
|
+
const boundary = separator < 0 ? tokens.length : separator;
|
|
246
|
+
const index = tokens.findIndex((token, position) => position < boundary && names.includes(token));
|
|
233
247
|
if (index < 0)
|
|
234
248
|
return false;
|
|
235
249
|
tokens.splice(index, 1);
|
|
@@ -243,6 +257,21 @@ function positive(value, name) {
|
|
|
243
257
|
throw new UsageError(`${name} must be a positive integer`);
|
|
244
258
|
return parsed;
|
|
245
259
|
}
|
|
260
|
+
function timestamp(value, name) {
|
|
261
|
+
if (value === undefined)
|
|
262
|
+
return undefined;
|
|
263
|
+
const parsed = new Date(value);
|
|
264
|
+
if (Number.isNaN(parsed.valueOf()))
|
|
265
|
+
throw new UsageError(`${name} must be an RFC 3339 timestamp`);
|
|
266
|
+
return parsed.toISOString();
|
|
267
|
+
}
|
|
268
|
+
function oneOf(value, name, values) {
|
|
269
|
+
if (value === undefined)
|
|
270
|
+
return undefined;
|
|
271
|
+
if (!values.includes(value))
|
|
272
|
+
throw new UsageError(`${name} must be one of: ${values.join(", ")}`);
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
246
275
|
function environment(tokens) {
|
|
247
276
|
const values = [];
|
|
248
277
|
for (;;) {
|
|
@@ -338,6 +367,21 @@ function executionOutput(io, json, sandboxId, result) {
|
|
|
338
367
|
}
|
|
339
368
|
return result.exitCode ?? 1;
|
|
340
369
|
}
|
|
370
|
+
function logsOutput(io, json, logs) {
|
|
371
|
+
if (json) {
|
|
372
|
+
emit(io, true, { logs }, "");
|
|
373
|
+
return 0;
|
|
374
|
+
}
|
|
375
|
+
if (!logs.entries.length)
|
|
376
|
+
write(io.stdout, "No logs found.\n");
|
|
377
|
+
for (const entry of logs.entries) {
|
|
378
|
+
const process = entry.process_id ? ` ${entry.process_id}` : "";
|
|
379
|
+
write(io.stdout, `${entry.timestamp}\t${entry.stream}\t${entry.source}${process}\t${entry.message}\n`);
|
|
380
|
+
}
|
|
381
|
+
write(io.stderr, `sandbox=${logs.sandbox_id} entries=${logs.entries.length} retentionSeconds=${logs.retention_seconds}` +
|
|
382
|
+
`${logs.truncated ? " truncated=true" : ""}\n`);
|
|
383
|
+
return 0;
|
|
384
|
+
}
|
|
341
385
|
export async function runCli(argv, supplied = {}) {
|
|
342
386
|
const env = supplied.env ?? process.env;
|
|
343
387
|
const io = supplied.io ?? { stdout: process.stdout, stderr: process.stderr };
|
|
@@ -356,13 +400,13 @@ export async function runCli(argv, supplied = {}) {
|
|
|
356
400
|
const skillText = supplied.readSkill ?? readSkill;
|
|
357
401
|
const syncSkills = supplied.syncManagedSkills ?? syncManagedSkills;
|
|
358
402
|
const args = [...argv];
|
|
359
|
-
const json =
|
|
360
|
-
const versionRequested = args[0] === "version" ||
|
|
403
|
+
const json = globalFlag(args, "--json");
|
|
404
|
+
const versionRequested = args[0] === "version" || globalFlag(args, "--version", "-V", "-v");
|
|
361
405
|
if (versionRequested) {
|
|
362
406
|
emit(io, json, { version: CLI_VERSION }, `${CLI_VERSION}\n`);
|
|
363
407
|
return 0;
|
|
364
408
|
}
|
|
365
|
-
const helpRequested =
|
|
409
|
+
const helpRequested = globalFlag(args, "--help", "-h");
|
|
366
410
|
if (!args.length || args[0] === "help" || helpRequested) {
|
|
367
411
|
const helpTarget = args[0] === "help" ? args[1] : args[0];
|
|
368
412
|
write(io.stdout, helpFor(helpTarget));
|
|
@@ -534,6 +578,20 @@ export async function runCli(argv, supplied = {}) {
|
|
|
534
578
|
emit(io, json, { sandbox }, sandboxLine(sandbox));
|
|
535
579
|
return 0;
|
|
536
580
|
}
|
|
581
|
+
if (action === "logs") {
|
|
582
|
+
const since = timestamp(option(args, "since"), "--since");
|
|
583
|
+
const until = timestamp(option(args, "until"), "--until");
|
|
584
|
+
const stream = oneOf(option(args, "stream"), "--stream", ["stdout", "stderr"]);
|
|
585
|
+
const source = oneOf(option(args, "source"), "--source", ["workload", "execute", "process"]);
|
|
586
|
+
const limit = positive(option(args, "limit"), "--limit");
|
|
587
|
+
if (limit !== undefined && limit > 5_000)
|
|
588
|
+
throw new UsageError("--limit must be 5000 or fewer");
|
|
589
|
+
if (since && until && since >= until)
|
|
590
|
+
throw new UsageError("--since must be earlier than --until");
|
|
591
|
+
if (args.length)
|
|
592
|
+
throw new UsageError(`Unknown sandbox logs option: ${args[0]}`);
|
|
593
|
+
return logsOutput(io, json, await client.logs(id, { since, until, stream, source, limit }));
|
|
594
|
+
}
|
|
537
595
|
if (action === "delete") {
|
|
538
596
|
if (!flag(args, "yes") || args.length)
|
|
539
597
|
throw new UsageError("sandbox delete requires SANDBOX_ID --yes");
|
package/dist/client.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type Sandbox = {
|
|
|
5
5
|
name: string;
|
|
6
6
|
state: "cold" | "running";
|
|
7
7
|
runtimeId: string | null;
|
|
8
|
+
retainedRuntimeId: string | null;
|
|
8
9
|
image: string | null;
|
|
9
10
|
createdAt: number;
|
|
10
11
|
lastUsedAt: number | null;
|
|
@@ -23,6 +24,20 @@ export type Execution = {
|
|
|
23
24
|
stderrTruncated: boolean;
|
|
24
25
|
wallTimeSeconds: number;
|
|
25
26
|
};
|
|
27
|
+
export type SandboxLogEntry = {
|
|
28
|
+
timestamp: string;
|
|
29
|
+
stream: "stdout" | "stderr";
|
|
30
|
+
source: "workload" | "execute" | "process";
|
|
31
|
+
message: string;
|
|
32
|
+
pod_uid: string;
|
|
33
|
+
process_id?: string;
|
|
34
|
+
};
|
|
35
|
+
export type SandboxLogs = {
|
|
36
|
+
sandbox_id: string;
|
|
37
|
+
entries: SandboxLogEntry[];
|
|
38
|
+
truncated: boolean;
|
|
39
|
+
retention_seconds: number;
|
|
40
|
+
};
|
|
26
41
|
export declare class BoxComputeHttpError extends Error {
|
|
27
42
|
readonly status: number;
|
|
28
43
|
readonly code?: string | undefined;
|
|
@@ -46,5 +61,12 @@ export declare class BoxComputeClient {
|
|
|
46
61
|
maxOutputBytes?: number;
|
|
47
62
|
env?: Record<string, string>;
|
|
48
63
|
}): Promise<Execution>;
|
|
64
|
+
logs(id: string, input?: {
|
|
65
|
+
since?: string;
|
|
66
|
+
until?: string;
|
|
67
|
+
stream?: "stdout" | "stderr";
|
|
68
|
+
source?: "workload" | "execute" | "process";
|
|
69
|
+
limit?: number;
|
|
70
|
+
}): Promise<SandboxLogs>;
|
|
49
71
|
delete(id: string): Promise<void>;
|
|
50
72
|
}
|
package/dist/client.js
CHANGED
|
@@ -66,6 +66,21 @@ export class BoxComputeClient {
|
|
|
66
66
|
body: JSON.stringify(input),
|
|
67
67
|
})).result;
|
|
68
68
|
}
|
|
69
|
+
async logs(id, input = {}) {
|
|
70
|
+
const query = new URLSearchParams();
|
|
71
|
+
if (input.since)
|
|
72
|
+
query.set("since", input.since);
|
|
73
|
+
if (input.until)
|
|
74
|
+
query.set("until", input.until);
|
|
75
|
+
if (input.stream)
|
|
76
|
+
query.set("stream", input.stream);
|
|
77
|
+
if (input.source)
|
|
78
|
+
query.set("source", input.source);
|
|
79
|
+
if (input.limit !== undefined)
|
|
80
|
+
query.set("limit", String(input.limit));
|
|
81
|
+
const suffix = query.size ? `?${query}` : "";
|
|
82
|
+
return (await this.request(`/api/v2/sandboxes/${encodeURIComponent(id)}/logs${suffix}`)).logs;
|
|
83
|
+
}
|
|
69
84
|
async delete(id) {
|
|
70
85
|
await this.request(`/api/v2/sandboxes/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
71
86
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boxcompute/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Connect local AI agents to BoxCompute sandboxes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"boxcompute",
|
|
@@ -11,18 +11,19 @@
|
|
|
11
11
|
"homepage": "https://app.boxcompute.ai",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/boxcompute/
|
|
15
|
-
"directory": "apps/cli"
|
|
14
|
+
"url": "git+https://github.com/boxcompute/cli.git"
|
|
16
15
|
},
|
|
17
16
|
"bugs": {
|
|
18
|
-
"url": "https://github.com/boxcompute/
|
|
17
|
+
"url": "https://github.com/boxcompute/cli/issues"
|
|
19
18
|
},
|
|
19
|
+
"license": "UNLICENSED",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"bin": {
|
|
22
22
|
"bxc": "dist/cli.js",
|
|
23
23
|
"bcompute": "dist/cli.js"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
|
+
"CHANGELOG.md",
|
|
26
27
|
"dist",
|
|
27
28
|
"skills"
|
|
28
29
|
],
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
"publishConfig": {
|
|
33
34
|
"access": "public"
|
|
34
35
|
},
|
|
36
|
+
"packageManager": "bun@1.3.14",
|
|
35
37
|
"scripts": {
|
|
36
38
|
"build": "tsc -p tsconfig.build.json",
|
|
37
39
|
"lint": "oxlint --max-warnings 0 src test",
|
|
@@ -44,6 +44,11 @@ sandbox unless the user explicitly places those secrets in scope.
|
|
|
44
44
|
## Lifecycle and safety
|
|
45
45
|
|
|
46
46
|
- Inspect uncertain state with `bxc --json sandbox status SANDBOX_ID`.
|
|
47
|
+
- Read runtime output with `bxc sandbox logs SANDBOX_ID`. Add `--json` for
|
|
48
|
+
structured entries or narrow results with `--since`, `--until`, `--stream`,
|
|
49
|
+
`--source`, and `--limit`. Log reads do not start stopped compute, and remain
|
|
50
|
+
available for the provider's nominal 30-day retention period after deletion;
|
|
51
|
+
storage pressure may shorten that period, so logs are not an archive.
|
|
47
52
|
- Sandboxes persist across commands; do not destroy one merely because the
|
|
48
53
|
current task is finished.
|
|
49
54
|
- `bxc sandbox delete SANDBOX_ID --yes` destroys the remote runtime and is
|