@slock-ai/computer 0.0.16-alpha.0 → 0.0.16
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 +126 -67
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +63 -8
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -29879,11 +29879,11 @@ function serverDir(slockHome, serverId) {
|
|
|
29879
29879
|
function serverAttachmentPath(slockHome, serverId) {
|
|
29880
29880
|
return path2.join(serverDir(slockHome, serverId), "runner.state.json");
|
|
29881
29881
|
}
|
|
29882
|
-
function
|
|
29883
|
-
return path2.join(serverDir(slockHome, serverId), "
|
|
29882
|
+
function serverRunnerPidPath(slockHome, serverId) {
|
|
29883
|
+
return path2.join(serverDir(slockHome, serverId), "server-runner.pid");
|
|
29884
29884
|
}
|
|
29885
|
-
function
|
|
29886
|
-
return path2.join(serverDir(slockHome, serverId), "
|
|
29885
|
+
function serverRunnerLogPath(slockHome, serverId) {
|
|
29886
|
+
return path2.join(serverDir(slockHome, serverId), "server-runner.log");
|
|
29887
29887
|
}
|
|
29888
29888
|
function serverManagedFlagPath(slockHome, serverId) {
|
|
29889
29889
|
return path2.join(serverDir(slockHome, serverId), "managed.flag");
|
|
@@ -29891,17 +29891,30 @@ function serverManagedFlagPath(slockHome, serverId) {
|
|
|
29891
29891
|
function serverHealthPath(slockHome, serverId) {
|
|
29892
29892
|
return path2.join(serverDir(slockHome, serverId), "health.json");
|
|
29893
29893
|
}
|
|
29894
|
+
function serviceRunDir(slockHome) {
|
|
29895
|
+
return path2.join(computerDir(slockHome), "run");
|
|
29896
|
+
}
|
|
29894
29897
|
function serviceStatePath(slockHome) {
|
|
29895
|
-
return path2.join(
|
|
29898
|
+
return path2.join(serviceRunDir(slockHome), "service.state.json");
|
|
29896
29899
|
}
|
|
29897
29900
|
function servicePidPath(slockHome) {
|
|
29901
|
+
return path2.join(serviceRunDir(slockHome), "service.pid");
|
|
29902
|
+
}
|
|
29903
|
+
function legacyServicePidPath(slockHome) {
|
|
29898
29904
|
return path2.join(computerDir(slockHome), "service.pid");
|
|
29899
29905
|
}
|
|
29900
29906
|
function legacySupervisorPidPath(slockHome) {
|
|
29901
29907
|
return path2.join(computerDir(slockHome), "supervisor.pid");
|
|
29902
29908
|
}
|
|
29909
|
+
function servicePidReadFallback(slockHome) {
|
|
29910
|
+
return [
|
|
29911
|
+
servicePidPath(slockHome),
|
|
29912
|
+
legacyServicePidPath(slockHome),
|
|
29913
|
+
legacySupervisorPidPath(slockHome)
|
|
29914
|
+
];
|
|
29915
|
+
}
|
|
29903
29916
|
function serviceLogPath(slockHome) {
|
|
29904
|
-
return path2.join(
|
|
29917
|
+
return path2.join(serviceRunDir(slockHome), "service.log");
|
|
29905
29918
|
}
|
|
29906
29919
|
function serviceVersionPath(slockHome) {
|
|
29907
29920
|
return path2.join(computerDir(slockHome), "service-version.json");
|
|
@@ -30877,7 +30890,7 @@ async function cleanupAllStalePidfiles(slockHome) {
|
|
|
30877
30890
|
}
|
|
30878
30891
|
const attached = await listAttachedServerIds(slockHome);
|
|
30879
30892
|
for (const sid of attached) {
|
|
30880
|
-
const p =
|
|
30893
|
+
const p = serverRunnerPidPath(slockHome, sid);
|
|
30881
30894
|
if (await cleanupStalePidfile(p)) cleaned.push(p);
|
|
30882
30895
|
}
|
|
30883
30896
|
return cleaned;
|
|
@@ -30925,7 +30938,7 @@ async function cleanupOrphanProcesses(slockHome, psSpawn) {
|
|
|
30925
30938
|
const supPid = await readPidfileAt(servicePidPath(slockHome));
|
|
30926
30939
|
if (supPid !== null) knownPids.add(supPid);
|
|
30927
30940
|
for (const sid of managed) {
|
|
30928
|
-
const pid = await readPidfileAt(
|
|
30941
|
+
const pid = await readPidfileAt(serverRunnerPidPath(slockHome, sid));
|
|
30929
30942
|
if (pid !== null) knownPids.add(pid);
|
|
30930
30943
|
}
|
|
30931
30944
|
if (supPid === null) return signaled;
|
|
@@ -31168,6 +31181,50 @@ async function emitRunnerStateTransition(slockHome, serverId, fromState, toState
|
|
|
31168
31181
|
|
|
31169
31182
|
// src/services/start.ts
|
|
31170
31183
|
init_esm_shims();
|
|
31184
|
+
|
|
31185
|
+
// src/internal/service-pid-fallback.ts
|
|
31186
|
+
init_esm_shims();
|
|
31187
|
+
async function findLiveServicePid(slockHome, deps = {}) {
|
|
31188
|
+
const readPidfile = deps.readPidfile ?? readPidfileAt;
|
|
31189
|
+
const isAlive = deps.isProcessAlive ?? isProcessAlive2;
|
|
31190
|
+
const clearStale = deps.clearPidfile ?? clearPidfileAt;
|
|
31191
|
+
return walkFallback(slockHome, readPidfile, isAlive, clearStale);
|
|
31192
|
+
}
|
|
31193
|
+
async function findLiveServicePidReadOnly(slockHome, deps = {}) {
|
|
31194
|
+
const readPidfile = deps.readPidfile ?? readPidfileAt;
|
|
31195
|
+
const isAlive = deps.isProcessAlive ?? isProcessAlive2;
|
|
31196
|
+
return walkFallback(slockHome, readPidfile, isAlive, async () => void 0);
|
|
31197
|
+
}
|
|
31198
|
+
async function walkFallback(slockHome, readPidfile, isAlive, clearStale) {
|
|
31199
|
+
const candidates = servicePidReadFallback(slockHome);
|
|
31200
|
+
let firstStalePidfile = null;
|
|
31201
|
+
let firstStalePid = null;
|
|
31202
|
+
for (const candidate of candidates) {
|
|
31203
|
+
const candidatePid = await readPidfile(candidate);
|
|
31204
|
+
if (candidatePid === null) continue;
|
|
31205
|
+
if (isAlive(candidatePid)) {
|
|
31206
|
+
return {
|
|
31207
|
+
pid: candidatePid,
|
|
31208
|
+
pidfilePath: candidate,
|
|
31209
|
+
firstStalePidfile,
|
|
31210
|
+
firstStalePid
|
|
31211
|
+
};
|
|
31212
|
+
}
|
|
31213
|
+
await clearStale(candidate);
|
|
31214
|
+
if (firstStalePidfile === null) {
|
|
31215
|
+
firstStalePidfile = candidate;
|
|
31216
|
+
firstStalePid = candidatePid;
|
|
31217
|
+
}
|
|
31218
|
+
}
|
|
31219
|
+
return {
|
|
31220
|
+
pid: null,
|
|
31221
|
+
pidfilePath: candidates[0],
|
|
31222
|
+
firstStalePidfile,
|
|
31223
|
+
firstStalePid
|
|
31224
|
+
};
|
|
31225
|
+
}
|
|
31226
|
+
|
|
31227
|
+
// src/services/start.ts
|
|
31171
31228
|
var START_ENSURE_TIMEOUT_MS = 15e3;
|
|
31172
31229
|
var START_ENSURE_POLL_INTERVAL_MS = 100;
|
|
31173
31230
|
function emit4(opts, event) {
|
|
@@ -31189,7 +31246,7 @@ async function waitForManagedDaemonPids(slockHome, serverIds, opts) {
|
|
|
31189
31246
|
while (ready.size < serverIds.length) {
|
|
31190
31247
|
for (const serverId of serverIds) {
|
|
31191
31248
|
if (ready.has(serverId)) continue;
|
|
31192
|
-
const pid = await readPidfile(
|
|
31249
|
+
const pid = await readPidfile(serverRunnerPidPath(slockHome, serverId));
|
|
31193
31250
|
if (pid && isAlive(pid)) ready.set(serverId, pid);
|
|
31194
31251
|
}
|
|
31195
31252
|
if (ready.size === serverIds.length) return ready;
|
|
@@ -31201,8 +31258,8 @@ async function waitForManagedDaemonPids(slockHome, serverIds, opts) {
|
|
|
31201
31258
|
}
|
|
31202
31259
|
function buildTimeoutMessage(slockHome, serverIds, ready, input) {
|
|
31203
31260
|
const missing = serverIds.filter((id) => !ready.has(id));
|
|
31204
|
-
const target = input.serverId && missing.length === 1 ? `${input.serverLabel ?? input.serverId}` : `${missing.length}
|
|
31205
|
-
return `Timed out waiting for ${target} to start. Run \`slock-computer status\` and inspect ${serviceLogPath(slockHome)} plus per-server
|
|
31261
|
+
const target = input.serverId && missing.length === 1 ? `${input.serverLabel ?? input.serverId}` : `${missing.length} server runner(s): ${missing.join(", ")}`;
|
|
31262
|
+
return `Timed out waiting for ${target} to start. Run \`slock-computer status\` and inspect ${serviceLogPath(slockHome)} plus per-server server-runner logs under ~/.slock/computer/servers/<serverId>/server-runner.log.`;
|
|
31206
31263
|
}
|
|
31207
31264
|
async function start(input, options = {}) {
|
|
31208
31265
|
options.signal?.throwIfAborted?.();
|
|
@@ -31230,8 +31287,11 @@ async function start(input, options = {}) {
|
|
|
31230
31287
|
for (const id of managedTargets) {
|
|
31231
31288
|
await setServerManaged(slockHome, id);
|
|
31232
31289
|
}
|
|
31233
|
-
const existing = await
|
|
31234
|
-
|
|
31290
|
+
const { pid: existing } = await findLiveServicePid(slockHome, {
|
|
31291
|
+
readPidfile: options.readPidfile,
|
|
31292
|
+
isProcessAlive: options.isProcessAlive
|
|
31293
|
+
});
|
|
31294
|
+
if (existing !== null) {
|
|
31235
31295
|
emit4(options, {
|
|
31236
31296
|
type: "already_running",
|
|
31237
31297
|
servicePid: existing,
|
|
@@ -31327,7 +31387,7 @@ async function pollReadyOnce(slockHome, serverIds, opts) {
|
|
|
31327
31387
|
const isAlive = opts.isProcessAlive ?? isProcessAlive2;
|
|
31328
31388
|
const ready = /* @__PURE__ */ new Map();
|
|
31329
31389
|
for (const serverId of serverIds) {
|
|
31330
|
-
const pid = await readPidfile(
|
|
31390
|
+
const pid = await readPidfile(serverRunnerPidPath(slockHome, serverId));
|
|
31331
31391
|
if (pid && isAlive(pid)) ready.set(serverId, pid);
|
|
31332
31392
|
}
|
|
31333
31393
|
return ready;
|
|
@@ -31357,26 +31417,23 @@ async function stop(input = {}, options = {}) {
|
|
|
31357
31417
|
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
31358
31418
|
const pollIntervalMs = options.pollIntervalMs ?? STOP_POLL_INTERVAL_MS;
|
|
31359
31419
|
const timeoutMs = options.timeoutMs ?? STOP_TIMEOUT_MS;
|
|
31360
|
-
|
|
31361
|
-
|
|
31362
|
-
|
|
31363
|
-
|
|
31364
|
-
const legacyPid = await readPidfile(legacyPidfilePath);
|
|
31365
|
-
if (legacyPid !== null) {
|
|
31366
|
-
pidfilePath = legacyPidfilePath;
|
|
31367
|
-
pid = legacyPid;
|
|
31368
|
-
}
|
|
31369
|
-
}
|
|
31420
|
+
const { pid, pidfilePath, firstStalePidfile, firstStalePid } = await findLiveServicePid(slockHome, {
|
|
31421
|
+
readPidfile,
|
|
31422
|
+
isProcessAlive: isAlive
|
|
31423
|
+
});
|
|
31370
31424
|
emit5(options, { type: "stopping", pid });
|
|
31371
31425
|
if (pid === null) {
|
|
31426
|
+
if (firstStalePidfile !== null && firstStalePid !== null) {
|
|
31427
|
+
emit5(options, { type: "stale_pidfile_cleared", pid: firstStalePid });
|
|
31428
|
+
return {
|
|
31429
|
+
status: "stale_pidfile_cleared",
|
|
31430
|
+
pid: firstStalePid,
|
|
31431
|
+
pidfilePath: firstStalePidfile
|
|
31432
|
+
};
|
|
31433
|
+
}
|
|
31372
31434
|
emit5(options, { type: "not_running" });
|
|
31373
31435
|
return { status: "not_running", pid: null, pidfilePath };
|
|
31374
31436
|
}
|
|
31375
|
-
if (!isAlive(pid)) {
|
|
31376
|
-
await clearPidfileAt(pidfilePath);
|
|
31377
|
-
emit5(options, { type: "stale_pidfile_cleared", pid });
|
|
31378
|
-
return { status: "stale_pidfile_cleared", pid, pidfilePath };
|
|
31379
|
-
}
|
|
31380
31437
|
options.signal?.throwIfAborted?.();
|
|
31381
31438
|
try {
|
|
31382
31439
|
killer(pid);
|
|
@@ -31504,8 +31561,8 @@ async function detach(input, options = {}) {
|
|
|
31504
31561
|
await clearServerManaged(slockHome, serverId);
|
|
31505
31562
|
const subtree = [
|
|
31506
31563
|
serverAttachmentPath(slockHome, serverId),
|
|
31507
|
-
|
|
31508
|
-
|
|
31564
|
+
serverRunnerPidPath(slockHome, serverId),
|
|
31565
|
+
serverRunnerLogPath(slockHome, serverId)
|
|
31509
31566
|
];
|
|
31510
31567
|
for (const p of subtree) await clearPidfileAt(p);
|
|
31511
31568
|
emit6(options, { type: "subtree_cleared", serverId, serverLabel });
|
|
@@ -31526,7 +31583,7 @@ function buildResidentSpawn(mode, serverId, selfEntry = process.argv[1] ?? "", e
|
|
|
31526
31583
|
}
|
|
31527
31584
|
var PARENT_LOCK_HELD_ENV_VAR = "SLOCK_COMPUTER_PARENT_MUTATION_LOCK_HELD";
|
|
31528
31585
|
async function spawnDetachedService(slockHome) {
|
|
31529
|
-
await mkdir8(
|
|
31586
|
+
await mkdir8(serviceRunDir(slockHome), { recursive: true });
|
|
31530
31587
|
const supLogFd = await open(serviceLogPath(slockHome), "a");
|
|
31531
31588
|
const { command, args } = buildResidentSpawn("__service", null);
|
|
31532
31589
|
const child = spawn2(command, args, {
|
|
@@ -31703,14 +31760,14 @@ async function readServiceVersionEvidence(slockHome) {
|
|
|
31703
31760
|
}
|
|
31704
31761
|
async function runService() {
|
|
31705
31762
|
const slockHome = resolveSlockHome();
|
|
31706
|
-
await mkdir8(
|
|
31763
|
+
await mkdir8(serviceRunDir(slockHome), { recursive: true });
|
|
31707
31764
|
await runServiceStartupRecovery(slockHome);
|
|
31708
31765
|
await writePidfileAt(servicePidPath(slockHome), process.pid);
|
|
31709
31766
|
await writeServiceVersionEvidence(slockHome);
|
|
31710
31767
|
const children = /* @__PURE__ */ new Map();
|
|
31711
31768
|
const { [PARENT_LOCK_HELD_ENV_VAR]: _parentLockMarker, ...childEnv } = process.env;
|
|
31712
31769
|
const spawnChild = async (serverId) => {
|
|
31713
|
-
const logPath =
|
|
31770
|
+
const logPath = serverRunnerLogPath(slockHome, serverId);
|
|
31714
31771
|
await mkdir8(dirname8(logPath), { recursive: true });
|
|
31715
31772
|
const logFd = await open(logPath, "a");
|
|
31716
31773
|
const { command, args } = buildResidentSpawn("__run", serverId);
|
|
@@ -31723,11 +31780,11 @@ async function runService() {
|
|
|
31723
31780
|
if (!child.pid) return;
|
|
31724
31781
|
const handle = { serverId, child, stopping: false };
|
|
31725
31782
|
children.set(serverId, handle);
|
|
31726
|
-
await writePidfileAt(
|
|
31783
|
+
await writePidfileAt(serverRunnerPidPath(slockHome, serverId), child.pid);
|
|
31727
31784
|
child.on("exit", (code, signal) => {
|
|
31728
31785
|
void (async () => {
|
|
31729
31786
|
children.delete(serverId);
|
|
31730
|
-
await clearPidfileAt(
|
|
31787
|
+
await clearPidfileAt(serverRunnerPidPath(slockHome, serverId));
|
|
31731
31788
|
if (handle.stopping) return;
|
|
31732
31789
|
const classification = classifyRunnerExit(code, signal);
|
|
31733
31790
|
if (classification === "config-error") {
|
|
@@ -31736,7 +31793,7 @@ async function runService() {
|
|
|
31736
31793
|
} catch {
|
|
31737
31794
|
}
|
|
31738
31795
|
process.stderr.write(
|
|
31739
|
-
`Service: server ${serverId} child exited with EX_CONFIG (${EX_CONFIG_EXIT_CODE}); marked degraded, NOT auto-restarting. See ${
|
|
31796
|
+
`Service: server ${serverId} child exited with EX_CONFIG (${EX_CONFIG_EXIT_CODE}); marked degraded, NOT auto-restarting. See ${serverRunnerLogPath(slockHome, serverId)} for the actionable error.
|
|
31740
31797
|
`
|
|
31741
31798
|
);
|
|
31742
31799
|
return;
|
|
@@ -31847,7 +31904,7 @@ async function runStart(opts = {}, deps = {}) {
|
|
|
31847
31904
|
info(
|
|
31848
31905
|
`Managing ${sb.managedCount} of ${sb.attachedCount} attached server(s). Logs: ${sb.logPath}`
|
|
31849
31906
|
);
|
|
31850
|
-
info(`Per-server
|
|
31907
|
+
info(`Per-server server-runner logs: ~/.slock/computer/servers/<serverId>/server-runner.log`);
|
|
31851
31908
|
info(`Check state with \`slock-computer status\`.`);
|
|
31852
31909
|
}
|
|
31853
31910
|
}
|
|
@@ -32208,6 +32265,10 @@ async function pidStatus(pidfile) {
|
|
|
32208
32265
|
const pid = await readPidfileAt(pidfile);
|
|
32209
32266
|
return pid !== null && isProcessAlive2(pid) ? { running: true, pid } : { running: false };
|
|
32210
32267
|
}
|
|
32268
|
+
async function serviceState(slockHome) {
|
|
32269
|
+
const { pid } = await findLiveServicePidReadOnly(slockHome);
|
|
32270
|
+
return pid !== null ? { running: true, pid } : { running: false };
|
|
32271
|
+
}
|
|
32211
32272
|
async function deriveHealth(slockHome, serverId, daemon) {
|
|
32212
32273
|
if (!daemon.running) return "offline";
|
|
32213
32274
|
if (await isDegraded(slockHome, serverId)) return "degraded";
|
|
@@ -32218,19 +32279,19 @@ async function buildStatusReport(installRoot) {
|
|
|
32218
32279
|
const session = sessionRead.session;
|
|
32219
32280
|
const attachments = await listServerAttachments(installRoot);
|
|
32220
32281
|
const service = {
|
|
32221
|
-
...await
|
|
32282
|
+
...await serviceState(installRoot),
|
|
32222
32283
|
logPath: serviceLogPath(installRoot)
|
|
32223
32284
|
};
|
|
32224
32285
|
const servers = [];
|
|
32225
32286
|
for (const a of attachments) {
|
|
32226
|
-
const daemon = await pidStatus(
|
|
32287
|
+
const daemon = await pidStatus(serverRunnerPidPath(installRoot, a.serverId));
|
|
32227
32288
|
servers.push({
|
|
32228
32289
|
serverId: a.serverId,
|
|
32229
32290
|
serverSlug: a.serverSlug ?? null,
|
|
32230
32291
|
serverMachineId: a.serverMachineId,
|
|
32231
32292
|
serverUrl: a.serverUrl,
|
|
32232
32293
|
attachedAt: a.attachedAt ?? null,
|
|
32233
|
-
|
|
32294
|
+
serverRunnerLogPath: serverRunnerLogPath(installRoot, a.serverId),
|
|
32234
32295
|
daemon,
|
|
32235
32296
|
health: await deriveHealth(installRoot, a.serverId, daemon)
|
|
32236
32297
|
});
|
|
@@ -32277,7 +32338,7 @@ async function runStatus(opts) {
|
|
|
32277
32338
|
info(
|
|
32278
32339
|
` ${pad(formatServerSlugDisplay(s.serverSlug), 24)}${pad(s.health, 12)}${pad(dcol, 24)}${pad(s.serverMachineId, 38)}${s.serverUrl}`
|
|
32279
32340
|
);
|
|
32280
|
-
info(`
|
|
32341
|
+
info(` Server runner log: ${s.serverRunnerLogPath}`);
|
|
32281
32342
|
}
|
|
32282
32343
|
if (report.servers.some((s) => s.health === "degraded")) {
|
|
32283
32344
|
info("");
|
|
@@ -32618,14 +32679,14 @@ import { readFile as readFile10 } from "fs/promises";
|
|
|
32618
32679
|
var DEFAULT_LINES = 200;
|
|
32619
32680
|
async function runLogs(opts) {
|
|
32620
32681
|
const home = resolveSlockHome();
|
|
32621
|
-
const file = opts.service ? serviceLogPath(home) :
|
|
32682
|
+
const file = opts.service ? serviceLogPath(home) : serverRunnerLogPath(home, await resolveTargetServerId({ server: opts.server }));
|
|
32622
32683
|
let content;
|
|
32623
32684
|
try {
|
|
32624
32685
|
content = await readFile10(file, "utf8");
|
|
32625
32686
|
} catch {
|
|
32626
32687
|
fail(
|
|
32627
32688
|
"NO_DAEMON_LOG",
|
|
32628
|
-
opts.service ? `No service log at ${file}. Start the service first (\`slock-computer start\`).` : `No
|
|
32689
|
+
opts.service ? `No service log at ${file}. Start the service first (\`slock-computer start\`).` : `No server runner log at ${file}. Start its server runner first (\`slock-computer start\`).`
|
|
32629
32690
|
);
|
|
32630
32691
|
}
|
|
32631
32692
|
const n = Number.isInteger(opts.lines) && opts.lines > 0 ? opts.lines : DEFAULT_LINES;
|
|
@@ -33537,13 +33598,8 @@ async function restartPhase(slockHome, deps = {}) {
|
|
|
33537
33598
|
return { ok: false, reason: "health_check_timeout" };
|
|
33538
33599
|
}
|
|
33539
33600
|
async function defaultReadServicePid(slockHome) {
|
|
33540
|
-
|
|
33541
|
-
|
|
33542
|
-
const pid = Number.parseInt(raw, 10);
|
|
33543
|
-
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
33544
|
-
} catch {
|
|
33545
|
-
return null;
|
|
33546
|
-
}
|
|
33601
|
+
const { pid } = await findLiveServicePid(slockHome);
|
|
33602
|
+
return pid;
|
|
33547
33603
|
}
|
|
33548
33604
|
async function defaultKillService(pid) {
|
|
33549
33605
|
try {
|
|
@@ -33861,7 +33917,7 @@ async function rollingDaemonHealthCheck(slockHome, deps = {}) {
|
|
|
33861
33917
|
}
|
|
33862
33918
|
async function defaultReadDaemonPid(slockHome, serverId) {
|
|
33863
33919
|
try {
|
|
33864
|
-
const raw = (await readFile14(
|
|
33920
|
+
const raw = (await readFile14(serverRunnerPidPath(slockHome, serverId), "utf8")).trim();
|
|
33865
33921
|
const pid = Number.parseInt(raw, 10);
|
|
33866
33922
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
33867
33923
|
} catch {
|
|
@@ -34067,6 +34123,8 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34067
34123
|
if (drainMode !== void 0 && drainMode !== "drain") {
|
|
34068
34124
|
info(`Drain mode: ${drainMode}${drainMode === "force" ? " (in-flight turns will be dropped)" : ""}.`);
|
|
34069
34125
|
}
|
|
34126
|
+
const readBundledFn = deps.readBundledDaemonVersion ?? readBundledDaemonVersion;
|
|
34127
|
+
const fromBundledDaemonVersion = await readBundledFn(currentBinaryDir);
|
|
34070
34128
|
const runUpgradeFn = deps.runUpgradeFn ?? runUpgrade;
|
|
34071
34129
|
const spawnFreshService = deps.spawnFreshService ?? defaultSpawnFreshService;
|
|
34072
34130
|
const outcome = await runUpgradeFn(slockHome, {
|
|
@@ -34084,17 +34142,18 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34084
34142
|
spawnFreshService: () => spawnFreshService(slockHome)
|
|
34085
34143
|
}
|
|
34086
34144
|
});
|
|
34087
|
-
const
|
|
34088
|
-
const
|
|
34089
|
-
const
|
|
34145
|
+
const toBundledDaemonVersion = await readBundledFn(currentBinaryDir);
|
|
34146
|
+
const bundle = (version, bundledDaemonVersion) => bundledDaemonVersion !== null ? { computerVersion: version, bundledDaemonVersion } : { computerVersion: version };
|
|
34147
|
+
const fromBundle = bundle(fromVersion, fromBundledDaemonVersion);
|
|
34148
|
+
const toBundle = bundle(targetVersion, toBundledDaemonVersion);
|
|
34090
34149
|
const logTrigger = opts.trigger ?? "cli";
|
|
34091
34150
|
if (outcome.ok) {
|
|
34092
34151
|
info(
|
|
34093
34152
|
`Upgrade ${fromVersion} \u2192 ${targetVersion} succeeded (health-OK in ${outcome.restart?.healthAfterMs ?? 0}ms).`
|
|
34094
34153
|
);
|
|
34095
34154
|
await appendUpgradeLogEntry(slockHome, {
|
|
34096
|
-
fromBundle
|
|
34097
|
-
toBundle
|
|
34155
|
+
fromBundle,
|
|
34156
|
+
toBundle,
|
|
34098
34157
|
channel: channel2,
|
|
34099
34158
|
trigger: logTrigger,
|
|
34100
34159
|
outcome: "ok"
|
|
@@ -34114,8 +34173,8 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34114
34173
|
`Upgrade ${fromVersion} \u2192 ${targetVersion} succeeded; cleanup phase reported a non-fatal issue: ${outcome.reason ?? "unknown"}. Active layout + service are healthy.`
|
|
34115
34174
|
);
|
|
34116
34175
|
await appendUpgradeLogEntry(slockHome, {
|
|
34117
|
-
fromBundle
|
|
34118
|
-
toBundle
|
|
34176
|
+
fromBundle,
|
|
34177
|
+
toBundle,
|
|
34119
34178
|
channel: channel2,
|
|
34120
34179
|
trigger: logTrigger,
|
|
34121
34180
|
outcome: "ok"
|
|
@@ -34125,8 +34184,8 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34125
34184
|
}
|
|
34126
34185
|
const code = mapFailurePhaseToCode(outcome);
|
|
34127
34186
|
await appendUpgradeLogEntry(slockHome, {
|
|
34128
|
-
fromBundle
|
|
34129
|
-
toBundle
|
|
34187
|
+
fromBundle,
|
|
34188
|
+
toBundle,
|
|
34130
34189
|
channel: channel2,
|
|
34131
34190
|
trigger: logTrigger,
|
|
34132
34191
|
outcome: "err",
|
|
@@ -34499,10 +34558,10 @@ async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
|
|
|
34499
34558
|
const activeVersion = await readServiceVersionEvidence(slockHome);
|
|
34500
34559
|
return { outcome, activeVersion };
|
|
34501
34560
|
}
|
|
34502
|
-
async function runUpgradeInstallSmokeCli(slockHome, opts, writer = (s) => process.stdout.write(s)) {
|
|
34561
|
+
async function runUpgradeInstallSmokeCli(slockHome, opts, writer = (s) => process.stdout.write(s), deps = {}) {
|
|
34503
34562
|
let report;
|
|
34504
34563
|
try {
|
|
34505
|
-
report = await runUpgradeInstallSmoke(slockHome, opts);
|
|
34564
|
+
report = await runUpgradeInstallSmoke(slockHome, opts, deps);
|
|
34506
34565
|
} catch (e) {
|
|
34507
34566
|
const msg = e instanceof Error ? e.message : String(e);
|
|
34508
34567
|
writer(
|
|
@@ -34589,10 +34648,10 @@ program2.command("setup").argument("<serverSlug>", "target Slock server slug (ca
|
|
|
34589
34648
|
program2.command("detach").argument("<serverSlug>", "server slug to detach from this Computer (canonical form `/myserver`)").description("Remove ONE server's local attachment; never touches user-session or other servers.").action(withCliExit(async (serverSlug) => {
|
|
34590
34649
|
await withMutationLock(async () => runDetach(await resolveTargetServerId({ server: serverSlug }), serverSlug));
|
|
34591
34650
|
}));
|
|
34592
|
-
program2.command("status").description("Show this Computer's aggregate state (login + service + per-server
|
|
34651
|
+
program2.command("status").description("Show this Computer's aggregate state (login + service + per-server server-runners).").option("--json", "emit the machine-readable report").action(withCliExit(async (opts) => {
|
|
34593
34652
|
await runStatus({ json: opts.json });
|
|
34594
34653
|
}));
|
|
34595
|
-
program2.command("start").argument("[serverSlug]", "optional: verify this server is attached and ensure its
|
|
34654
|
+
program2.command("start").argument("[serverSlug]", "optional: verify this server is attached and ensure its server-runner is reconciled (default: ensure all attached)").description("Start/ensure the Computer service (manages all per-server server-runners).").option("--foreground", "stay in this terminal instead of detaching").action(withCliExit(async (serverSlug, opts) => {
|
|
34596
34655
|
await withMutationLock(
|
|
34597
34656
|
async () => runStart({
|
|
34598
34657
|
foreground: opts.foreground,
|
|
@@ -34601,7 +34660,7 @@ program2.command("start").argument("[serverSlug]", "optional: verify this server
|
|
|
34601
34660
|
})
|
|
34602
34661
|
);
|
|
34603
34662
|
}));
|
|
34604
|
-
program2.command("stop").description("Stop the Computer service (and all managed per-server
|
|
34663
|
+
program2.command("stop").description("Stop the Computer service (and all managed per-server server-runners).").action(withCliExit(async () => {
|
|
34605
34664
|
await withMutationLock(() => runStop());
|
|
34606
34665
|
}));
|
|
34607
34666
|
program2.command("doctor").argument("[serverSlug]", "optional: scope detail (recent crashes) to one server").description("Diagnose login + per-server attachments + per-server preflight (no secrets).").option("--json", "emit the machine-readable report").option("--cleanup", "after diagnosis, run the local residue cleanup pass").option("--fix", "alias for --cleanup (same behavior)").option("--reset-health", "clear <serverSlug>'s crash history so service resumes auto-restart").action(
|
|
@@ -34631,7 +34690,7 @@ program2.command("reset").description("Clear a degraded state and resume the ser
|
|
|
34631
34690
|
);
|
|
34632
34691
|
})
|
|
34633
34692
|
);
|
|
34634
|
-
program2.command("logs").description("Tail one server's
|
|
34693
|
+
program2.command("logs").description("Tail one server's server-runner log (or the service log); secrets redacted.").option("--lines <n>", "trailing lines to show (default 200)", (v) => Number.parseInt(v, 10)).option("--server <slug>", "select target server slug (required when \u22652 attached)").option("--service", "tail the global service log instead of a per-server server-runner log").action(withCliExit(async (opts) => {
|
|
34635
34694
|
await runLogs({ lines: opts.lines, server: opts.server ?? null, service: !!opts.service });
|
|
34636
34695
|
}));
|
|
34637
34696
|
var runners = program2.command("runners").description("Computer runner control plane (per-server scoped; \xA712 whitelist server-side).");
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ interface ServerStatusRow {
|
|
|
11
11
|
serverMachineId: string;
|
|
12
12
|
serverUrl: string;
|
|
13
13
|
attachedAt: string | null;
|
|
14
|
-
|
|
14
|
+
serverRunnerLogPath: string;
|
|
15
15
|
daemon: DaemonState;
|
|
16
16
|
/** v6 §11 health enum surfaced in `status` table (PR-H §3.3). */
|
|
17
17
|
health: ServerHealth;
|
package/dist/lib/index.js
CHANGED
|
@@ -128,20 +128,36 @@ function serverDir(slockHome, serverId) {
|
|
|
128
128
|
function serverAttachmentPath(slockHome, serverId) {
|
|
129
129
|
return path.join(serverDir(slockHome, serverId), "runner.state.json");
|
|
130
130
|
}
|
|
131
|
-
function
|
|
132
|
-
return path.join(serverDir(slockHome, serverId), "
|
|
131
|
+
function serverRunnerPidPath(slockHome, serverId) {
|
|
132
|
+
return path.join(serverDir(slockHome, serverId), "server-runner.pid");
|
|
133
133
|
}
|
|
134
|
-
function
|
|
135
|
-
return path.join(serverDir(slockHome, serverId), "
|
|
134
|
+
function serverRunnerLogPath(slockHome, serverId) {
|
|
135
|
+
return path.join(serverDir(slockHome, serverId), "server-runner.log");
|
|
136
136
|
}
|
|
137
137
|
function serverHealthPath(slockHome, serverId) {
|
|
138
138
|
return path.join(serverDir(slockHome, serverId), "health.json");
|
|
139
139
|
}
|
|
140
|
+
function serviceRunDir(slockHome) {
|
|
141
|
+
return path.join(computerDir(slockHome), "run");
|
|
142
|
+
}
|
|
140
143
|
function servicePidPath(slockHome) {
|
|
144
|
+
return path.join(serviceRunDir(slockHome), "service.pid");
|
|
145
|
+
}
|
|
146
|
+
function legacyServicePidPath(slockHome) {
|
|
141
147
|
return path.join(computerDir(slockHome), "service.pid");
|
|
142
148
|
}
|
|
149
|
+
function legacySupervisorPidPath(slockHome) {
|
|
150
|
+
return path.join(computerDir(slockHome), "supervisor.pid");
|
|
151
|
+
}
|
|
152
|
+
function servicePidReadFallback(slockHome) {
|
|
153
|
+
return [
|
|
154
|
+
servicePidPath(slockHome),
|
|
155
|
+
legacyServicePidPath(slockHome),
|
|
156
|
+
legacySupervisorPidPath(slockHome)
|
|
157
|
+
];
|
|
158
|
+
}
|
|
143
159
|
function serviceLogPath(slockHome) {
|
|
144
|
-
return path.join(
|
|
160
|
+
return path.join(serviceRunDir(slockHome), "service.log");
|
|
145
161
|
}
|
|
146
162
|
|
|
147
163
|
// src/serverState.ts
|
|
@@ -220,6 +236,41 @@ function isProcessAlive(pid) {
|
|
|
220
236
|
}
|
|
221
237
|
}
|
|
222
238
|
|
|
239
|
+
// src/internal/service-pid-fallback.ts
|
|
240
|
+
async function findLiveServicePidReadOnly(slockHome, deps = {}) {
|
|
241
|
+
const readPidfile = deps.readPidfile ?? readPidfileAt;
|
|
242
|
+
const isAlive = deps.isProcessAlive ?? isProcessAlive;
|
|
243
|
+
return walkFallback(slockHome, readPidfile, isAlive, async () => void 0);
|
|
244
|
+
}
|
|
245
|
+
async function walkFallback(slockHome, readPidfile, isAlive, clearStale) {
|
|
246
|
+
const candidates = servicePidReadFallback(slockHome);
|
|
247
|
+
let firstStalePidfile = null;
|
|
248
|
+
let firstStalePid = null;
|
|
249
|
+
for (const candidate of candidates) {
|
|
250
|
+
const candidatePid = await readPidfile(candidate);
|
|
251
|
+
if (candidatePid === null) continue;
|
|
252
|
+
if (isAlive(candidatePid)) {
|
|
253
|
+
return {
|
|
254
|
+
pid: candidatePid,
|
|
255
|
+
pidfilePath: candidate,
|
|
256
|
+
firstStalePidfile,
|
|
257
|
+
firstStalePid
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
await clearStale(candidate);
|
|
261
|
+
if (firstStalePidfile === null) {
|
|
262
|
+
firstStalePidfile = candidate;
|
|
263
|
+
firstStalePid = candidatePid;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
pid: null,
|
|
268
|
+
pidfilePath: candidates[0],
|
|
269
|
+
firstStalePidfile,
|
|
270
|
+
firstStalePid
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
223
274
|
// src/health.ts
|
|
224
275
|
import { readFile as readFile4, writeFile as writeFile3, unlink as unlink3, mkdir as mkdir3, appendFile } from "fs/promises";
|
|
225
276
|
import { dirname as dirname3 } from "path";
|
|
@@ -274,6 +325,10 @@ async function pidStatus(pidfile) {
|
|
|
274
325
|
const pid = await readPidfileAt(pidfile);
|
|
275
326
|
return pid !== null && isProcessAlive(pid) ? { running: true, pid } : { running: false };
|
|
276
327
|
}
|
|
328
|
+
async function serviceState(slockHome) {
|
|
329
|
+
const { pid } = await findLiveServicePidReadOnly(slockHome);
|
|
330
|
+
return pid !== null ? { running: true, pid } : { running: false };
|
|
331
|
+
}
|
|
277
332
|
async function deriveHealth(slockHome, serverId, daemon) {
|
|
278
333
|
if (!daemon.running) return "offline";
|
|
279
334
|
if (await isDegraded(slockHome, serverId)) return "degraded";
|
|
@@ -284,19 +339,19 @@ async function buildStatusReport(installRoot) {
|
|
|
284
339
|
const session = sessionRead.session;
|
|
285
340
|
const attachments = await listServerAttachments(installRoot);
|
|
286
341
|
const service = {
|
|
287
|
-
...await
|
|
342
|
+
...await serviceState(installRoot),
|
|
288
343
|
logPath: serviceLogPath(installRoot)
|
|
289
344
|
};
|
|
290
345
|
const servers = [];
|
|
291
346
|
for (const a of attachments) {
|
|
292
|
-
const daemon = await pidStatus(
|
|
347
|
+
const daemon = await pidStatus(serverRunnerPidPath(installRoot, a.serverId));
|
|
293
348
|
servers.push({
|
|
294
349
|
serverId: a.serverId,
|
|
295
350
|
serverSlug: a.serverSlug ?? null,
|
|
296
351
|
serverMachineId: a.serverMachineId,
|
|
297
352
|
serverUrl: a.serverUrl,
|
|
298
353
|
attachedAt: a.attachedAt ?? null,
|
|
299
|
-
|
|
354
|
+
serverRunnerLogPath: serverRunnerLogPath(installRoot, a.serverId),
|
|
300
355
|
daemon,
|
|
301
356
|
health: await deriveHealth(installRoot, a.serverId, daemon)
|
|
302
357
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slock-ai/computer",
|
|
3
|
-
"version": "0.0.16
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "Slock Computer — standalone human/local-machine control-plane CLI (login + attach). Distinct from the agent-facing @slock-ai/cli.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"commander": "^12.1.0",
|
|
29
29
|
"proper-lockfile": "^4.1.2",
|
|
30
30
|
"undici": "^7.24.7",
|
|
31
|
-
"@slock-ai/daemon": "0.55.
|
|
31
|
+
"@slock-ai/daemon": "0.55.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^25.5.0",
|