@botiverse/raft-computer 0.0.58 → 0.0.60
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 +818 -427
- package/dist/lib/index.d.ts +9 -5
- package/dist/lib/index.js +37 -40
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5874,7 +5874,7 @@ var require_connect = __commonJS({
|
|
|
5874
5874
|
const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions);
|
|
5875
5875
|
timeout = timeout == null ? 1e4 : timeout;
|
|
5876
5876
|
allowH2 = allowH2 != null ? allowH2 : false;
|
|
5877
|
-
return function
|
|
5877
|
+
return function connect3({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) {
|
|
5878
5878
|
let socket;
|
|
5879
5879
|
if (protocol === "https:") {
|
|
5880
5880
|
if (!tls) {
|
|
@@ -11552,7 +11552,7 @@ var require_client = __commonJS({
|
|
|
11552
11552
|
tls,
|
|
11553
11553
|
strictContentLength,
|
|
11554
11554
|
maxCachedSessions,
|
|
11555
|
-
connect:
|
|
11555
|
+
connect: connect4,
|
|
11556
11556
|
maxRequestsPerClient,
|
|
11557
11557
|
localAddress,
|
|
11558
11558
|
maxResponseSize,
|
|
@@ -11609,7 +11609,7 @@ var require_client = __commonJS({
|
|
|
11609
11609
|
if (bodyTimeout != null && (!Number.isInteger(bodyTimeout) || bodyTimeout < 0)) {
|
|
11610
11610
|
throw new InvalidArgumentError2("bodyTimeout must be a positive integer or zero");
|
|
11611
11611
|
}
|
|
11612
|
-
if (
|
|
11612
|
+
if (connect4 != null && typeof connect4 !== "function" && typeof connect4 !== "object") {
|
|
11613
11613
|
throw new InvalidArgumentError2("connect must be a function or an object");
|
|
11614
11614
|
}
|
|
11615
11615
|
if (maxRequestsPerClient != null && (!Number.isInteger(maxRequestsPerClient) || maxRequestsPerClient < 0)) {
|
|
@@ -11643,8 +11643,8 @@ var require_client = __commonJS({
|
|
|
11643
11643
|
throw new InvalidArgumentError2("pingInterval must be a positive integer, greater or equal to 0");
|
|
11644
11644
|
}
|
|
11645
11645
|
super();
|
|
11646
|
-
if (typeof
|
|
11647
|
-
|
|
11646
|
+
if (typeof connect4 !== "function") {
|
|
11647
|
+
connect4 = buildConnector({
|
|
11648
11648
|
...tls,
|
|
11649
11649
|
maxCachedSessions,
|
|
11650
11650
|
allowH2,
|
|
@@ -11652,14 +11652,14 @@ var require_client = __commonJS({
|
|
|
11652
11652
|
socketPath,
|
|
11653
11653
|
timeout: connectTimeout,
|
|
11654
11654
|
...typeof autoSelectFamily === "boolean" ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : void 0,
|
|
11655
|
-
...
|
|
11655
|
+
...connect4
|
|
11656
11656
|
});
|
|
11657
11657
|
} else if (socketPath != null) {
|
|
11658
|
-
const customConnect =
|
|
11659
|
-
|
|
11658
|
+
const customConnect = connect4;
|
|
11659
|
+
connect4 = (opts, callback) => customConnect({ ...opts, socketPath }, callback);
|
|
11660
11660
|
}
|
|
11661
11661
|
this[kUrl] = util.parseOrigin(url);
|
|
11662
|
-
this[kConnector] =
|
|
11662
|
+
this[kConnector] = connect4;
|
|
11663
11663
|
this[kPipelining] = pipelining != null ? pipelining : 1;
|
|
11664
11664
|
this[kMaxHeadersSize] = maxHeaderSize;
|
|
11665
11665
|
this[kKeepAliveDefaultTimeout] = keepAliveTimeout == null ? 4e3 : keepAliveTimeout;
|
|
@@ -11717,7 +11717,7 @@ var require_client = __commonJS({
|
|
|
11717
11717
|
);
|
|
11718
11718
|
}
|
|
11719
11719
|
[kConnect](cb) {
|
|
11720
|
-
|
|
11720
|
+
connect3(this);
|
|
11721
11721
|
this.once("connect", cb);
|
|
11722
11722
|
}
|
|
11723
11723
|
[kDispatch](opts, handler) {
|
|
@@ -11779,7 +11779,7 @@ var require_client = __commonJS({
|
|
|
11779
11779
|
assert(client[kSize] === 0);
|
|
11780
11780
|
}
|
|
11781
11781
|
}
|
|
11782
|
-
function
|
|
11782
|
+
function connect3(client) {
|
|
11783
11783
|
assert(!client[kConnecting]);
|
|
11784
11784
|
assert(!client[kHTTPContext]);
|
|
11785
11785
|
let { host, hostname, protocol, port } = client[kUrl];
|
|
@@ -11958,7 +11958,7 @@ var require_client = __commonJS({
|
|
|
11958
11958
|
return;
|
|
11959
11959
|
}
|
|
11960
11960
|
if (!client[kHTTPContext]) {
|
|
11961
|
-
|
|
11961
|
+
connect3(client);
|
|
11962
11962
|
return;
|
|
11963
11963
|
}
|
|
11964
11964
|
if (client[kHTTPContext].destroyed) {
|
|
@@ -12257,7 +12257,7 @@ var require_pool = __commonJS({
|
|
|
12257
12257
|
constructor(origin, {
|
|
12258
12258
|
connections,
|
|
12259
12259
|
factory = defaultFactory,
|
|
12260
|
-
connect:
|
|
12260
|
+
connect: connect3,
|
|
12261
12261
|
connectTimeout,
|
|
12262
12262
|
tls,
|
|
12263
12263
|
maxCachedSessions,
|
|
@@ -12274,24 +12274,24 @@ var require_pool = __commonJS({
|
|
|
12274
12274
|
if (typeof factory !== "function") {
|
|
12275
12275
|
throw new InvalidArgumentError2("factory must be a function.");
|
|
12276
12276
|
}
|
|
12277
|
-
if (
|
|
12277
|
+
if (connect3 != null && typeof connect3 !== "function" && typeof connect3 !== "object") {
|
|
12278
12278
|
throw new InvalidArgumentError2("connect must be a function or an object");
|
|
12279
12279
|
}
|
|
12280
|
-
if (typeof
|
|
12281
|
-
|
|
12280
|
+
if (typeof connect3 !== "function") {
|
|
12281
|
+
connect3 = buildConnector({
|
|
12282
12282
|
...tls,
|
|
12283
12283
|
maxCachedSessions,
|
|
12284
12284
|
allowH2,
|
|
12285
12285
|
socketPath,
|
|
12286
12286
|
timeout: connectTimeout,
|
|
12287
12287
|
...typeof autoSelectFamily === "boolean" ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : void 0,
|
|
12288
|
-
...
|
|
12288
|
+
...connect3
|
|
12289
12289
|
});
|
|
12290
12290
|
}
|
|
12291
12291
|
super();
|
|
12292
12292
|
this[kConnections] = connections || null;
|
|
12293
12293
|
this[kUrl] = util.parseOrigin(origin);
|
|
12294
|
-
this[kOptions] = { ...util.deepClone(options), connect:
|
|
12294
|
+
this[kOptions] = { ...util.deepClone(options), connect: connect3, allowH2, clientTtl, socketPath };
|
|
12295
12295
|
this[kOptions].interceptors = options.interceptors ? { ...options.interceptors } : void 0;
|
|
12296
12296
|
this[kFactory] = factory;
|
|
12297
12297
|
this.on("connect", (origin2, targets) => {
|
|
@@ -12510,7 +12510,7 @@ var require_round_robin_pool = __commonJS({
|
|
|
12510
12510
|
constructor(origin, {
|
|
12511
12511
|
connections,
|
|
12512
12512
|
factory = defaultFactory,
|
|
12513
|
-
connect:
|
|
12513
|
+
connect: connect3,
|
|
12514
12514
|
connectTimeout,
|
|
12515
12515
|
tls,
|
|
12516
12516
|
maxCachedSessions,
|
|
@@ -12527,24 +12527,24 @@ var require_round_robin_pool = __commonJS({
|
|
|
12527
12527
|
if (typeof factory !== "function") {
|
|
12528
12528
|
throw new InvalidArgumentError2("factory must be a function.");
|
|
12529
12529
|
}
|
|
12530
|
-
if (
|
|
12530
|
+
if (connect3 != null && typeof connect3 !== "function" && typeof connect3 !== "object") {
|
|
12531
12531
|
throw new InvalidArgumentError2("connect must be a function or an object");
|
|
12532
12532
|
}
|
|
12533
|
-
if (typeof
|
|
12534
|
-
|
|
12533
|
+
if (typeof connect3 !== "function") {
|
|
12534
|
+
connect3 = buildConnector({
|
|
12535
12535
|
...tls,
|
|
12536
12536
|
maxCachedSessions,
|
|
12537
12537
|
allowH2,
|
|
12538
12538
|
socketPath,
|
|
12539
12539
|
timeout: connectTimeout,
|
|
12540
12540
|
...typeof autoSelectFamily === "boolean" ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : void 0,
|
|
12541
|
-
...
|
|
12541
|
+
...connect3
|
|
12542
12542
|
});
|
|
12543
12543
|
}
|
|
12544
12544
|
super();
|
|
12545
12545
|
this[kConnections] = connections || null;
|
|
12546
12546
|
this[kUrl] = util.parseOrigin(origin);
|
|
12547
|
-
this[kOptions] = { ...util.deepClone(options), connect:
|
|
12547
|
+
this[kOptions] = { ...util.deepClone(options), connect: connect3, allowH2, clientTtl, socketPath };
|
|
12548
12548
|
this[kOptions].interceptors = options.interceptors ? { ...options.interceptors } : void 0;
|
|
12549
12549
|
this[kFactory] = factory;
|
|
12550
12550
|
this[kIndex] = -1;
|
|
@@ -12619,21 +12619,21 @@ var require_agent = __commonJS({
|
|
|
12619
12619
|
return opts && opts.connections === 1 ? new Client(origin, opts) : new Pool(origin, opts);
|
|
12620
12620
|
}
|
|
12621
12621
|
var Agent = class extends DispatcherBase {
|
|
12622
|
-
constructor({ factory = defaultFactory, maxOrigins = Infinity, connect:
|
|
12622
|
+
constructor({ factory = defaultFactory, maxOrigins = Infinity, connect: connect3, ...options } = {}) {
|
|
12623
12623
|
if (typeof factory !== "function") {
|
|
12624
12624
|
throw new InvalidArgumentError2("factory must be a function.");
|
|
12625
12625
|
}
|
|
12626
|
-
if (
|
|
12626
|
+
if (connect3 != null && typeof connect3 !== "function" && typeof connect3 !== "object") {
|
|
12627
12627
|
throw new InvalidArgumentError2("connect must be a function or an object");
|
|
12628
12628
|
}
|
|
12629
12629
|
if (typeof maxOrigins !== "number" || Number.isNaN(maxOrigins) || maxOrigins <= 0) {
|
|
12630
12630
|
throw new InvalidArgumentError2("maxOrigins must be a number greater than 0");
|
|
12631
12631
|
}
|
|
12632
12632
|
super();
|
|
12633
|
-
if (
|
|
12634
|
-
|
|
12633
|
+
if (connect3 && typeof connect3 !== "function") {
|
|
12634
|
+
connect3 = { ...connect3 };
|
|
12635
12635
|
}
|
|
12636
|
-
this[kOptions] = { ...util.deepClone(options), maxOrigins, connect:
|
|
12636
|
+
this[kOptions] = { ...util.deepClone(options), maxOrigins, connect: connect3 };
|
|
12637
12637
|
this[kFactory] = factory;
|
|
12638
12638
|
this[kClients] = /* @__PURE__ */ new Map();
|
|
12639
12639
|
this[kOrigins] = /* @__PURE__ */ new Set();
|
|
@@ -13432,16 +13432,16 @@ var require_proxy_agent = __commonJS({
|
|
|
13432
13432
|
}
|
|
13433
13433
|
var Http1ProxyWrapper = class extends DispatcherBase {
|
|
13434
13434
|
#client;
|
|
13435
|
-
constructor(proxyUrl, { headers = {}, connect:
|
|
13435
|
+
constructor(proxyUrl, { headers = {}, connect: connect3, factory }) {
|
|
13436
13436
|
if (!proxyUrl) {
|
|
13437
13437
|
throw new InvalidArgumentError2("Proxy URL is mandatory");
|
|
13438
13438
|
}
|
|
13439
13439
|
super();
|
|
13440
13440
|
this[kProxyHeaders] = headers;
|
|
13441
13441
|
if (factory) {
|
|
13442
|
-
this.#client = factory(proxyUrl, { connect:
|
|
13442
|
+
this.#client = factory(proxyUrl, { connect: connect3 });
|
|
13443
13443
|
} else {
|
|
13444
|
-
this.#client = new Client(proxyUrl, { connect:
|
|
13444
|
+
this.#client = new Client(proxyUrl, { connect: connect3 });
|
|
13445
13445
|
}
|
|
13446
13446
|
}
|
|
13447
13447
|
[kDispatch](opts, handler) {
|
|
@@ -13502,7 +13502,7 @@ var require_proxy_agent = __commonJS({
|
|
|
13502
13502
|
} else if (username && password) {
|
|
13503
13503
|
this[kProxyHeaders]["proxy-authorization"] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password)}`).toString("base64")}`;
|
|
13504
13504
|
}
|
|
13505
|
-
const
|
|
13505
|
+
const connect3 = buildConnector({ ...opts.proxyTls });
|
|
13506
13506
|
this[kConnectEndpoint] = buildConnector({ ...opts.requestTls });
|
|
13507
13507
|
const agentFactory = opts.factory || defaultAgentFactory;
|
|
13508
13508
|
const factory = (origin2, options) => {
|
|
@@ -13510,7 +13510,7 @@ var require_proxy_agent = __commonJS({
|
|
|
13510
13510
|
if (this[kProxy].protocol === "socks5:" || this[kProxy].protocol === "socks:") {
|
|
13511
13511
|
return new Socks5ProxyAgent(this[kProxy].uri, {
|
|
13512
13512
|
headers: this[kProxyHeaders],
|
|
13513
|
-
connect:
|
|
13513
|
+
connect: connect3,
|
|
13514
13514
|
factory: agentFactory,
|
|
13515
13515
|
username: opts.username || username,
|
|
13516
13516
|
password: opts.password || password,
|
|
@@ -13520,7 +13520,7 @@ var require_proxy_agent = __commonJS({
|
|
|
13520
13520
|
if (!this[kTunnelProxy] && protocol2 === "http:" && this[kProxy].protocol === "http:") {
|
|
13521
13521
|
return new Http1ProxyWrapper(this[kProxy].uri, {
|
|
13522
13522
|
headers: this[kProxyHeaders],
|
|
13523
|
-
connect:
|
|
13523
|
+
connect: connect3,
|
|
13524
13524
|
factory: agentFactory
|
|
13525
13525
|
});
|
|
13526
13526
|
}
|
|
@@ -13529,7 +13529,7 @@ var require_proxy_agent = __commonJS({
|
|
|
13529
13529
|
if (protocol === "socks5:" || protocol === "socks:") {
|
|
13530
13530
|
this[kClient] = null;
|
|
13531
13531
|
} else {
|
|
13532
|
-
this[kClient] = clientFactory(url, { connect:
|
|
13532
|
+
this[kClient] = clientFactory(url, { connect: connect3 });
|
|
13533
13533
|
}
|
|
13534
13534
|
this[kAgent] = new Agent({
|
|
13535
13535
|
...opts,
|
|
@@ -14139,7 +14139,7 @@ var require_h2c_client = __commonJS({
|
|
|
14139
14139
|
"h2c-client: Only h2c protocol is supported"
|
|
14140
14140
|
);
|
|
14141
14141
|
}
|
|
14142
|
-
const { connect:
|
|
14142
|
+
const { connect: connect3, maxConcurrentStreams, pipelining, ...opts } = clientOpts ?? {};
|
|
14143
14143
|
let defaultMaxConcurrentStreams = 100;
|
|
14144
14144
|
let defaultPipelining = 100;
|
|
14145
14145
|
if (maxConcurrentStreams != null && Number.isInteger(maxConcurrentStreams) && maxConcurrentStreams > 0) {
|
|
@@ -15328,10 +15328,10 @@ var require_api_connect = __commonJS({
|
|
|
15328
15328
|
}
|
|
15329
15329
|
}
|
|
15330
15330
|
};
|
|
15331
|
-
function
|
|
15331
|
+
function connect3(opts, callback) {
|
|
15332
15332
|
if (callback === void 0) {
|
|
15333
15333
|
return new Promise((resolve2, reject) => {
|
|
15334
|
-
|
|
15334
|
+
connect3.call(this, opts, (err, data) => {
|
|
15335
15335
|
return err ? reject(err) : resolve2(data);
|
|
15336
15336
|
});
|
|
15337
15337
|
});
|
|
@@ -15348,7 +15348,7 @@ var require_api_connect = __commonJS({
|
|
|
15348
15348
|
queueMicrotask(() => callback(err, { opaque }));
|
|
15349
15349
|
}
|
|
15350
15350
|
}
|
|
15351
|
-
module.exports =
|
|
15351
|
+
module.exports = connect3;
|
|
15352
15352
|
}
|
|
15353
15353
|
});
|
|
15354
15354
|
|
|
@@ -29995,18 +29995,8 @@ function serviceStatePath(slockHome) {
|
|
|
29995
29995
|
function servicePidPath(slockHome) {
|
|
29996
29996
|
return path2.join(serviceRunDir(slockHome), "service.pid");
|
|
29997
29997
|
}
|
|
29998
|
-
function legacyServicePidPath(slockHome) {
|
|
29999
|
-
return path2.join(computerDir(slockHome), "service.pid");
|
|
30000
|
-
}
|
|
30001
|
-
function legacySupervisorPidPath(slockHome) {
|
|
30002
|
-
return path2.join(computerDir(slockHome), "supervisor.pid");
|
|
30003
|
-
}
|
|
30004
29998
|
function servicePidReadFallback(slockHome) {
|
|
30005
|
-
return [
|
|
30006
|
-
servicePidPath(slockHome),
|
|
30007
|
-
legacyServicePidPath(slockHome),
|
|
30008
|
-
legacySupervisorPidPath(slockHome)
|
|
30009
|
-
];
|
|
29999
|
+
return [servicePidPath(slockHome)];
|
|
30010
30000
|
}
|
|
30011
30001
|
function serviceLogPath(slockHome) {
|
|
30012
30002
|
return path2.join(serviceRunDir(slockHome), "service.log");
|
|
@@ -30512,8 +30502,8 @@ async function runAttach(opts) {
|
|
|
30512
30502
|
// src/setup.ts
|
|
30513
30503
|
init_esm_shims();
|
|
30514
30504
|
var import_undici3 = __toESM(require_undici(), 1);
|
|
30515
|
-
import { chmod as chmod6, mkdir as
|
|
30516
|
-
import { dirname as
|
|
30505
|
+
import { chmod as chmod6, mkdir as mkdir13, readFile as readFile12, rename as rename4, rm as rm3, writeFile as writeFile11 } from "fs/promises";
|
|
30506
|
+
import { dirname as dirname12 } from "path";
|
|
30517
30507
|
|
|
30518
30508
|
// src/lib/migration.ts
|
|
30519
30509
|
init_esm_shims();
|
|
@@ -31039,8 +31029,8 @@ function readComputerVersion(moduleUrl = import.meta.url) {
|
|
|
31039
31029
|
var COMPUTER_VERSION = readComputerVersion();
|
|
31040
31030
|
|
|
31041
31031
|
// src/service.ts
|
|
31042
|
-
import { mkdir as
|
|
31043
|
-
import { dirname as
|
|
31032
|
+
import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile10, open, rename as rename3 } from "fs/promises";
|
|
31033
|
+
import { dirname as dirname11, join as joinPath } from "path";
|
|
31044
31034
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
31045
31035
|
|
|
31046
31036
|
// src/cleanup.ts
|
|
@@ -31400,6 +31390,53 @@ async function emitRunnerStateTransition(slockHome, serverId, fromState, toState
|
|
|
31400
31390
|
}
|
|
31401
31391
|
}
|
|
31402
31392
|
|
|
31393
|
+
// src/lib/runnerStateMachine.ts
|
|
31394
|
+
init_esm_shims();
|
|
31395
|
+
var RUNNER_TRIGGER = {
|
|
31396
|
+
spawn: "spawn",
|
|
31397
|
+
spawnFailed: "spawn-failed",
|
|
31398
|
+
ready: "ready",
|
|
31399
|
+
exitGraceful: "exit-graceful",
|
|
31400
|
+
exitCrash: "exit-crash",
|
|
31401
|
+
exitCrashDegraded: "exit-crash-degraded",
|
|
31402
|
+
exitConfigError: "exit-config-error",
|
|
31403
|
+
detachStop: "detach-stop",
|
|
31404
|
+
shutdownStop: "shutdown-stop",
|
|
31405
|
+
reset: "reset"
|
|
31406
|
+
};
|
|
31407
|
+
function nextRunnerStateOnExit(exitClass, budgetBreached) {
|
|
31408
|
+
if (exitClass === "config-error") return "degraded";
|
|
31409
|
+
if (exitClass === "crash") return budgetBreached ? "degraded" : "crashed";
|
|
31410
|
+
return "stopped";
|
|
31411
|
+
}
|
|
31412
|
+
function exitTrigger(exitClass, budgetBreached) {
|
|
31413
|
+
if (exitClass === "config-error") return RUNNER_TRIGGER.exitConfigError;
|
|
31414
|
+
if (exitClass === "crash") {
|
|
31415
|
+
return budgetBreached ? RUNNER_TRIGGER.exitCrashDegraded : RUNNER_TRIGGER.exitCrash;
|
|
31416
|
+
}
|
|
31417
|
+
return RUNNER_TRIGGER.exitGraceful;
|
|
31418
|
+
}
|
|
31419
|
+
function canSpawn(rec, wanted, now) {
|
|
31420
|
+
if (!wanted) return false;
|
|
31421
|
+
if (!rec) return true;
|
|
31422
|
+
if (rec.child) return false;
|
|
31423
|
+
if (rec.lifecycle !== "crashed" && rec.lifecycle !== "stopped") return false;
|
|
31424
|
+
return now >= (rec.backoffUntil ?? 0);
|
|
31425
|
+
}
|
|
31426
|
+
function rehydrateRunnerRecord(serverId, degraded) {
|
|
31427
|
+
return {
|
|
31428
|
+
serverId,
|
|
31429
|
+
lifecycle: degraded ? "degraded" : "stopped",
|
|
31430
|
+
stopping: false
|
|
31431
|
+
};
|
|
31432
|
+
}
|
|
31433
|
+
function applyRunnerReset(rec) {
|
|
31434
|
+
if (rec.lifecycle !== "degraded") return false;
|
|
31435
|
+
rec.lifecycle = "stopped";
|
|
31436
|
+
rec.backoffUntil = void 0;
|
|
31437
|
+
return true;
|
|
31438
|
+
}
|
|
31439
|
+
|
|
31403
31440
|
// src/services/start.ts
|
|
31404
31441
|
init_esm_shims();
|
|
31405
31442
|
|
|
@@ -32575,121 +32612,570 @@ async function listRunners(installRoot, opts = {}) {
|
|
|
32575
32612
|
return { servers };
|
|
32576
32613
|
}
|
|
32577
32614
|
|
|
32578
|
-
// src/
|
|
32579
|
-
|
|
32580
|
-
|
|
32581
|
-
|
|
32582
|
-
|
|
32615
|
+
// src/reset.ts
|
|
32616
|
+
init_esm_shims();
|
|
32617
|
+
|
|
32618
|
+
// src/serviceState.ts
|
|
32619
|
+
init_esm_shims();
|
|
32620
|
+
import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile9, appendFile as appendFile3 } from "fs/promises";
|
|
32621
|
+
import { dirname as dirname10 } from "path";
|
|
32622
|
+
|
|
32623
|
+
// src/lib/state.ts
|
|
32624
|
+
init_esm_shims();
|
|
32625
|
+
var SERVICE_STATE_VALUES = [
|
|
32626
|
+
"starting",
|
|
32627
|
+
"running",
|
|
32628
|
+
"degraded",
|
|
32629
|
+
"stopping",
|
|
32630
|
+
"stopped"
|
|
32631
|
+
];
|
|
32632
|
+
function isServiceState(value) {
|
|
32633
|
+
return typeof value === "string" && SERVICE_STATE_VALUES.includes(value);
|
|
32634
|
+
}
|
|
32635
|
+
|
|
32636
|
+
// src/serviceState.ts
|
|
32637
|
+
var DEFAULT_STATE = {
|
|
32638
|
+
state: "running",
|
|
32639
|
+
crashHistory: []
|
|
32640
|
+
};
|
|
32641
|
+
async function readServiceState(slockHome) {
|
|
32583
32642
|
try {
|
|
32584
|
-
|
|
32643
|
+
const raw = await readFile10(serviceStatePath(slockHome), "utf8");
|
|
32644
|
+
const parsed = JSON.parse(raw);
|
|
32645
|
+
if (!parsed || typeof parsed !== "object") return { ...DEFAULT_STATE };
|
|
32646
|
+
const obj = parsed;
|
|
32647
|
+
const state = isServiceState(obj.state) ? obj.state : "running";
|
|
32648
|
+
const crashHistory = Array.isArray(obj.crashHistory) ? obj.crashHistory.filter(isCrashEntry) : [];
|
|
32649
|
+
return { state, crashHistory };
|
|
32585
32650
|
} catch {
|
|
32586
|
-
|
|
32651
|
+
return { ...DEFAULT_STATE };
|
|
32587
32652
|
}
|
|
32588
|
-
return cachedIsSea;
|
|
32589
32653
|
}
|
|
32590
|
-
function
|
|
32591
|
-
const
|
|
32592
|
-
|
|
32593
|
-
|
|
32594
|
-
}
|
|
32595
|
-
return { command: process.execPath, args: [...execArgv, selfEntry, ...tail] };
|
|
32654
|
+
async function writeServiceState(slockHome, file) {
|
|
32655
|
+
const path3 = serviceStatePath(slockHome);
|
|
32656
|
+
await mkdir11(dirname10(path3), { recursive: true });
|
|
32657
|
+
await writeFile9(path3, JSON.stringify(file), { mode: 384 });
|
|
32596
32658
|
}
|
|
32597
|
-
|
|
32598
|
-
|
|
32599
|
-
|
|
32600
|
-
|
|
32601
|
-
const { command, args } = buildResidentSpawn("__service", null);
|
|
32602
|
-
const child = spawn2(command, args, {
|
|
32603
|
-
detached: true,
|
|
32604
|
-
stdio: ["ignore", supLogFd.fd, supLogFd.fd],
|
|
32605
|
-
windowsHide: true,
|
|
32606
|
-
env: { ...process.env, [PARENT_LOCK_HELD_ENV_VAR]: "1" }
|
|
32607
|
-
});
|
|
32608
|
-
child.on("error", (err) => {
|
|
32609
|
-
process.stderr.write(
|
|
32610
|
-
`${JSON.stringify({ ok: false, code: "SUPERVISOR_SPAWN_FAILED", message: err.message })}
|
|
32611
|
-
`
|
|
32612
|
-
);
|
|
32613
|
-
});
|
|
32614
|
-
const pid = child.pid;
|
|
32615
|
-
child.unref();
|
|
32616
|
-
await supLogFd.close();
|
|
32617
|
-
if (!pid) {
|
|
32618
|
-
throw new Error("SUPERVISOR_SPAWN_FAILED: could not spawn the service process");
|
|
32619
|
-
}
|
|
32620
|
-
await writePidfileAt(servicePidPath(slockHome), pid);
|
|
32621
|
-
return pid;
|
|
32659
|
+
function isCrashEntry(value) {
|
|
32660
|
+
if (!value || typeof value !== "object") return false;
|
|
32661
|
+
const obj = value;
|
|
32662
|
+
return typeof obj.at === "string";
|
|
32622
32663
|
}
|
|
32623
|
-
|
|
32624
|
-
|
|
32625
|
-
|
|
32664
|
+
async function emitServiceStateTransition(slockHome, fromState, toState, trigger) {
|
|
32665
|
+
const entry = {
|
|
32666
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32667
|
+
kind: "service-state-changed",
|
|
32668
|
+
fromState,
|
|
32669
|
+
toState,
|
|
32670
|
+
trigger
|
|
32671
|
+
};
|
|
32626
32672
|
try {
|
|
32627
|
-
const
|
|
32628
|
-
await mkdir11(
|
|
32629
|
-
await
|
|
32630
|
-
joinPath(dir, "package.json"),
|
|
32631
|
-
`${JSON.stringify({ name: "slock-computer-sea-runtime", version: COMPUTER_VERSION })}
|
|
32632
|
-
`
|
|
32633
|
-
);
|
|
32634
|
-
process.env.PI_PACKAGE_DIR = dir;
|
|
32673
|
+
const path3 = serviceLogPath(slockHome);
|
|
32674
|
+
await mkdir11(dirname10(path3), { recursive: true });
|
|
32675
|
+
await appendFile3(path3, JSON.stringify(entry) + "\n");
|
|
32635
32676
|
} catch {
|
|
32636
32677
|
}
|
|
32637
32678
|
}
|
|
32638
|
-
|
|
32639
|
-
await
|
|
32640
|
-
|
|
32679
|
+
async function clearServiceCrashHistory(slockHome) {
|
|
32680
|
+
const current = await readServiceState(slockHome);
|
|
32681
|
+
const previousState = current.state;
|
|
32682
|
+
const clearedCrashCount = current.crashHistory.length;
|
|
32683
|
+
const next = { state: "running", crashHistory: [] };
|
|
32684
|
+
await writeServiceState(slockHome, next);
|
|
32685
|
+
await emitServiceStateTransition(
|
|
32686
|
+
slockHome,
|
|
32687
|
+
previousState,
|
|
32688
|
+
"running",
|
|
32689
|
+
"reset-service"
|
|
32690
|
+
);
|
|
32691
|
+
return { previousState, clearedCrashCount };
|
|
32692
|
+
}
|
|
32693
|
+
|
|
32694
|
+
// src/targetServer.ts
|
|
32695
|
+
init_esm_shims();
|
|
32696
|
+
function attachmentLabel(a) {
|
|
32697
|
+
return formatServerSlugDisplay(a.serverSlug);
|
|
32698
|
+
}
|
|
32699
|
+
async function refreshAttachmentSlug(home, attachment) {
|
|
32641
32700
|
try {
|
|
32642
|
-
|
|
32643
|
-
|
|
32644
|
-
|
|
32645
|
-
|
|
32646
|
-
|
|
32647
|
-
|
|
32648
|
-
|
|
32649
|
-
|
|
32650
|
-
|
|
32651
|
-
|
|
32652
|
-
|
|
32653
|
-
|
|
32701
|
+
const client = new ComputerAttachClient(attachment.serverUrl, "");
|
|
32702
|
+
const result = await client.preflight(attachment.apiKey);
|
|
32703
|
+
if (!result.ok || !result.serverSlug || result.serverSlug === attachment.serverSlug) {
|
|
32704
|
+
return attachment;
|
|
32705
|
+
}
|
|
32706
|
+
const updated = { ...attachment, serverSlug: result.serverSlug };
|
|
32707
|
+
await writeServerAttachment(home, updated);
|
|
32708
|
+
return updated;
|
|
32709
|
+
} catch {
|
|
32710
|
+
return attachment;
|
|
32711
|
+
}
|
|
32712
|
+
}
|
|
32713
|
+
async function listAttachmentsWithFreshSlugs(home) {
|
|
32714
|
+
const attachments = await listServerAttachments(home);
|
|
32715
|
+
return await Promise.all(attachments.map((a) => refreshAttachmentSlug(home, a)));
|
|
32716
|
+
}
|
|
32717
|
+
async function resolveTargetServerId(opts) {
|
|
32718
|
+
const home = resolveSlockHome();
|
|
32719
|
+
let attachments = await listServerAttachments(home);
|
|
32720
|
+
if (attachments.length === 0) {
|
|
32721
|
+
fail("NO_ATTACHMENT", "No server attachments yet. Run `slock-computer attach /<serverSlug>` (e.g. `/myserver`) first.");
|
|
32722
|
+
}
|
|
32723
|
+
const requested = normalizeServerSlug(opts.server ?? "");
|
|
32724
|
+
if (requested) {
|
|
32725
|
+
let found = attachments.find((a) => a.serverSlug === requested);
|
|
32726
|
+
if (!found) {
|
|
32727
|
+
attachments = await listAttachmentsWithFreshSlugs(home);
|
|
32728
|
+
found = attachments.find((a) => a.serverSlug === requested);
|
|
32729
|
+
}
|
|
32730
|
+
if (!found) {
|
|
32731
|
+
fail(
|
|
32732
|
+
"NOT_ATTACHED",
|
|
32733
|
+
`Server slug ${formatServerSlugDisplay(requested)} is not attached. Attached server slugs: ${attachments.map(attachmentLabel).join(", ")}.`
|
|
32654
32734
|
);
|
|
32655
|
-
process.exit(EX_CONFIG_EXIT_CODE);
|
|
32656
32735
|
}
|
|
32657
|
-
|
|
32736
|
+
return found.serverId;
|
|
32658
32737
|
}
|
|
32659
|
-
|
|
32660
|
-
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
|
|
32665
|
-
|
|
32666
|
-
|
|
32667
|
-
|
|
32668
|
-
|
|
32669
|
-
|
|
32670
|
-
|
|
32671
|
-
|
|
32672
|
-
|
|
32673
|
-
|
|
32674
|
-
|
|
32675
|
-
|
|
32676
|
-
|
|
32677
|
-
|
|
32678
|
-
|
|
32679
|
-
|
|
32680
|
-
|
|
32681
|
-
|
|
32682
|
-
|
|
32683
|
-
|
|
32684
|
-
|
|
32685
|
-
|
|
32738
|
+
if (attachments.length === 1) return attachments[0].serverId;
|
|
32739
|
+
fail(
|
|
32740
|
+
"AMBIGUOUS_SERVER",
|
|
32741
|
+
`Multiple servers attached (${attachments.map(attachmentLabel).join(", ")}). Pass \`--server /<serverSlug>\` (e.g. \`--server ${attachmentLabel(attachments[0])}\`) to choose one.`
|
|
32742
|
+
);
|
|
32743
|
+
}
|
|
32744
|
+
async function resolveTargetAttachment(opts) {
|
|
32745
|
+
const serverId = await resolveTargetServerId(opts);
|
|
32746
|
+
const a = await readServerAttachment(resolveSlockHome(), serverId);
|
|
32747
|
+
if (!a) {
|
|
32748
|
+
const label = opts.server ?? serverId;
|
|
32749
|
+
fail("INVALID_ATTACHMENT", `Attachment for ${label} is missing/invalid. Re-run \`slock-computer attach ${label}\`.`);
|
|
32750
|
+
}
|
|
32751
|
+
return a;
|
|
32752
|
+
}
|
|
32753
|
+
|
|
32754
|
+
// src/lib/ipc-client.ts
|
|
32755
|
+
init_esm_shims();
|
|
32756
|
+
import { connect as connect2 } from "net";
|
|
32757
|
+
import { randomUUID } from "crypto";
|
|
32758
|
+
var DEFAULT_PROTOCOL_VERSION = 1;
|
|
32759
|
+
var CLIENT_KIND = "lib";
|
|
32760
|
+
var CLIENT_VERSION = "0.0.0";
|
|
32761
|
+
function resolveTransportPath2(installRoot) {
|
|
32762
|
+
return process.platform === "win32" ? serviceWindowsPipeName(installRoot) : serviceSocketPath(installRoot);
|
|
32763
|
+
}
|
|
32764
|
+
async function connectService(installRoot, options = {}) {
|
|
32765
|
+
const protocolVersion = options.protocolVersion ?? DEFAULT_PROTOCOL_VERSION;
|
|
32766
|
+
const transportPath = resolveTransportPath2(installRoot);
|
|
32767
|
+
const socket = await new Promise((resolve2, reject) => {
|
|
32768
|
+
const s = connect2({ path: transportPath });
|
|
32769
|
+
s.once("connect", () => resolve2(s));
|
|
32770
|
+
s.once("error", (err) => reject(
|
|
32771
|
+
new ServiceClientError("IPC_PROTOCOL_HANDSHAKE_FAILED", `unable to connect to ${transportPath}: ${err.message}`, err)
|
|
32772
|
+
));
|
|
32773
|
+
});
|
|
32774
|
+
const decoder = new FrameDecoder();
|
|
32775
|
+
const pendingRequests = /* @__PURE__ */ new Map();
|
|
32776
|
+
const eventQueue = [];
|
|
32777
|
+
const eventWaiters = [];
|
|
32778
|
+
let closed = false;
|
|
32779
|
+
let protocolError = null;
|
|
32780
|
+
function terminate(error) {
|
|
32781
|
+
if (closed) return;
|
|
32782
|
+
closed = true;
|
|
32783
|
+
if (error) protocolError = error;
|
|
32784
|
+
socket.destroy();
|
|
32785
|
+
for (const pending of pendingRequests.values()) {
|
|
32786
|
+
pending.reject(error ?? new ServiceClientError("IPC_CLIENT_CLOSED", "service client closed before response"));
|
|
32787
|
+
}
|
|
32788
|
+
pendingRequests.clear();
|
|
32789
|
+
for (const waiter of eventWaiters) waiter.resolve(null);
|
|
32790
|
+
eventWaiters.length = 0;
|
|
32791
|
+
}
|
|
32792
|
+
function dispatchFrame(frame) {
|
|
32793
|
+
if (!isRecord2(frame) || typeof frame.type !== "string") {
|
|
32794
|
+
throw new ServiceClientError("IPC_MALFORMED_FRAME", "frame is missing required `type` field");
|
|
32795
|
+
}
|
|
32796
|
+
if (frame.type === "event") {
|
|
32797
|
+
const event = { kind: String(frame.kind), payload: frame.payload };
|
|
32798
|
+
if (eventWaiters.length > 0) {
|
|
32799
|
+
const waiter = eventWaiters.shift();
|
|
32800
|
+
waiter.resolve(event);
|
|
32801
|
+
} else {
|
|
32802
|
+
eventQueue.push(event);
|
|
32686
32803
|
}
|
|
32687
|
-
|
|
32688
|
-
|
|
32804
|
+
return;
|
|
32805
|
+
}
|
|
32806
|
+
if (frame.type === "response") {
|
|
32807
|
+
const id = String(frame.id ?? "");
|
|
32808
|
+
const pending = pendingRequests.get(id);
|
|
32809
|
+
if (!pending) return;
|
|
32810
|
+
pendingRequests.delete(id);
|
|
32811
|
+
if ("error" in frame && isRecord2(frame.error)) {
|
|
32812
|
+
const code = String(frame.error.code ?? "IPC_MALFORMED_FRAME");
|
|
32813
|
+
const message = String(frame.error.message ?? "service request failed");
|
|
32814
|
+
pending.reject(new ServiceClientError(code, message));
|
|
32815
|
+
} else {
|
|
32816
|
+
pending.resolve(frame.result);
|
|
32689
32817
|
}
|
|
32690
|
-
|
|
32691
|
-
|
|
32692
|
-
|
|
32818
|
+
return;
|
|
32819
|
+
}
|
|
32820
|
+
if (frame.type === "ping") {
|
|
32821
|
+
socket.write(encodeFrame({ type: "pong" }));
|
|
32822
|
+
return;
|
|
32823
|
+
}
|
|
32824
|
+
if (frame.type === "pong") {
|
|
32825
|
+
return;
|
|
32826
|
+
}
|
|
32827
|
+
}
|
|
32828
|
+
socket.on("data", (chunk) => {
|
|
32829
|
+
try {
|
|
32830
|
+
decoder.push(chunk);
|
|
32831
|
+
const frames = decoder.drain();
|
|
32832
|
+
for (const frame of frames) {
|
|
32833
|
+
dispatchFrame(frame);
|
|
32834
|
+
}
|
|
32835
|
+
} catch (error) {
|
|
32836
|
+
terminate(error instanceof ServiceClientError ? error : new ServiceClientError("IPC_MALFORMED_FRAME", "frame decode failed", error));
|
|
32837
|
+
}
|
|
32838
|
+
});
|
|
32839
|
+
socket.on("close", () => terminate(null));
|
|
32840
|
+
socket.on("error", (err) => terminate(
|
|
32841
|
+
new ServiceClientError("IPC_MALFORMED_FRAME", `socket error: ${err.message}`, err)
|
|
32842
|
+
));
|
|
32843
|
+
await new Promise((resolve2, reject) => {
|
|
32844
|
+
let settled = false;
|
|
32845
|
+
const settle = (action) => {
|
|
32846
|
+
if (settled) return;
|
|
32847
|
+
settled = true;
|
|
32848
|
+
socket.removeListener("close", onHandshakeClose);
|
|
32849
|
+
socket.removeListener("error", onHandshakeError);
|
|
32850
|
+
action();
|
|
32851
|
+
};
|
|
32852
|
+
const onHandshakeClose = () => settle(() => {
|
|
32853
|
+
reject(new ServiceClientError(
|
|
32854
|
+
"IPC_PROTOCOL_HANDSHAKE_FAILED",
|
|
32855
|
+
"service closed connection before sending hello-ack"
|
|
32856
|
+
));
|
|
32857
|
+
});
|
|
32858
|
+
const onHandshakeError = (err) => settle(() => {
|
|
32859
|
+
reject(new ServiceClientError(
|
|
32860
|
+
"IPC_PROTOCOL_HANDSHAKE_FAILED",
|
|
32861
|
+
`handshake socket error: ${err.message}`,
|
|
32862
|
+
err
|
|
32863
|
+
));
|
|
32864
|
+
});
|
|
32865
|
+
socket.once("close", onHandshakeClose);
|
|
32866
|
+
socket.once("error", onHandshakeError);
|
|
32867
|
+
const onFirstFrame = (chunk) => {
|
|
32868
|
+
socket.removeListener("data", onFirstFrame);
|
|
32869
|
+
try {
|
|
32870
|
+
decoder.push(chunk);
|
|
32871
|
+
const frames = decoder.drain();
|
|
32872
|
+
if (frames.length === 0) {
|
|
32873
|
+
socket.once("data", onFirstFrame);
|
|
32874
|
+
return;
|
|
32875
|
+
}
|
|
32876
|
+
const [firstFrame, ...remaining] = frames;
|
|
32877
|
+
if (!isRecord2(firstFrame) || typeof firstFrame.type !== "string") {
|
|
32878
|
+
settle(() => reject(new ServiceClientError("IPC_PROTOCOL_HANDSHAKE_FAILED", "handshake response missing `type`")));
|
|
32879
|
+
return;
|
|
32880
|
+
}
|
|
32881
|
+
if (firstFrame.type === "hello-reject") {
|
|
32882
|
+
const reason = String(firstFrame.reason ?? "IPC_PROTOCOL_VERSION_UNSUPPORTED");
|
|
32883
|
+
settle(() => reject(new ServiceClientError(
|
|
32884
|
+
reason === "IPC_PROTOCOL_VERSION_UNSUPPORTED" ? "IPC_PROTOCOL_VERSION_UNSUPPORTED" : "IPC_PROTOCOL_HANDSHAKE_FAILED",
|
|
32885
|
+
`service rejected handshake: ${String(firstFrame.reason ?? "unknown")}`
|
|
32886
|
+
)));
|
|
32887
|
+
return;
|
|
32888
|
+
}
|
|
32889
|
+
if (firstFrame.type !== "hello-ack") {
|
|
32890
|
+
settle(() => reject(new ServiceClientError("IPC_PROTOCOL_HANDSHAKE_FAILED", `expected hello-ack, got \`${firstFrame.type}\``)));
|
|
32891
|
+
return;
|
|
32892
|
+
}
|
|
32893
|
+
for (const frame of remaining) {
|
|
32894
|
+
dispatchFrame(frame);
|
|
32895
|
+
}
|
|
32896
|
+
settle(resolve2);
|
|
32897
|
+
} catch (error) {
|
|
32898
|
+
settle(() => reject(error instanceof ServiceClientError ? error : new ServiceClientError("IPC_PROTOCOL_HANDSHAKE_FAILED", "handshake decode failed", error)));
|
|
32899
|
+
}
|
|
32900
|
+
};
|
|
32901
|
+
socket.once("data", onFirstFrame);
|
|
32902
|
+
socket.write(encodeFrame({
|
|
32903
|
+
type: "hello",
|
|
32904
|
+
protocolVersion,
|
|
32905
|
+
clientKind: CLIENT_KIND,
|
|
32906
|
+
clientVersion: CLIENT_VERSION
|
|
32907
|
+
}));
|
|
32908
|
+
});
|
|
32909
|
+
const events = {
|
|
32910
|
+
[Symbol.asyncIterator]() {
|
|
32911
|
+
return {
|
|
32912
|
+
async next() {
|
|
32913
|
+
if (eventQueue.length > 0) {
|
|
32914
|
+
return { value: eventQueue.shift(), done: false };
|
|
32915
|
+
}
|
|
32916
|
+
if (closed) {
|
|
32917
|
+
if (protocolError) throw protocolError;
|
|
32918
|
+
return { value: void 0, done: true };
|
|
32919
|
+
}
|
|
32920
|
+
return new Promise((resolve2, reject) => {
|
|
32921
|
+
eventWaiters.push({
|
|
32922
|
+
resolve: (event) => {
|
|
32923
|
+
if (event === null) {
|
|
32924
|
+
if (protocolError) reject(protocolError);
|
|
32925
|
+
else resolve2({ value: void 0, done: true });
|
|
32926
|
+
} else {
|
|
32927
|
+
resolve2({ value: event, done: false });
|
|
32928
|
+
}
|
|
32929
|
+
}
|
|
32930
|
+
});
|
|
32931
|
+
});
|
|
32932
|
+
},
|
|
32933
|
+
async return() {
|
|
32934
|
+
terminate(null);
|
|
32935
|
+
return { value: void 0, done: true };
|
|
32936
|
+
}
|
|
32937
|
+
};
|
|
32938
|
+
}
|
|
32939
|
+
};
|
|
32940
|
+
const client = {
|
|
32941
|
+
events,
|
|
32942
|
+
async request(method, params, _options = {}) {
|
|
32943
|
+
if (closed) {
|
|
32944
|
+
throw protocolError ?? new ServiceClientError("IPC_CLIENT_CLOSED", "service client is closed");
|
|
32945
|
+
}
|
|
32946
|
+
const id = randomUUID();
|
|
32947
|
+
return new Promise((resolve2, reject) => {
|
|
32948
|
+
pendingRequests.set(id, {
|
|
32949
|
+
resolve: (value) => resolve2(value),
|
|
32950
|
+
reject
|
|
32951
|
+
});
|
|
32952
|
+
try {
|
|
32953
|
+
socket.write(encodeFrame({ type: "request", id, method, params }));
|
|
32954
|
+
} catch (error) {
|
|
32955
|
+
pendingRequests.delete(id);
|
|
32956
|
+
reject(error);
|
|
32957
|
+
}
|
|
32958
|
+
});
|
|
32959
|
+
},
|
|
32960
|
+
async close() {
|
|
32961
|
+
terminate(null);
|
|
32962
|
+
}
|
|
32963
|
+
};
|
|
32964
|
+
return client;
|
|
32965
|
+
}
|
|
32966
|
+
function isRecord2(value) {
|
|
32967
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32968
|
+
}
|
|
32969
|
+
|
|
32970
|
+
// src/reset.ts
|
|
32971
|
+
async function resetViaServiceOrDisk(slockHome, viaService, direct) {
|
|
32972
|
+
let client = null;
|
|
32973
|
+
try {
|
|
32974
|
+
client = await connectService(slockHome);
|
|
32975
|
+
} catch {
|
|
32976
|
+
client = null;
|
|
32977
|
+
}
|
|
32978
|
+
if (!client) return direct();
|
|
32979
|
+
try {
|
|
32980
|
+
return await viaService(client);
|
|
32981
|
+
} finally {
|
|
32982
|
+
await client.close();
|
|
32983
|
+
}
|
|
32984
|
+
}
|
|
32985
|
+
async function resetService(installRoot) {
|
|
32986
|
+
const { previousState, clearedCrashCount } = await clearServiceCrashHistory(installRoot);
|
|
32987
|
+
return {
|
|
32988
|
+
status: "ok",
|
|
32989
|
+
previousState,
|
|
32990
|
+
clearedCrashCount
|
|
32991
|
+
};
|
|
32992
|
+
}
|
|
32993
|
+
async function resetRunner(installRoot, serverId) {
|
|
32994
|
+
const attachment = await readServerAttachment(installRoot, serverId);
|
|
32995
|
+
if (!attachment) {
|
|
32996
|
+
return { status: "not-found", serverId };
|
|
32997
|
+
}
|
|
32998
|
+
const outcome = await resetRunnerHealth(installRoot, serverId);
|
|
32999
|
+
if (outcome.status === "not-found") {
|
|
33000
|
+
return { status: "not-found", serverId };
|
|
33001
|
+
}
|
|
33002
|
+
return {
|
|
33003
|
+
status: "ok",
|
|
33004
|
+
serverId,
|
|
33005
|
+
previousState: outcome.previousState ?? "running",
|
|
33006
|
+
clearedCrashCount: outcome.clearedCrashCount ?? 0
|
|
33007
|
+
};
|
|
33008
|
+
}
|
|
33009
|
+
async function runReset(opts) {
|
|
33010
|
+
const wantsService = opts.service === true;
|
|
33011
|
+
const wantsRunner = opts.runner === true;
|
|
33012
|
+
if (wantsService && wantsRunner) {
|
|
33013
|
+
fail(
|
|
33014
|
+
"RESET_SCOPE_AMBIGUOUS",
|
|
33015
|
+
"`--service` and `--runner` are mutually exclusive. Pass exactly one."
|
|
33016
|
+
);
|
|
33017
|
+
}
|
|
33018
|
+
if (!wantsService && !wantsRunner) {
|
|
33019
|
+
fail(
|
|
33020
|
+
"RESET_SCOPE_MISSING",
|
|
33021
|
+
"Pass `--service` to clear the service-level crash history, or `--runner` to clear a per-runner crash history."
|
|
33022
|
+
);
|
|
33023
|
+
}
|
|
33024
|
+
const slockHome = resolveSlockHome();
|
|
33025
|
+
if (wantsService) {
|
|
33026
|
+
const result2 = await resetViaServiceOrDisk(
|
|
33027
|
+
slockHome,
|
|
33028
|
+
(client) => client.request("reset-service", void 0),
|
|
33029
|
+
() => resetService(slockHome)
|
|
33030
|
+
);
|
|
33031
|
+
if (opts.json) {
|
|
33032
|
+
info(JSON.stringify(result2));
|
|
33033
|
+
return;
|
|
33034
|
+
}
|
|
33035
|
+
info(
|
|
33036
|
+
`Reset service crash history (previous state: ${result2.previousState}; cleared ${result2.clearedCrashCount} entr${result2.clearedCrashCount === 1 ? "y" : "ies"}).`
|
|
33037
|
+
);
|
|
33038
|
+
info("Service state transitioned to `running`. Runners were not touched.");
|
|
33039
|
+
return;
|
|
33040
|
+
}
|
|
33041
|
+
const serverId = await resolveTargetServerId({ server: opts.server });
|
|
33042
|
+
const result = await resetViaServiceOrDisk(
|
|
33043
|
+
slockHome,
|
|
33044
|
+
(client) => client.request("reset-runner", { serverId }),
|
|
33045
|
+
() => resetRunner(slockHome, serverId)
|
|
33046
|
+
);
|
|
33047
|
+
if (result.status === "not-found") {
|
|
33048
|
+
fail(
|
|
33049
|
+
"RESET_RUNNER_NOT_FOUND",
|
|
33050
|
+
`Runner for server ${serverId} was not found.`
|
|
33051
|
+
);
|
|
33052
|
+
}
|
|
33053
|
+
if (opts.json) {
|
|
33054
|
+
info(JSON.stringify(result));
|
|
33055
|
+
return;
|
|
33056
|
+
}
|
|
33057
|
+
info(
|
|
33058
|
+
`Reset runner crash history for server ${result.serverId} (previous state: ${result.previousState}; cleared ${result.clearedCrashCount} entr${result.clearedCrashCount === 1 ? "y" : "ies"}).`
|
|
33059
|
+
);
|
|
33060
|
+
info("Runner state transitioned to `running`. Process was not respawned.");
|
|
33061
|
+
}
|
|
33062
|
+
|
|
33063
|
+
// src/service.ts
|
|
33064
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
33065
|
+
var seaRequire = createRequire2(import.meta.url);
|
|
33066
|
+
var cachedIsSea;
|
|
33067
|
+
function isSeaBinary() {
|
|
33068
|
+
if (cachedIsSea !== void 0) return cachedIsSea;
|
|
33069
|
+
try {
|
|
33070
|
+
cachedIsSea = seaRequire("node:sea").isSea();
|
|
33071
|
+
} catch {
|
|
33072
|
+
cachedIsSea = false;
|
|
33073
|
+
}
|
|
33074
|
+
return cachedIsSea;
|
|
33075
|
+
}
|
|
33076
|
+
function buildResidentSpawn(mode, serverId, selfEntry = process.argv[1] ?? "", execArgv = process.execArgv, isSea = isSeaBinary()) {
|
|
33077
|
+
const tail = serverId ? [mode, serverId] : [mode];
|
|
33078
|
+
if (isSea) {
|
|
33079
|
+
return { command: process.execPath, args: [...execArgv, ...tail] };
|
|
33080
|
+
}
|
|
33081
|
+
return { command: process.execPath, args: [...execArgv, selfEntry, ...tail] };
|
|
33082
|
+
}
|
|
33083
|
+
var PARENT_LOCK_HELD_ENV_VAR = "SLOCK_COMPUTER_PARENT_MUTATION_LOCK_HELD";
|
|
33084
|
+
async function spawnDetachedService(slockHome) {
|
|
33085
|
+
await mkdir12(serviceRunDir(slockHome), { recursive: true });
|
|
33086
|
+
const supLogFd = await open(serviceLogPath(slockHome), "a");
|
|
33087
|
+
const { command, args } = buildResidentSpawn("__service", null);
|
|
33088
|
+
const child = spawn2(command, args, {
|
|
33089
|
+
detached: true,
|
|
33090
|
+
stdio: ["ignore", supLogFd.fd, supLogFd.fd],
|
|
33091
|
+
windowsHide: true,
|
|
33092
|
+
env: { ...process.env, [PARENT_LOCK_HELD_ENV_VAR]: "1" }
|
|
33093
|
+
});
|
|
33094
|
+
child.on("error", (err) => {
|
|
33095
|
+
process.stderr.write(
|
|
33096
|
+
`${JSON.stringify({ ok: false, code: "SUPERVISOR_SPAWN_FAILED", message: err.message })}
|
|
33097
|
+
`
|
|
33098
|
+
);
|
|
33099
|
+
});
|
|
33100
|
+
const pid = child.pid;
|
|
33101
|
+
child.unref();
|
|
33102
|
+
await supLogFd.close();
|
|
33103
|
+
if (!pid) {
|
|
33104
|
+
throw new Error("SUPERVISOR_SPAWN_FAILED: could not spawn the service process");
|
|
33105
|
+
}
|
|
33106
|
+
await writePidfileAt(servicePidPath(slockHome), pid);
|
|
33107
|
+
return pid;
|
|
33108
|
+
}
|
|
33109
|
+
var EX_CONFIG_EXIT_CODE = 78;
|
|
33110
|
+
async function ensureSeaRuntimePackageDir() {
|
|
33111
|
+
if (!isSeaBinary() || process.env.PI_PACKAGE_DIR) return;
|
|
33112
|
+
try {
|
|
33113
|
+
const dir = joinPath(resolveSlockHome(), "runtime-pkg");
|
|
33114
|
+
await mkdir12(dir, { recursive: true });
|
|
33115
|
+
await writeFile10(
|
|
33116
|
+
joinPath(dir, "package.json"),
|
|
33117
|
+
`${JSON.stringify({ name: "slock-computer-sea-runtime", version: COMPUTER_VERSION })}
|
|
33118
|
+
`
|
|
33119
|
+
);
|
|
33120
|
+
process.env.PI_PACKAGE_DIR = dir;
|
|
33121
|
+
} catch {
|
|
33122
|
+
}
|
|
33123
|
+
}
|
|
33124
|
+
var defaultCoreFactory = async (creds) => {
|
|
33125
|
+
await ensureSeaRuntimePackageDir();
|
|
33126
|
+
let coreMod;
|
|
33127
|
+
try {
|
|
33128
|
+
coreMod = await import("@botiverse/raft-daemon/core");
|
|
33129
|
+
} catch (err) {
|
|
33130
|
+
const code = err?.code;
|
|
33131
|
+
const isModuleMissing = code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || err instanceof Error && /Cannot find (module|package)/i.test(err.message);
|
|
33132
|
+
if (isModuleMissing) {
|
|
33133
|
+
process.stderr.write(
|
|
33134
|
+
`slock-computer: per-server daemon failed to load \`@botiverse/raft-daemon/core\`.
|
|
33135
|
+
Reason: ${err instanceof Error ? err.message : String(err)}
|
|
33136
|
+
This usually means a source-run workspace without a built daemon dist.
|
|
33137
|
+
Fix: run \`pnpm --filter @botiverse/raft-daemon build\` once, then \`slock-computer doctor <server> --reset-health && slock-computer start\`.
|
|
33138
|
+
(Packaged/npx installs ship the dist already \u2014 this only affects local source-run setups.)
|
|
33139
|
+
`
|
|
33140
|
+
);
|
|
33141
|
+
process.exit(EX_CONFIG_EXIT_CODE);
|
|
33142
|
+
}
|
|
33143
|
+
throw err;
|
|
33144
|
+
}
|
|
33145
|
+
process.env.SLOCK_COMPUTER_VERSION = COMPUTER_VERSION;
|
|
33146
|
+
return new coreMod.DaemonCore({
|
|
33147
|
+
serverUrl: creds.serverUrl,
|
|
33148
|
+
apiKey: creds.apiKey,
|
|
33149
|
+
localTrace: true,
|
|
33150
|
+
// In a SEA single-binary there is no sidecar `slock` CLI script for the
|
|
33151
|
+
// daemon to resolve, so inject the `__cli` sentinel: the agent CLI wrapper
|
|
33152
|
+
// then execs `<exe> __cli "$@"`, re-execing this binary in CLI mode
|
|
33153
|
+
// (runBundledSlockCli). Normal installs leave this unset → the daemon
|
|
33154
|
+
// resolves the bundled CLI dist path as before.
|
|
33155
|
+
slockCliPath: isSeaBinary() ? "__cli" : void 0,
|
|
33156
|
+
// Managed-Computer remote control (server → WS → runner). This runner
|
|
33157
|
+
// is the service's in-process `__run` child, so:
|
|
33158
|
+
// restart → SIGTERM ourselves → runResident's shutdown does
|
|
33159
|
+
// core.stop() + exit 0 → classifyRunnerExit = graceful → the
|
|
33160
|
+
// service supervisor respawns this runner (effective restart).
|
|
33161
|
+
// upgrade (SEA + requestId) → run the SEA upgrade IN-PROCESS so we can
|
|
33162
|
+
// stream `computer:upgrade:progress` over this live WS, then SIGTERM
|
|
33163
|
+
// ourselves; the supervisor respawns the swapped binary and the new
|
|
33164
|
+
// process reports `done` via onComputerUpgradeReconcile on reconnect.
|
|
33165
|
+
// upgrade (non-SEA dev run, or no requestId) → SEA-only no-op. The
|
|
33166
|
+
// npm-install-root upgrade path is retired: a managed Computer is a
|
|
33167
|
+
// SEA single binary; a source/dev run has no binary to self-swap.
|
|
33168
|
+
onComputerControl: (action, ctx) => {
|
|
33169
|
+
if (action === "restart") {
|
|
33170
|
+
process.kill(process.pid, "SIGTERM");
|
|
33171
|
+
return;
|
|
33172
|
+
}
|
|
33173
|
+
if (isSeaBinary() && ctx.requestId) {
|
|
33174
|
+
return runManagedSeaUpgrade(ctx.requestId, ctx);
|
|
33175
|
+
}
|
|
33176
|
+
console.warn(
|
|
33177
|
+
`[Computer] Ignoring upgrade request: in-process SEA upgrade unavailable (isSea=${isSeaBinary()}, requestId=${ctx.requestId ? "present" : "absent"}). Managed upgrade requires a SEA binary invoked with a requestId.`
|
|
33178
|
+
);
|
|
32693
33179
|
},
|
|
32694
33180
|
// Blip-stitch: on every (re)connect, if THIS process booted from a freshly
|
|
32695
33181
|
// swapped binary (a pending-upgrade marker is present), report the upgrade
|
|
@@ -32778,9 +33264,6 @@ async function runResident(serverId, deps = {}) {
|
|
|
32778
33264
|
}
|
|
32779
33265
|
var RECONCILE_INTERVAL_MS = 5e3;
|
|
32780
33266
|
var CHILD_RESTART_BACKOFF_MS = 2e3;
|
|
32781
|
-
function canSupervisorSpawnChild(serverId, running, restarting) {
|
|
32782
|
-
return !running.has(serverId) && !restarting.has(serverId);
|
|
32783
|
-
}
|
|
32784
33267
|
function formatReadySummary(ready, serverIds, opts) {
|
|
32785
33268
|
if (opts.serverId && serverIds.length === 1) {
|
|
32786
33269
|
const pid = ready.get(opts.serverId);
|
|
@@ -32821,10 +33304,10 @@ async function runServiceStartupRecovery(slockHome) {
|
|
|
32821
33304
|
}
|
|
32822
33305
|
async function resolveServiceIdentity() {
|
|
32823
33306
|
const here = fileURLToPath2(import.meta.url);
|
|
32824
|
-
const installRoot =
|
|
33307
|
+
const installRoot = dirname11(dirname11(here));
|
|
32825
33308
|
let version = null;
|
|
32826
33309
|
try {
|
|
32827
|
-
const raw = await
|
|
33310
|
+
const raw = await readFile11(joinPath(installRoot, "package.json"), "utf8");
|
|
32828
33311
|
const parsed = JSON.parse(raw);
|
|
32829
33312
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
32830
33313
|
version = parsed.version;
|
|
@@ -32847,7 +33330,7 @@ async function writeServiceVersionEvidence(slockHome) {
|
|
|
32847
33330
|
};
|
|
32848
33331
|
const dest = serviceVersionPath(slockHome);
|
|
32849
33332
|
const tmp = `${dest}.tmp`;
|
|
32850
|
-
await
|
|
33333
|
+
await writeFile10(tmp, JSON.stringify(payload) + "\n", { mode: 384 });
|
|
32851
33334
|
await rename3(tmp, dest);
|
|
32852
33335
|
} catch (err) {
|
|
32853
33336
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -32857,7 +33340,7 @@ async function writeServiceVersionEvidence(slockHome) {
|
|
|
32857
33340
|
);
|
|
32858
33341
|
}
|
|
32859
33342
|
}
|
|
32860
|
-
async function startServiceIpcSeam(slockHome) {
|
|
33343
|
+
async function startServiceIpcSeam(slockHome, mutations) {
|
|
32861
33344
|
const handlers = {
|
|
32862
33345
|
"service-status": async () => readServiceStatus(slockHome),
|
|
32863
33346
|
"runner-status": async ({ serverId }) => {
|
|
@@ -32870,7 +33353,18 @@ async function startServiceIpcSeam(slockHome) {
|
|
|
32870
33353
|
throw err;
|
|
32871
33354
|
}
|
|
32872
33355
|
},
|
|
32873
|
-
"list-runners": async () => listRunners(slockHome)
|
|
33356
|
+
"list-runners": async () => listRunners(slockHome),
|
|
33357
|
+
"reset-service": async () => mutations ? mutations.resetService() : resetService(slockHome),
|
|
33358
|
+
"reset-runner": async ({ serverId }) => mutations ? mutations.resetRunner(serverId) : resetRunner(slockHome, serverId),
|
|
33359
|
+
"upgrade-start": async (params) => {
|
|
33360
|
+
if (!mutations) {
|
|
33361
|
+
throw new ServiceClientError(
|
|
33362
|
+
"IPC_MALFORMED_FRAME",
|
|
33363
|
+
"upgrade-start requires a live supervisor mutation surface"
|
|
33364
|
+
);
|
|
33365
|
+
}
|
|
33366
|
+
return mutations.upgradeStart(params);
|
|
33367
|
+
}
|
|
32874
33368
|
};
|
|
32875
33369
|
const ipc = createIpcServer({ installRoot: slockHome, handlers });
|
|
32876
33370
|
try {
|
|
@@ -32888,17 +33382,33 @@ async function startServiceIpcSeam(slockHome) {
|
|
|
32888
33382
|
}
|
|
32889
33383
|
async function runService() {
|
|
32890
33384
|
const slockHome = resolveSlockHome();
|
|
32891
|
-
await
|
|
33385
|
+
await mkdir12(serviceRunDir(slockHome), { recursive: true });
|
|
32892
33386
|
await runServiceStartupRecovery(slockHome);
|
|
32893
33387
|
await writePidfileAt(servicePidPath(slockHome), process.pid);
|
|
32894
33388
|
await writeServiceVersionEvidence(slockHome);
|
|
32895
|
-
const
|
|
32896
|
-
|
|
33389
|
+
const runners2 = /* @__PURE__ */ new Map();
|
|
33390
|
+
let shuttingDown = false;
|
|
33391
|
+
let inFlightUpgrade = null;
|
|
32897
33392
|
const { [PARENT_LOCK_HELD_ENV_VAR]: _parentLockMarker, ...childEnv } = process.env;
|
|
33393
|
+
const emitTransition = (serverId, from, to, trigger) => {
|
|
33394
|
+
void emitRunnerStateTransition(slockHome, serverId, from, to, trigger);
|
|
33395
|
+
};
|
|
33396
|
+
let scheduleReconcile = () => {
|
|
33397
|
+
};
|
|
32898
33398
|
const spawnChild = async (serverId) => {
|
|
32899
|
-
if (
|
|
33399
|
+
if (shuttingDown) return;
|
|
33400
|
+
const existing = runners2.get(serverId);
|
|
33401
|
+
if (existing?.child) return;
|
|
33402
|
+
const fromState = existing?.lifecycle ?? "stopped";
|
|
33403
|
+
const record = existing ?? { serverId, lifecycle: "stopped", stopping: false };
|
|
33404
|
+
record.lifecycle = "starting";
|
|
33405
|
+
record.stopping = false;
|
|
33406
|
+
record.backoffUntil = void 0;
|
|
33407
|
+
record.child = void 0;
|
|
33408
|
+
runners2.set(serverId, record);
|
|
33409
|
+
emitTransition(serverId, fromState, "starting", RUNNER_TRIGGER.spawn);
|
|
32900
33410
|
const logPath = serverRunnerLogPath(slockHome, serverId);
|
|
32901
|
-
await
|
|
33411
|
+
await mkdir12(dirname11(logPath), { recursive: true });
|
|
32902
33412
|
const logFd = await open(logPath, "a");
|
|
32903
33413
|
const { command, args } = buildResidentSpawn("__run", serverId);
|
|
32904
33414
|
const child = spawn2(command, args, {
|
|
@@ -32907,89 +33417,159 @@ async function runService() {
|
|
|
32907
33417
|
env: childEnv
|
|
32908
33418
|
});
|
|
32909
33419
|
await logFd.close();
|
|
32910
|
-
if (!child.pid)
|
|
32911
|
-
|
|
32912
|
-
|
|
33420
|
+
if (!child.pid) {
|
|
33421
|
+
record.lifecycle = "crashed";
|
|
33422
|
+
record.backoffUntil = Date.now() + CHILD_RESTART_BACKOFF_MS;
|
|
33423
|
+
emitTransition(serverId, "starting", "crashed", RUNNER_TRIGGER.spawnFailed);
|
|
33424
|
+
scheduleReconcile(CHILD_RESTART_BACKOFF_MS);
|
|
33425
|
+
return;
|
|
33426
|
+
}
|
|
33427
|
+
record.child = child;
|
|
33428
|
+
record.lifecycle = "running";
|
|
33429
|
+
emitTransition(serverId, "starting", "running", RUNNER_TRIGGER.ready);
|
|
32913
33430
|
await writePidfileAt(serverRunnerPidPath(slockHome, serverId), child.pid);
|
|
32914
33431
|
child.on("exit", (code, signal) => {
|
|
32915
33432
|
void (async () => {
|
|
32916
|
-
|
|
33433
|
+
const rec = runners2.get(serverId);
|
|
33434
|
+
if (!rec) return;
|
|
33435
|
+
rec.child = void 0;
|
|
32917
33436
|
await clearPidfileAt(serverRunnerPidPath(slockHome, serverId));
|
|
32918
|
-
if (
|
|
32919
|
-
|
|
32920
|
-
|
|
33437
|
+
if (rec.stopping) {
|
|
33438
|
+
const trigger = shuttingDown ? RUNNER_TRIGGER.shutdownStop : RUNNER_TRIGGER.detachStop;
|
|
33439
|
+
const prev2 = rec.lifecycle;
|
|
33440
|
+
rec.lifecycle = "stopped";
|
|
33441
|
+
emitTransition(serverId, prev2, "stopped", trigger);
|
|
33442
|
+
return;
|
|
33443
|
+
}
|
|
33444
|
+
const exitClass = classifyRunnerExit(code, signal);
|
|
33445
|
+
if (exitClass === "config-error") {
|
|
32921
33446
|
try {
|
|
32922
33447
|
await markFatalConfig(slockHome, serverId, code, signal);
|
|
32923
33448
|
} catch {
|
|
32924
33449
|
}
|
|
33450
|
+
const prev2 = rec.lifecycle;
|
|
33451
|
+
rec.lifecycle = "degraded";
|
|
33452
|
+
emitTransition(serverId, prev2, "degraded", RUNNER_TRIGGER.exitConfigError);
|
|
32925
33453
|
process.stderr.write(
|
|
32926
33454
|
`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.
|
|
32927
33455
|
`
|
|
32928
33456
|
);
|
|
32929
33457
|
return;
|
|
32930
33458
|
}
|
|
32931
|
-
if (
|
|
32932
|
-
restarting.add(serverId);
|
|
33459
|
+
if (exitClass === "crash") {
|
|
32933
33460
|
try {
|
|
32934
|
-
await
|
|
32935
|
-
|
|
32936
|
-
await spawnChild(serverId);
|
|
32937
|
-
}
|
|
32938
|
-
} finally {
|
|
32939
|
-
restarting.delete(serverId);
|
|
33461
|
+
await recordCrash(slockHome, serverId, code, signal);
|
|
33462
|
+
} catch {
|
|
32940
33463
|
}
|
|
32941
|
-
return;
|
|
32942
|
-
}
|
|
32943
|
-
try {
|
|
32944
|
-
await recordCrash(slockHome, serverId, code, signal);
|
|
32945
|
-
} catch {
|
|
32946
33464
|
}
|
|
32947
|
-
|
|
33465
|
+
const budgetBreached = exitClass === "crash" ? await isDegraded(slockHome, serverId) : false;
|
|
33466
|
+
const prev = rec.lifecycle;
|
|
33467
|
+
const to = nextRunnerStateOnExit(exitClass, budgetBreached);
|
|
33468
|
+
rec.lifecycle = to;
|
|
33469
|
+
emitTransition(serverId, prev, to, exitTrigger(exitClass, budgetBreached));
|
|
33470
|
+
if (to === "degraded") {
|
|
32948
33471
|
process.stderr.write(
|
|
32949
33472
|
`Service: server ${serverId} marked degraded (>=3 crashes in 60s); skipping auto-restart. Run \`slock-computer doctor ${serverId} --reset-health\` after fixing the underlying issue.
|
|
32950
33473
|
`
|
|
32951
33474
|
);
|
|
32952
33475
|
return;
|
|
32953
33476
|
}
|
|
32954
|
-
|
|
32955
|
-
|
|
32956
|
-
await new Promise((r) => setTimeout(r, CHILD_RESTART_BACKOFF_MS));
|
|
32957
|
-
if (await readServerAttachment(slockHome, serverId) && !shuttingDown) {
|
|
32958
|
-
await spawnChild(serverId);
|
|
32959
|
-
}
|
|
32960
|
-
} finally {
|
|
32961
|
-
restarting.delete(serverId);
|
|
32962
|
-
}
|
|
33477
|
+
rec.backoffUntil = Date.now() + CHILD_RESTART_BACKOFF_MS;
|
|
33478
|
+
scheduleReconcile(CHILD_RESTART_BACKOFF_MS);
|
|
32963
33479
|
})();
|
|
32964
33480
|
});
|
|
32965
33481
|
};
|
|
32966
|
-
const killChild = (
|
|
32967
|
-
|
|
33482
|
+
const killChild = (rec) => {
|
|
33483
|
+
rec.stopping = true;
|
|
32968
33484
|
try {
|
|
32969
|
-
|
|
33485
|
+
rec.child?.kill("SIGTERM");
|
|
32970
33486
|
} catch {
|
|
32971
33487
|
}
|
|
32972
33488
|
};
|
|
32973
33489
|
const reconcile = async () => {
|
|
33490
|
+
if (shuttingDown) return;
|
|
32974
33491
|
const wanted = new Set(await listManagedServerIds(slockHome));
|
|
33492
|
+
const now = Date.now();
|
|
32975
33493
|
for (const id of wanted) {
|
|
32976
|
-
if (
|
|
33494
|
+
if (canSpawn(runners2.get(id), true, now)) await spawnChild(id);
|
|
32977
33495
|
}
|
|
32978
|
-
for (const [id,
|
|
32979
|
-
if (!wanted.has(id)) killChild(
|
|
33496
|
+
for (const [id, rec] of runners2) {
|
|
33497
|
+
if (!wanted.has(id) && rec.child) killChild(rec);
|
|
32980
33498
|
}
|
|
32981
33499
|
};
|
|
32982
|
-
|
|
32983
|
-
|
|
33500
|
+
scheduleReconcile = (delayMs) => {
|
|
33501
|
+
setTimeout(() => void reconcile(), Math.max(0, delayMs));
|
|
33502
|
+
};
|
|
33503
|
+
const ipc = await startServiceIpcSeam(slockHome, {
|
|
33504
|
+
resetService: () => resetService(slockHome),
|
|
33505
|
+
resetRunner: async (serverId) => {
|
|
33506
|
+
const result = await resetRunner(slockHome, serverId);
|
|
33507
|
+
if (result.status === "ok") {
|
|
33508
|
+
const rec = runners2.get(serverId);
|
|
33509
|
+
if (rec && applyRunnerReset(rec)) {
|
|
33510
|
+
emitTransition(serverId, "degraded", "stopped", RUNNER_TRIGGER.reset);
|
|
33511
|
+
scheduleReconcile(0);
|
|
33512
|
+
}
|
|
33513
|
+
}
|
|
33514
|
+
return result;
|
|
33515
|
+
},
|
|
33516
|
+
// `upgrade-start` (phase ①b): the running service drives the SEA upgrade
|
|
33517
|
+
// in-process (reusing the tested resolveAndRunSeaUpgrade) instead of the
|
|
33518
|
+
// service spawning a `slock-computer upgrade` subprocess — single-writer
|
|
33519
|
+
// initiation. Async: resolve the target version (so the `started` response
|
|
33520
|
+
// carries it), then kick off download+verify+swap WITHOUT awaiting so the
|
|
33521
|
+
// IPC response flushes first; the swap + self-restart use the EXISTING
|
|
33522
|
+
// mechanism unchanged (path 1 — restart is orthogonal, evolving under XX).
|
|
33523
|
+
// A second concurrent upgrade-start is reported `already-running`.
|
|
33524
|
+
upgradeStart: async ({ targetVersion: explicit }) => {
|
|
33525
|
+
if (!isSeaBinary()) {
|
|
33526
|
+
throw new ServiceClientError(
|
|
33527
|
+
"IPC_MALFORMED_FRAME",
|
|
33528
|
+
"upgrade-start is SEA-only; this service is a non-SEA (dev/npm) run"
|
|
33529
|
+
);
|
|
33530
|
+
}
|
|
33531
|
+
if (inFlightUpgrade) {
|
|
33532
|
+
return { status: "already-running", ...inFlightUpgrade };
|
|
33533
|
+
}
|
|
33534
|
+
const channel2 = await readChannel(slockHome);
|
|
33535
|
+
const baseUrl = resolveUpgradeBaseUrl();
|
|
33536
|
+
const resolved = explicit ?? await resolveSeaTargetVersion(channel2, baseUrl) ?? COMPUTER_VERSION;
|
|
33537
|
+
const upgradeId = randomUUID2();
|
|
33538
|
+
inFlightUpgrade = { upgradeId, targetVersion: resolved };
|
|
33539
|
+
void (async () => {
|
|
33540
|
+
try {
|
|
33541
|
+
await resolveAndRunSeaUpgrade({
|
|
33542
|
+
slockHome,
|
|
33543
|
+
requestId: upgradeId,
|
|
33544
|
+
fromVersion: COMPUTER_VERSION,
|
|
33545
|
+
currentBinaryPath: process.execPath,
|
|
33546
|
+
nowIso: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33547
|
+
deps: explicit ? { resolveTargetVersion: async () => explicit } : void 0
|
|
33548
|
+
});
|
|
33549
|
+
} catch (err) {
|
|
33550
|
+
process.stderr.write(
|
|
33551
|
+
`Service: upgrade ${upgradeId} failed: ${err instanceof Error ? err.message : String(err)}
|
|
33552
|
+
`
|
|
33553
|
+
);
|
|
33554
|
+
} finally {
|
|
33555
|
+
inFlightUpgrade = null;
|
|
33556
|
+
}
|
|
33557
|
+
})();
|
|
33558
|
+
return { status: "started", upgradeId, targetVersion: resolved };
|
|
33559
|
+
}
|
|
33560
|
+
});
|
|
32984
33561
|
const shutdown = () => {
|
|
32985
33562
|
if (shuttingDown) return;
|
|
32986
33563
|
shuttingDown = true;
|
|
32987
|
-
for (const
|
|
33564
|
+
for (const rec of runners2.values()) if (rec.child) killChild(rec);
|
|
32988
33565
|
void ipc.close();
|
|
32989
33566
|
void clearPidfileAt(servicePidPath(slockHome)).then(() => process.exit(0));
|
|
32990
33567
|
};
|
|
32991
33568
|
process.on("SIGTERM", shutdown);
|
|
32992
33569
|
process.on("SIGINT", shutdown);
|
|
33570
|
+
for (const id of await listManagedServerIds(slockHome)) {
|
|
33571
|
+
runners2.set(id, rehydrateRunnerRecord(id, await isDegraded(slockHome, id)));
|
|
33572
|
+
}
|
|
32993
33573
|
await reconcile();
|
|
32994
33574
|
const timer = setInterval(() => {
|
|
32995
33575
|
void reconcile();
|
|
@@ -33111,7 +33691,7 @@ async function runDetach(serverId, serverLabel = serverId) {
|
|
|
33111
33691
|
var USER_SESSION_EXPIRY_LEEWAY_MS = 3e4;
|
|
33112
33692
|
async function readUserSessionAuth(slockHome) {
|
|
33113
33693
|
try {
|
|
33114
|
-
const parsed = JSON.parse(await
|
|
33694
|
+
const parsed = JSON.parse(await readFile12(userSessionPath(slockHome), "utf8"));
|
|
33115
33695
|
return {
|
|
33116
33696
|
accessToken: typeof parsed.accessToken === "string" ? parsed.accessToken : "",
|
|
33117
33697
|
...typeof parsed.serverUrl === "string" ? { serverUrl: parsed.serverUrl } : {}
|
|
@@ -33122,7 +33702,7 @@ async function readUserSessionAuth(slockHome) {
|
|
|
33122
33702
|
}
|
|
33123
33703
|
async function hasValidUserSession(slockHome) {
|
|
33124
33704
|
try {
|
|
33125
|
-
const parsed = JSON.parse(await
|
|
33705
|
+
const parsed = JSON.parse(await readFile12(userSessionPath(slockHome), "utf8"));
|
|
33126
33706
|
return parsed.kind === "user-session" && typeof parsed.accessToken === "string" && parsed.accessToken.length > 0 && !isJwtExpired(parsed.accessToken);
|
|
33127
33707
|
} catch {
|
|
33128
33708
|
return false;
|
|
@@ -33142,7 +33722,7 @@ async function refreshUserSession(slockHome, serverUrl) {
|
|
|
33142
33722
|
const file = userSessionPath(slockHome);
|
|
33143
33723
|
let session;
|
|
33144
33724
|
try {
|
|
33145
|
-
session = JSON.parse(await
|
|
33725
|
+
session = JSON.parse(await readFile12(file, "utf8"));
|
|
33146
33726
|
} catch {
|
|
33147
33727
|
return false;
|
|
33148
33728
|
}
|
|
@@ -33161,8 +33741,8 @@ async function refreshUserSession(slockHome, serverUrl) {
|
|
|
33161
33741
|
if (res.status !== 200 || typeof body?.accessToken !== "string" || typeof body.refreshToken !== "string") {
|
|
33162
33742
|
return false;
|
|
33163
33743
|
}
|
|
33164
|
-
await
|
|
33165
|
-
await
|
|
33744
|
+
await mkdir13(dirname12(file), { recursive: true });
|
|
33745
|
+
await writeFile11(
|
|
33166
33746
|
tmpFile,
|
|
33167
33747
|
JSON.stringify(
|
|
33168
33748
|
{
|
|
@@ -33491,68 +34071,6 @@ async function runSetup(opts, deps = {}) {
|
|
|
33491
34071
|
|
|
33492
34072
|
// src/runners.ts
|
|
33493
34073
|
init_esm_shims();
|
|
33494
|
-
|
|
33495
|
-
// src/targetServer.ts
|
|
33496
|
-
init_esm_shims();
|
|
33497
|
-
function attachmentLabel(a) {
|
|
33498
|
-
return formatServerSlugDisplay(a.serverSlug);
|
|
33499
|
-
}
|
|
33500
|
-
async function refreshAttachmentSlug(home, attachment) {
|
|
33501
|
-
try {
|
|
33502
|
-
const client = new ComputerAttachClient(attachment.serverUrl, "");
|
|
33503
|
-
const result = await client.preflight(attachment.apiKey);
|
|
33504
|
-
if (!result.ok || !result.serverSlug || result.serverSlug === attachment.serverSlug) {
|
|
33505
|
-
return attachment;
|
|
33506
|
-
}
|
|
33507
|
-
const updated = { ...attachment, serverSlug: result.serverSlug };
|
|
33508
|
-
await writeServerAttachment(home, updated);
|
|
33509
|
-
return updated;
|
|
33510
|
-
} catch {
|
|
33511
|
-
return attachment;
|
|
33512
|
-
}
|
|
33513
|
-
}
|
|
33514
|
-
async function listAttachmentsWithFreshSlugs(home) {
|
|
33515
|
-
const attachments = await listServerAttachments(home);
|
|
33516
|
-
return await Promise.all(attachments.map((a) => refreshAttachmentSlug(home, a)));
|
|
33517
|
-
}
|
|
33518
|
-
async function resolveTargetServerId(opts) {
|
|
33519
|
-
const home = resolveSlockHome();
|
|
33520
|
-
let attachments = await listServerAttachments(home);
|
|
33521
|
-
if (attachments.length === 0) {
|
|
33522
|
-
fail("NO_ATTACHMENT", "No server attachments yet. Run `slock-computer attach /<serverSlug>` (e.g. `/myserver`) first.");
|
|
33523
|
-
}
|
|
33524
|
-
const requested = normalizeServerSlug(opts.server ?? "");
|
|
33525
|
-
if (requested) {
|
|
33526
|
-
let found = attachments.find((a) => a.serverSlug === requested);
|
|
33527
|
-
if (!found) {
|
|
33528
|
-
attachments = await listAttachmentsWithFreshSlugs(home);
|
|
33529
|
-
found = attachments.find((a) => a.serverSlug === requested);
|
|
33530
|
-
}
|
|
33531
|
-
if (!found) {
|
|
33532
|
-
fail(
|
|
33533
|
-
"NOT_ATTACHED",
|
|
33534
|
-
`Server slug ${formatServerSlugDisplay(requested)} is not attached. Attached server slugs: ${attachments.map(attachmentLabel).join(", ")}.`
|
|
33535
|
-
);
|
|
33536
|
-
}
|
|
33537
|
-
return found.serverId;
|
|
33538
|
-
}
|
|
33539
|
-
if (attachments.length === 1) return attachments[0].serverId;
|
|
33540
|
-
fail(
|
|
33541
|
-
"AMBIGUOUS_SERVER",
|
|
33542
|
-
`Multiple servers attached (${attachments.map(attachmentLabel).join(", ")}). Pass \`--server /<serverSlug>\` (e.g. \`--server ${attachmentLabel(attachments[0])}\`) to choose one.`
|
|
33543
|
-
);
|
|
33544
|
-
}
|
|
33545
|
-
async function resolveTargetAttachment(opts) {
|
|
33546
|
-
const serverId = await resolveTargetServerId(opts);
|
|
33547
|
-
const a = await readServerAttachment(resolveSlockHome(), serverId);
|
|
33548
|
-
if (!a) {
|
|
33549
|
-
const label = opts.server ?? serverId;
|
|
33550
|
-
fail("INVALID_ATTACHMENT", `Attachment for ${label} is missing/invalid. Re-run \`slock-computer attach ${label}\`.`);
|
|
33551
|
-
}
|
|
33552
|
-
return a;
|
|
33553
|
-
}
|
|
33554
|
-
|
|
33555
|
-
// src/runners.ts
|
|
33556
34074
|
async function runRunnersList(opts) {
|
|
33557
34075
|
const serverId = await resolveTargetServerId({ server: opts.server });
|
|
33558
34076
|
const installRoot = resolveSlockHome();
|
|
@@ -33757,14 +34275,14 @@ async function runDoctor(opts) {
|
|
|
33757
34275
|
|
|
33758
34276
|
// src/logs.ts
|
|
33759
34277
|
init_esm_shims();
|
|
33760
|
-
import { readFile as
|
|
34278
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
33761
34279
|
var DEFAULT_LINES = 200;
|
|
33762
34280
|
async function runLogs(opts) {
|
|
33763
34281
|
const home = resolveSlockHome();
|
|
33764
34282
|
const file = opts.service ? serviceLogPath(home) : serverRunnerLogPath(home, await resolveTargetServerId({ server: opts.server }));
|
|
33765
34283
|
let content;
|
|
33766
34284
|
try {
|
|
33767
|
-
content = await
|
|
34285
|
+
content = await readFile13(file, "utf8");
|
|
33768
34286
|
} catch {
|
|
33769
34287
|
fail(
|
|
33770
34288
|
"NO_DAEMON_LOG",
|
|
@@ -33778,156 +34296,6 @@ async function runLogs(opts) {
|
|
|
33778
34296
|
for (const line of tail) info(redactSecrets(line));
|
|
33779
34297
|
}
|
|
33780
34298
|
|
|
33781
|
-
// src/reset.ts
|
|
33782
|
-
init_esm_shims();
|
|
33783
|
-
|
|
33784
|
-
// src/serviceState.ts
|
|
33785
|
-
init_esm_shims();
|
|
33786
|
-
import { mkdir as mkdir13, readFile as readFile13, writeFile as writeFile11, appendFile as appendFile3 } from "fs/promises";
|
|
33787
|
-
import { dirname as dirname12 } from "path";
|
|
33788
|
-
|
|
33789
|
-
// src/lib/state.ts
|
|
33790
|
-
init_esm_shims();
|
|
33791
|
-
var SERVICE_STATE_VALUES = [
|
|
33792
|
-
"starting",
|
|
33793
|
-
"running",
|
|
33794
|
-
"degraded",
|
|
33795
|
-
"stopping",
|
|
33796
|
-
"stopped"
|
|
33797
|
-
];
|
|
33798
|
-
function isServiceState(value) {
|
|
33799
|
-
return typeof value === "string" && SERVICE_STATE_VALUES.includes(value);
|
|
33800
|
-
}
|
|
33801
|
-
|
|
33802
|
-
// src/serviceState.ts
|
|
33803
|
-
var DEFAULT_STATE = {
|
|
33804
|
-
state: "running",
|
|
33805
|
-
crashHistory: []
|
|
33806
|
-
};
|
|
33807
|
-
async function readServiceState(slockHome) {
|
|
33808
|
-
try {
|
|
33809
|
-
const raw = await readFile13(serviceStatePath(slockHome), "utf8");
|
|
33810
|
-
const parsed = JSON.parse(raw);
|
|
33811
|
-
if (!parsed || typeof parsed !== "object") return { ...DEFAULT_STATE };
|
|
33812
|
-
const obj = parsed;
|
|
33813
|
-
const state = isServiceState(obj.state) ? obj.state : "running";
|
|
33814
|
-
const crashHistory = Array.isArray(obj.crashHistory) ? obj.crashHistory.filter(isCrashEntry) : [];
|
|
33815
|
-
return { state, crashHistory };
|
|
33816
|
-
} catch {
|
|
33817
|
-
return { ...DEFAULT_STATE };
|
|
33818
|
-
}
|
|
33819
|
-
}
|
|
33820
|
-
async function writeServiceState(slockHome, file) {
|
|
33821
|
-
const path3 = serviceStatePath(slockHome);
|
|
33822
|
-
await mkdir13(dirname12(path3), { recursive: true });
|
|
33823
|
-
await writeFile11(path3, JSON.stringify(file), { mode: 384 });
|
|
33824
|
-
}
|
|
33825
|
-
function isCrashEntry(value) {
|
|
33826
|
-
if (!value || typeof value !== "object") return false;
|
|
33827
|
-
const obj = value;
|
|
33828
|
-
return typeof obj.at === "string";
|
|
33829
|
-
}
|
|
33830
|
-
async function emitServiceStateTransition(slockHome, fromState, toState, trigger) {
|
|
33831
|
-
const entry = {
|
|
33832
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33833
|
-
kind: "service-state-changed",
|
|
33834
|
-
fromState,
|
|
33835
|
-
toState,
|
|
33836
|
-
trigger
|
|
33837
|
-
};
|
|
33838
|
-
try {
|
|
33839
|
-
const path3 = serviceLogPath(slockHome);
|
|
33840
|
-
await mkdir13(dirname12(path3), { recursive: true });
|
|
33841
|
-
await appendFile3(path3, JSON.stringify(entry) + "\n");
|
|
33842
|
-
} catch {
|
|
33843
|
-
}
|
|
33844
|
-
}
|
|
33845
|
-
async function clearServiceCrashHistory(slockHome) {
|
|
33846
|
-
const current = await readServiceState(slockHome);
|
|
33847
|
-
const previousState = current.state;
|
|
33848
|
-
const clearedCrashCount = current.crashHistory.length;
|
|
33849
|
-
const next = { state: "running", crashHistory: [] };
|
|
33850
|
-
await writeServiceState(slockHome, next);
|
|
33851
|
-
await emitServiceStateTransition(
|
|
33852
|
-
slockHome,
|
|
33853
|
-
previousState,
|
|
33854
|
-
"running",
|
|
33855
|
-
"reset-service"
|
|
33856
|
-
);
|
|
33857
|
-
return { previousState, clearedCrashCount };
|
|
33858
|
-
}
|
|
33859
|
-
|
|
33860
|
-
// src/reset.ts
|
|
33861
|
-
async function resetService(installRoot) {
|
|
33862
|
-
const { previousState, clearedCrashCount } = await clearServiceCrashHistory(installRoot);
|
|
33863
|
-
return {
|
|
33864
|
-
status: "ok",
|
|
33865
|
-
previousState,
|
|
33866
|
-
clearedCrashCount
|
|
33867
|
-
};
|
|
33868
|
-
}
|
|
33869
|
-
async function resetRunner(installRoot, serverId) {
|
|
33870
|
-
const attachment = await readServerAttachment(installRoot, serverId);
|
|
33871
|
-
if (!attachment) {
|
|
33872
|
-
return { status: "not-found", serverId };
|
|
33873
|
-
}
|
|
33874
|
-
const outcome = await resetRunnerHealth(installRoot, serverId);
|
|
33875
|
-
if (outcome.status === "not-found") {
|
|
33876
|
-
return { status: "not-found", serverId };
|
|
33877
|
-
}
|
|
33878
|
-
return {
|
|
33879
|
-
status: "ok",
|
|
33880
|
-
serverId,
|
|
33881
|
-
previousState: outcome.previousState ?? "running",
|
|
33882
|
-
clearedCrashCount: outcome.clearedCrashCount ?? 0
|
|
33883
|
-
};
|
|
33884
|
-
}
|
|
33885
|
-
async function runReset(opts) {
|
|
33886
|
-
const wantsService = opts.service === true;
|
|
33887
|
-
const wantsRunner = opts.runner === true;
|
|
33888
|
-
if (wantsService && wantsRunner) {
|
|
33889
|
-
fail(
|
|
33890
|
-
"RESET_SCOPE_AMBIGUOUS",
|
|
33891
|
-
"`--service` and `--runner` are mutually exclusive. Pass exactly one."
|
|
33892
|
-
);
|
|
33893
|
-
}
|
|
33894
|
-
if (!wantsService && !wantsRunner) {
|
|
33895
|
-
fail(
|
|
33896
|
-
"RESET_SCOPE_MISSING",
|
|
33897
|
-
"Pass `--service` to clear the service-level crash history, or `--runner` to clear a per-runner crash history."
|
|
33898
|
-
);
|
|
33899
|
-
}
|
|
33900
|
-
const slockHome = resolveSlockHome();
|
|
33901
|
-
if (wantsService) {
|
|
33902
|
-
const result2 = await resetService(slockHome);
|
|
33903
|
-
if (opts.json) {
|
|
33904
|
-
info(JSON.stringify(result2));
|
|
33905
|
-
return;
|
|
33906
|
-
}
|
|
33907
|
-
info(
|
|
33908
|
-
`Reset service crash history (previous state: ${result2.previousState}; cleared ${result2.clearedCrashCount} entr${result2.clearedCrashCount === 1 ? "y" : "ies"}).`
|
|
33909
|
-
);
|
|
33910
|
-
info("Service state transitioned to `running`. Runners were not touched.");
|
|
33911
|
-
return;
|
|
33912
|
-
}
|
|
33913
|
-
const serverId = await resolveTargetServerId({ server: opts.server });
|
|
33914
|
-
const result = await resetRunner(slockHome, serverId);
|
|
33915
|
-
if (result.status === "not-found") {
|
|
33916
|
-
fail(
|
|
33917
|
-
"RESET_RUNNER_NOT_FOUND",
|
|
33918
|
-
`Runner for server ${serverId} was not found.`
|
|
33919
|
-
);
|
|
33920
|
-
}
|
|
33921
|
-
if (opts.json) {
|
|
33922
|
-
info(JSON.stringify(result));
|
|
33923
|
-
return;
|
|
33924
|
-
}
|
|
33925
|
-
info(
|
|
33926
|
-
`Reset runner crash history for server ${result.serverId} (previous state: ${result.previousState}; cleared ${result.clearedCrashCount} entr${result.clearedCrashCount === 1 ? "y" : "ies"}).`
|
|
33927
|
-
);
|
|
33928
|
-
info("Runner state transitioned to `running`. Process was not respawned.");
|
|
33929
|
-
}
|
|
33930
|
-
|
|
33931
34299
|
// src/concurrency.ts
|
|
33932
34300
|
init_esm_shims();
|
|
33933
34301
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
@@ -33988,11 +34356,9 @@ function resolveUpgradeTrigger(raw) {
|
|
|
33988
34356
|
return raw === "web" || raw === "tray" ? raw : "cli";
|
|
33989
34357
|
}
|
|
33990
34358
|
var UPGRADE_ERROR_CODES = [
|
|
33991
|
-
"UPGRADE_DEPS_CHANGED",
|
|
33992
34359
|
"UPGRADE_NETWORK_FAILED",
|
|
33993
34360
|
"UPGRADE_INTEGRITY_FAILED",
|
|
33994
34361
|
"UPGRADE_SWAP_FAILED",
|
|
33995
|
-
"UPGRADE_RESTART_FAILED",
|
|
33996
34362
|
"UPGRADE_NO_TARGET",
|
|
33997
34363
|
"UPGRADE_ALREADY_RUNNING"
|
|
33998
34364
|
];
|
|
@@ -34345,6 +34711,31 @@ program2.command("upgrade").description(
|
|
|
34345
34711
|
async (opts) => {
|
|
34346
34712
|
const slockHome = resolveSlockHome();
|
|
34347
34713
|
const trigger = resolveUpgradeTrigger(process.env.SLOCK_UPGRADE_TRIGGER);
|
|
34714
|
+
if (!opts.dryRun && !opts.channel) {
|
|
34715
|
+
let client = null;
|
|
34716
|
+
try {
|
|
34717
|
+
client = await connectService(slockHome);
|
|
34718
|
+
} catch {
|
|
34719
|
+
client = null;
|
|
34720
|
+
}
|
|
34721
|
+
if (client) {
|
|
34722
|
+
try {
|
|
34723
|
+
const r = await client.request("upgrade-start", {
|
|
34724
|
+
targetVersion: opts.targetVersion
|
|
34725
|
+
});
|
|
34726
|
+
if (r.status === "already-running") {
|
|
34727
|
+
info(`An upgrade to ${r.targetVersion} is already running (id ${r.upgradeId}).`);
|
|
34728
|
+
} else {
|
|
34729
|
+
info(
|
|
34730
|
+
`Upgrade to ${r.targetVersion} started (id ${r.upgradeId}). The service downloads, verifies, swaps the binary, and restarts on it; run \`slock-computer status\` to confirm.`
|
|
34731
|
+
);
|
|
34732
|
+
}
|
|
34733
|
+
} finally {
|
|
34734
|
+
await client.close();
|
|
34735
|
+
}
|
|
34736
|
+
return;
|
|
34737
|
+
}
|
|
34738
|
+
}
|
|
34348
34739
|
try {
|
|
34349
34740
|
await withMutationLock(
|
|
34350
34741
|
() => runUpgradeCli(slockHome, {
|