@boxcompute/cli 0.2.0 → 0.2.2
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 +6 -4
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +111 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,9 +61,11 @@ Deploy the server release with both Sandbox API v1 and v2 before publishing CLI
|
|
|
61
61
|
contract. After that, clients need only upgrade the package:
|
|
62
62
|
|
|
63
63
|
```sh
|
|
64
|
-
|
|
64
|
+
bxc update
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
`bxc up` is the short alias. If the automatic update cannot invoke npm, use
|
|
68
|
+
`npm install --global @boxcompute/cli@latest` manually. Updating preserves the
|
|
69
|
+
saved BoxCompute credential and refreshes untouched managed skills before the
|
|
70
|
+
client's next agent session. The new CLI uses v2 for multi-instance sandboxes
|
|
71
|
+
and gives a server-first upgrade message if it reaches an older deployment.
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
2
3
|
import { type Connection } from "./config.js";
|
|
3
4
|
import { detectHarnesses, installSkill, readSkill, removeSkill, syncManagedSkills } from "./skill.js";
|
|
4
5
|
type Io = {
|
|
@@ -12,6 +13,7 @@ export type CliDependencies = {
|
|
|
12
13
|
now?: () => number;
|
|
13
14
|
sleep?: (milliseconds: number) => Promise<void>;
|
|
14
15
|
openBrowser?: (url: string) => void;
|
|
16
|
+
installUpdate?: (version: string) => Promise<void>;
|
|
15
17
|
loadConnection?: (env: NodeJS.ProcessEnv) => Promise<Connection>;
|
|
16
18
|
loadSavedUrl?: (env: NodeJS.ProcessEnv) => Promise<string | null>;
|
|
17
19
|
saveConnection?: (url: string, token: string, env: NodeJS.ProcessEnv) => Promise<void>;
|
|
@@ -22,6 +24,18 @@ export type CliDependencies = {
|
|
|
22
24
|
readSkill?: typeof readSkill;
|
|
23
25
|
syncManagedSkills?: typeof syncManagedSkills;
|
|
24
26
|
};
|
|
27
|
+
type BrowserRuntime = {
|
|
28
|
+
env?: NodeJS.ProcessEnv;
|
|
29
|
+
kernelRelease?: string;
|
|
30
|
+
spawn?: typeof spawn;
|
|
31
|
+
system?: NodeJS.Platform;
|
|
32
|
+
};
|
|
33
|
+
export declare function browserLaunch(url: string, runtime?: Pick<BrowserRuntime, "env" | "kernelRelease" | "system">): {
|
|
34
|
+
command: string;
|
|
35
|
+
args: string[];
|
|
36
|
+
detached: boolean;
|
|
37
|
+
};
|
|
38
|
+
export declare function openBrowser(url: string, runtime?: BrowserRuntime): void;
|
|
25
39
|
export declare function runCli(argv: string[], supplied?: CliDependencies): Promise<number>;
|
|
26
40
|
export declare function formatCliError(error: unknown): string;
|
|
27
41
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { readFileSync, realpathSync } from "node:fs";
|
|
4
|
-
import { hostname, platform } from "node:os";
|
|
4
|
+
import { hostname, platform, release } from "node:os";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { BoxComputeClient, BoxComputeHttpError, publicRequest, } from "./client.js";
|
|
7
7
|
import { clearConnection, loadConnection, loadSavedUrl, saveConnection, } from "./config.js";
|
|
@@ -15,6 +15,7 @@ Usage: bxc [options] [command]
|
|
|
15
15
|
Commands:
|
|
16
16
|
|
|
17
17
|
version Print the version number and exit
|
|
18
|
+
update [alias: up] Update the CLI to the latest npm release
|
|
18
19
|
login Log in through BoxCompute in your browser
|
|
19
20
|
logout Revoke and remove the saved CLI credential
|
|
20
21
|
auth [alias: login] Authentication commands
|
|
@@ -48,6 +49,7 @@ Login options:
|
|
|
48
49
|
Examples:
|
|
49
50
|
|
|
50
51
|
$ bxc login
|
|
52
|
+
$ bxc update
|
|
51
53
|
$ bxc skill detect
|
|
52
54
|
$ bxc skill install
|
|
53
55
|
$ bxc workspaces
|
|
@@ -105,13 +107,104 @@ Supported harnesses:
|
|
|
105
107
|
class UsageError extends Error {
|
|
106
108
|
constructor(message) { super(message); this.name = "UsageError"; }
|
|
107
109
|
}
|
|
108
|
-
function
|
|
109
|
-
const system = platform();
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
export function browserLaunch(url, runtime = {}) {
|
|
111
|
+
const system = runtime.system ?? platform();
|
|
112
|
+
const env = runtime.env ?? process.env;
|
|
113
|
+
const kernelRelease = runtime.kernelRelease ?? release();
|
|
114
|
+
const isWsl = system === "linux" && Boolean(env.WSL_DISTRO_NAME || env.WSL_INTEROP || /microsoft/i.test(kernelRelease));
|
|
115
|
+
if (isWsl)
|
|
116
|
+
return { command: "explorer.exe", args: [url], detached: false };
|
|
117
|
+
if (system === "darwin")
|
|
118
|
+
return { command: "open", args: [url], detached: true };
|
|
119
|
+
if (system === "win32")
|
|
120
|
+
return { command: "cmd", args: ["/c", "start", "", url], detached: true };
|
|
121
|
+
return { command: "xdg-open", args: [url], detached: true };
|
|
122
|
+
}
|
|
123
|
+
export function openBrowser(url, runtime = {}) {
|
|
124
|
+
const launch = browserLaunch(url, runtime);
|
|
125
|
+
try {
|
|
126
|
+
const child = (runtime.spawn ?? spawn)(launch.command, launch.args, {
|
|
127
|
+
detached: launch.detached,
|
|
128
|
+
stdio: "ignore",
|
|
129
|
+
});
|
|
130
|
+
child.on("error", () => undefined);
|
|
131
|
+
child.unref();
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// The approval URL is always printed before this best-effort launch. Keep
|
|
135
|
+
// polling so headless shells and restricted WSL interop can authenticate.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function releaseVersion(value) {
|
|
139
|
+
if (typeof value !== "string")
|
|
140
|
+
return null;
|
|
141
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(value);
|
|
142
|
+
if (!match)
|
|
143
|
+
return null;
|
|
144
|
+
const parts = match.slice(1).map(Number);
|
|
145
|
+
return parts.every(Number.isSafeInteger) ? parts : null;
|
|
146
|
+
}
|
|
147
|
+
function compareReleaseVersions(left, right) {
|
|
148
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
149
|
+
if (left[index] !== right[index])
|
|
150
|
+
return left[index] - right[index];
|
|
151
|
+
}
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
async function installCliUpdate(version) {
|
|
155
|
+
const executable = platform() === "win32" ? "npm.cmd" : "npm";
|
|
156
|
+
await new Promise((resolve, reject) => {
|
|
157
|
+
let child;
|
|
158
|
+
try {
|
|
159
|
+
child = spawn(executable, ["install", "--global", `@boxcompute/cli@${version}`], {
|
|
160
|
+
stdio: ["ignore", "ignore", "inherit"],
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
reject(error);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
child.once("error", reject);
|
|
168
|
+
child.once("exit", (code) => {
|
|
169
|
+
if (code === 0)
|
|
170
|
+
resolve();
|
|
171
|
+
else
|
|
172
|
+
reject(new Error(`npm exited with code ${code ?? "unknown"}`));
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async function updateCli(dependencies) {
|
|
177
|
+
let response;
|
|
178
|
+
try {
|
|
179
|
+
response = await dependencies.fetch("https://registry.npmjs.org/%40boxcompute%2Fcli/latest", {
|
|
180
|
+
headers: { accept: "application/json" },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
throw new Error(`Could not check npm for updates: ${error?.message ?? String(error)}`);
|
|
185
|
+
}
|
|
186
|
+
if (!response.ok)
|
|
187
|
+
throw new Error(`Could not check npm for updates (HTTP ${response.status})`);
|
|
188
|
+
const latest = (await response.json()).version;
|
|
189
|
+
const currentParts = releaseVersion(CLI_VERSION);
|
|
190
|
+
const latestParts = releaseVersion(latest);
|
|
191
|
+
if (!currentParts || !latestParts || typeof latest !== "string") {
|
|
192
|
+
throw new Error("npm returned an invalid BoxCompute CLI version");
|
|
193
|
+
}
|
|
194
|
+
if (compareReleaseVersions(latestParts, currentParts) <= 0) {
|
|
195
|
+
emit(dependencies.io, dependencies.json, { updated: false, version: CLI_VERSION }, `BoxCompute CLI is already up to date (${CLI_VERSION}).\n`);
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
write(dependencies.io.stderr, `Updating BoxCompute CLI from ${CLI_VERSION} to ${latest}…\n`);
|
|
199
|
+
try {
|
|
200
|
+
await dependencies.install(latest);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
throw new Error(`Could not install @boxcompute/cli@${latest}: ${error?.message ?? String(error)}. ` +
|
|
204
|
+
`Run \`npm install --global @boxcompute/cli@${latest}\` manually.`);
|
|
205
|
+
}
|
|
206
|
+
emit(dependencies.io, dependencies.json, { updated: true, previousVersion: CLI_VERSION, version: latest }, `Updated BoxCompute CLI to ${latest}.\n`);
|
|
207
|
+
return 0;
|
|
115
208
|
}
|
|
116
209
|
const delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
117
210
|
const write = (stream, value) => { stream.write(value); };
|
|
@@ -251,7 +344,8 @@ export async function runCli(argv, supplied = {}) {
|
|
|
251
344
|
const fetchImpl = supplied.fetch ?? fetch;
|
|
252
345
|
const now = supplied.now ?? Date.now;
|
|
253
346
|
const sleep = supplied.sleep ?? delay;
|
|
254
|
-
const
|
|
347
|
+
const openBrowserImpl = supplied.openBrowser ?? openBrowser;
|
|
348
|
+
const installUpdate = supplied.installUpdate ?? installCliUpdate;
|
|
255
349
|
const load = supplied.loadConnection ?? loadConnection;
|
|
256
350
|
const savedUrl = supplied.loadSavedUrl ?? loadSavedUrl;
|
|
257
351
|
const save = supplied.saveConnection ?? saveConnection;
|
|
@@ -277,6 +371,8 @@ export async function runCli(argv, supplied = {}) {
|
|
|
277
371
|
let command = args.shift();
|
|
278
372
|
if (command === "login")
|
|
279
373
|
command = "auth";
|
|
374
|
+
if (command === "up")
|
|
375
|
+
command = "update";
|
|
280
376
|
if (command === "skills")
|
|
281
377
|
command = "skill";
|
|
282
378
|
if (command === "list" || command === "ls")
|
|
@@ -323,7 +419,12 @@ export async function runCli(argv, supplied = {}) {
|
|
|
323
419
|
emit(io, json, { authenticated: false }, "BoxCompute CLI credential revoked and removed.\n");
|
|
324
420
|
return 0;
|
|
325
421
|
}
|
|
326
|
-
return authenticate(args, { env, io, json, fetch: fetchImpl, now, sleep, openBrowser, loadSavedUrl: savedUrl, saveConnection: save });
|
|
422
|
+
return authenticate(args, { env, io, json, fetch: fetchImpl, now, sleep, openBrowser: openBrowserImpl, loadSavedUrl: savedUrl, saveConnection: save });
|
|
423
|
+
}
|
|
424
|
+
if (command === "update") {
|
|
425
|
+
if (args.length)
|
|
426
|
+
throw new UsageError("update takes no options");
|
|
427
|
+
return updateCli({ fetch: fetchImpl, install: installUpdate, io, json });
|
|
327
428
|
}
|
|
328
429
|
if (command === "skill") {
|
|
329
430
|
let action = args.shift();
|