@slock-ai/computer 0.0.15 → 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 +291 -221
- package/dist/lib/index.d.ts +5 -5
- package/dist/lib/index.js +69 -14
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -29737,7 +29737,7 @@ var ComputerAttachClient = class {
|
|
|
29737
29737
|
* over the machine. On success the server marks the machine row migrated
|
|
29738
29738
|
* and mints a fresh `sk_computer_*`. The raw legacy key MUST NOT be
|
|
29739
29739
|
* persisted by the caller — only the freshly-minted sk_computer_* lands
|
|
29740
|
-
* in
|
|
29740
|
+
* in runner.state.json.
|
|
29741
29741
|
*/
|
|
29742
29742
|
async adoptLegacy(legacyApiKey, name) {
|
|
29743
29743
|
const res = await (0, import_undici.fetch)(this.url("/api/computer/adopt-legacy"), {
|
|
@@ -29877,13 +29877,13 @@ function serverDir(slockHome, serverId) {
|
|
|
29877
29877
|
return path2.join(serversDir(slockHome), assertValidServerId(serverId));
|
|
29878
29878
|
}
|
|
29879
29879
|
function serverAttachmentPath(slockHome, serverId) {
|
|
29880
|
-
return path2.join(serverDir(slockHome, serverId), "
|
|
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,33 @@ 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");
|
|
29899
|
+
}
|
|
29900
|
+
function servicePidPath(slockHome) {
|
|
29901
|
+
return path2.join(serviceRunDir(slockHome), "service.pid");
|
|
29902
|
+
}
|
|
29903
|
+
function legacyServicePidPath(slockHome) {
|
|
29904
|
+
return path2.join(computerDir(slockHome), "service.pid");
|
|
29896
29905
|
}
|
|
29897
|
-
function
|
|
29906
|
+
function legacySupervisorPidPath(slockHome) {
|
|
29898
29907
|
return path2.join(computerDir(slockHome), "supervisor.pid");
|
|
29899
29908
|
}
|
|
29900
|
-
function
|
|
29901
|
-
return
|
|
29909
|
+
function servicePidReadFallback(slockHome) {
|
|
29910
|
+
return [
|
|
29911
|
+
servicePidPath(slockHome),
|
|
29912
|
+
legacyServicePidPath(slockHome),
|
|
29913
|
+
legacySupervisorPidPath(slockHome)
|
|
29914
|
+
];
|
|
29915
|
+
}
|
|
29916
|
+
function serviceLogPath(slockHome) {
|
|
29917
|
+
return path2.join(serviceRunDir(slockHome), "service.log");
|
|
29902
29918
|
}
|
|
29903
|
-
function
|
|
29904
|
-
return path2.join(computerDir(slockHome), "
|
|
29919
|
+
function serviceVersionPath(slockHome) {
|
|
29920
|
+
return path2.join(computerDir(slockHome), "service-version.json");
|
|
29905
29921
|
}
|
|
29906
29922
|
var HOSTNAME_SUFFIXES = [
|
|
29907
29923
|
".fritz.box",
|
|
@@ -30799,7 +30815,7 @@ async function appendAdoptionLog(slockHome, line) {
|
|
|
30799
30815
|
}
|
|
30800
30816
|
}
|
|
30801
30817
|
|
|
30802
|
-
// src/
|
|
30818
|
+
// src/service.ts
|
|
30803
30819
|
init_esm_shims();
|
|
30804
30820
|
import { spawn as spawn2 } from "child_process";
|
|
30805
30821
|
import { mkdir as mkdir8, readFile as readFile7, writeFile as writeFile7, open, rename as rename2 } from "fs/promises";
|
|
@@ -30869,12 +30885,12 @@ async function cleanupStalePidfile(pidfilePath) {
|
|
|
30869
30885
|
}
|
|
30870
30886
|
async function cleanupAllStalePidfiles(slockHome) {
|
|
30871
30887
|
const cleaned = [];
|
|
30872
|
-
if (await cleanupStalePidfile(
|
|
30873
|
-
cleaned.push(
|
|
30888
|
+
if (await cleanupStalePidfile(servicePidPath(slockHome))) {
|
|
30889
|
+
cleaned.push(servicePidPath(slockHome));
|
|
30874
30890
|
}
|
|
30875
30891
|
const attached = await listAttachedServerIds(slockHome);
|
|
30876
30892
|
for (const sid of attached) {
|
|
30877
|
-
const p =
|
|
30893
|
+
const p = serverRunnerPidPath(slockHome, sid);
|
|
30878
30894
|
if (await cleanupStalePidfile(p)) cleaned.push(p);
|
|
30879
30895
|
}
|
|
30880
30896
|
return cleaned;
|
|
@@ -30919,10 +30935,10 @@ async function cleanupOrphanProcesses(slockHome, psSpawn) {
|
|
|
30919
30935
|
const managed = new Set(await listManagedServerIds(slockHome));
|
|
30920
30936
|
const signaled = [];
|
|
30921
30937
|
const knownPids = /* @__PURE__ */ new Set();
|
|
30922
|
-
const supPid = await readPidfileAt(
|
|
30938
|
+
const supPid = await readPidfileAt(servicePidPath(slockHome));
|
|
30923
30939
|
if (supPid !== null) knownPids.add(supPid);
|
|
30924
30940
|
for (const sid of managed) {
|
|
30925
|
-
const pid = await readPidfileAt(
|
|
30941
|
+
const pid = await readPidfileAt(serverRunnerPidPath(slockHome, sid));
|
|
30926
30942
|
if (pid !== null) knownPids.add(pid);
|
|
30927
30943
|
}
|
|
30928
30944
|
if (supPid === null) return signaled;
|
|
@@ -31156,7 +31172,7 @@ async function emitRunnerStateTransition(slockHome, serverId, fromState, toState
|
|
|
31156
31172
|
trigger
|
|
31157
31173
|
};
|
|
31158
31174
|
try {
|
|
31159
|
-
const path3 =
|
|
31175
|
+
const path3 = serviceLogPath(slockHome);
|
|
31160
31176
|
await mkdir7(dirname7(path3), { recursive: true });
|
|
31161
31177
|
await appendFile2(path3, JSON.stringify(entry) + "\n");
|
|
31162
31178
|
} catch {
|
|
@@ -31165,6 +31181,50 @@ async function emitRunnerStateTransition(slockHome, serverId, fromState, toState
|
|
|
31165
31181
|
|
|
31166
31182
|
// src/services/start.ts
|
|
31167
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
|
|
31168
31228
|
var START_ENSURE_TIMEOUT_MS = 15e3;
|
|
31169
31229
|
var START_ENSURE_POLL_INTERVAL_MS = 100;
|
|
31170
31230
|
function emit4(opts, event) {
|
|
@@ -31186,7 +31246,7 @@ async function waitForManagedDaemonPids(slockHome, serverIds, opts) {
|
|
|
31186
31246
|
while (ready.size < serverIds.length) {
|
|
31187
31247
|
for (const serverId of serverIds) {
|
|
31188
31248
|
if (ready.has(serverId)) continue;
|
|
31189
|
-
const pid = await readPidfile(
|
|
31249
|
+
const pid = await readPidfile(serverRunnerPidPath(slockHome, serverId));
|
|
31190
31250
|
if (pid && isAlive(pid)) ready.set(serverId, pid);
|
|
31191
31251
|
}
|
|
31192
31252
|
if (ready.size === serverIds.length) return ready;
|
|
@@ -31198,8 +31258,8 @@ async function waitForManagedDaemonPids(slockHome, serverIds, opts) {
|
|
|
31198
31258
|
}
|
|
31199
31259
|
function buildTimeoutMessage(slockHome, serverIds, ready, input) {
|
|
31200
31260
|
const missing = serverIds.filter((id) => !ready.has(id));
|
|
31201
|
-
const target = input.serverId && missing.length === 1 ? `${input.serverLabel ?? input.serverId}` : `${missing.length}
|
|
31202
|
-
return `Timed out waiting for ${target} to start. Run \`slock-computer status\` and inspect ${
|
|
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.`;
|
|
31203
31263
|
}
|
|
31204
31264
|
async function start(input, options = {}) {
|
|
31205
31265
|
options.signal?.throwIfAborted?.();
|
|
@@ -31227,11 +31287,14 @@ async function start(input, options = {}) {
|
|
|
31227
31287
|
for (const id of managedTargets) {
|
|
31228
31288
|
await setServerManaged(slockHome, id);
|
|
31229
31289
|
}
|
|
31230
|
-
const existing = await
|
|
31231
|
-
|
|
31290
|
+
const { pid: existing } = await findLiveServicePid(slockHome, {
|
|
31291
|
+
readPidfile: options.readPidfile,
|
|
31292
|
+
isProcessAlive: options.isProcessAlive
|
|
31293
|
+
});
|
|
31294
|
+
if (existing !== null) {
|
|
31232
31295
|
emit4(options, {
|
|
31233
31296
|
type: "already_running",
|
|
31234
|
-
|
|
31297
|
+
servicePid: existing,
|
|
31235
31298
|
managedTargets,
|
|
31236
31299
|
attachedCount: attached.length
|
|
31237
31300
|
});
|
|
@@ -31248,8 +31311,8 @@ async function start(input, options = {}) {
|
|
|
31248
31311
|
managedTargets,
|
|
31249
31312
|
attachedCount: attached.length,
|
|
31250
31313
|
ready: ready2,
|
|
31251
|
-
|
|
31252
|
-
|
|
31314
|
+
servicePid: existing,
|
|
31315
|
+
serviceLogPath: serviceLogPath(slockHome)
|
|
31253
31316
|
};
|
|
31254
31317
|
}
|
|
31255
31318
|
if (input.foreground) {
|
|
@@ -31258,11 +31321,11 @@ async function start(input, options = {}) {
|
|
|
31258
31321
|
managedTargets,
|
|
31259
31322
|
attachedCount: attached.length
|
|
31260
31323
|
});
|
|
31261
|
-
const
|
|
31324
|
+
const service = options.runService ?? runService;
|
|
31262
31325
|
const previousParentLockMarker = process.env[PARENT_LOCK_HELD_ENV_VAR];
|
|
31263
31326
|
process.env[PARENT_LOCK_HELD_ENV_VAR] = "1";
|
|
31264
31327
|
try {
|
|
31265
|
-
await
|
|
31328
|
+
await service();
|
|
31266
31329
|
} finally {
|
|
31267
31330
|
if (previousParentLockMarker === void 0) delete process.env[PARENT_LOCK_HELD_ENV_VAR];
|
|
31268
31331
|
else process.env[PARENT_LOCK_HELD_ENV_VAR] = previousParentLockMarker;
|
|
@@ -31272,34 +31335,34 @@ async function start(input, options = {}) {
|
|
|
31272
31335
|
managedTargets,
|
|
31273
31336
|
attachedCount: attached.length,
|
|
31274
31337
|
ready: /* @__PURE__ */ new Map(),
|
|
31275
|
-
|
|
31276
|
-
|
|
31338
|
+
servicePid: null,
|
|
31339
|
+
serviceLogPath: serviceLogPath(slockHome)
|
|
31277
31340
|
};
|
|
31278
31341
|
}
|
|
31279
31342
|
options.signal?.throwIfAborted?.();
|
|
31280
31343
|
let pid;
|
|
31281
31344
|
try {
|
|
31282
|
-
pid = await (options.
|
|
31345
|
+
pid = await (options.spawnDetachedService ?? spawnDetachedService)(slockHome);
|
|
31283
31346
|
} catch (err) {
|
|
31284
31347
|
const msg = err instanceof Error ? err.message : String(err);
|
|
31285
31348
|
throw new ComputerServiceError("SUPERVISOR_SPAWN_FAILED", msg, err);
|
|
31286
31349
|
}
|
|
31287
31350
|
emit4(options, {
|
|
31288
31351
|
type: "spawned",
|
|
31289
|
-
|
|
31352
|
+
servicePid: pid,
|
|
31290
31353
|
managedTargets,
|
|
31291
31354
|
attachedCount: attached.length
|
|
31292
31355
|
});
|
|
31293
31356
|
if (options.signal?.aborted) {
|
|
31294
31357
|
const ready2 = await pollReadyOnce(slockHome, managedTargets, options);
|
|
31295
|
-
emit4(options, { type: "aborted",
|
|
31358
|
+
emit4(options, { type: "aborted", servicePid: pid, managedTargets, ready: ready2 });
|
|
31296
31359
|
return {
|
|
31297
31360
|
status: "aborted",
|
|
31298
31361
|
managedTargets,
|
|
31299
31362
|
attachedCount: attached.length,
|
|
31300
31363
|
ready: ready2,
|
|
31301
|
-
|
|
31302
|
-
|
|
31364
|
+
servicePid: pid,
|
|
31365
|
+
serviceLogPath: serviceLogPath(slockHome)
|
|
31303
31366
|
};
|
|
31304
31367
|
}
|
|
31305
31368
|
const ready = await waitForManagedDaemonPids(slockHome, managedTargets, options);
|
|
@@ -31315,8 +31378,8 @@ async function start(input, options = {}) {
|
|
|
31315
31378
|
managedTargets,
|
|
31316
31379
|
attachedCount: attached.length,
|
|
31317
31380
|
ready,
|
|
31318
|
-
|
|
31319
|
-
|
|
31381
|
+
servicePid: pid,
|
|
31382
|
+
serviceLogPath: serviceLogPath(slockHome)
|
|
31320
31383
|
};
|
|
31321
31384
|
}
|
|
31322
31385
|
async function pollReadyOnce(slockHome, serverIds, opts) {
|
|
@@ -31324,7 +31387,7 @@ async function pollReadyOnce(slockHome, serverIds, opts) {
|
|
|
31324
31387
|
const isAlive = opts.isProcessAlive ?? isProcessAlive2;
|
|
31325
31388
|
const ready = /* @__PURE__ */ new Map();
|
|
31326
31389
|
for (const serverId of serverIds) {
|
|
31327
|
-
const pid = await readPidfile(
|
|
31390
|
+
const pid = await readPidfile(serverRunnerPidPath(slockHome, serverId));
|
|
31328
31391
|
if (pid && isAlive(pid)) ready.set(serverId, pid);
|
|
31329
31392
|
}
|
|
31330
31393
|
return ready;
|
|
@@ -31348,24 +31411,29 @@ async function stop(input = {}, options = {}) {
|
|
|
31348
31411
|
const slockHome = resolveSlockHome();
|
|
31349
31412
|
const readPidfile = options.readPidfile ?? readPidfileAt;
|
|
31350
31413
|
const isAlive = options.isProcessAlive ?? isProcessAlive2;
|
|
31351
|
-
const killer = options.
|
|
31414
|
+
const killer = options.killService ?? ((pid2) => {
|
|
31352
31415
|
process.kill(pid2, "SIGTERM");
|
|
31353
31416
|
});
|
|
31354
31417
|
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
31355
31418
|
const pollIntervalMs = options.pollIntervalMs ?? STOP_POLL_INTERVAL_MS;
|
|
31356
31419
|
const timeoutMs = options.timeoutMs ?? STOP_TIMEOUT_MS;
|
|
31357
|
-
const pidfilePath =
|
|
31358
|
-
|
|
31420
|
+
const { pid, pidfilePath, firstStalePidfile, firstStalePid } = await findLiveServicePid(slockHome, {
|
|
31421
|
+
readPidfile,
|
|
31422
|
+
isProcessAlive: isAlive
|
|
31423
|
+
});
|
|
31359
31424
|
emit5(options, { type: "stopping", pid });
|
|
31360
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
|
+
}
|
|
31361
31434
|
emit5(options, { type: "not_running" });
|
|
31362
31435
|
return { status: "not_running", pid: null, pidfilePath };
|
|
31363
31436
|
}
|
|
31364
|
-
if (!isAlive(pid)) {
|
|
31365
|
-
await clearPidfileAt(pidfilePath);
|
|
31366
|
-
emit5(options, { type: "stale_pidfile_cleared", pid });
|
|
31367
|
-
return { status: "stale_pidfile_cleared", pid, pidfilePath };
|
|
31368
|
-
}
|
|
31369
31437
|
options.signal?.throwIfAborted?.();
|
|
31370
31438
|
try {
|
|
31371
31439
|
killer(pid);
|
|
@@ -31373,7 +31441,7 @@ async function stop(input = {}, options = {}) {
|
|
|
31373
31441
|
const cause = err instanceof Error ? err : new Error(String(err));
|
|
31374
31442
|
throw new ComputerServiceError(
|
|
31375
31443
|
"STOP_SIGNAL_FAILED",
|
|
31376
|
-
`Failed to send SIGTERM to
|
|
31444
|
+
`Failed to send SIGTERM to service (pid ${pid}): ${cause.message}. Check process permissions or run: kill ${pid}`,
|
|
31377
31445
|
err
|
|
31378
31446
|
);
|
|
31379
31447
|
}
|
|
@@ -31392,7 +31460,7 @@ async function stop(input = {}, options = {}) {
|
|
|
31392
31460
|
}
|
|
31393
31461
|
throw new ComputerServiceError(
|
|
31394
31462
|
"STOP_TIMEOUT",
|
|
31395
|
-
`
|
|
31463
|
+
`Service (pid ${pid}) did not exit within ${timeoutMs}ms after SIGTERM. Force-kill with: kill -9 ${pid}`
|
|
31396
31464
|
);
|
|
31397
31465
|
}
|
|
31398
31466
|
|
|
@@ -31493,8 +31561,8 @@ async function detach(input, options = {}) {
|
|
|
31493
31561
|
await clearServerManaged(slockHome, serverId);
|
|
31494
31562
|
const subtree = [
|
|
31495
31563
|
serverAttachmentPath(slockHome, serverId),
|
|
31496
|
-
|
|
31497
|
-
|
|
31564
|
+
serverRunnerPidPath(slockHome, serverId),
|
|
31565
|
+
serverRunnerLogPath(slockHome, serverId)
|
|
31498
31566
|
];
|
|
31499
31567
|
for (const p of subtree) await clearPidfileAt(p);
|
|
31500
31568
|
emit6(options, { type: "subtree_cleared", serverId, serverLabel });
|
|
@@ -31508,16 +31576,16 @@ async function detach(input, options = {}) {
|
|
|
31508
31576
|
};
|
|
31509
31577
|
}
|
|
31510
31578
|
|
|
31511
|
-
// src/
|
|
31579
|
+
// src/service.ts
|
|
31512
31580
|
function buildResidentSpawn(mode, serverId, selfEntry = process.argv[1] ?? "", execArgv = process.execArgv) {
|
|
31513
31581
|
const tail = serverId ? [mode, serverId] : [mode];
|
|
31514
31582
|
return { command: process.execPath, args: [...execArgv, selfEntry, ...tail] };
|
|
31515
31583
|
}
|
|
31516
31584
|
var PARENT_LOCK_HELD_ENV_VAR = "SLOCK_COMPUTER_PARENT_MUTATION_LOCK_HELD";
|
|
31517
|
-
async function
|
|
31518
|
-
await mkdir8(
|
|
31519
|
-
const supLogFd = await open(
|
|
31520
|
-
const { command, args } = buildResidentSpawn("
|
|
31585
|
+
async function spawnDetachedService(slockHome) {
|
|
31586
|
+
await mkdir8(serviceRunDir(slockHome), { recursive: true });
|
|
31587
|
+
const supLogFd = await open(serviceLogPath(slockHome), "a");
|
|
31588
|
+
const { command, args } = buildResidentSpawn("__service", null);
|
|
31521
31589
|
const child = spawn2(command, args, {
|
|
31522
31590
|
detached: true,
|
|
31523
31591
|
stdio: ["ignore", supLogFd.fd, supLogFd.fd],
|
|
@@ -31534,9 +31602,9 @@ async function spawnDetachedSupervisor(slockHome) {
|
|
|
31534
31602
|
child.unref();
|
|
31535
31603
|
await supLogFd.close();
|
|
31536
31604
|
if (!pid) {
|
|
31537
|
-
throw new Error("SUPERVISOR_SPAWN_FAILED: could not spawn the
|
|
31605
|
+
throw new Error("SUPERVISOR_SPAWN_FAILED: could not spawn the service process");
|
|
31538
31606
|
}
|
|
31539
|
-
await writePidfileAt(
|
|
31607
|
+
await writePidfileAt(servicePidPath(slockHome), pid);
|
|
31540
31608
|
return pid;
|
|
31541
31609
|
}
|
|
31542
31610
|
var EX_CONFIG_EXIT_CODE = 78;
|
|
@@ -31562,7 +31630,7 @@ var defaultCoreFactory = async (creds) => {
|
|
|
31562
31630
|
}
|
|
31563
31631
|
return new coreMod.DaemonCore({ serverUrl: creds.serverUrl, apiKey: creds.apiKey, localTrace: true });
|
|
31564
31632
|
};
|
|
31565
|
-
function
|
|
31633
|
+
function classifyRunnerExit(code, signal) {
|
|
31566
31634
|
if (code === EX_CONFIG_EXIT_CODE) return "config-error";
|
|
31567
31635
|
if (signal === "SIGTERM" || signal === "SIGINT") return "graceful";
|
|
31568
31636
|
if (code === 0) return "graceful";
|
|
@@ -31607,18 +31675,18 @@ function formatReadySummary(ready, serverIds, opts) {
|
|
|
31607
31675
|
}
|
|
31608
31676
|
return `Daemons for ${serverIds.length} managed server(s) are running.`;
|
|
31609
31677
|
}
|
|
31610
|
-
async function
|
|
31678
|
+
async function runServiceStartupRecovery(slockHome) {
|
|
31611
31679
|
const parentHoldsLock = process.env[PARENT_LOCK_HELD_ENV_VAR] === "1";
|
|
31612
31680
|
try {
|
|
31613
31681
|
if (parentHoldsLock) {
|
|
31614
31682
|
process.stderr.write(
|
|
31615
|
-
"
|
|
31683
|
+
"Service startup: parent CLI holds mutation lock \u2014 skipping lock cleanup.\n"
|
|
31616
31684
|
);
|
|
31617
31685
|
} else {
|
|
31618
31686
|
const releasedLocks = await forceReleaseLock(slockHome);
|
|
31619
31687
|
if (releasedLocks.length > 0) {
|
|
31620
31688
|
process.stderr.write(
|
|
31621
|
-
`
|
|
31689
|
+
`Service startup: force-released ${releasedLocks.length} stale lock(s).
|
|
31622
31690
|
`
|
|
31623
31691
|
);
|
|
31624
31692
|
}
|
|
@@ -31626,19 +31694,19 @@ async function runSupervisorStartupRecovery(slockHome) {
|
|
|
31626
31694
|
const report = await runFullCleanup(slockHome, { skipLockCleanup: parentHoldsLock });
|
|
31627
31695
|
if (report.anyAction) {
|
|
31628
31696
|
process.stderr.write(
|
|
31629
|
-
`
|
|
31697
|
+
`Service startup recovery: cleaned ${report.stalePidfiles.length} pidfile(s), ${report.powerLossRecovered.length} quarantined, ${report.tmpFilesCleared.length} tmp file(s), ${report.staleLocks.length} stale lock(s).
|
|
31630
31698
|
`
|
|
31631
31699
|
);
|
|
31632
31700
|
}
|
|
31633
31701
|
} catch (err) {
|
|
31634
31702
|
const msg = err instanceof Error ? err.message : String(err);
|
|
31635
31703
|
process.stderr.write(
|
|
31636
|
-
`
|
|
31704
|
+
`Service startup recovery pass failed: ${msg}. Continuing; cleanup will retry on next service restart.
|
|
31637
31705
|
`
|
|
31638
31706
|
);
|
|
31639
31707
|
}
|
|
31640
31708
|
}
|
|
31641
|
-
async function
|
|
31709
|
+
async function resolveServiceIdentity() {
|
|
31642
31710
|
const here = fileURLToPath2(import.meta.url);
|
|
31643
31711
|
const installRoot = dirname8(dirname8(here));
|
|
31644
31712
|
let version = null;
|
|
@@ -31652,30 +31720,30 @@ async function resolveSupervisorIdentity() {
|
|
|
31652
31720
|
}
|
|
31653
31721
|
return { installRoot, version };
|
|
31654
31722
|
}
|
|
31655
|
-
async function
|
|
31723
|
+
async function writeServiceVersionEvidence(slockHome) {
|
|
31656
31724
|
try {
|
|
31657
|
-
const ident = await
|
|
31725
|
+
const ident = await resolveServiceIdentity();
|
|
31658
31726
|
const payload = {
|
|
31659
31727
|
version: ident.version,
|
|
31660
31728
|
installRoot: ident.installRoot,
|
|
31661
31729
|
pid: process.pid,
|
|
31662
31730
|
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
31663
31731
|
};
|
|
31664
|
-
const dest =
|
|
31732
|
+
const dest = serviceVersionPath(slockHome);
|
|
31665
31733
|
const tmp = `${dest}.tmp`;
|
|
31666
31734
|
await writeFile7(tmp, JSON.stringify(payload) + "\n", { mode: 384 });
|
|
31667
31735
|
await rename2(tmp, dest);
|
|
31668
31736
|
} catch (err) {
|
|
31669
31737
|
const msg = err instanceof Error ? err.message : String(err);
|
|
31670
31738
|
process.stderr.write(
|
|
31671
|
-
`
|
|
31739
|
+
`Service: failed to write version evidence: ${msg}. Continuing.
|
|
31672
31740
|
`
|
|
31673
31741
|
);
|
|
31674
31742
|
}
|
|
31675
31743
|
}
|
|
31676
|
-
async function
|
|
31744
|
+
async function readServiceVersionEvidence(slockHome) {
|
|
31677
31745
|
try {
|
|
31678
|
-
const raw = await readFile7(
|
|
31746
|
+
const raw = await readFile7(serviceVersionPath(slockHome), "utf8");
|
|
31679
31747
|
const parsed = JSON.parse(raw);
|
|
31680
31748
|
if (typeof parsed.installRoot !== "string" || typeof parsed.pid !== "number" || typeof parsed.writtenAt !== "string" || parsed.version !== null && typeof parsed.version !== "string") {
|
|
31681
31749
|
return null;
|
|
@@ -31690,16 +31758,16 @@ async function readSupervisorVersionEvidence(slockHome) {
|
|
|
31690
31758
|
return null;
|
|
31691
31759
|
}
|
|
31692
31760
|
}
|
|
31693
|
-
async function
|
|
31761
|
+
async function runService() {
|
|
31694
31762
|
const slockHome = resolveSlockHome();
|
|
31695
|
-
await mkdir8(
|
|
31696
|
-
await
|
|
31697
|
-
await writePidfileAt(
|
|
31698
|
-
await
|
|
31763
|
+
await mkdir8(serviceRunDir(slockHome), { recursive: true });
|
|
31764
|
+
await runServiceStartupRecovery(slockHome);
|
|
31765
|
+
await writePidfileAt(servicePidPath(slockHome), process.pid);
|
|
31766
|
+
await writeServiceVersionEvidence(slockHome);
|
|
31699
31767
|
const children = /* @__PURE__ */ new Map();
|
|
31700
31768
|
const { [PARENT_LOCK_HELD_ENV_VAR]: _parentLockMarker, ...childEnv } = process.env;
|
|
31701
31769
|
const spawnChild = async (serverId) => {
|
|
31702
|
-
const logPath =
|
|
31770
|
+
const logPath = serverRunnerLogPath(slockHome, serverId);
|
|
31703
31771
|
await mkdir8(dirname8(logPath), { recursive: true });
|
|
31704
31772
|
const logFd = await open(logPath, "a");
|
|
31705
31773
|
const { command, args } = buildResidentSpawn("__run", serverId);
|
|
@@ -31712,20 +31780,20 @@ async function runSupervise() {
|
|
|
31712
31780
|
if (!child.pid) return;
|
|
31713
31781
|
const handle = { serverId, child, stopping: false };
|
|
31714
31782
|
children.set(serverId, handle);
|
|
31715
|
-
await writePidfileAt(
|
|
31783
|
+
await writePidfileAt(serverRunnerPidPath(slockHome, serverId), child.pid);
|
|
31716
31784
|
child.on("exit", (code, signal) => {
|
|
31717
31785
|
void (async () => {
|
|
31718
31786
|
children.delete(serverId);
|
|
31719
|
-
await clearPidfileAt(
|
|
31787
|
+
await clearPidfileAt(serverRunnerPidPath(slockHome, serverId));
|
|
31720
31788
|
if (handle.stopping) return;
|
|
31721
|
-
const classification =
|
|
31789
|
+
const classification = classifyRunnerExit(code, signal);
|
|
31722
31790
|
if (classification === "config-error") {
|
|
31723
31791
|
try {
|
|
31724
31792
|
await markFatalConfig(slockHome, serverId, code, signal);
|
|
31725
31793
|
} catch {
|
|
31726
31794
|
}
|
|
31727
31795
|
process.stderr.write(
|
|
31728
|
-
`
|
|
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.
|
|
31729
31797
|
`
|
|
31730
31798
|
);
|
|
31731
31799
|
return;
|
|
@@ -31743,7 +31811,7 @@ async function runSupervise() {
|
|
|
31743
31811
|
}
|
|
31744
31812
|
if (await isDegraded(slockHome, serverId)) {
|
|
31745
31813
|
process.stderr.write(
|
|
31746
|
-
`
|
|
31814
|
+
`Service: server ${serverId} marked degraded (>=3 crashes in 60s); skipping auto-restart. Run \`slock-computer doctor ${serverId} --reset-health\` after fixing the underlying issue.
|
|
31747
31815
|
`
|
|
31748
31816
|
);
|
|
31749
31817
|
return;
|
|
@@ -31776,7 +31844,7 @@ async function runSupervise() {
|
|
|
31776
31844
|
if (shuttingDown) return;
|
|
31777
31845
|
shuttingDown = true;
|
|
31778
31846
|
for (const handle of children.values()) killChild(handle);
|
|
31779
|
-
void clearPidfileAt(
|
|
31847
|
+
void clearPidfileAt(servicePidPath(slockHome)).then(() => process.exit(0));
|
|
31780
31848
|
};
|
|
31781
31849
|
process.on("SIGTERM", shutdown);
|
|
31782
31850
|
process.on("SIGINT", shutdown);
|
|
@@ -31798,7 +31866,7 @@ async function runStart(opts = {}, deps = {}) {
|
|
|
31798
31866
|
serverLabel: opts.serverLabel ?? null
|
|
31799
31867
|
},
|
|
31800
31868
|
{
|
|
31801
|
-
|
|
31869
|
+
spawnDetachedService: deps.spawnDetachedService,
|
|
31802
31870
|
readPidfile: deps.readPidfile,
|
|
31803
31871
|
isProcessAlive: deps.isProcessAlive,
|
|
31804
31872
|
sleep: deps.sleep,
|
|
@@ -31806,17 +31874,17 @@ async function runStart(opts = {}, deps = {}) {
|
|
|
31806
31874
|
ensurePollIntervalMs: deps.ensurePollIntervalMs,
|
|
31807
31875
|
onEvent: (event) => {
|
|
31808
31876
|
if (event.type === "already_running") {
|
|
31809
|
-
info(`
|
|
31877
|
+
info(`Service already running (pid ${event.servicePid}).`);
|
|
31810
31878
|
} else if (event.type === "running") {
|
|
31811
31879
|
info(
|
|
31812
|
-
`Running
|
|
31880
|
+
`Running service in the foreground (managing ${event.managedTargets.length} of ${event.attachedCount} attached server(s)). Ctrl-C to stop.`
|
|
31813
31881
|
);
|
|
31814
31882
|
} else if (event.type === "spawned") {
|
|
31815
|
-
info(`
|
|
31883
|
+
info(`Service started (pid ${event.servicePid}); keeps running after this terminal closes.`);
|
|
31816
31884
|
spawnedBackground = {
|
|
31817
31885
|
managedCount: event.managedTargets.length,
|
|
31818
31886
|
attachedCount: event.attachedCount,
|
|
31819
|
-
logPath:
|
|
31887
|
+
logPath: serviceLogPath(resolveSlockHome())
|
|
31820
31888
|
};
|
|
31821
31889
|
} else if (event.type === "ready") {
|
|
31822
31890
|
info(formatReadySummary(event.ready, event.managedTargets, opts));
|
|
@@ -31836,7 +31904,7 @@ async function runStart(opts = {}, deps = {}) {
|
|
|
31836
31904
|
info(
|
|
31837
31905
|
`Managing ${sb.managedCount} of ${sb.attachedCount} attached server(s). Logs: ${sb.logPath}`
|
|
31838
31906
|
);
|
|
31839
|
-
info(`Per-server
|
|
31907
|
+
info(`Per-server server-runner logs: ~/.slock/computer/servers/<serverId>/server-runner.log`);
|
|
31840
31908
|
info(`Check state with \`slock-computer status\`.`);
|
|
31841
31909
|
}
|
|
31842
31910
|
}
|
|
@@ -31847,17 +31915,17 @@ async function runStop(deps = {}) {
|
|
|
31847
31915
|
{
|
|
31848
31916
|
readPidfile: deps.readPidfile,
|
|
31849
31917
|
isProcessAlive: deps.isProcessAlive,
|
|
31850
|
-
|
|
31918
|
+
killService: deps.killService,
|
|
31851
31919
|
sleep: deps.sleep,
|
|
31852
31920
|
pollIntervalMs: deps.pollIntervalMs,
|
|
31853
31921
|
timeoutMs: deps.timeoutMs,
|
|
31854
31922
|
onEvent: (event) => {
|
|
31855
31923
|
if (event.type === "not_running") {
|
|
31856
|
-
info("
|
|
31924
|
+
info("Service not running.");
|
|
31857
31925
|
} else if (event.type === "stale_pidfile_cleared") {
|
|
31858
|
-
info(`
|
|
31926
|
+
info(`Service not running (cleared stale pidfile for pid ${event.pid}).`);
|
|
31859
31927
|
} else if (event.type === "stopped") {
|
|
31860
|
-
info(`Stopped
|
|
31928
|
+
info(`Stopped service (pid ${event.pid}).`);
|
|
31861
31929
|
}
|
|
31862
31930
|
}
|
|
31863
31931
|
}
|
|
@@ -31882,7 +31950,7 @@ async function runDetach(serverId, serverLabel = serverId) {
|
|
|
31882
31950
|
} else if (event.type === "detached") {
|
|
31883
31951
|
info(`Detached from server ${event.serverLabel}.`);
|
|
31884
31952
|
info(
|
|
31885
|
-
`The
|
|
31953
|
+
`The service (if running) will stop that server's daemon on its next reconcile tick.`
|
|
31886
31954
|
);
|
|
31887
31955
|
}
|
|
31888
31956
|
}
|
|
@@ -32197,6 +32265,10 @@ async function pidStatus(pidfile) {
|
|
|
32197
32265
|
const pid = await readPidfileAt(pidfile);
|
|
32198
32266
|
return pid !== null && isProcessAlive2(pid) ? { running: true, pid } : { running: false };
|
|
32199
32267
|
}
|
|
32268
|
+
async function serviceState(slockHome) {
|
|
32269
|
+
const { pid } = await findLiveServicePidReadOnly(slockHome);
|
|
32270
|
+
return pid !== null ? { running: true, pid } : { running: false };
|
|
32271
|
+
}
|
|
32200
32272
|
async function deriveHealth(slockHome, serverId, daemon) {
|
|
32201
32273
|
if (!daemon.running) return "offline";
|
|
32202
32274
|
if (await isDegraded(slockHome, serverId)) return "degraded";
|
|
@@ -32206,20 +32278,20 @@ async function buildStatusReport(installRoot) {
|
|
|
32206
32278
|
const sessionRead = await readUserSession(userSessionPath(installRoot));
|
|
32207
32279
|
const session = sessionRead.session;
|
|
32208
32280
|
const attachments = await listServerAttachments(installRoot);
|
|
32209
|
-
const
|
|
32210
|
-
...await
|
|
32211
|
-
logPath:
|
|
32281
|
+
const service = {
|
|
32282
|
+
...await serviceState(installRoot),
|
|
32283
|
+
logPath: serviceLogPath(installRoot)
|
|
32212
32284
|
};
|
|
32213
32285
|
const servers = [];
|
|
32214
32286
|
for (const a of attachments) {
|
|
32215
|
-
const daemon = await pidStatus(
|
|
32287
|
+
const daemon = await pidStatus(serverRunnerPidPath(installRoot, a.serverId));
|
|
32216
32288
|
servers.push({
|
|
32217
32289
|
serverId: a.serverId,
|
|
32218
32290
|
serverSlug: a.serverSlug ?? null,
|
|
32219
32291
|
serverMachineId: a.serverMachineId,
|
|
32220
32292
|
serverUrl: a.serverUrl,
|
|
32221
32293
|
attachedAt: a.attachedAt ?? null,
|
|
32222
|
-
|
|
32294
|
+
serverRunnerLogPath: serverRunnerLogPath(installRoot, a.serverId),
|
|
32223
32295
|
daemon,
|
|
32224
32296
|
health: await deriveHealth(installRoot, a.serverId, daemon)
|
|
32225
32297
|
});
|
|
@@ -32231,7 +32303,7 @@ async function buildStatusReport(installRoot) {
|
|
|
32231
32303
|
userId: session ? str(session.userId) : null,
|
|
32232
32304
|
loginServerUrl: session ? str(session.serverUrl) : null,
|
|
32233
32305
|
userSessionError: sessionRead.state === "invalid" ? sessionRead.error : null,
|
|
32234
|
-
|
|
32306
|
+
service,
|
|
32235
32307
|
servers
|
|
32236
32308
|
};
|
|
32237
32309
|
}
|
|
@@ -32252,9 +32324,9 @@ async function runStatus(opts) {
|
|
|
32252
32324
|
);
|
|
32253
32325
|
if (report.loginServerUrl) info(`Login server: ${report.loginServerUrl}`);
|
|
32254
32326
|
info(
|
|
32255
|
-
`
|
|
32327
|
+
`Service: ${report.service.running ? `running (pid ${report.service.pid})` : "stopped \u2014 run `slock-computer start`"}`
|
|
32256
32328
|
);
|
|
32257
|
-
info(`
|
|
32329
|
+
info(`Service log: ${report.service.logPath}`);
|
|
32258
32330
|
info("");
|
|
32259
32331
|
if (report.servers.length === 0) {
|
|
32260
32332
|
info("Attachments: none \u2014 run `slock-computer attach /<serverSlug>` (e.g. `/myserver`).");
|
|
@@ -32266,7 +32338,7 @@ async function runStatus(opts) {
|
|
|
32266
32338
|
info(
|
|
32267
32339
|
` ${pad(formatServerSlugDisplay(s.serverSlug), 24)}${pad(s.health, 12)}${pad(dcol, 24)}${pad(s.serverMachineId, 38)}${s.serverUrl}`
|
|
32268
32340
|
);
|
|
32269
|
-
info(`
|
|
32341
|
+
info(` Server runner log: ${s.serverRunnerLogPath}`);
|
|
32270
32342
|
}
|
|
32271
32343
|
if (report.servers.some((s) => s.health === "degraded")) {
|
|
32272
32344
|
info("");
|
|
@@ -32486,8 +32558,8 @@ async function runDoctorChecks() {
|
|
|
32486
32558
|
report.userSessionError ? { name: "user session", ok: false, detail: "invalid user session file \u2014 re-run `slock-computer login`" } : report.loggedIn ? { name: "user session", ok: true, detail: `logged in (user ${report.userId ?? "?"})` } : { name: "user session", ok: false, detail: "not logged in \u2014 run `slock-computer login`" }
|
|
32487
32559
|
);
|
|
32488
32560
|
checks.push(
|
|
32489
|
-
report.
|
|
32490
|
-
name: "
|
|
32561
|
+
report.service.running ? { name: "service", ok: true, detail: `running (pid ${report.service.pid})` } : {
|
|
32562
|
+
name: "service",
|
|
32491
32563
|
ok: true,
|
|
32492
32564
|
detail: "stopped (run `slock-computer start` when you want background)"
|
|
32493
32565
|
}
|
|
@@ -32533,7 +32605,7 @@ async function runDoctor(opts) {
|
|
|
32533
32605
|
if (opts.resetHealth && opts.serverId) {
|
|
32534
32606
|
await resetHealth(slockHome, opts.serverId);
|
|
32535
32607
|
info(`Reset health state for server ${opts.serverLabel ?? opts.serverId}.`);
|
|
32536
|
-
info(`
|
|
32608
|
+
info(`Service will resume auto-restart on next daemon exit.`);
|
|
32537
32609
|
}
|
|
32538
32610
|
const checks = await runDoctorChecks();
|
|
32539
32611
|
const allOk = checks.every((c) => c.ok);
|
|
@@ -32607,14 +32679,14 @@ import { readFile as readFile10 } from "fs/promises";
|
|
|
32607
32679
|
var DEFAULT_LINES = 200;
|
|
32608
32680
|
async function runLogs(opts) {
|
|
32609
32681
|
const home = resolveSlockHome();
|
|
32610
|
-
const file = opts.
|
|
32682
|
+
const file = opts.service ? serviceLogPath(home) : serverRunnerLogPath(home, await resolveTargetServerId({ server: opts.server }));
|
|
32611
32683
|
let content;
|
|
32612
32684
|
try {
|
|
32613
32685
|
content = await readFile10(file, "utf8");
|
|
32614
32686
|
} catch {
|
|
32615
32687
|
fail(
|
|
32616
32688
|
"NO_DAEMON_LOG",
|
|
32617
|
-
opts.
|
|
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\`).`
|
|
32618
32690
|
);
|
|
32619
32691
|
}
|
|
32620
32692
|
const n = Number.isInteger(opts.lines) && opts.lines > 0 ? opts.lines : DEFAULT_LINES;
|
|
@@ -32682,7 +32754,7 @@ async function emitServiceStateTransition(slockHome, fromState, toState, trigger
|
|
|
32682
32754
|
trigger
|
|
32683
32755
|
};
|
|
32684
32756
|
try {
|
|
32685
|
-
const path3 =
|
|
32757
|
+
const path3 = serviceLogPath(slockHome);
|
|
32686
32758
|
await mkdir10(dirname10(path3), { recursive: true });
|
|
32687
32759
|
await appendFile3(path3, JSON.stringify(entry) + "\n");
|
|
32688
32760
|
} catch {
|
|
@@ -32866,7 +32938,7 @@ async function runChannelSet(slockHome, raw) {
|
|
|
32866
32938
|
}
|
|
32867
32939
|
await writeChannel(slockHome, parsed);
|
|
32868
32940
|
info(`Channel set to ${parsed}.`);
|
|
32869
|
-
info(`Note:
|
|
32941
|
+
info(`Note: service reads channel at startup; restart \`slock-computer start\` to apply.`);
|
|
32870
32942
|
}
|
|
32871
32943
|
|
|
32872
32944
|
// src/upgradeCli.ts
|
|
@@ -33372,49 +33444,49 @@ async function rollbackSwap(currentBinaryDir, deps = {}) {
|
|
|
33372
33444
|
}
|
|
33373
33445
|
}
|
|
33374
33446
|
async function rollbackRestart(slockHome, currentBinaryDir, deps = {}) {
|
|
33375
|
-
const
|
|
33447
|
+
const readServicePid = deps.readServicePid ?? (() => defaultReadServicePid(slockHome));
|
|
33376
33448
|
const isProcessAlive4 = deps.isProcessAlive ?? defaultIsProcessAlive;
|
|
33377
|
-
const
|
|
33378
|
-
const
|
|
33449
|
+
const killService = deps.killService ?? defaultKillService;
|
|
33450
|
+
const forceKillService = deps.forceKillService ?? defaultForceKillService;
|
|
33379
33451
|
const waitForExit = deps.waitForExit ?? defaultWaitForExit;
|
|
33380
|
-
const
|
|
33452
|
+
const spawnFreshService = deps.spawnFreshService;
|
|
33381
33453
|
const healthCheck = deps.healthCheck ?? (() => defaultHealthCheck(slockHome));
|
|
33382
33454
|
const healthTimeoutMs = deps.healthTimeoutMs ?? 3e4;
|
|
33383
33455
|
const healthPollIntervalMs = deps.healthPollIntervalMs ?? 500;
|
|
33384
|
-
let
|
|
33456
|
+
let serviceStopped = false;
|
|
33385
33457
|
try {
|
|
33386
|
-
const pid = await
|
|
33458
|
+
const pid = await readServicePid();
|
|
33387
33459
|
if (pid === null || !isProcessAlive4(pid)) {
|
|
33388
|
-
|
|
33460
|
+
serviceStopped = true;
|
|
33389
33461
|
} else {
|
|
33390
|
-
await
|
|
33462
|
+
await killService(pid);
|
|
33391
33463
|
const exited = await waitForExit(pid, 1e4);
|
|
33392
33464
|
if (!exited) {
|
|
33393
|
-
await
|
|
33465
|
+
await forceKillService(pid);
|
|
33394
33466
|
const escalatedExit = await waitForExit(pid, 5e3);
|
|
33395
|
-
|
|
33467
|
+
serviceStopped = escalatedExit;
|
|
33396
33468
|
} else {
|
|
33397
|
-
|
|
33469
|
+
serviceStopped = true;
|
|
33398
33470
|
}
|
|
33399
33471
|
}
|
|
33400
33472
|
} catch (e) {
|
|
33401
33473
|
return {
|
|
33402
33474
|
binaryRestored: false,
|
|
33403
|
-
|
|
33404
|
-
|
|
33405
|
-
|
|
33475
|
+
serviceStopped: false,
|
|
33476
|
+
serviceRespawned: false,
|
|
33477
|
+
serviceHealthy: false,
|
|
33406
33478
|
failedStep: "stop",
|
|
33407
33479
|
reason: e instanceof Error ? e.message : String(e)
|
|
33408
33480
|
};
|
|
33409
33481
|
}
|
|
33410
|
-
if (!
|
|
33482
|
+
if (!serviceStopped) {
|
|
33411
33483
|
return {
|
|
33412
33484
|
binaryRestored: false,
|
|
33413
|
-
|
|
33414
|
-
|
|
33415
|
-
|
|
33485
|
+
serviceStopped: false,
|
|
33486
|
+
serviceRespawned: false,
|
|
33487
|
+
serviceHealthy: false,
|
|
33416
33488
|
failedStep: "stop",
|
|
33417
|
-
reason: "new-version
|
|
33489
|
+
reason: "new-version service did not exit (graceful + SIGKILL both timed out)"
|
|
33418
33490
|
};
|
|
33419
33491
|
}
|
|
33420
33492
|
const swapRb = await rollbackSwap(currentBinaryDir, {
|
|
@@ -33424,31 +33496,31 @@ async function rollbackRestart(slockHome, currentBinaryDir, deps = {}) {
|
|
|
33424
33496
|
if (!swapRb.rolledBack) {
|
|
33425
33497
|
return {
|
|
33426
33498
|
binaryRestored: false,
|
|
33427
|
-
|
|
33428
|
-
|
|
33429
|
-
|
|
33499
|
+
serviceStopped: true,
|
|
33500
|
+
serviceRespawned: false,
|
|
33501
|
+
serviceHealthy: false,
|
|
33430
33502
|
failedStep: "binary",
|
|
33431
33503
|
reason: "could not restore .prev \u2192 currentBinaryDir"
|
|
33432
33504
|
};
|
|
33433
33505
|
}
|
|
33434
|
-
if (!
|
|
33506
|
+
if (!spawnFreshService) {
|
|
33435
33507
|
return {
|
|
33436
33508
|
binaryRestored: true,
|
|
33437
|
-
|
|
33438
|
-
|
|
33439
|
-
|
|
33509
|
+
serviceStopped: true,
|
|
33510
|
+
serviceRespawned: false,
|
|
33511
|
+
serviceHealthy: false,
|
|
33440
33512
|
failedStep: "respawn",
|
|
33441
|
-
reason: "no
|
|
33513
|
+
reason: "no spawnFreshService callback wired; old service not respawned"
|
|
33442
33514
|
};
|
|
33443
33515
|
}
|
|
33444
33516
|
try {
|
|
33445
|
-
await
|
|
33517
|
+
await spawnFreshService();
|
|
33446
33518
|
} catch (e) {
|
|
33447
33519
|
return {
|
|
33448
33520
|
binaryRestored: true,
|
|
33449
|
-
|
|
33450
|
-
|
|
33451
|
-
|
|
33521
|
+
serviceStopped: true,
|
|
33522
|
+
serviceRespawned: false,
|
|
33523
|
+
serviceHealthy: false,
|
|
33452
33524
|
failedStep: "respawn",
|
|
33453
33525
|
reason: e instanceof Error ? e.message : String(e)
|
|
33454
33526
|
};
|
|
@@ -33465,53 +33537,53 @@ async function rollbackRestart(slockHome, currentBinaryDir, deps = {}) {
|
|
|
33465
33537
|
if (!healthy) {
|
|
33466
33538
|
return {
|
|
33467
33539
|
binaryRestored: true,
|
|
33468
|
-
|
|
33469
|
-
|
|
33470
|
-
|
|
33540
|
+
serviceStopped: true,
|
|
33541
|
+
serviceRespawned: true,
|
|
33542
|
+
serviceHealthy: false,
|
|
33471
33543
|
failedStep: "health",
|
|
33472
|
-
reason: "old
|
|
33544
|
+
reason: "old service failed health check after rollback respawn"
|
|
33473
33545
|
};
|
|
33474
33546
|
}
|
|
33475
33547
|
return {
|
|
33476
33548
|
binaryRestored: true,
|
|
33477
|
-
|
|
33478
|
-
|
|
33479
|
-
|
|
33549
|
+
serviceStopped: true,
|
|
33550
|
+
serviceRespawned: true,
|
|
33551
|
+
serviceHealthy: true
|
|
33480
33552
|
};
|
|
33481
33553
|
}
|
|
33482
33554
|
async function restartPhase(slockHome, deps = {}) {
|
|
33483
|
-
const
|
|
33484
|
-
const
|
|
33485
|
-
const
|
|
33555
|
+
const readServicePid = deps.readServicePid ?? (() => defaultReadServicePid(slockHome));
|
|
33556
|
+
const killService = deps.killService ?? defaultKillService;
|
|
33557
|
+
const forceKillService = deps.forceKillService ?? defaultForceKillService;
|
|
33486
33558
|
const waitForExit = deps.waitForExit ?? defaultWaitForExit;
|
|
33487
|
-
const
|
|
33559
|
+
const spawnFreshService = deps.spawnFreshService;
|
|
33488
33560
|
const healthCheck = deps.healthCheck ?? (() => defaultHealthCheck(slockHome));
|
|
33489
33561
|
const healthTimeoutMs = deps.healthTimeoutMs ?? 3e4;
|
|
33490
33562
|
const healthPollIntervalMs = deps.healthPollIntervalMs ?? 500;
|
|
33491
33563
|
const forceKill = deps.forceKill === true;
|
|
33492
|
-
const oldPid = await
|
|
33564
|
+
const oldPid = await readServicePid();
|
|
33493
33565
|
if (oldPid !== null) {
|
|
33494
33566
|
try {
|
|
33495
33567
|
if (forceKill) {
|
|
33496
|
-
await
|
|
33568
|
+
await forceKillService(oldPid);
|
|
33497
33569
|
} else {
|
|
33498
|
-
await
|
|
33570
|
+
await killService(oldPid);
|
|
33499
33571
|
const exited = await waitForExit(oldPid, 1e4);
|
|
33500
33572
|
if (!exited) {
|
|
33501
|
-
await
|
|
33573
|
+
await forceKillService(oldPid);
|
|
33502
33574
|
const escalatedExit = await waitForExit(oldPid, 5e3);
|
|
33503
33575
|
if (!escalatedExit) {
|
|
33504
|
-
return { ok: false, reason: "
|
|
33576
|
+
return { ok: false, reason: "service_kill_failed" };
|
|
33505
33577
|
}
|
|
33506
33578
|
}
|
|
33507
33579
|
}
|
|
33508
33580
|
} catch {
|
|
33509
|
-
return { ok: false, reason: "
|
|
33581
|
+
return { ok: false, reason: "service_kill_failed" };
|
|
33510
33582
|
}
|
|
33511
33583
|
}
|
|
33512
|
-
if (
|
|
33584
|
+
if (spawnFreshService) {
|
|
33513
33585
|
try {
|
|
33514
|
-
await
|
|
33586
|
+
await spawnFreshService();
|
|
33515
33587
|
} catch {
|
|
33516
33588
|
return { ok: false, reason: "spawn_failed" };
|
|
33517
33589
|
}
|
|
@@ -33525,16 +33597,11 @@ async function restartPhase(slockHome, deps = {}) {
|
|
|
33525
33597
|
}
|
|
33526
33598
|
return { ok: false, reason: "health_check_timeout" };
|
|
33527
33599
|
}
|
|
33528
|
-
async function
|
|
33529
|
-
|
|
33530
|
-
|
|
33531
|
-
const pid = Number.parseInt(raw, 10);
|
|
33532
|
-
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
33533
|
-
} catch {
|
|
33534
|
-
return null;
|
|
33535
|
-
}
|
|
33600
|
+
async function defaultReadServicePid(slockHome) {
|
|
33601
|
+
const { pid } = await findLiveServicePid(slockHome);
|
|
33602
|
+
return pid;
|
|
33536
33603
|
}
|
|
33537
|
-
async function
|
|
33604
|
+
async function defaultKillService(pid) {
|
|
33538
33605
|
try {
|
|
33539
33606
|
process.kill(pid, "SIGTERM");
|
|
33540
33607
|
} catch (err) {
|
|
@@ -33542,7 +33609,7 @@ async function defaultKillSupervisor(pid) {
|
|
|
33542
33609
|
throw err;
|
|
33543
33610
|
}
|
|
33544
33611
|
}
|
|
33545
|
-
async function
|
|
33612
|
+
async function defaultForceKillService(pid) {
|
|
33546
33613
|
try {
|
|
33547
33614
|
process.kill(pid, "SIGKILL");
|
|
33548
33615
|
} catch (err) {
|
|
@@ -33566,7 +33633,7 @@ async function defaultWaitForExit(pid, timeoutMs) {
|
|
|
33566
33633
|
return false;
|
|
33567
33634
|
}
|
|
33568
33635
|
async function defaultHealthCheck(slockHome) {
|
|
33569
|
-
const pid = await
|
|
33636
|
+
const pid = await defaultReadServicePid(slockHome);
|
|
33570
33637
|
if (pid === null) return false;
|
|
33571
33638
|
try {
|
|
33572
33639
|
process.kill(pid, 0);
|
|
@@ -33742,11 +33809,11 @@ async function runUpgrade(slockHome, opts) {
|
|
|
33742
33809
|
};
|
|
33743
33810
|
}
|
|
33744
33811
|
const restart = await restartPhase(slockHome, {
|
|
33745
|
-
|
|
33746
|
-
|
|
33747
|
-
|
|
33812
|
+
readServicePid: deps.readServicePid,
|
|
33813
|
+
killService: deps.killService,
|
|
33814
|
+
forceKillService: deps.forceKillService,
|
|
33748
33815
|
waitForExit: deps.waitForExit,
|
|
33749
|
-
|
|
33816
|
+
spawnFreshService: deps.spawnFreshService,
|
|
33750
33817
|
healthCheck: deps.healthCheck,
|
|
33751
33818
|
healthTimeoutMs: deps.healthTimeoutMs,
|
|
33752
33819
|
healthPollIntervalMs: deps.healthPollIntervalMs,
|
|
@@ -33754,12 +33821,12 @@ async function runUpgrade(slockHome, opts) {
|
|
|
33754
33821
|
});
|
|
33755
33822
|
if (!restart.ok) {
|
|
33756
33823
|
const rb = await rollbackRestart(slockHome, opts.currentBinaryDir, {
|
|
33757
|
-
|
|
33824
|
+
readServicePid: deps.readServicePid,
|
|
33758
33825
|
isProcessAlive: deps.isProcessAlive,
|
|
33759
|
-
|
|
33760
|
-
|
|
33826
|
+
killService: deps.killService,
|
|
33827
|
+
forceKillService: deps.forceKillService,
|
|
33761
33828
|
waitForExit: deps.waitForExit,
|
|
33762
|
-
|
|
33829
|
+
spawnFreshService: deps.spawnFreshService,
|
|
33763
33830
|
healthCheck: deps.healthCheck,
|
|
33764
33831
|
healthTimeoutMs: deps.healthTimeoutMs,
|
|
33765
33832
|
healthPollIntervalMs: deps.healthPollIntervalMs,
|
|
@@ -33774,7 +33841,7 @@ async function runUpgrade(slockHome, opts) {
|
|
|
33774
33841
|
verify,
|
|
33775
33842
|
swap,
|
|
33776
33843
|
restart,
|
|
33777
|
-
rolledBack: rb.binaryRestored && rb.
|
|
33844
|
+
rolledBack: rb.binaryRestored && rb.serviceHealthy,
|
|
33778
33845
|
rollback: rb
|
|
33779
33846
|
};
|
|
33780
33847
|
}
|
|
@@ -33787,12 +33854,12 @@ async function runUpgrade(slockHome, opts) {
|
|
|
33787
33854
|
});
|
|
33788
33855
|
if (!rolling.ok) {
|
|
33789
33856
|
const rb = await rollbackRestart(slockHome, opts.currentBinaryDir, {
|
|
33790
|
-
|
|
33857
|
+
readServicePid: deps.readServicePid,
|
|
33791
33858
|
isProcessAlive: deps.isProcessAlive,
|
|
33792
|
-
|
|
33793
|
-
|
|
33859
|
+
killService: deps.killService,
|
|
33860
|
+
forceKillService: deps.forceKillService,
|
|
33794
33861
|
waitForExit: deps.waitForExit,
|
|
33795
|
-
|
|
33862
|
+
spawnFreshService: deps.spawnFreshService,
|
|
33796
33863
|
healthCheck: deps.healthCheck,
|
|
33797
33864
|
healthTimeoutMs: deps.healthTimeoutMs,
|
|
33798
33865
|
healthPollIntervalMs: deps.healthPollIntervalMs,
|
|
@@ -33809,7 +33876,7 @@ async function runUpgrade(slockHome, opts) {
|
|
|
33809
33876
|
swap,
|
|
33810
33877
|
restart,
|
|
33811
33878
|
rolling,
|
|
33812
|
-
rolledBack: rb.binaryRestored && rb.
|
|
33879
|
+
rolledBack: rb.binaryRestored && rb.serviceHealthy,
|
|
33813
33880
|
rollback: rb
|
|
33814
33881
|
};
|
|
33815
33882
|
}
|
|
@@ -33850,7 +33917,7 @@ async function rollingDaemonHealthCheck(slockHome, deps = {}) {
|
|
|
33850
33917
|
}
|
|
33851
33918
|
async function defaultReadDaemonPid(slockHome, serverId) {
|
|
33852
33919
|
try {
|
|
33853
|
-
const raw = (await readFile14(
|
|
33920
|
+
const raw = (await readFile14(serverRunnerPidPath(slockHome, serverId), "utf8")).trim();
|
|
33854
33921
|
const pid = Number.parseInt(raw, 10);
|
|
33855
33922
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
33856
33923
|
} catch {
|
|
@@ -33956,8 +34023,8 @@ async function readBundledDaemonVersion(binaryDir) {
|
|
|
33956
34023
|
return null;
|
|
33957
34024
|
}
|
|
33958
34025
|
}
|
|
33959
|
-
async function
|
|
33960
|
-
await
|
|
34026
|
+
async function defaultSpawnFreshService(slockHome) {
|
|
34027
|
+
await spawnDetachedService(slockHome);
|
|
33961
34028
|
}
|
|
33962
34029
|
async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
33963
34030
|
let channel2;
|
|
@@ -34056,8 +34123,10 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34056
34123
|
if (drainMode !== void 0 && drainMode !== "drain") {
|
|
34057
34124
|
info(`Drain mode: ${drainMode}${drainMode === "force" ? " (in-flight turns will be dropped)" : ""}.`);
|
|
34058
34125
|
}
|
|
34126
|
+
const readBundledFn = deps.readBundledDaemonVersion ?? readBundledDaemonVersion;
|
|
34127
|
+
const fromBundledDaemonVersion = await readBundledFn(currentBinaryDir);
|
|
34059
34128
|
const runUpgradeFn = deps.runUpgradeFn ?? runUpgrade;
|
|
34060
|
-
const
|
|
34129
|
+
const spawnFreshService = deps.spawnFreshService ?? defaultSpawnFreshService;
|
|
34061
34130
|
const outcome = await runUpgradeFn(slockHome, {
|
|
34062
34131
|
targetVersion,
|
|
34063
34132
|
fromVersion,
|
|
@@ -34065,25 +34134,26 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34065
34134
|
currentBinaryDir,
|
|
34066
34135
|
drainMode,
|
|
34067
34136
|
deps: {
|
|
34068
|
-
// Wire the production
|
|
34137
|
+
// Wire the production service-spawn into BOTH phase 5 (happy)
|
|
34069
34138
|
// and the operational-rollback respawn path inside `runUpgrade`.
|
|
34070
34139
|
// Without this, phase 5 falls through to a no-spawn health-poll
|
|
34071
34140
|
// and rollback later trips `failedStep="respawn"`. See Dayu
|
|
34072
34141
|
// blocker (#wg-slock-computer:b43b36fb msg=911eb84e).
|
|
34073
|
-
|
|
34142
|
+
spawnFreshService: () => spawnFreshService(slockHome)
|
|
34074
34143
|
}
|
|
34075
34144
|
});
|
|
34076
|
-
const
|
|
34077
|
-
const
|
|
34078
|
-
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);
|
|
34079
34149
|
const logTrigger = opts.trigger ?? "cli";
|
|
34080
34150
|
if (outcome.ok) {
|
|
34081
34151
|
info(
|
|
34082
34152
|
`Upgrade ${fromVersion} \u2192 ${targetVersion} succeeded (health-OK in ${outcome.restart?.healthAfterMs ?? 0}ms).`
|
|
34083
34153
|
);
|
|
34084
34154
|
await appendUpgradeLogEntry(slockHome, {
|
|
34085
|
-
fromBundle
|
|
34086
|
-
toBundle
|
|
34155
|
+
fromBundle,
|
|
34156
|
+
toBundle,
|
|
34087
34157
|
channel: channel2,
|
|
34088
34158
|
trigger: logTrigger,
|
|
34089
34159
|
outcome: "ok"
|
|
@@ -34100,11 +34170,11 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34100
34170
|
}
|
|
34101
34171
|
if (outcome.phase === "cleanup") {
|
|
34102
34172
|
info(
|
|
34103
|
-
`Upgrade ${fromVersion} \u2192 ${targetVersion} succeeded; cleanup phase reported a non-fatal issue: ${outcome.reason ?? "unknown"}. Active layout +
|
|
34173
|
+
`Upgrade ${fromVersion} \u2192 ${targetVersion} succeeded; cleanup phase reported a non-fatal issue: ${outcome.reason ?? "unknown"}. Active layout + service are healthy.`
|
|
34104
34174
|
);
|
|
34105
34175
|
await appendUpgradeLogEntry(slockHome, {
|
|
34106
|
-
fromBundle
|
|
34107
|
-
toBundle
|
|
34176
|
+
fromBundle,
|
|
34177
|
+
toBundle,
|
|
34108
34178
|
channel: channel2,
|
|
34109
34179
|
trigger: logTrigger,
|
|
34110
34180
|
outcome: "ok"
|
|
@@ -34114,8 +34184,8 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34114
34184
|
}
|
|
34115
34185
|
const code = mapFailurePhaseToCode(outcome);
|
|
34116
34186
|
await appendUpgradeLogEntry(slockHome, {
|
|
34117
|
-
fromBundle
|
|
34118
|
-
toBundle
|
|
34187
|
+
fromBundle,
|
|
34188
|
+
toBundle,
|
|
34119
34189
|
channel: channel2,
|
|
34120
34190
|
trigger: logTrigger,
|
|
34121
34191
|
outcome: "err",
|
|
@@ -34273,8 +34343,8 @@ function buildSimulatedDeps(slockHome, opts) {
|
|
|
34273
34343
|
fsRename: opts.simulateFail === "swap" ? async () => {
|
|
34274
34344
|
throw new Error("simulated swap failure: fsRename");
|
|
34275
34345
|
} : void 0,
|
|
34276
|
-
|
|
34277
|
-
|
|
34346
|
+
readServicePid: async () => null,
|
|
34347
|
+
spawnFreshService: async () => {
|
|
34278
34348
|
await maybeSleep();
|
|
34279
34349
|
spawnCalls += 1;
|
|
34280
34350
|
if (opts.simulateFail === "restart" && spawnCalls === 1) {
|
|
@@ -34366,7 +34436,7 @@ async function captureStateSnapshot(slockHome, version, currentBinaryDir) {
|
|
|
34366
34436
|
upgradeSnapshot: await pathInfo(upgradeSnapshotPath(slockHome)),
|
|
34367
34437
|
stagingDir: stagingExists,
|
|
34368
34438
|
stagingEntries,
|
|
34369
|
-
|
|
34439
|
+
servicePid: await pathInfo(servicePidPath(slockHome)),
|
|
34370
34440
|
channelFile: await pathInfo(join8(computerDir(slockHome), "channel")),
|
|
34371
34441
|
computerDir: await pathInfo(computerDir(slockHome)),
|
|
34372
34442
|
servers,
|
|
@@ -34445,8 +34515,8 @@ async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
|
|
|
34445
34515
|
const currentBinaryDir = opts.currentBinaryDir ?? (deps.currentBinaryDir ?? defaultCurrentBinaryDirLocal)();
|
|
34446
34516
|
const fromVersion = opts.fromVersion ?? await (deps.currentVersion ?? defaultCurrentVersionLocal)();
|
|
34447
34517
|
const channel2 = opts.channel ?? "latest";
|
|
34448
|
-
const
|
|
34449
|
-
await
|
|
34518
|
+
const spawnFreshService = deps.spawnFreshService ?? (async (h) => {
|
|
34519
|
+
await spawnDetachedService(h);
|
|
34450
34520
|
});
|
|
34451
34521
|
await mkdir16(slockHome, { recursive: true });
|
|
34452
34522
|
const outcome = await runUpgrade(slockHome, {
|
|
@@ -34474,7 +34544,7 @@ async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
|
|
|
34474
34544
|
// dependencies from the registry; this smoke isolates the swap /
|
|
34475
34545
|
// restart mechanics and keeps dependency hydration out of scope.
|
|
34476
34546
|
npmInstall: async () => ({ exitCode: 0, stderr: "" }),
|
|
34477
|
-
|
|
34547
|
+
spawnFreshService: () => spawnFreshService(slockHome),
|
|
34478
34548
|
// PR-E 18/n: the workspace-built tarball still carries
|
|
34479
34549
|
// `workspace:*` (or `file:` after pack-rewrite in some workflows)
|
|
34480
34550
|
// for `@slock-ai/daemon`, which production preflight rejects as
|
|
@@ -34485,13 +34555,13 @@ async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
|
|
|
34485
34555
|
preflight: { allowUnsafeSpec: true }
|
|
34486
34556
|
}
|
|
34487
34557
|
});
|
|
34488
|
-
const activeVersion = await
|
|
34558
|
+
const activeVersion = await readServiceVersionEvidence(slockHome);
|
|
34489
34559
|
return { outcome, activeVersion };
|
|
34490
34560
|
}
|
|
34491
|
-
async function runUpgradeInstallSmokeCli(slockHome, opts, writer = (s) => process.stdout.write(s)) {
|
|
34561
|
+
async function runUpgradeInstallSmokeCli(slockHome, opts, writer = (s) => process.stdout.write(s), deps = {}) {
|
|
34492
34562
|
let report;
|
|
34493
34563
|
try {
|
|
34494
|
-
report = await runUpgradeInstallSmoke(slockHome, opts);
|
|
34564
|
+
report = await runUpgradeInstallSmoke(slockHome, opts, deps);
|
|
34495
34565
|
} catch (e) {
|
|
34496
34566
|
const msg = e instanceof Error ? e.message : String(e);
|
|
34497
34567
|
writer(
|
|
@@ -34556,12 +34626,12 @@ program2.name("slock-computer").description("Slock Computer \u2014 local-machine
|
|
|
34556
34626
|
program2.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) => {
|
|
34557
34627
|
await runLogin({ serverUrl: opts.serverUrl });
|
|
34558
34628
|
}));
|
|
34559
|
-
program2.command("attach").argument("<serverSlug>", "target Slock server slug (canonical form `/myserver`; bare `myserver` accepted)").description("Attach this Computer to one Slock server (add-not-replace; multi-server OK).").option("--server-url <url>", `Slock API base URL; defaults to the saved user session, SLOCK_SERVER_URL, or ${DEFAULT_SLOCK_SERVER_URL}`).option("--name <name>", "Computer display name; defaults to a sanitized hostname").option("--no-run", "only authorize + write local state; do not start the
|
|
34629
|
+
program2.command("attach").argument("<serverSlug>", "target Slock server slug (canonical form `/myserver`; bare `myserver` accepted)").description("Attach this Computer to one Slock server (add-not-replace; multi-server OK).").option("--server-url <url>", `Slock API base URL; defaults to the saved user session, SLOCK_SERVER_URL, or ${DEFAULT_SLOCK_SERVER_URL}`).option("--name <name>", "Computer display name; defaults to a sanitized hostname").option("--no-run", "only authorize + write local state; do not start the service").option("--foreground", "run the service in this terminal instead of the background").action(withCliExit(async (serverSlug, opts) => {
|
|
34560
34630
|
await withMutationLock(
|
|
34561
34631
|
() => runAttach({ serverSlug, serverUrl: opts.serverUrl, name: opts.name, run: opts.run, foreground: opts.foreground })
|
|
34562
34632
|
);
|
|
34563
34633
|
}));
|
|
34564
|
-
program2.command("setup").argument("<serverSlug>", "target Slock server slug (canonical form `/myserver`; bare `myserver` accepted)").description("Set up this Computer for one server: login if needed, attach (or \xA7X.1 migrate-prompt) if needed, then start.").option("--server-url <url>", `Slock API base URL; defaults to the saved user session, SLOCK_SERVER_URL, or ${DEFAULT_SLOCK_SERVER_URL}`).option("--name <name>", "Computer display name for a new attachment; defaults to a sanitized hostname").option("--no-start", "stop after login + attach; do not start the
|
|
34634
|
+
program2.command("setup").argument("<serverSlug>", "target Slock server slug (canonical form `/myserver`; bare `myserver` accepted)").description("Set up this Computer for one server: login if needed, attach (or \xA7X.1 migrate-prompt) if needed, then start.").option("--server-url <url>", `Slock API base URL; defaults to the saved user session, SLOCK_SERVER_URL, or ${DEFAULT_SLOCK_SERVER_URL}`).option("--name <name>", "Computer display name for a new attachment; defaults to a sanitized hostname").option("--no-start", "stop after login + attach; do not start the service").option("--foreground", "run the service in this terminal instead of the background").option("-y, --yes", "allow non-interactive setup after confirming the planned actions").action(
|
|
34565
34635
|
withCliExit(async (serverSlug, opts) => {
|
|
34566
34636
|
await withMutationLock(
|
|
34567
34637
|
() => runSetup({
|
|
@@ -34578,10 +34648,10 @@ program2.command("setup").argument("<serverSlug>", "target Slock server slug (ca
|
|
|
34578
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) => {
|
|
34579
34649
|
await withMutationLock(async () => runDetach(await resolveTargetServerId({ server: serverSlug }), serverSlug));
|
|
34580
34650
|
}));
|
|
34581
|
-
program2.command("status").description("Show this Computer's aggregate state (login +
|
|
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) => {
|
|
34582
34652
|
await runStatus({ json: opts.json });
|
|
34583
34653
|
}));
|
|
34584
|
-
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) => {
|
|
34585
34655
|
await withMutationLock(
|
|
34586
34656
|
async () => runStart({
|
|
34587
34657
|
foreground: opts.foreground,
|
|
@@ -34590,10 +34660,10 @@ program2.command("start").argument("[serverSlug]", "optional: verify this server
|
|
|
34590
34660
|
})
|
|
34591
34661
|
);
|
|
34592
34662
|
}));
|
|
34593
|
-
program2.command("stop").description("Stop the Computer
|
|
34663
|
+
program2.command("stop").description("Stop the Computer service (and all managed per-server server-runners).").action(withCliExit(async () => {
|
|
34594
34664
|
await withMutationLock(() => runStop());
|
|
34595
34665
|
}));
|
|
34596
|
-
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
|
|
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(
|
|
34597
34667
|
withCliExit(
|
|
34598
34668
|
async (serverSlug, opts) => {
|
|
34599
34669
|
const serverId = serverSlug ? await resolveTargetServerId({ server: serverSlug }) : void 0;
|
|
@@ -34608,7 +34678,7 @@ program2.command("doctor").argument("[serverSlug]", "optional: scope detail (rec
|
|
|
34608
34678
|
}
|
|
34609
34679
|
)
|
|
34610
34680
|
);
|
|
34611
|
-
program2.command("reset").description("Clear a degraded state and resume the
|
|
34681
|
+
program2.command("reset").description("Clear a degraded state and resume the service's auto-restart loop.").option("--service", "clear the service-level crash history (cascade record)").option("--runner", "clear a runner's crash history (selected by `--server`)").option("--server <slug>", "with `--runner`, select target server slug (required when \u22652 attached)").option("--json", "emit the machine-readable result").action(
|
|
34612
34682
|
withCliExit(async (opts) => {
|
|
34613
34683
|
await withMutationLock(
|
|
34614
34684
|
() => runReset({
|
|
@@ -34620,8 +34690,8 @@ program2.command("reset").description("Clear a degraded state and resume the sup
|
|
|
34620
34690
|
);
|
|
34621
34691
|
})
|
|
34622
34692
|
);
|
|
34623
|
-
program2.command("logs").description("Tail one server's
|
|
34624
|
-
await runLogs({ lines: opts.lines, server: opts.server ?? null,
|
|
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) => {
|
|
34694
|
+
await runLogs({ lines: opts.lines, server: opts.server ?? null, service: !!opts.service });
|
|
34625
34695
|
}));
|
|
34626
34696
|
var runners = program2.command("runners").description("Computer runner control plane (per-server scoped; \xA712 whitelist server-side).");
|
|
34627
34697
|
runners.command("list").description("List runners on one attached server.").option("--json", "emit the machine-readable list").option("--server <slug>", "select target server slug (required when \u22652 attached)").action(withCliExit(async (opts) => {
|
|
@@ -34701,8 +34771,8 @@ program2.command("upgrade").description(
|
|
|
34701
34771
|
}
|
|
34702
34772
|
)
|
|
34703
34773
|
);
|
|
34704
|
-
program2.command("
|
|
34705
|
-
await
|
|
34774
|
+
program2.command("__service", { hidden: true }).action(withCliExit(async () => {
|
|
34775
|
+
await runService();
|
|
34706
34776
|
}));
|
|
34707
34777
|
program2.command("__run", { hidden: true }).argument("<serverId>", "server id this daemon child is bound to").action(withCliExit(async (serverId) => {
|
|
34708
34778
|
await runResident(serverId);
|