@slock-ai/computer 0.0.8 → 0.0.9
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/dist/index.js +163 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -314,9 +314,16 @@ function formatUpgradeLogTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
|
314
314
|
|
|
315
315
|
// src/output.ts
|
|
316
316
|
var CliExit = class extends Error {
|
|
317
|
-
|
|
318
|
-
|
|
317
|
+
/**
|
|
318
|
+
* v8.3.3 PR-2c — carry the closed-set / stderr token through the thrown
|
|
319
|
+
* error so callers can pattern-match without parsing stderr. Optional
|
|
320
|
+
* because some callsites throw `new CliExit(code)` directly without a
|
|
321
|
+
* named token (e.g. the harness EX_CONFIG paths).
|
|
322
|
+
*/
|
|
323
|
+
constructor(exitCode, code) {
|
|
324
|
+
super(`CliExit(${exitCode}${code ? ` ${code}` : ""})`);
|
|
319
325
|
this.exitCode = exitCode;
|
|
326
|
+
this.code = code;
|
|
320
327
|
this.name = "CliExit";
|
|
321
328
|
}
|
|
322
329
|
};
|
|
@@ -327,7 +334,7 @@ function info(line) {
|
|
|
327
334
|
function fail(code, message, exitCode = 1) {
|
|
328
335
|
process.stderr.write(`${JSON.stringify({ ok: false, code, message })}
|
|
329
336
|
`);
|
|
330
|
-
throw new CliExit(exitCode);
|
|
337
|
+
throw new CliExit(exitCode, code);
|
|
331
338
|
}
|
|
332
339
|
|
|
333
340
|
// src/serverUrl.ts
|
|
@@ -351,7 +358,11 @@ async function runLogin(opts) {
|
|
|
351
358
|
try {
|
|
352
359
|
grant = await client.authorize("slock-computer");
|
|
353
360
|
} catch (err) {
|
|
354
|
-
|
|
361
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
362
|
+
fail(
|
|
363
|
+
"DEVICE_AUTHORIZE_FAILED",
|
|
364
|
+
`Could not start device login at ${baseUrl}: ${reason}. Check that the server URL is correct and reachable.`
|
|
365
|
+
);
|
|
355
366
|
}
|
|
356
367
|
const verificationUri = grant.verificationUriComplete || grant.verificationUri;
|
|
357
368
|
const verifyUrl = new URL(verificationUri, baseUrl).toString();
|
|
@@ -3396,7 +3407,49 @@ async function locateStagedTarball(stagedPath) {
|
|
|
3396
3407
|
// src/upgradeLog.ts
|
|
3397
3408
|
import { chmod as chmod5, mkdir as mkdir12, open as open2 } from "fs/promises";
|
|
3398
3409
|
var FILE_MODE = 384;
|
|
3410
|
+
var UPGRADE_ERROR_CODES = [
|
|
3411
|
+
"UPGRADE_DEPS_CHANGED",
|
|
3412
|
+
"UPGRADE_NETWORK_FAILED",
|
|
3413
|
+
"UPGRADE_INTEGRITY_FAILED",
|
|
3414
|
+
"UPGRADE_SWAP_FAILED",
|
|
3415
|
+
"UPGRADE_RESTART_FAILED",
|
|
3416
|
+
"UPGRADE_NO_TARGET",
|
|
3417
|
+
"UPGRADE_ALREADY_RUNNING"
|
|
3418
|
+
];
|
|
3419
|
+
var UPGRADE_ERROR_CODE_SET = new Set(UPGRADE_ERROR_CODES);
|
|
3420
|
+
function assertUpgradeLogEntry(entry) {
|
|
3421
|
+
const e = entry;
|
|
3422
|
+
if (e.outcome === "ok") {
|
|
3423
|
+
if (e.errorCode !== void 0) {
|
|
3424
|
+
throw new TypeError(
|
|
3425
|
+
`UpgradeLogEntry contract violation: outcome="ok" must not carry errorCode (got "${e.errorCode}").`
|
|
3426
|
+
);
|
|
3427
|
+
}
|
|
3428
|
+
} else if (e.outcome === "err") {
|
|
3429
|
+
if (e.errorCode === void 0) {
|
|
3430
|
+
throw new TypeError(
|
|
3431
|
+
`UpgradeLogEntry contract violation: outcome="err" requires errorCode from the v8.3.3 closed-set (${UPGRADE_ERROR_CODES.join(" | ")}).`
|
|
3432
|
+
);
|
|
3433
|
+
}
|
|
3434
|
+
if (!UPGRADE_ERROR_CODE_SET.has(e.errorCode)) {
|
|
3435
|
+
throw new TypeError(
|
|
3436
|
+
`UpgradeLogEntry contract violation: errorCode "${e.errorCode}" is not in the v8.3.3 closed 7-value set (${UPGRADE_ERROR_CODES.join(" | ")}).`
|
|
3437
|
+
);
|
|
3438
|
+
}
|
|
3439
|
+
} else {
|
|
3440
|
+
throw new TypeError(
|
|
3441
|
+
`UpgradeLogEntry contract violation: outcome must be "ok" or "err" (got "${e.outcome}").`
|
|
3442
|
+
);
|
|
3443
|
+
}
|
|
3444
|
+
if (typeof e.fromBundle?.computerVersion !== "string" || e.fromBundle.computerVersion.length === 0) {
|
|
3445
|
+
throw new TypeError(`UpgradeLogEntry contract violation: fromBundle.computerVersion must be a non-empty string.`);
|
|
3446
|
+
}
|
|
3447
|
+
if (typeof e.toBundle?.computerVersion !== "string" || e.toBundle.computerVersion.length === 0) {
|
|
3448
|
+
throw new TypeError(`UpgradeLogEntry contract violation: toBundle.computerVersion must be a non-empty string.`);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3399
3451
|
async function appendUpgradeLogEntry(slockHome, entry) {
|
|
3452
|
+
assertUpgradeLogEntry(entry);
|
|
3400
3453
|
await mkdir12(computerDir(slockHome), { recursive: true });
|
|
3401
3454
|
const path2 = upgradeLogPath(slockHome);
|
|
3402
3455
|
const at = entry.at ?? formatUpgradeLogTimestamp();
|
|
@@ -3417,6 +3470,19 @@ async function appendUpgradeLogEntry(slockHome, entry) {
|
|
|
3417
3470
|
function isEphemeralNpxContext(binaryDir) {
|
|
3418
3471
|
return binaryDir.split(/[\\/]/).includes("_npx");
|
|
3419
3472
|
}
|
|
3473
|
+
async function readBundledDaemonVersion(binaryDir) {
|
|
3474
|
+
try {
|
|
3475
|
+
const pkgPath = join7(binaryDir, "package.json");
|
|
3476
|
+
const raw = await readFile12(pkgPath, "utf8");
|
|
3477
|
+
const parsed = JSON.parse(raw);
|
|
3478
|
+
const pinned = parsed.dependencies?.["@slock-ai/daemon"];
|
|
3479
|
+
if (typeof pinned !== "string" || pinned.length === 0) return null;
|
|
3480
|
+
if (pinned === "workspace:*") return null;
|
|
3481
|
+
return pinned;
|
|
3482
|
+
} catch {
|
|
3483
|
+
return null;
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3420
3486
|
async function defaultSpawnFreshSupervisor(slockHome) {
|
|
3421
3487
|
await spawnDetachedSupervisor(slockHome);
|
|
3422
3488
|
}
|
|
@@ -3470,16 +3536,20 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
3470
3536
|
if (isEphemeralFn(currentBinaryDir)) {
|
|
3471
3537
|
fail(
|
|
3472
3538
|
"UPGRADE_EPHEMERAL_CONTEXT",
|
|
3473
|
-
`
|
|
3474
|
-
The currently installed Computer service runs from a different install root, which this command cannot reach. To upgrade, manually replace the persistent install:
|
|
3475
|
-
npx -y @slock-ai/computer@latest stop
|
|
3476
|
-
rm -rf ~/.npm/_npx/<persistent-hash>/
|
|
3477
|
-
npx -y @slock-ai/computer@latest start
|
|
3478
|
-
(Find the persistent hash via: ls -d ~/.npm/_npx/*/node_modules/@slock-ai/computer)`
|
|
3539
|
+
`You're running upgrade via the npx ephemeral entrypoint, which can't reach your installed Computer service. Please run the installed \`slock-computer upgrade\` instead. If \`slock-computer\` isn't on your PATH, install it globally first: \`npm i -g @slock-ai/computer@latest\`.`
|
|
3479
3540
|
);
|
|
3480
3541
|
}
|
|
3481
3542
|
if (targetVersion === fromVersion) {
|
|
3482
|
-
info(`Already at ${fromVersion} (channel ${channel2}).
|
|
3543
|
+
info(`Already at ${fromVersion} (channel ${channel2}). No upgrade target.`);
|
|
3544
|
+
await appendUpgradeLogEntry(slockHome, {
|
|
3545
|
+
fromBundle: { computerVersion: fromVersion },
|
|
3546
|
+
toBundle: { computerVersion: fromVersion },
|
|
3547
|
+
channel: channel2,
|
|
3548
|
+
trigger: opts.trigger ?? "cli",
|
|
3549
|
+
outcome: "err",
|
|
3550
|
+
errorCode: "UPGRADE_NO_TARGET"
|
|
3551
|
+
}).catch(() => {
|
|
3552
|
+
});
|
|
3483
3553
|
return;
|
|
3484
3554
|
}
|
|
3485
3555
|
info(`Planning upgrade: ${fromVersion} \u2192 ${targetVersion} (channel ${channel2}).`);
|
|
@@ -3530,9 +3600,9 @@ The currently installed Computer service runs from a different install root, whi
|
|
|
3530
3600
|
spawnFreshSupervisor: () => spawnFreshSupervisor(slockHome)
|
|
3531
3601
|
}
|
|
3532
3602
|
});
|
|
3533
|
-
const
|
|
3534
|
-
|
|
3535
|
-
}
|
|
3603
|
+
const readBundledFn = deps.readBundledDaemonVersion ?? readBundledDaemonVersion;
|
|
3604
|
+
const bundledDaemonVersion = await readBundledFn(currentBinaryDir);
|
|
3605
|
+
const bundle = (version) => bundledDaemonVersion !== null ? { computerVersion: version, bundledDaemonVersion } : { computerVersion: version };
|
|
3536
3606
|
const logTrigger = opts.trigger ?? "cli";
|
|
3537
3607
|
if (outcome.ok) {
|
|
3538
3608
|
info(
|
|
@@ -3548,8 +3618,28 @@ The currently installed Computer service runs from a different install root, whi
|
|
|
3548
3618
|
});
|
|
3549
3619
|
return;
|
|
3550
3620
|
}
|
|
3551
|
-
const code = mapFailurePhaseToCode(outcome);
|
|
3552
3621
|
const rolledBack = outcome.rolledBack === true ? " (swap rolled back)" : "";
|
|
3622
|
+
if (outcome.phase === "drain") {
|
|
3623
|
+
info(
|
|
3624
|
+
`Upgrade deferred: --drain=defer and daemons are busy. Re-run when idle, or use --force to interrupt in-flight turns.`
|
|
3625
|
+
);
|
|
3626
|
+
return;
|
|
3627
|
+
}
|
|
3628
|
+
if (outcome.phase === "cleanup") {
|
|
3629
|
+
info(
|
|
3630
|
+
`Upgrade ${fromVersion} \u2192 ${targetVersion} succeeded; cleanup phase reported a non-fatal issue: ${outcome.reason ?? "unknown"}. Active layout + supervisor are healthy.`
|
|
3631
|
+
);
|
|
3632
|
+
await appendUpgradeLogEntry(slockHome, {
|
|
3633
|
+
fromBundle: bundle(fromVersion),
|
|
3634
|
+
toBundle: bundle(targetVersion),
|
|
3635
|
+
channel: channel2,
|
|
3636
|
+
trigger: logTrigger,
|
|
3637
|
+
outcome: "ok"
|
|
3638
|
+
}).catch(() => {
|
|
3639
|
+
});
|
|
3640
|
+
return;
|
|
3641
|
+
}
|
|
3642
|
+
const code = mapFailurePhaseToCode(outcome);
|
|
3553
3643
|
await appendUpgradeLogEntry(slockHome, {
|
|
3554
3644
|
fromBundle: bundle(fromVersion),
|
|
3555
3645
|
toBundle: bundle(targetVersion),
|
|
@@ -3566,26 +3656,27 @@ The currently installed Computer service runs from a different install root, whi
|
|
|
3566
3656
|
}
|
|
3567
3657
|
function mapFailurePhaseToCode(outcome) {
|
|
3568
3658
|
switch (outcome.phase) {
|
|
3569
|
-
case "drain":
|
|
3570
|
-
return "UPGRADE_DEFERRED";
|
|
3571
3659
|
case "stage":
|
|
3572
|
-
return "
|
|
3660
|
+
return "UPGRADE_NETWORK_FAILED";
|
|
3573
3661
|
case "verify":
|
|
3574
|
-
return "
|
|
3662
|
+
return "UPGRADE_INTEGRITY_FAILED";
|
|
3575
3663
|
case "preflight":
|
|
3576
3664
|
return "UPGRADE_DEPS_CHANGED";
|
|
3577
3665
|
case "snapshot":
|
|
3578
|
-
return "
|
|
3666
|
+
return "UPGRADE_SWAP_FAILED";
|
|
3579
3667
|
case "extract":
|
|
3580
|
-
return "
|
|
3668
|
+
return "UPGRADE_INTEGRITY_FAILED";
|
|
3581
3669
|
case "swap":
|
|
3582
3670
|
return "UPGRADE_SWAP_FAILED";
|
|
3583
3671
|
case "restart":
|
|
3584
3672
|
return "UPGRADE_RESTART_FAILED";
|
|
3585
3673
|
case "rolling_health":
|
|
3586
|
-
return "
|
|
3674
|
+
return "UPGRADE_RESTART_FAILED";
|
|
3675
|
+
case "drain":
|
|
3587
3676
|
case "cleanup":
|
|
3588
|
-
|
|
3677
|
+
throw new Error(
|
|
3678
|
+
`mapFailurePhaseToCode: phase=${outcome.phase} should be handled by caller (silenced), not mapped to a closed-set code`
|
|
3679
|
+
);
|
|
3589
3680
|
}
|
|
3590
3681
|
}
|
|
3591
3682
|
async function defaultFetchDistTags() {
|
|
@@ -3946,6 +4037,19 @@ async function defaultCurrentVersionLocal() {
|
|
|
3946
4037
|
);
|
|
3947
4038
|
}
|
|
3948
4039
|
|
|
4040
|
+
// src/version.ts
|
|
4041
|
+
import { createRequire as createRequire2 } from "module";
|
|
4042
|
+
function readComputerVersion(moduleUrl = import.meta.url) {
|
|
4043
|
+
try {
|
|
4044
|
+
const require2 = createRequire2(moduleUrl);
|
|
4045
|
+
const pkg = require2("../package.json");
|
|
4046
|
+
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "0.0.0-dev";
|
|
4047
|
+
} catch {
|
|
4048
|
+
return "0.0.0-dev";
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
var COMPUTER_VERSION = readComputerVersion();
|
|
4052
|
+
|
|
3949
4053
|
// src/index.ts
|
|
3950
4054
|
function withCliExit(fn) {
|
|
3951
4055
|
return async (...args) => {
|
|
@@ -3961,7 +4065,7 @@ function withCliExit(fn) {
|
|
|
3961
4065
|
};
|
|
3962
4066
|
}
|
|
3963
4067
|
var program = new Command();
|
|
3964
|
-
program.name("slock-computer").description("Slock Computer \u2014 local-machine control plane (login + N per-server attachments).").version(
|
|
4068
|
+
program.name("slock-computer").description("Slock Computer \u2014 local-machine control plane (login + N per-server attachments).").version(COMPUTER_VERSION);
|
|
3965
4069
|
program.command("login").description("Log in via device-code (one user identity per Computer / SLOCK_HOME).").option("--server-url <url>", `Slock API base URL; defaults to SLOCK_SERVER_URL or ${DEFAULT_SLOCK_SERVER_URL}`).action(withCliExit(async (opts) => {
|
|
3966
4070
|
await runLogin({ serverUrl: opts.serverUrl });
|
|
3967
4071
|
}));
|
|
@@ -4081,15 +4185,41 @@ program.command("upgrade").description(
|
|
|
4081
4185
|
}
|
|
4082
4186
|
drain = opts.drain;
|
|
4083
4187
|
}
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4188
|
+
const slockHome = resolveSlockHome();
|
|
4189
|
+
try {
|
|
4190
|
+
await withMutationLock(
|
|
4191
|
+
() => runUpgradeCli(slockHome, {
|
|
4192
|
+
dryRun: opts.dryRun,
|
|
4193
|
+
channel: opts.channel,
|
|
4194
|
+
targetVersion: opts.targetVersion,
|
|
4195
|
+
drain,
|
|
4196
|
+
force: opts.force
|
|
4197
|
+
})
|
|
4198
|
+
);
|
|
4199
|
+
} catch (err) {
|
|
4200
|
+
if (err instanceof CliExit && err.code === "CONCURRENT_OPERATION") {
|
|
4201
|
+
try {
|
|
4202
|
+
const currentVersion = await defaultCurrentVersion();
|
|
4203
|
+
const channel2 = opts.channel ?? await readChannel(slockHome);
|
|
4204
|
+
await appendUpgradeLogEntry(slockHome, {
|
|
4205
|
+
fromBundle: { computerVersion: currentVersion },
|
|
4206
|
+
toBundle: { computerVersion: currentVersion },
|
|
4207
|
+
channel: channel2,
|
|
4208
|
+
trigger: "cli",
|
|
4209
|
+
outcome: "err",
|
|
4210
|
+
errorCode: "UPGRADE_ALREADY_RUNNING"
|
|
4211
|
+
}).catch(() => {
|
|
4212
|
+
});
|
|
4213
|
+
} catch {
|
|
4214
|
+
}
|
|
4215
|
+
process.stderr.write(
|
|
4216
|
+
`slock-computer: UPGRADE_ALREADY_RUNNING: Another upgrade attempt is currently holding the mutation lock. Wait for it to finish and retry, or check \`~/.slock/computer/upgrade.log\` for the in-flight attempt.
|
|
4217
|
+
`
|
|
4218
|
+
);
|
|
4219
|
+
throw new CliExit(1, "UPGRADE_ALREADY_RUNNING");
|
|
4220
|
+
}
|
|
4221
|
+
throw err;
|
|
4222
|
+
}
|
|
4093
4223
|
}
|
|
4094
4224
|
)
|
|
4095
4225
|
);
|
package/package.json
CHANGED