@botiverse/raft-computer 0.0.59 → 0.0.61
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 +274 -193
- package/dist/lib/index.js +57 -9
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -29592,6 +29592,7 @@ var {
|
|
|
29592
29592
|
|
|
29593
29593
|
// src/login.ts
|
|
29594
29594
|
init_esm_shims();
|
|
29595
|
+
import { unlink } from "fs/promises";
|
|
29595
29596
|
|
|
29596
29597
|
// src/output.ts
|
|
29597
29598
|
init_esm_shims();
|
|
@@ -29619,6 +29620,124 @@ function fail(code, message, exitCode = 1) {
|
|
|
29619
29620
|
throw new CliExit(exitCode, code);
|
|
29620
29621
|
}
|
|
29621
29622
|
|
|
29623
|
+
// src/paths.ts
|
|
29624
|
+
init_esm_shims();
|
|
29625
|
+
import { createHash } from "crypto";
|
|
29626
|
+
import os from "os";
|
|
29627
|
+
import path2 from "path";
|
|
29628
|
+
var CURRENT_SCHEMA_VERSION = 1;
|
|
29629
|
+
function resolveSlockHome(env = process.env, homeDir = os.homedir()) {
|
|
29630
|
+
const configured = env.SLOCK_HOME?.trim();
|
|
29631
|
+
const raw = configured && configured.length > 0 ? configured : path2.join(homeDir, ".slock");
|
|
29632
|
+
return path2.resolve(expandHome(raw, homeDir));
|
|
29633
|
+
}
|
|
29634
|
+
function expandHome(input, homeDir) {
|
|
29635
|
+
if (input === "~") return homeDir;
|
|
29636
|
+
if (input.startsWith("~/")) return path2.join(homeDir, input.slice(2));
|
|
29637
|
+
return input;
|
|
29638
|
+
}
|
|
29639
|
+
function computerDir(slockHome) {
|
|
29640
|
+
return path2.join(slockHome, "computer");
|
|
29641
|
+
}
|
|
29642
|
+
function userSessionPath(slockHome) {
|
|
29643
|
+
return path2.join(computerDir(slockHome), "user-session.json");
|
|
29644
|
+
}
|
|
29645
|
+
var SERVER_ID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
29646
|
+
function isValidServerId(serverId) {
|
|
29647
|
+
return SERVER_ID_RE.test(serverId);
|
|
29648
|
+
}
|
|
29649
|
+
function assertValidServerId(serverId) {
|
|
29650
|
+
if (!isValidServerId(serverId)) {
|
|
29651
|
+
throw new Error(`invalid server id: ${JSON.stringify(serverId)} (expected a UUID)`);
|
|
29652
|
+
}
|
|
29653
|
+
return serverId;
|
|
29654
|
+
}
|
|
29655
|
+
function serversDir(slockHome) {
|
|
29656
|
+
return path2.join(computerDir(slockHome), "servers");
|
|
29657
|
+
}
|
|
29658
|
+
function serverDir(slockHome, serverId) {
|
|
29659
|
+
return path2.join(serversDir(slockHome), assertValidServerId(serverId));
|
|
29660
|
+
}
|
|
29661
|
+
function serverAttachmentPath(slockHome, serverId) {
|
|
29662
|
+
return path2.join(serverDir(slockHome, serverId), "runner.state.json");
|
|
29663
|
+
}
|
|
29664
|
+
function legacyServerAttachmentPath(slockHome, serverId) {
|
|
29665
|
+
return path2.join(serverDir(slockHome, serverId), "attachment.json");
|
|
29666
|
+
}
|
|
29667
|
+
function serverRunnerPidPath(slockHome, serverId) {
|
|
29668
|
+
return path2.join(serverDir(slockHome, serverId), "runner.pid");
|
|
29669
|
+
}
|
|
29670
|
+
function serverRunnerLogPath(slockHome, serverId) {
|
|
29671
|
+
return path2.join(serverDir(slockHome, serverId), "runner.log");
|
|
29672
|
+
}
|
|
29673
|
+
function serverManagedFlagPath(slockHome, serverId) {
|
|
29674
|
+
return path2.join(serverDir(slockHome, serverId), "managed.flag");
|
|
29675
|
+
}
|
|
29676
|
+
function serverHealthPath(slockHome, serverId) {
|
|
29677
|
+
return path2.join(serverDir(slockHome, serverId), "health.json");
|
|
29678
|
+
}
|
|
29679
|
+
function serviceRunDir(slockHome) {
|
|
29680
|
+
return path2.join(computerDir(slockHome), "run");
|
|
29681
|
+
}
|
|
29682
|
+
function serviceStatePath(slockHome) {
|
|
29683
|
+
return path2.join(serviceRunDir(slockHome), "service.state.json");
|
|
29684
|
+
}
|
|
29685
|
+
function servicePidPath(slockHome) {
|
|
29686
|
+
return path2.join(serviceRunDir(slockHome), "service.pid");
|
|
29687
|
+
}
|
|
29688
|
+
function servicePidReadFallback(slockHome) {
|
|
29689
|
+
return [servicePidPath(slockHome)];
|
|
29690
|
+
}
|
|
29691
|
+
function serviceLogPath(slockHome) {
|
|
29692
|
+
return path2.join(serviceRunDir(slockHome), "service.log");
|
|
29693
|
+
}
|
|
29694
|
+
function serviceSocketPath(slockHome) {
|
|
29695
|
+
return path2.join(computerDir(slockHome), "run", "service.sock");
|
|
29696
|
+
}
|
|
29697
|
+
function serviceWindowsPipeName(slockHome) {
|
|
29698
|
+
const hash = createHash("sha256").update(computerDir(slockHome)).digest("hex").slice(0, 16);
|
|
29699
|
+
return `\\\\.\\pipe\\slock-computer-${hash}`;
|
|
29700
|
+
}
|
|
29701
|
+
function serviceVersionPath(slockHome) {
|
|
29702
|
+
return path2.join(computerDir(slockHome), "service-version.json");
|
|
29703
|
+
}
|
|
29704
|
+
var HOSTNAME_SUFFIXES = [
|
|
29705
|
+
".fritz.box",
|
|
29706
|
+
".localdomain",
|
|
29707
|
+
".local",
|
|
29708
|
+
".lan",
|
|
29709
|
+
".home"
|
|
29710
|
+
];
|
|
29711
|
+
function shortHostnameHash(hostname) {
|
|
29712
|
+
return createHash("sha256").update(hostname).digest("hex").slice(0, 8);
|
|
29713
|
+
}
|
|
29714
|
+
function deriveDefaultComputerName(hostname = os.hostname()) {
|
|
29715
|
+
const original = hostname.trim();
|
|
29716
|
+
let name = original;
|
|
29717
|
+
let lower = name.toLowerCase();
|
|
29718
|
+
for (; ; ) {
|
|
29719
|
+
const suffix = HOSTNAME_SUFFIXES.find((candidate) => lower.endsWith(candidate));
|
|
29720
|
+
if (!suffix) break;
|
|
29721
|
+
name = name.slice(0, -suffix.length);
|
|
29722
|
+
lower = name.toLowerCase();
|
|
29723
|
+
}
|
|
29724
|
+
name = name.replace(/[\s'"]+/g, "-").replace(/-+$/g, "");
|
|
29725
|
+
return name.length > 0 ? name : `slock-computer-${shortHostnameHash(original)}`;
|
|
29726
|
+
}
|
|
29727
|
+
function adoptionLogPath(slockHome) {
|
|
29728
|
+
return path2.join(computerDir(slockHome), "adoption.log");
|
|
29729
|
+
}
|
|
29730
|
+
function channelPath(slockHome) {
|
|
29731
|
+
return path2.join(computerDir(slockHome), "channel");
|
|
29732
|
+
}
|
|
29733
|
+
function upgradeLogPath(slockHome) {
|
|
29734
|
+
return path2.join(computerDir(slockHome), "upgrade.log");
|
|
29735
|
+
}
|
|
29736
|
+
function formatUpgradeLogTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
29737
|
+
const iso = date.toISOString();
|
|
29738
|
+
return iso.replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
29739
|
+
}
|
|
29740
|
+
|
|
29622
29741
|
// src/services/errors.ts
|
|
29623
29742
|
init_esm_shims();
|
|
29624
29743
|
var ComputerServiceError = class extends Error {
|
|
@@ -29931,120 +30050,6 @@ var RunnersClient = class {
|
|
|
29931
30050
|
}
|
|
29932
30051
|
};
|
|
29933
30052
|
|
|
29934
|
-
// src/paths.ts
|
|
29935
|
-
init_esm_shims();
|
|
29936
|
-
import { createHash } from "crypto";
|
|
29937
|
-
import os from "os";
|
|
29938
|
-
import path2 from "path";
|
|
29939
|
-
function resolveSlockHome(env = process.env, homeDir = os.homedir()) {
|
|
29940
|
-
const configured = env.SLOCK_HOME?.trim();
|
|
29941
|
-
const raw = configured && configured.length > 0 ? configured : path2.join(homeDir, ".slock");
|
|
29942
|
-
return path2.resolve(expandHome(raw, homeDir));
|
|
29943
|
-
}
|
|
29944
|
-
function expandHome(input, homeDir) {
|
|
29945
|
-
if (input === "~") return homeDir;
|
|
29946
|
-
if (input.startsWith("~/")) return path2.join(homeDir, input.slice(2));
|
|
29947
|
-
return input;
|
|
29948
|
-
}
|
|
29949
|
-
function computerDir(slockHome) {
|
|
29950
|
-
return path2.join(slockHome, "computer");
|
|
29951
|
-
}
|
|
29952
|
-
function userSessionPath(slockHome) {
|
|
29953
|
-
return path2.join(computerDir(slockHome), "user-session.json");
|
|
29954
|
-
}
|
|
29955
|
-
var SERVER_ID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
29956
|
-
function isValidServerId(serverId) {
|
|
29957
|
-
return SERVER_ID_RE.test(serverId);
|
|
29958
|
-
}
|
|
29959
|
-
function assertValidServerId(serverId) {
|
|
29960
|
-
if (!isValidServerId(serverId)) {
|
|
29961
|
-
throw new Error(`invalid server id: ${JSON.stringify(serverId)} (expected a UUID)`);
|
|
29962
|
-
}
|
|
29963
|
-
return serverId;
|
|
29964
|
-
}
|
|
29965
|
-
function serversDir(slockHome) {
|
|
29966
|
-
return path2.join(computerDir(slockHome), "servers");
|
|
29967
|
-
}
|
|
29968
|
-
function serverDir(slockHome, serverId) {
|
|
29969
|
-
return path2.join(serversDir(slockHome), assertValidServerId(serverId));
|
|
29970
|
-
}
|
|
29971
|
-
function serverAttachmentPath(slockHome, serverId) {
|
|
29972
|
-
return path2.join(serverDir(slockHome, serverId), "runner.state.json");
|
|
29973
|
-
}
|
|
29974
|
-
function serverRunnerPidPath(slockHome, serverId) {
|
|
29975
|
-
return path2.join(serverDir(slockHome, serverId), "server-runner.pid");
|
|
29976
|
-
}
|
|
29977
|
-
function serverRunnerLogPath(slockHome, serverId) {
|
|
29978
|
-
return path2.join(serverDir(slockHome, serverId), "server-runner.log");
|
|
29979
|
-
}
|
|
29980
|
-
function serverManagedFlagPath(slockHome, serverId) {
|
|
29981
|
-
return path2.join(serverDir(slockHome, serverId), "managed.flag");
|
|
29982
|
-
}
|
|
29983
|
-
function serverHealthPath(slockHome, serverId) {
|
|
29984
|
-
return path2.join(serverDir(slockHome, serverId), "health.json");
|
|
29985
|
-
}
|
|
29986
|
-
function serviceRunDir(slockHome) {
|
|
29987
|
-
return path2.join(computerDir(slockHome), "run");
|
|
29988
|
-
}
|
|
29989
|
-
function serviceStatePath(slockHome) {
|
|
29990
|
-
return path2.join(serviceRunDir(slockHome), "service.state.json");
|
|
29991
|
-
}
|
|
29992
|
-
function servicePidPath(slockHome) {
|
|
29993
|
-
return path2.join(serviceRunDir(slockHome), "service.pid");
|
|
29994
|
-
}
|
|
29995
|
-
function servicePidReadFallback(slockHome) {
|
|
29996
|
-
return [servicePidPath(slockHome)];
|
|
29997
|
-
}
|
|
29998
|
-
function serviceLogPath(slockHome) {
|
|
29999
|
-
return path2.join(serviceRunDir(slockHome), "service.log");
|
|
30000
|
-
}
|
|
30001
|
-
function serviceSocketPath(slockHome) {
|
|
30002
|
-
return path2.join(computerDir(slockHome), "run", "service.sock");
|
|
30003
|
-
}
|
|
30004
|
-
function serviceWindowsPipeName(slockHome) {
|
|
30005
|
-
const hash = createHash("sha256").update(computerDir(slockHome)).digest("hex").slice(0, 16);
|
|
30006
|
-
return `\\\\.\\pipe\\slock-computer-${hash}`;
|
|
30007
|
-
}
|
|
30008
|
-
function serviceVersionPath(slockHome) {
|
|
30009
|
-
return path2.join(computerDir(slockHome), "service-version.json");
|
|
30010
|
-
}
|
|
30011
|
-
var HOSTNAME_SUFFIXES = [
|
|
30012
|
-
".fritz.box",
|
|
30013
|
-
".localdomain",
|
|
30014
|
-
".local",
|
|
30015
|
-
".lan",
|
|
30016
|
-
".home"
|
|
30017
|
-
];
|
|
30018
|
-
function shortHostnameHash(hostname) {
|
|
30019
|
-
return createHash("sha256").update(hostname).digest("hex").slice(0, 8);
|
|
30020
|
-
}
|
|
30021
|
-
function deriveDefaultComputerName(hostname = os.hostname()) {
|
|
30022
|
-
const original = hostname.trim();
|
|
30023
|
-
let name = original;
|
|
30024
|
-
let lower = name.toLowerCase();
|
|
30025
|
-
for (; ; ) {
|
|
30026
|
-
const suffix = HOSTNAME_SUFFIXES.find((candidate) => lower.endsWith(candidate));
|
|
30027
|
-
if (!suffix) break;
|
|
30028
|
-
name = name.slice(0, -suffix.length);
|
|
30029
|
-
lower = name.toLowerCase();
|
|
30030
|
-
}
|
|
30031
|
-
name = name.replace(/[\s'"]+/g, "-").replace(/-+$/g, "");
|
|
30032
|
-
return name.length > 0 ? name : `slock-computer-${shortHostnameHash(original)}`;
|
|
30033
|
-
}
|
|
30034
|
-
function adoptionLogPath(slockHome) {
|
|
30035
|
-
return path2.join(computerDir(slockHome), "adoption.log");
|
|
30036
|
-
}
|
|
30037
|
-
function channelPath(slockHome) {
|
|
30038
|
-
return path2.join(computerDir(slockHome), "channel");
|
|
30039
|
-
}
|
|
30040
|
-
function upgradeLogPath(slockHome) {
|
|
30041
|
-
return path2.join(computerDir(slockHome), "upgrade.log");
|
|
30042
|
-
}
|
|
30043
|
-
function formatUpgradeLogTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
30044
|
-
const iso = date.toISOString();
|
|
30045
|
-
return iso.replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
30046
|
-
}
|
|
30047
|
-
|
|
30048
30053
|
// src/serverUrl.ts
|
|
30049
30054
|
init_esm_shims();
|
|
30050
30055
|
var DEFAULT_SLOCK_SERVER_URL = "https://api.raft.build";
|
|
@@ -30145,6 +30150,8 @@ async function login(input, options = {}) {
|
|
|
30145
30150
|
JSON.stringify(
|
|
30146
30151
|
{
|
|
30147
30152
|
kind: "user-session",
|
|
30153
|
+
// On-disk schema version; readers tolerate a missing value.
|
|
30154
|
+
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
30148
30155
|
userId: r.userId,
|
|
30149
30156
|
accessToken: r.accessToken,
|
|
30150
30157
|
refreshToken: r.refreshToken,
|
|
@@ -30193,21 +30200,36 @@ async function runLogin(opts) {
|
|
|
30193
30200
|
throw err;
|
|
30194
30201
|
}
|
|
30195
30202
|
}
|
|
30203
|
+
async function runLogout() {
|
|
30204
|
+
const sessionPath = userSessionPath(resolveSlockHome());
|
|
30205
|
+
try {
|
|
30206
|
+
await unlink(sessionPath);
|
|
30207
|
+
info(`Logged out. Cleared user session at ${sessionPath}.`);
|
|
30208
|
+
} catch (err) {
|
|
30209
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
30210
|
+
info("Already logged out (no saved user session).");
|
|
30211
|
+
return;
|
|
30212
|
+
}
|
|
30213
|
+
throw err;
|
|
30214
|
+
}
|
|
30215
|
+
}
|
|
30196
30216
|
|
|
30197
30217
|
// src/attach.ts
|
|
30198
30218
|
init_esm_shims();
|
|
30199
30219
|
|
|
30200
30220
|
// src/serverState.ts
|
|
30201
30221
|
init_esm_shims();
|
|
30202
|
-
import { readFile, readdir, writeFile as writeFile2, mkdir as mkdir2, unlink, access, chmod } from "fs/promises";
|
|
30222
|
+
import { readFile, readdir, writeFile as writeFile2, mkdir as mkdir2, unlink as unlink2, access, chmod } from "fs/promises";
|
|
30203
30223
|
import { dirname as dirname2 } from "path";
|
|
30204
30224
|
import { constants as fsConstants } from "fs";
|
|
30205
30225
|
function parseAttachment(raw) {
|
|
30206
30226
|
try {
|
|
30207
30227
|
const a = JSON.parse(raw);
|
|
30208
30228
|
if (a.kind === "computer-attachment" && typeof a.serverId === "string" && typeof a.serverMachineId === "string" && typeof a.apiKey === "string" && a.apiKey.length > 0 && typeof a.serverUrl === "string") {
|
|
30229
|
+
const schemaVersion = typeof a.schemaVersion === "number" ? a.schemaVersion : CURRENT_SCHEMA_VERSION;
|
|
30209
30230
|
return {
|
|
30210
30231
|
kind: "computer-attachment",
|
|
30232
|
+
schemaVersion,
|
|
30211
30233
|
serverId: a.serverId,
|
|
30212
30234
|
serverSlug: typeof a.serverSlug === "string" && a.serverSlug.length > 0 ? a.serverSlug : void 0,
|
|
30213
30235
|
serverMachineId: a.serverMachineId,
|
|
@@ -30231,13 +30253,43 @@ async function readAttachmentAt(path3) {
|
|
|
30231
30253
|
}
|
|
30232
30254
|
async function readServerAttachment(slockHome, serverId) {
|
|
30233
30255
|
if (!isValidServerId(serverId)) return null;
|
|
30234
|
-
|
|
30256
|
+
const current = await readAttachmentAt(serverAttachmentPath(slockHome, serverId));
|
|
30257
|
+
const legacy = await readAttachmentAt(legacyServerAttachmentPath(slockHome, serverId));
|
|
30258
|
+
let effective;
|
|
30259
|
+
let decidedByLegacy;
|
|
30260
|
+
if (!current) {
|
|
30261
|
+
effective = legacy;
|
|
30262
|
+
decidedByLegacy = legacy !== null;
|
|
30263
|
+
} else if (!legacy) {
|
|
30264
|
+
effective = current;
|
|
30265
|
+
decidedByLegacy = false;
|
|
30266
|
+
} else if (
|
|
30267
|
+
// Adopted-machine-still-wins: when the legacy attachment is an adopted
|
|
30268
|
+
// (working) machine and the current one is a different, not-adopted machine
|
|
30269
|
+
// (e.g. a fresh attach that the server rejects), prefer the legacy creds.
|
|
30270
|
+
legacy.adoptedFromLegacy === true && current.adoptedFromLegacy !== true && legacy.serverMachineId !== current.serverMachineId
|
|
30271
|
+
) {
|
|
30272
|
+
effective = legacy;
|
|
30273
|
+
decidedByLegacy = true;
|
|
30274
|
+
} else {
|
|
30275
|
+
effective = current;
|
|
30276
|
+
decidedByLegacy = false;
|
|
30277
|
+
}
|
|
30278
|
+
if (decidedByLegacy && effective) {
|
|
30279
|
+
try {
|
|
30280
|
+
await writeServerAttachment(slockHome, effective);
|
|
30281
|
+
await unlink2(legacyServerAttachmentPath(slockHome, serverId));
|
|
30282
|
+
} catch {
|
|
30283
|
+
}
|
|
30284
|
+
}
|
|
30285
|
+
return effective;
|
|
30235
30286
|
}
|
|
30236
30287
|
async function writeServerAttachment(slockHome, attachment) {
|
|
30237
30288
|
if (!isValidServerId(attachment.serverId)) return;
|
|
30238
30289
|
const path3 = serverAttachmentPath(slockHome, attachment.serverId);
|
|
30239
30290
|
await mkdir2(dirname2(path3), { recursive: true });
|
|
30240
|
-
|
|
30291
|
+
const stamped = { ...attachment, schemaVersion: CURRENT_SCHEMA_VERSION };
|
|
30292
|
+
await writeFile2(path3, JSON.stringify(stamped, null, 2), { mode: 384 });
|
|
30241
30293
|
await chmod(path3, 384);
|
|
30242
30294
|
}
|
|
30243
30295
|
async function listAttachedServerIds(slockHome) {
|
|
@@ -30287,7 +30339,7 @@ async function setServerManaged(slockHome, serverId) {
|
|
|
30287
30339
|
async function clearServerManaged(slockHome, serverId) {
|
|
30288
30340
|
if (!isValidServerId(serverId)) return;
|
|
30289
30341
|
try {
|
|
30290
|
-
await
|
|
30342
|
+
await unlink2(serverManagedFlagPath(slockHome, serverId));
|
|
30291
30343
|
} catch {
|
|
30292
30344
|
}
|
|
30293
30345
|
}
|
|
@@ -30482,7 +30534,7 @@ async function runAttach(opts) {
|
|
|
30482
30534
|
if (opts.orchestrated) {
|
|
30483
30535
|
return;
|
|
30484
30536
|
}
|
|
30485
|
-
if (opts.
|
|
30537
|
+
if (opts.start === false) {
|
|
30486
30538
|
info("Next: run `slock-computer start` to run it in the background.");
|
|
30487
30539
|
} else {
|
|
30488
30540
|
info("Next: run `slock-computer start` (background) \u2014 closing the terminal is fine.");
|
|
@@ -31025,13 +31077,13 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
31025
31077
|
|
|
31026
31078
|
// src/cleanup.ts
|
|
31027
31079
|
init_esm_shims();
|
|
31028
|
-
import { readdir as readdir3, stat as stat3, unlink as
|
|
31080
|
+
import { readdir as readdir3, stat as stat3, unlink as unlink4, rm, rmdir, rename, mkdir as mkdir6 } from "fs/promises";
|
|
31029
31081
|
import { spawn } from "child_process";
|
|
31030
31082
|
import { join as join3 } from "path";
|
|
31031
31083
|
|
|
31032
31084
|
// src/internal/process-primitives.ts
|
|
31033
31085
|
init_esm_shims();
|
|
31034
|
-
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5, unlink as
|
|
31086
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5, unlink as unlink3 } from "fs/promises";
|
|
31035
31087
|
import { dirname as dirname5 } from "path";
|
|
31036
31088
|
async function readPidfileAt(pidfilePath) {
|
|
31037
31089
|
try {
|
|
@@ -31057,7 +31109,7 @@ async function writePidfileAt(pidfilePath, pid) {
|
|
|
31057
31109
|
}
|
|
31058
31110
|
async function clearPidfileAt(pidfilePath) {
|
|
31059
31111
|
try {
|
|
31060
|
-
await
|
|
31112
|
+
await unlink3(pidfilePath);
|
|
31061
31113
|
} catch {
|
|
31062
31114
|
}
|
|
31063
31115
|
}
|
|
@@ -31078,7 +31130,7 @@ async function cleanupStalePidfile(pidfilePath) {
|
|
|
31078
31130
|
if (pid === null) return false;
|
|
31079
31131
|
if (isProcessAlive2(pid)) return false;
|
|
31080
31132
|
try {
|
|
31081
|
-
await
|
|
31133
|
+
await unlink4(pidfilePath);
|
|
31082
31134
|
return true;
|
|
31083
31135
|
} catch {
|
|
31084
31136
|
return false;
|
|
@@ -31226,7 +31278,7 @@ async function cleanupTmpFiles(slockHome) {
|
|
|
31226
31278
|
try {
|
|
31227
31279
|
const s = await stat3(snap);
|
|
31228
31280
|
if (Date.now() - s.mtimeMs > TMP_MAX_AGE_MS) {
|
|
31229
|
-
await
|
|
31281
|
+
await unlink4(snap);
|
|
31230
31282
|
removed.push(snap);
|
|
31231
31283
|
}
|
|
31232
31284
|
} catch {
|
|
@@ -31270,7 +31322,7 @@ async function runFullCleanup(slockHome, options = {}) {
|
|
|
31270
31322
|
|
|
31271
31323
|
// src/health.ts
|
|
31272
31324
|
init_esm_shims();
|
|
31273
|
-
import { readFile as readFile6, writeFile as writeFile6, unlink as
|
|
31325
|
+
import { readFile as readFile6, writeFile as writeFile6, unlink as unlink5, mkdir as mkdir7, appendFile as appendFile2 } from "fs/promises";
|
|
31274
31326
|
import { dirname as dirname7 } from "path";
|
|
31275
31327
|
var CRASH_WINDOW_MS = 6e4;
|
|
31276
31328
|
var DEGRADED_THRESHOLD = 3;
|
|
@@ -31280,7 +31332,9 @@ async function readHealthFile(slockHome, serverId) {
|
|
|
31280
31332
|
const raw = await readFile6(serverHealthPath(slockHome, serverId), "utf8");
|
|
31281
31333
|
const parsed = JSON.parse(raw);
|
|
31282
31334
|
if (parsed && typeof parsed === "object" && Array.isArray(parsed.crashes)) {
|
|
31283
|
-
|
|
31335
|
+
const file = parsed;
|
|
31336
|
+
if (typeof file.schemaVersion !== "number") file.schemaVersion = CURRENT_SCHEMA_VERSION;
|
|
31337
|
+
return file;
|
|
31284
31338
|
}
|
|
31285
31339
|
} catch {
|
|
31286
31340
|
}
|
|
@@ -31289,7 +31343,8 @@ async function readHealthFile(slockHome, serverId) {
|
|
|
31289
31343
|
async function writeHealthFile(slockHome, serverId, file) {
|
|
31290
31344
|
const path3 = serverHealthPath(slockHome, serverId);
|
|
31291
31345
|
await mkdir7(dirname7(path3), { recursive: true });
|
|
31292
|
-
|
|
31346
|
+
const stamped = { ...file, schemaVersion: CURRENT_SCHEMA_VERSION };
|
|
31347
|
+
await writeFile6(path3, JSON.stringify(stamped), { mode: 384 });
|
|
31293
31348
|
}
|
|
31294
31349
|
async function recordCrash(slockHome, serverId, exitCode, signal, nowMs = Date.now()) {
|
|
31295
31350
|
if (!isValidServerId(serverId)) return;
|
|
@@ -31338,7 +31393,7 @@ async function markFatalConfig(slockHome, serverId, exitCode, signal, nowMs = Da
|
|
|
31338
31393
|
async function resetHealth(slockHome, serverId) {
|
|
31339
31394
|
if (!isValidServerId(serverId)) return;
|
|
31340
31395
|
try {
|
|
31341
|
-
await
|
|
31396
|
+
await unlink5(serverHealthPath(slockHome, serverId));
|
|
31342
31397
|
} catch {
|
|
31343
31398
|
}
|
|
31344
31399
|
}
|
|
@@ -31507,7 +31562,7 @@ async function waitForManagedDaemonPids(slockHome, serverIds, opts) {
|
|
|
31507
31562
|
function buildTimeoutMessage(slockHome, serverIds, ready, input) {
|
|
31508
31563
|
const missing = serverIds.filter((id) => !ready.has(id));
|
|
31509
31564
|
const target = input.serverId && missing.length === 1 ? `${input.serverLabel ?? input.serverId}` : `${missing.length} server runner(s): ${missing.join(", ")}`;
|
|
31510
|
-
return `Timed out waiting for ${target} to start. Run \`slock-computer status\` and inspect ${serviceLogPath(slockHome)} plus per-server
|
|
31565
|
+
return `Timed out waiting for ${target} to start. Run \`slock-computer status\` and inspect ${serviceLogPath(slockHome)} plus per-server runner logs under ~/.slock/computer/servers/<serverId>/runner.log.`;
|
|
31511
31566
|
}
|
|
31512
31567
|
async function start(input, options = {}) {
|
|
31513
31568
|
options.signal?.throwIfAborted?.();
|
|
@@ -31827,7 +31882,7 @@ async function detach(input, options = {}) {
|
|
|
31827
31882
|
// src/upgradeSea.ts
|
|
31828
31883
|
init_esm_shims();
|
|
31829
31884
|
import { createHash as createHash3 } from "crypto";
|
|
31830
|
-
import { chmod as chmod4, mkdir as mkdir9, readFile as readFile8, rename as rename2, rm as rm2, writeFile as writeFile8 } from "fs/promises";
|
|
31885
|
+
import { access as access2, chmod as chmod4, mkdir as mkdir9, readFile as readFile8, rename as rename2, rm as rm2, writeFile as writeFile8 } from "fs/promises";
|
|
31831
31886
|
import { join as join4 } from "path";
|
|
31832
31887
|
|
|
31833
31888
|
// src/channel.ts
|
|
@@ -31983,6 +32038,24 @@ async function swapSeaPhase(currentBinaryPath, stagedBinaryPath) {
|
|
|
31983
32038
|
}
|
|
31984
32039
|
return { prevBinaryPath, currentBinaryPath };
|
|
31985
32040
|
}
|
|
32041
|
+
async function rollbackSeaSwap(swap) {
|
|
32042
|
+
await rm2(swap.currentBinaryPath, { force: true });
|
|
32043
|
+
await rename2(swap.prevBinaryPath, swap.currentBinaryPath);
|
|
32044
|
+
await chmod4(swap.currentBinaryPath, 493);
|
|
32045
|
+
}
|
|
32046
|
+
function seaPrevBinaryPath(currentBinaryPath) {
|
|
32047
|
+
return `${currentBinaryPath}.prev`;
|
|
32048
|
+
}
|
|
32049
|
+
async function rollbackToPrev(currentBinaryPath) {
|
|
32050
|
+
const prevBinaryPath = seaPrevBinaryPath(currentBinaryPath);
|
|
32051
|
+
try {
|
|
32052
|
+
await access2(prevBinaryPath);
|
|
32053
|
+
} catch {
|
|
32054
|
+
throw new Error("UPGRADE_NO_ROLLBACK");
|
|
32055
|
+
}
|
|
32056
|
+
await rollbackSeaSwap({ prevBinaryPath, currentBinaryPath });
|
|
32057
|
+
return { restoredFrom: prevBinaryPath, currentBinaryPath };
|
|
32058
|
+
}
|
|
31986
32059
|
function pendingUpgradeMarkerPath(slockHome) {
|
|
31987
32060
|
return join4(slockHome, "upgrade-pending.json");
|
|
31988
32061
|
}
|
|
@@ -31990,7 +32063,8 @@ async function writePendingUpgradeMarker(slockHome, marker) {
|
|
|
31990
32063
|
const path3 = pendingUpgradeMarkerPath(slockHome);
|
|
31991
32064
|
const tmp = `${path3}.tmp`;
|
|
31992
32065
|
await mkdir9(slockHome, { recursive: true });
|
|
31993
|
-
|
|
32066
|
+
const stamped = { ...marker, schemaVersion: CURRENT_SCHEMA_VERSION };
|
|
32067
|
+
await writeFile8(tmp, `${JSON.stringify(stamped, null, 2)}
|
|
31994
32068
|
`, "utf8");
|
|
31995
32069
|
await rename2(tmp, path3);
|
|
31996
32070
|
}
|
|
@@ -31999,7 +32073,8 @@ async function readPendingUpgradeMarker(slockHome) {
|
|
|
31999
32073
|
const raw = await readFile8(pendingUpgradeMarkerPath(slockHome), "utf8");
|
|
32000
32074
|
const parsed = JSON.parse(raw);
|
|
32001
32075
|
if (typeof parsed.requestId === "string" && typeof parsed.targetVersion === "string" && typeof parsed.fromVersion === "string" && typeof parsed.startedAt === "string") {
|
|
32002
|
-
|
|
32076
|
+
const schemaVersion = typeof parsed.schemaVersion === "number" ? parsed.schemaVersion : CURRENT_SCHEMA_VERSION;
|
|
32077
|
+
return { ...parsed, schemaVersion };
|
|
32003
32078
|
}
|
|
32004
32079
|
return null;
|
|
32005
32080
|
} catch {
|
|
@@ -32118,7 +32193,7 @@ async function resolveAndRunSeaUpgrade(opts) {
|
|
|
32118
32193
|
// src/internal/ipc-server.ts
|
|
32119
32194
|
init_esm_shims();
|
|
32120
32195
|
import { connect, createServer } from "net";
|
|
32121
|
-
import { chmod as chmod5, mkdir as mkdir10, stat as stat4, unlink as
|
|
32196
|
+
import { chmod as chmod5, mkdir as mkdir10, stat as stat4, unlink as unlink6 } from "fs/promises";
|
|
32122
32197
|
import { dirname as dirname9 } from "path";
|
|
32123
32198
|
|
|
32124
32199
|
// src/internal/ipc-codec.ts
|
|
@@ -32229,7 +32304,7 @@ async function probeAndClearStaleSocket(socketPath) {
|
|
|
32229
32304
|
return;
|
|
32230
32305
|
}
|
|
32231
32306
|
try {
|
|
32232
|
-
await
|
|
32307
|
+
await unlink6(socketPath);
|
|
32233
32308
|
} catch (err) {
|
|
32234
32309
|
if (isErrnoException(err) && err.code === "ENOENT") return;
|
|
32235
32310
|
throw err;
|
|
@@ -32495,12 +32570,8 @@ async function buildStatusReport(installRoot) {
|
|
|
32495
32570
|
function pad(s, n) {
|
|
32496
32571
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
32497
32572
|
}
|
|
32498
|
-
async function runStatus(
|
|
32573
|
+
async function runStatus() {
|
|
32499
32574
|
const report = await buildStatusReport(resolveSlockHome());
|
|
32500
|
-
if (opts.json) {
|
|
32501
|
-
info(JSON.stringify(report, null, 2));
|
|
32502
|
-
return;
|
|
32503
|
-
}
|
|
32504
32575
|
info("");
|
|
32505
32576
|
info(`SLOCK_HOME: ${report.slockHome}`);
|
|
32506
32577
|
const loginDetail = report.userSessionError ? "no \u2014 user session file is invalid; re-run `slock-computer login`" : report.loggedIn ? `yes (user ${report.userId ?? "?"})` : "no \u2014 run `slock-computer login`";
|
|
@@ -32625,6 +32696,7 @@ function isServiceState(value) {
|
|
|
32625
32696
|
|
|
32626
32697
|
// src/serviceState.ts
|
|
32627
32698
|
var DEFAULT_STATE = {
|
|
32699
|
+
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
32628
32700
|
state: "running",
|
|
32629
32701
|
crashHistory: []
|
|
32630
32702
|
};
|
|
@@ -32634,9 +32706,10 @@ async function readServiceState(slockHome) {
|
|
|
32634
32706
|
const parsed = JSON.parse(raw);
|
|
32635
32707
|
if (!parsed || typeof parsed !== "object") return { ...DEFAULT_STATE };
|
|
32636
32708
|
const obj = parsed;
|
|
32709
|
+
const schemaVersion = typeof obj.schemaVersion === "number" ? obj.schemaVersion : CURRENT_SCHEMA_VERSION;
|
|
32637
32710
|
const state = isServiceState(obj.state) ? obj.state : "running";
|
|
32638
32711
|
const crashHistory = Array.isArray(obj.crashHistory) ? obj.crashHistory.filter(isCrashEntry) : [];
|
|
32639
|
-
return { state, crashHistory };
|
|
32712
|
+
return { schemaVersion, state, crashHistory };
|
|
32640
32713
|
} catch {
|
|
32641
32714
|
return { ...DEFAULT_STATE };
|
|
32642
32715
|
}
|
|
@@ -32644,7 +32717,8 @@ async function readServiceState(slockHome) {
|
|
|
32644
32717
|
async function writeServiceState(slockHome, file) {
|
|
32645
32718
|
const path3 = serviceStatePath(slockHome);
|
|
32646
32719
|
await mkdir11(dirname10(path3), { recursive: true });
|
|
32647
|
-
|
|
32720
|
+
const stamped = { ...file, schemaVersion: CURRENT_SCHEMA_VERSION };
|
|
32721
|
+
await writeFile9(path3, JSON.stringify(stamped), { mode: 384 });
|
|
32648
32722
|
}
|
|
32649
32723
|
function isCrashEntry(value) {
|
|
32650
32724
|
if (!value || typeof value !== "object") return false;
|
|
@@ -32728,7 +32802,7 @@ async function resolveTargetServerId(opts) {
|
|
|
32728
32802
|
if (attachments.length === 1) return attachments[0].serverId;
|
|
32729
32803
|
fail(
|
|
32730
32804
|
"AMBIGUOUS_SERVER",
|
|
32731
|
-
`Multiple servers attached (${attachments.map(attachmentLabel).join(", ")}). Pass
|
|
32805
|
+
`Multiple servers attached (${attachments.map(attachmentLabel).join(", ")}). Pass the server slug positionally (e.g. \`${attachmentLabel(attachments[0])}\`) to choose one.`
|
|
32732
32806
|
);
|
|
32733
32807
|
}
|
|
32734
32808
|
async function resolveTargetAttachment(opts) {
|
|
@@ -33018,17 +33092,13 @@ async function runReset(opts) {
|
|
|
33018
33092
|
(client) => client.request("reset-service", void 0),
|
|
33019
33093
|
() => resetService(slockHome)
|
|
33020
33094
|
);
|
|
33021
|
-
if (opts.json) {
|
|
33022
|
-
info(JSON.stringify(result2));
|
|
33023
|
-
return;
|
|
33024
|
-
}
|
|
33025
33095
|
info(
|
|
33026
33096
|
`Reset service crash history (previous state: ${result2.previousState}; cleared ${result2.clearedCrashCount} entr${result2.clearedCrashCount === 1 ? "y" : "ies"}).`
|
|
33027
33097
|
);
|
|
33028
33098
|
info("Service state transitioned to `running`. Runners were not touched.");
|
|
33029
33099
|
return;
|
|
33030
33100
|
}
|
|
33031
|
-
const serverId = await resolveTargetServerId({ server: opts.
|
|
33101
|
+
const serverId = await resolveTargetServerId({ server: opts.serverSlug });
|
|
33032
33102
|
const result = await resetViaServiceOrDisk(
|
|
33033
33103
|
slockHome,
|
|
33034
33104
|
(client) => client.request("reset-runner", { serverId }),
|
|
@@ -33040,10 +33110,6 @@ async function runReset(opts) {
|
|
|
33040
33110
|
`Runner for server ${serverId} was not found.`
|
|
33041
33111
|
);
|
|
33042
33112
|
}
|
|
33043
|
-
if (opts.json) {
|
|
33044
|
-
info(JSON.stringify(result));
|
|
33045
|
-
return;
|
|
33046
|
-
}
|
|
33047
33113
|
info(
|
|
33048
33114
|
`Reset runner crash history for server ${result.serverId} (previous state: ${result.previousState}; cleared ${result.clearedCrashCount} entr${result.clearedCrashCount === 1 ? "y" : "ies"}).`
|
|
33049
33115
|
);
|
|
@@ -33616,7 +33682,7 @@ async function runStart(opts = {}, deps = {}) {
|
|
|
33616
33682
|
info(
|
|
33617
33683
|
`Managing ${sb.managedCount} of ${sb.attachedCount} attached server(s). Logs: ${sb.logPath}`
|
|
33618
33684
|
);
|
|
33619
|
-
info(`Per-server
|
|
33685
|
+
info(`Per-server runner logs: ~/.slock/computer/servers/<serverId>/runner.log`);
|
|
33620
33686
|
info(`Check state with \`slock-computer status\`.`);
|
|
33621
33687
|
}
|
|
33622
33688
|
}
|
|
@@ -33737,6 +33803,8 @@ async function refreshUserSession(slockHome, serverUrl) {
|
|
|
33737
33803
|
JSON.stringify(
|
|
33738
33804
|
{
|
|
33739
33805
|
kind: "user-session",
|
|
33806
|
+
// Stamp the current on-disk schema version on every write.
|
|
33807
|
+
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
33740
33808
|
userId: session.userId,
|
|
33741
33809
|
accessToken: body.accessToken,
|
|
33742
33810
|
refreshToken: body.refreshToken,
|
|
@@ -34036,7 +34104,7 @@ async function runSetup(opts, deps = {}) {
|
|
|
34036
34104
|
serverSlug: opts.serverSlug,
|
|
34037
34105
|
serverUrl: opts.serverUrl,
|
|
34038
34106
|
name: opts.name,
|
|
34039
|
-
|
|
34107
|
+
start: false,
|
|
34040
34108
|
orchestrated: true
|
|
34041
34109
|
});
|
|
34042
34110
|
}
|
|
@@ -34079,16 +34147,6 @@ async function runRunnersList(opts) {
|
|
|
34079
34147
|
`Could not list runners on ${label} (${block.code}). Check --server-url / server version.`
|
|
34080
34148
|
);
|
|
34081
34149
|
}
|
|
34082
|
-
if (opts.json) {
|
|
34083
|
-
info(
|
|
34084
|
-
JSON.stringify(
|
|
34085
|
-
{ server: label, serverId: block.serverId, whitelist: block.whitelist, runners: block.runners },
|
|
34086
|
-
null,
|
|
34087
|
-
2
|
|
34088
|
-
)
|
|
34089
|
-
);
|
|
34090
|
-
return;
|
|
34091
|
-
}
|
|
34092
34150
|
if (block.runners.length === 0) {
|
|
34093
34151
|
info(`No runners on server ${label}.`);
|
|
34094
34152
|
return;
|
|
@@ -34105,7 +34163,7 @@ async function runRunnersList(opts) {
|
|
|
34105
34163
|
}
|
|
34106
34164
|
async function runRunnersStop(agentId, opts = {}) {
|
|
34107
34165
|
if (!agentId || agentId.trim().length === 0) {
|
|
34108
|
-
fail("AGENT_ID_REQUIRED", "Usage: slock-computer runners stop <agentId> [
|
|
34166
|
+
fail("AGENT_ID_REQUIRED", "Usage: slock-computer runners stop <agentId> [serverSlug]");
|
|
34109
34167
|
}
|
|
34110
34168
|
const a = await resolveTargetAttachment({ server: opts.server });
|
|
34111
34169
|
const client = new RunnersClient(a.serverUrl, a.apiKey);
|
|
@@ -34207,14 +34265,6 @@ async function runDoctor(opts) {
|
|
|
34207
34265
|
if (opts.serverId) {
|
|
34208
34266
|
crashes = await readCrashHistory(slockHome, opts.serverId);
|
|
34209
34267
|
}
|
|
34210
|
-
if (opts.json) {
|
|
34211
|
-
const payload = { ok: allOk, checks };
|
|
34212
|
-
if (cleanupReport) payload.cleanup = cleanupReport;
|
|
34213
|
-
if (crashes.length > 0) payload.crashes = crashes;
|
|
34214
|
-
info(redactSecrets(JSON.stringify(payload, null, 2)));
|
|
34215
|
-
process.exitCode = allOk ? 0 : 1;
|
|
34216
|
-
return;
|
|
34217
|
-
}
|
|
34218
34268
|
info("");
|
|
34219
34269
|
for (const c of checks) {
|
|
34220
34270
|
info(redactSecrets(`${c.ok ? "\u2713" : "\u2717"} ${c.name.padEnd(48)} ${c.detail}`));
|
|
@@ -34230,7 +34280,7 @@ async function runDoctor(opts) {
|
|
|
34230
34280
|
info(` - ${c.at}${code}${sig}`);
|
|
34231
34281
|
}
|
|
34232
34282
|
info(` \u2192 Once you fix the underlying issue, run`);
|
|
34233
|
-
info(` \`slock-computer reset --runner
|
|
34283
|
+
info(` \`slock-computer reset --runner ${opts.serverLabel ?? opts.serverId}\` to resume auto-restart.`);
|
|
34234
34284
|
}
|
|
34235
34285
|
if (cleanupReport) {
|
|
34236
34286
|
info("");
|
|
@@ -34421,6 +34471,32 @@ async function runUpgradeCli(slockHome, opts, deps = {}) {
|
|
|
34421
34471
|
"`slock-computer upgrade` self-upgrades the SEA single-binary; this run is not a SEA binary (npm-installed or source/dev). Re-install the SEA binary to upgrade: `curl -fsSL https://github.com/botiverse/slock/releases/latest/download/install.sh | sh`."
|
|
34422
34472
|
);
|
|
34423
34473
|
}
|
|
34474
|
+
if (opts.rollback) {
|
|
34475
|
+
if (opts.targetVersion !== void 0 || opts.channel !== void 0 || opts.dryRun) {
|
|
34476
|
+
fail(
|
|
34477
|
+
"UPGRADE_ROLLBACK_CONFLICT",
|
|
34478
|
+
"`--rollback` cannot be combined with --target-version, --channel, or --dry-run. Rollback restores the previous binary and resolves/downloads nothing."
|
|
34479
|
+
);
|
|
34480
|
+
}
|
|
34481
|
+
const currentBinaryPath2 = (deps.currentBinaryPath ?? (() => process.execPath))();
|
|
34482
|
+
const rollbackFn = deps.rollbackToPrevFn ?? rollbackToPrev;
|
|
34483
|
+
try {
|
|
34484
|
+
const r = await rollbackFn(currentBinaryPath2);
|
|
34485
|
+
info(
|
|
34486
|
+
`Rolled back: restored the previous binary from ${r.restoredFrom}. Restart the Computer service to run the restored binary (the managed supervisor does this automatically).`
|
|
34487
|
+
);
|
|
34488
|
+
} catch (e) {
|
|
34489
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
34490
|
+
if (/UPGRADE_NO_ROLLBACK/.test(msg)) {
|
|
34491
|
+
fail(
|
|
34492
|
+
"UPGRADE_NO_ROLLBACK",
|
|
34493
|
+
"Nothing to roll back: no previous binary (`<exe>.prev`) exists. A rollback target is only kept after a successful upgrade swap."
|
|
34494
|
+
);
|
|
34495
|
+
}
|
|
34496
|
+
fail("UPGRADE_SWAP_FAILED", `Rollback failed: ${msg}.`);
|
|
34497
|
+
}
|
|
34498
|
+
return;
|
|
34499
|
+
}
|
|
34424
34500
|
let channel2;
|
|
34425
34501
|
if (opts.channel !== void 0) {
|
|
34426
34502
|
const parsed = parseChannel(opts.channel);
|
|
@@ -34594,9 +34670,12 @@ program2.name("slock-computer").description("Slock Computer \u2014 local-machine
|
|
|
34594
34670
|
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) => {
|
|
34595
34671
|
await runLogin({ serverUrl: opts.serverUrl });
|
|
34596
34672
|
}));
|
|
34597
|
-
program2.command("
|
|
34673
|
+
program2.command("logout").description("Log out (clear the saved user session for this Computer); per-server attachments are untouched.").action(withCliExit(async () => {
|
|
34674
|
+
await runLogout();
|
|
34675
|
+
}));
|
|
34676
|
+
program2.command("attach").argument("<serverSlug>", SERVER_SLUG_TARGET_DESC).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-start", "only authorize + write local state; do not start the service").option("--foreground", FOREGROUND_DESC).action(withCliExit(async (serverSlug, opts) => {
|
|
34598
34677
|
await withMutationLock(
|
|
34599
|
-
() => runAttach({ serverSlug, serverUrl: opts.serverUrl, name: opts.name,
|
|
34678
|
+
() => runAttach({ serverSlug, serverUrl: opts.serverUrl, name: opts.name, start: opts.start, foreground: opts.foreground })
|
|
34600
34679
|
);
|
|
34601
34680
|
}));
|
|
34602
34681
|
program2.command("setup").argument("<serverSlug>", SERVER_SLUG_TARGET_DESC).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", FOREGROUND_DESC).option("-y, --yes", "allow non-interactive setup after confirming the planned actions").action(
|
|
@@ -34616,10 +34695,10 @@ program2.command("setup").argument("<serverSlug>", SERVER_SLUG_TARGET_DESC).desc
|
|
|
34616
34695
|
program2.command("detach").argument("<serverSlug>", `${SERVER_SLUG_TARGET_DESC} (the server to detach from this Computer)`).description("Remove ONE server's local attachment; never touches user-session or other servers.").action(withCliExit(async (serverSlug) => {
|
|
34617
34696
|
await withMutationLock(async () => runDetach(await resolveTargetServerId({ server: serverSlug }), serverSlug));
|
|
34618
34697
|
}));
|
|
34619
|
-
program2.command("status").description("Show this Computer's aggregate state (login + service + per-server
|
|
34620
|
-
await runStatus(
|
|
34698
|
+
program2.command("status").description("Show this Computer's aggregate state (login + service + per-server runners).").action(withCliExit(async () => {
|
|
34699
|
+
await runStatus();
|
|
34621
34700
|
}));
|
|
34622
|
-
program2.command("start").argument("[serverSlug]", SERVER_SLUG_OPTIONAL_DESC).description("Start/ensure the Computer service (manages all per-server
|
|
34701
|
+
program2.command("start").argument("[serverSlug]", SERVER_SLUG_OPTIONAL_DESC).description("Start/ensure the Computer service (manages all per-server runners).").option("--foreground", FOREGROUND_DESC).action(withCliExit(async (serverSlug, opts) => {
|
|
34623
34702
|
await withMutationLock(
|
|
34624
34703
|
async () => runStart({
|
|
34625
34704
|
foreground: opts.foreground,
|
|
@@ -34628,10 +34707,10 @@ program2.command("start").argument("[serverSlug]", SERVER_SLUG_OPTIONAL_DESC).de
|
|
|
34628
34707
|
})
|
|
34629
34708
|
);
|
|
34630
34709
|
}));
|
|
34631
|
-
program2.command("stop").description("Stop the Computer service (and all managed per-server
|
|
34710
|
+
program2.command("stop").description("Stop the Computer service (and all managed per-server runners).").action(withCliExit(async () => {
|
|
34632
34711
|
await withMutationLock(() => runStop());
|
|
34633
34712
|
}));
|
|
34634
|
-
program2.command("restart").argument("[serverSlug]", SERVER_SLUG_OPTIONAL_DESC).description("Restart the Computer service (stop + start; all managed per-server
|
|
34713
|
+
program2.command("restart").argument("[serverSlug]", SERVER_SLUG_OPTIONAL_DESC).description("Restart the Computer service (stop + start; all managed per-server runners).").option("--foreground", FOREGROUND_DESC).action(withCliExit(async (serverSlug, opts) => {
|
|
34635
34714
|
await withMutationLock(async () => {
|
|
34636
34715
|
await runStop();
|
|
34637
34716
|
await runStart({
|
|
@@ -34641,12 +34720,11 @@ program2.command("restart").argument("[serverSlug]", SERVER_SLUG_OPTIONAL_DESC).
|
|
|
34641
34720
|
});
|
|
34642
34721
|
});
|
|
34643
34722
|
}));
|
|
34644
|
-
program2.command("doctor").argument("[serverSlug]", `${SERVER_SLUG_OPTIONAL_DESC} (scopes recent-crash detail to that server)`).description("Diagnose login + per-server attachments + per-server preflight (no secrets).").option("--
|
|
34723
|
+
program2.command("doctor").argument("[serverSlug]", `${SERVER_SLUG_OPTIONAL_DESC} (scopes recent-crash detail to that server)`).description("Diagnose login + per-server attachments + per-server preflight (no secrets).").option("--fix", "after diagnosis, run the local residue cleanup pass").action(
|
|
34645
34724
|
withCliExit(
|
|
34646
34725
|
async (serverSlug, opts) => {
|
|
34647
34726
|
const serverId = serverSlug ? await resolveTargetServerId({ server: serverSlug }) : void 0;
|
|
34648
34727
|
await runDoctor({
|
|
34649
|
-
json: opts.json,
|
|
34650
34728
|
cleanup: opts.fix,
|
|
34651
34729
|
serverId,
|
|
34652
34730
|
serverLabel: serverSlug
|
|
@@ -34654,27 +34732,26 @@ program2.command("doctor").argument("[serverSlug]", `${SERVER_SLUG_OPTIONAL_DESC
|
|
|
34654
34732
|
}
|
|
34655
34733
|
)
|
|
34656
34734
|
);
|
|
34657
|
-
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
|
|
34658
|
-
withCliExit(async (opts) => {
|
|
34735
|
+
program2.command("reset").argument("[serverSlug]", `${SERVER_SLUG_OPTIONAL_DESC} (with \`--runner\`, selects the target runner)`).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 the [serverSlug] positional)").action(
|
|
34736
|
+
withCliExit(async (serverSlug, opts) => {
|
|
34659
34737
|
await withMutationLock(
|
|
34660
34738
|
() => runReset({
|
|
34661
34739
|
service: opts.service,
|
|
34662
34740
|
runner: opts.runner,
|
|
34663
|
-
|
|
34664
|
-
json: opts.json
|
|
34741
|
+
serverSlug
|
|
34665
34742
|
})
|
|
34666
34743
|
);
|
|
34667
34744
|
})
|
|
34668
34745
|
);
|
|
34669
|
-
program2.command("logs").argument("[serverSlug]", `${SERVER_SLUG_OPTIONAL_DESC} (required when \u22652 attached; ignored with --service)`).description("Tail one server's
|
|
34746
|
+
program2.command("logs").argument("[serverSlug]", `${SERVER_SLUG_OPTIONAL_DESC} (required when \u22652 attached; ignored with --service)`).description("Tail one server's runner log (or the service log); secrets redacted.").option("--lines <n>", "trailing lines to show (default 200)", (v) => Number.parseInt(v, 10)).option("--service", "tail the global service log instead of a per-server runner log").action(withCliExit(async (serverSlug, opts) => {
|
|
34670
34747
|
await runLogs({ lines: opts.lines, server: serverSlug ?? null, service: !!opts.service });
|
|
34671
34748
|
}));
|
|
34672
34749
|
var runners = program2.command("runners").description("Computer runner control plane (per-server scoped; \xA712 whitelist server-side).");
|
|
34673
|
-
runners.command("list").
|
|
34674
|
-
await runRunnersList({
|
|
34750
|
+
runners.command("list").argument("[serverSlug]", `${SERVER_SLUG_OPTIONAL_DESC} (required when \u22652 attached)`).description("List runners on one attached server.").action(withCliExit(async (serverSlug) => {
|
|
34751
|
+
await runRunnersList({ server: serverSlug ?? null });
|
|
34675
34752
|
}));
|
|
34676
|
-
runners.command("stop").argument("<agentId>", "id of the runner to stop").
|
|
34677
|
-
await withMutationLock(() => runRunnersStop(agentId, { server:
|
|
34753
|
+
runners.command("stop").argument("<agentId>", "id of the runner to stop").argument("[serverSlug]", `${SERVER_SLUG_OPTIONAL_DESC} (required when \u22652 attached)`).description("Stop a runner (server-mediated; reuses the orchestrator's agent stop).").action(withCliExit(async (agentId, serverSlug) => {
|
|
34754
|
+
await withMutationLock(() => runRunnersStop(agentId, { server: serverSlug ?? null }));
|
|
34678
34755
|
}));
|
|
34679
34756
|
var channel = program2.command("channel").description("Show or set the Computer release channel (latest | alpha | pinned:<semver>).");
|
|
34680
34757
|
channel.command("show").description("Show the current Computer release channel (default `latest` when unset).").action(
|
|
@@ -34696,12 +34773,15 @@ program2.command("upgrade").description(
|
|
|
34696
34773
|
"supervisor restarts on the swapped binary. SEA-only: a non-SEA (npm/dev)",
|
|
34697
34774
|
"run has no single binary to self-swap and is refused (UPGRADE_SEA_ONLY)."
|
|
34698
34775
|
].join("\n")
|
|
34699
|
-
).option("--dry-run", "download + sha256-verify only; do not swap or restart").option("--channel <name>", "override channel for this invocation (latest | alpha | pinned:<semver>)").option("--target-version <semver>", "override target version explicitly (bypasses channel resolution)").
|
|
34776
|
+
).option("--dry-run", "download + sha256-verify only; do not swap or restart").option("--channel <name>", "override channel for this invocation (latest | alpha | pinned:<semver>)").option("--target-version <semver>", "override target version explicitly (bypasses channel resolution)").option(
|
|
34777
|
+
"--rollback",
|
|
34778
|
+
"restore the previous binary (<exe>.prev) from the last successful upgrade; mutually exclusive with --target-version/--channel/--dry-run"
|
|
34779
|
+
).action(
|
|
34700
34780
|
withCliExit(
|
|
34701
34781
|
async (opts) => {
|
|
34702
34782
|
const slockHome = resolveSlockHome();
|
|
34703
34783
|
const trigger = resolveUpgradeTrigger(process.env.SLOCK_UPGRADE_TRIGGER);
|
|
34704
|
-
if (!opts.dryRun && !opts.channel) {
|
|
34784
|
+
if (!opts.dryRun && !opts.channel && !opts.rollback) {
|
|
34705
34785
|
let client = null;
|
|
34706
34786
|
try {
|
|
34707
34787
|
client = await connectService(slockHome);
|
|
@@ -34732,6 +34812,7 @@ program2.command("upgrade").description(
|
|
|
34732
34812
|
dryRun: opts.dryRun,
|
|
34733
34813
|
channel: opts.channel,
|
|
34734
34814
|
targetVersion: opts.targetVersion,
|
|
34815
|
+
rollback: opts.rollback,
|
|
34735
34816
|
trigger
|
|
34736
34817
|
})
|
|
34737
34818
|
);
|
package/dist/lib/index.js
CHANGED
|
@@ -333,6 +333,7 @@ import { constants as fsConstants } from "fs";
|
|
|
333
333
|
import { createHash } from "crypto";
|
|
334
334
|
import os from "os";
|
|
335
335
|
import path from "path";
|
|
336
|
+
var CURRENT_SCHEMA_VERSION = 1;
|
|
336
337
|
function computerDir(slockHome) {
|
|
337
338
|
return path.join(slockHome, "computer");
|
|
338
339
|
}
|
|
@@ -358,11 +359,14 @@ function serverDir(slockHome, serverId) {
|
|
|
358
359
|
function serverAttachmentPath(slockHome, serverId) {
|
|
359
360
|
return path.join(serverDir(slockHome, serverId), "runner.state.json");
|
|
360
361
|
}
|
|
362
|
+
function legacyServerAttachmentPath(slockHome, serverId) {
|
|
363
|
+
return path.join(serverDir(slockHome, serverId), "attachment.json");
|
|
364
|
+
}
|
|
361
365
|
function serverRunnerPidPath(slockHome, serverId) {
|
|
362
|
-
return path.join(serverDir(slockHome, serverId), "
|
|
366
|
+
return path.join(serverDir(slockHome, serverId), "runner.pid");
|
|
363
367
|
}
|
|
364
368
|
function serverRunnerLogPath(slockHome, serverId) {
|
|
365
|
-
return path.join(serverDir(slockHome, serverId), "
|
|
369
|
+
return path.join(serverDir(slockHome, serverId), "runner.log");
|
|
366
370
|
}
|
|
367
371
|
function serverHealthPath(slockHome, serverId) {
|
|
368
372
|
return path.join(serverDir(slockHome, serverId), "health.json");
|
|
@@ -385,8 +389,10 @@ function parseAttachment(raw) {
|
|
|
385
389
|
try {
|
|
386
390
|
const a = JSON.parse(raw);
|
|
387
391
|
if (a.kind === "computer-attachment" && typeof a.serverId === "string" && typeof a.serverMachineId === "string" && typeof a.apiKey === "string" && a.apiKey.length > 0 && typeof a.serverUrl === "string") {
|
|
392
|
+
const schemaVersion = typeof a.schemaVersion === "number" ? a.schemaVersion : CURRENT_SCHEMA_VERSION;
|
|
388
393
|
return {
|
|
389
394
|
kind: "computer-attachment",
|
|
395
|
+
schemaVersion,
|
|
390
396
|
serverId: a.serverId,
|
|
391
397
|
serverSlug: typeof a.serverSlug === "string" && a.serverSlug.length > 0 ? a.serverSlug : void 0,
|
|
392
398
|
serverMachineId: a.serverMachineId,
|
|
@@ -410,7 +416,44 @@ async function readAttachmentAt(path2) {
|
|
|
410
416
|
}
|
|
411
417
|
async function readServerAttachment(slockHome, serverId) {
|
|
412
418
|
if (!isValidServerId(serverId)) return null;
|
|
413
|
-
|
|
419
|
+
const current = await readAttachmentAt(serverAttachmentPath(slockHome, serverId));
|
|
420
|
+
const legacy = await readAttachmentAt(legacyServerAttachmentPath(slockHome, serverId));
|
|
421
|
+
let effective;
|
|
422
|
+
let decidedByLegacy;
|
|
423
|
+
if (!current) {
|
|
424
|
+
effective = legacy;
|
|
425
|
+
decidedByLegacy = legacy !== null;
|
|
426
|
+
} else if (!legacy) {
|
|
427
|
+
effective = current;
|
|
428
|
+
decidedByLegacy = false;
|
|
429
|
+
} else if (
|
|
430
|
+
// Adopted-machine-still-wins: when the legacy attachment is an adopted
|
|
431
|
+
// (working) machine and the current one is a different, not-adopted machine
|
|
432
|
+
// (e.g. a fresh attach that the server rejects), prefer the legacy creds.
|
|
433
|
+
legacy.adoptedFromLegacy === true && current.adoptedFromLegacy !== true && legacy.serverMachineId !== current.serverMachineId
|
|
434
|
+
) {
|
|
435
|
+
effective = legacy;
|
|
436
|
+
decidedByLegacy = true;
|
|
437
|
+
} else {
|
|
438
|
+
effective = current;
|
|
439
|
+
decidedByLegacy = false;
|
|
440
|
+
}
|
|
441
|
+
if (decidedByLegacy && effective) {
|
|
442
|
+
try {
|
|
443
|
+
await writeServerAttachment(slockHome, effective);
|
|
444
|
+
await unlink(legacyServerAttachmentPath(slockHome, serverId));
|
|
445
|
+
} catch {
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return effective;
|
|
449
|
+
}
|
|
450
|
+
async function writeServerAttachment(slockHome, attachment) {
|
|
451
|
+
if (!isValidServerId(attachment.serverId)) return;
|
|
452
|
+
const path2 = serverAttachmentPath(slockHome, attachment.serverId);
|
|
453
|
+
await mkdir(dirname(path2), { recursive: true });
|
|
454
|
+
const stamped = { ...attachment, schemaVersion: CURRENT_SCHEMA_VERSION };
|
|
455
|
+
await writeFile(path2, JSON.stringify(stamped, null, 2), { mode: 384 });
|
|
456
|
+
await chmod(path2, 384);
|
|
414
457
|
}
|
|
415
458
|
async function listAttachedServerIds(slockHome) {
|
|
416
459
|
let entries;
|
|
@@ -440,6 +483,9 @@ async function listServerAttachments(slockHome) {
|
|
|
440
483
|
import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
441
484
|
import { dirname as dirname2 } from "path";
|
|
442
485
|
|
|
486
|
+
// src/login.ts
|
|
487
|
+
import { unlink as unlink2 } from "fs/promises";
|
|
488
|
+
|
|
443
489
|
// src/services/login.ts
|
|
444
490
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
445
491
|
import { dirname as dirname3 } from "path";
|
|
@@ -475,12 +521,12 @@ import { dirname as dirname10, join as joinPath } from "path";
|
|
|
475
521
|
import { fileURLToPath } from "url";
|
|
476
522
|
|
|
477
523
|
// src/cleanup.ts
|
|
478
|
-
import { readdir as readdir3, stat as stat3, unlink as
|
|
524
|
+
import { readdir as readdir3, stat as stat3, unlink as unlink4, rm, rmdir, rename, mkdir as mkdir6 } from "fs/promises";
|
|
479
525
|
import { spawn } from "child_process";
|
|
480
526
|
import { join as join3 } from "path";
|
|
481
527
|
|
|
482
528
|
// src/internal/process-primitives.ts
|
|
483
|
-
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5, unlink as
|
|
529
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5, unlink as unlink3 } from "fs/promises";
|
|
484
530
|
import { dirname as dirname5 } from "path";
|
|
485
531
|
async function readPidfileAt(pidfilePath) {
|
|
486
532
|
try {
|
|
@@ -505,7 +551,7 @@ function isProcessAlive(pid) {
|
|
|
505
551
|
var TMP_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
506
552
|
|
|
507
553
|
// src/health.ts
|
|
508
|
-
import { readFile as readFile6, writeFile as writeFile6, unlink as
|
|
554
|
+
import { readFile as readFile6, writeFile as writeFile6, unlink as unlink5, mkdir as mkdir7, appendFile as appendFile2 } from "fs/promises";
|
|
509
555
|
import { dirname as dirname6 } from "path";
|
|
510
556
|
var CRASH_WINDOW_MS = 6e4;
|
|
511
557
|
var DEGRADED_THRESHOLD = 3;
|
|
@@ -515,7 +561,9 @@ async function readHealthFile(slockHome, serverId) {
|
|
|
515
561
|
const raw = await readFile6(serverHealthPath(slockHome, serverId), "utf8");
|
|
516
562
|
const parsed = JSON.parse(raw);
|
|
517
563
|
if (parsed && typeof parsed === "object" && Array.isArray(parsed.crashes)) {
|
|
518
|
-
|
|
564
|
+
const file = parsed;
|
|
565
|
+
if (typeof file.schemaVersion !== "number") file.schemaVersion = CURRENT_SCHEMA_VERSION;
|
|
566
|
+
return file;
|
|
519
567
|
}
|
|
520
568
|
} catch {
|
|
521
569
|
}
|
|
@@ -572,7 +620,7 @@ import { fetch as fetch3 } from "undici";
|
|
|
572
620
|
|
|
573
621
|
// src/upgradeSea.ts
|
|
574
622
|
import { createHash as createHash3 } from "crypto";
|
|
575
|
-
import { chmod as chmod4, mkdir as mkdir9, readFile as readFile8, rename as rename2, rm as rm2, writeFile as writeFile8 } from "fs/promises";
|
|
623
|
+
import { access as access2, chmod as chmod4, mkdir as mkdir9, readFile as readFile8, rename as rename2, rm as rm2, writeFile as writeFile8 } from "fs/promises";
|
|
576
624
|
import { join as join4 } from "path";
|
|
577
625
|
|
|
578
626
|
// src/channel.ts
|
|
@@ -581,7 +629,7 @@ import { dirname as dirname7 } from "path";
|
|
|
581
629
|
|
|
582
630
|
// src/internal/ipc-server.ts
|
|
583
631
|
import { connect, createServer } from "net";
|
|
584
|
-
import { chmod as chmod5, mkdir as mkdir10, stat as stat4, unlink as
|
|
632
|
+
import { chmod as chmod5, mkdir as mkdir10, stat as stat4, unlink as unlink6 } from "fs/promises";
|
|
585
633
|
import { dirname as dirname8 } from "path";
|
|
586
634
|
|
|
587
635
|
// src/internal/ipc-codec.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botiverse/raft-computer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.61",
|
|
4
4
|
"description": "Canonical Raft Computer — standalone human/local-machine control-plane CLI (login + attach). Provides raft-computer plus the legacy slock-computer alias; distinct from the agent-facing @botiverse/raft CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"commander": "^12.1.0",
|
|
32
32
|
"proper-lockfile": "^4.1.2",
|
|
33
33
|
"undici": "^7.24.7",
|
|
34
|
-
"@botiverse/raft-daemon": "0.
|
|
34
|
+
"@botiverse/raft-daemon": "0.63.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.5.0",
|