@slock-ai/computer 0.0.13-alpha.0 → 0.0.14
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 +1053 -539
- package/dist/lib/index.d.ts +250 -0
- package/dist/lib/index.js +107 -0
- package/package.json +9 -2
package/dist/index.js
CHANGED
|
@@ -6917,13 +6917,13 @@ var require_infra = __commonJS({
|
|
|
6917
6917
|
}
|
|
6918
6918
|
function collectASequenceOfCodePointsFast(char, input, position) {
|
|
6919
6919
|
const idx = input.indexOf(char, position.position);
|
|
6920
|
-
const
|
|
6920
|
+
const start2 = position.position;
|
|
6921
6921
|
if (idx === -1) {
|
|
6922
6922
|
position.position = input.length;
|
|
6923
|
-
return input.slice(
|
|
6923
|
+
return input.slice(start2);
|
|
6924
6924
|
}
|
|
6925
6925
|
position.position = idx;
|
|
6926
|
-
return input.slice(
|
|
6926
|
+
return input.slice(start2, position.position);
|
|
6927
6927
|
}
|
|
6928
6928
|
var ASCII_WHITESPACE_REPLACE_REGEX = /[\u0009\u000A\u000C\u000D\u0020]/g;
|
|
6929
6929
|
function forgivingBase64(data) {
|
|
@@ -9193,11 +9193,11 @@ var require_formdata_parser = __commonJS({
|
|
|
9193
9193
|
}
|
|
9194
9194
|
}
|
|
9195
9195
|
function collectASequenceOfBytes(condition, input, position) {
|
|
9196
|
-
let
|
|
9197
|
-
while (
|
|
9198
|
-
++
|
|
9196
|
+
let start2 = position.position;
|
|
9197
|
+
while (start2 < input.length && condition(input[start2])) {
|
|
9198
|
+
++start2;
|
|
9199
9199
|
}
|
|
9200
|
-
return input.subarray(position.position, position.position =
|
|
9200
|
+
return input.subarray(position.position, position.position = start2);
|
|
9201
9201
|
}
|
|
9202
9202
|
function removeChars(buf, leading, trailing, predicate) {
|
|
9203
9203
|
let lead = 0;
|
|
@@ -9210,12 +9210,12 @@ var require_formdata_parser = __commonJS({
|
|
|
9210
9210
|
}
|
|
9211
9211
|
return lead === 0 && trail === buf.length - 1 ? buf : buf.subarray(lead, trail + 1);
|
|
9212
9212
|
}
|
|
9213
|
-
function bufferStartsWith(buffer,
|
|
9214
|
-
if (buffer.length <
|
|
9213
|
+
function bufferStartsWith(buffer, start2, position) {
|
|
9214
|
+
if (buffer.length < start2.length) {
|
|
9215
9215
|
return false;
|
|
9216
9216
|
}
|
|
9217
|
-
for (let i = 0; i <
|
|
9218
|
-
if (
|
|
9217
|
+
for (let i = 0; i < start2.length; i++) {
|
|
9218
|
+
if (start2[i] !== buffer[position.position + i]) {
|
|
9219
9219
|
return false;
|
|
9220
9220
|
}
|
|
9221
9221
|
}
|
|
@@ -9657,8 +9657,8 @@ var require_client_h1 = __commonJS({
|
|
|
9657
9657
|
*/
|
|
9658
9658
|
wasm_on_status: (p, at, len) => {
|
|
9659
9659
|
assert(currentParser.ptr === p);
|
|
9660
|
-
const
|
|
9661
|
-
return currentParser.onStatus(new FastBuffer(currentBufferRef.buffer,
|
|
9660
|
+
const start2 = at - currentBufferPtr + currentBufferRef.byteOffset;
|
|
9661
|
+
return currentParser.onStatus(new FastBuffer(currentBufferRef.buffer, start2, len));
|
|
9662
9662
|
},
|
|
9663
9663
|
/**
|
|
9664
9664
|
* @param {number} p
|
|
@@ -9676,8 +9676,8 @@ var require_client_h1 = __commonJS({
|
|
|
9676
9676
|
*/
|
|
9677
9677
|
wasm_on_header_field: (p, at, len) => {
|
|
9678
9678
|
assert(currentParser.ptr === p);
|
|
9679
|
-
const
|
|
9680
|
-
return currentParser.onHeaderField(new FastBuffer(currentBufferRef.buffer,
|
|
9679
|
+
const start2 = at - currentBufferPtr + currentBufferRef.byteOffset;
|
|
9680
|
+
return currentParser.onHeaderField(new FastBuffer(currentBufferRef.buffer, start2, len));
|
|
9681
9681
|
},
|
|
9682
9682
|
/**
|
|
9683
9683
|
* @param {number} p
|
|
@@ -9687,8 +9687,8 @@ var require_client_h1 = __commonJS({
|
|
|
9687
9687
|
*/
|
|
9688
9688
|
wasm_on_header_value: (p, at, len) => {
|
|
9689
9689
|
assert(currentParser.ptr === p);
|
|
9690
|
-
const
|
|
9691
|
-
return currentParser.onHeaderValue(new FastBuffer(currentBufferRef.buffer,
|
|
9690
|
+
const start2 = at - currentBufferPtr + currentBufferRef.byteOffset;
|
|
9691
|
+
return currentParser.onHeaderValue(new FastBuffer(currentBufferRef.buffer, start2, len));
|
|
9692
9692
|
},
|
|
9693
9693
|
/**
|
|
9694
9694
|
* @param {number} p
|
|
@@ -9709,8 +9709,8 @@ var require_client_h1 = __commonJS({
|
|
|
9709
9709
|
*/
|
|
9710
9710
|
wasm_on_body: (p, at, len) => {
|
|
9711
9711
|
assert(currentParser.ptr === p);
|
|
9712
|
-
const
|
|
9713
|
-
return currentParser.onBody(new FastBuffer(currentBufferRef.buffer,
|
|
9712
|
+
const start2 = at - currentBufferPtr + currentBufferRef.byteOffset;
|
|
9713
|
+
return currentParser.onBody(new FastBuffer(currentBufferRef.buffer, start2, len));
|
|
9714
9714
|
},
|
|
9715
9715
|
/**
|
|
9716
9716
|
* @param {number} p
|
|
@@ -13962,8 +13962,8 @@ var require_retry_handler = __commonJS({
|
|
|
13962
13962
|
data: { count: this.retryCount }
|
|
13963
13963
|
});
|
|
13964
13964
|
}
|
|
13965
|
-
const { start, size, end = size ? size - 1 : null } = contentRange;
|
|
13966
|
-
assert(this.start ===
|
|
13965
|
+
const { start: start2, size, end = size ? size - 1 : null } = contentRange;
|
|
13966
|
+
assert(this.start === start2, "content-range mismatch");
|
|
13967
13967
|
assert(this.end == null || this.end === end, "content-range mismatch");
|
|
13968
13968
|
return;
|
|
13969
13969
|
}
|
|
@@ -13980,13 +13980,13 @@ var require_retry_handler = __commonJS({
|
|
|
13980
13980
|
);
|
|
13981
13981
|
return;
|
|
13982
13982
|
}
|
|
13983
|
-
const { start, size, end = size ? size - 1 : null } = range;
|
|
13983
|
+
const { start: start2, size, end = size ? size - 1 : null } = range;
|
|
13984
13984
|
assert(
|
|
13985
|
-
|
|
13985
|
+
start2 != null && Number.isFinite(start2),
|
|
13986
13986
|
"content-range mismatch"
|
|
13987
13987
|
);
|
|
13988
13988
|
assert(end != null && Number.isFinite(end), "invalid content-length");
|
|
13989
|
-
this.start =
|
|
13989
|
+
this.start = start2;
|
|
13990
13990
|
this.end = end;
|
|
13991
13991
|
}
|
|
13992
13992
|
if (this.end == null) {
|
|
@@ -14473,9 +14473,9 @@ var require_readable = __commonJS({
|
|
|
14473
14473
|
}
|
|
14474
14474
|
const { _readableState: state } = consume2.stream;
|
|
14475
14475
|
if (state.bufferIndex) {
|
|
14476
|
-
const
|
|
14476
|
+
const start2 = state.bufferIndex;
|
|
14477
14477
|
const end = state.buffer.length;
|
|
14478
|
-
for (let n =
|
|
14478
|
+
for (let n = start2; n < end; n++) {
|
|
14479
14479
|
consumePush(consume2, state.buffer[n]);
|
|
14480
14480
|
}
|
|
14481
14481
|
} else {
|
|
@@ -14500,11 +14500,11 @@ var require_readable = __commonJS({
|
|
|
14500
14500
|
}
|
|
14501
14501
|
const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks, length);
|
|
14502
14502
|
const bufferLength = buffer.length;
|
|
14503
|
-
const
|
|
14503
|
+
const start2 = bufferLength > 2 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191 ? 3 : 0;
|
|
14504
14504
|
if (!encoding || encoding === "utf8" || encoding === "utf-8") {
|
|
14505
|
-
return buffer.utf8Slice(
|
|
14505
|
+
return buffer.utf8Slice(start2, bufferLength);
|
|
14506
14506
|
} else {
|
|
14507
|
-
return buffer.subarray(
|
|
14507
|
+
return buffer.subarray(start2, bufferLength).toString(encoding);
|
|
14508
14508
|
}
|
|
14509
14509
|
}
|
|
14510
14510
|
function chunksConcat(chunks, length) {
|
|
@@ -16617,8 +16617,8 @@ var require_snapshot_recorder = __commonJS({
|
|
|
16617
16617
|
"../../node_modules/.pnpm/undici@7.24.8/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
16618
16618
|
"use strict";
|
|
16619
16619
|
init_esm_shims();
|
|
16620
|
-
var { writeFile:
|
|
16621
|
-
var { dirname:
|
|
16620
|
+
var { writeFile: writeFile12, readFile: readFile16, mkdir: mkdir16 } = __require("fs/promises");
|
|
16621
|
+
var { dirname: dirname13, resolve } = __require("path");
|
|
16622
16622
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
|
|
16623
16623
|
var { InvalidArgumentError: InvalidArgumentError2, UndiciError } = require_errors();
|
|
16624
16624
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -16819,7 +16819,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
16819
16819
|
throw new InvalidArgumentError2("Snapshot path is required");
|
|
16820
16820
|
}
|
|
16821
16821
|
try {
|
|
16822
|
-
const data = await
|
|
16822
|
+
const data = await readFile16(resolve(path3), "utf8");
|
|
16823
16823
|
const parsed = JSON.parse(data);
|
|
16824
16824
|
if (Array.isArray(parsed)) {
|
|
16825
16825
|
this.#snapshots.clear();
|
|
@@ -16849,12 +16849,12 @@ var require_snapshot_recorder = __commonJS({
|
|
|
16849
16849
|
throw new InvalidArgumentError2("Snapshot path is required");
|
|
16850
16850
|
}
|
|
16851
16851
|
const resolvedPath = resolve(path3);
|
|
16852
|
-
await
|
|
16852
|
+
await mkdir16(dirname13(resolvedPath), { recursive: true });
|
|
16853
16853
|
const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
|
|
16854
16854
|
hash,
|
|
16855
16855
|
snapshot
|
|
16856
16856
|
}));
|
|
16857
|
-
await
|
|
16857
|
+
await writeFile12(resolvedPath, JSON.stringify(data, null, 2), { flush: true });
|
|
16858
16858
|
}
|
|
16859
16859
|
/**
|
|
16860
16860
|
* Clears all recorded snapshots
|
|
@@ -22862,7 +22862,7 @@ var require_fetch = __commonJS({
|
|
|
22862
22862
|
function handleFetchDone(response) {
|
|
22863
22863
|
finalizeAndReportTiming(response, "fetch");
|
|
22864
22864
|
}
|
|
22865
|
-
function
|
|
22865
|
+
function fetch5(input, init = void 0) {
|
|
22866
22866
|
webidl.argumentLengthCheck(arguments, 1, "globalThis.fetch");
|
|
22867
22867
|
let p = createDeferredPromise();
|
|
22868
22868
|
let requestObject;
|
|
@@ -23892,7 +23892,7 @@ var require_fetch = __commonJS({
|
|
|
23892
23892
|
}
|
|
23893
23893
|
}
|
|
23894
23894
|
module.exports = {
|
|
23895
|
-
fetch:
|
|
23895
|
+
fetch: fetch5,
|
|
23896
23896
|
Fetch,
|
|
23897
23897
|
fetching,
|
|
23898
23898
|
finalizeAndReportTiming
|
|
@@ -27902,7 +27902,7 @@ var require_undici = __commonJS({
|
|
|
27902
27902
|
err.stack = stack ? `${stack}
|
|
27903
27903
|
${captureLines}` : capture.stack;
|
|
27904
27904
|
}
|
|
27905
|
-
module.exports.fetch = function
|
|
27905
|
+
module.exports.fetch = function fetch5(init, options = void 0) {
|
|
27906
27906
|
return fetchImpl(init, options).catch((err) => {
|
|
27907
27907
|
if (currentFilename) {
|
|
27908
27908
|
appendFetchStackTrace(err, currentFilename);
|
|
@@ -28040,10 +28040,10 @@ var require_polyfills = __commonJS({
|
|
|
28040
28040
|
if (platform === "win32") {
|
|
28041
28041
|
fs.rename = typeof fs.rename !== "function" ? fs.rename : (function(fs$rename) {
|
|
28042
28042
|
function rename5(from, to, cb) {
|
|
28043
|
-
var
|
|
28043
|
+
var start2 = Date.now();
|
|
28044
28044
|
var backoff = 0;
|
|
28045
28045
|
fs$rename(from, to, function CB(er) {
|
|
28046
|
-
if (er && (er.code === "EACCES" || er.code === "EPERM" || er.code === "EBUSY") && Date.now() -
|
|
28046
|
+
if (er && (er.code === "EACCES" || er.code === "EPERM" || er.code === "EBUSY") && Date.now() - start2 < 6e4) {
|
|
28047
28047
|
setTimeout(function() {
|
|
28048
28048
|
fs.stat(to, function(stater, st) {
|
|
28049
28049
|
if (stater && stater.code === "ENOENT")
|
|
@@ -28469,8 +28469,8 @@ var require_graceful_fs = __commonJS({
|
|
|
28469
28469
|
fs2.createReadStream = createReadStream;
|
|
28470
28470
|
fs2.createWriteStream = createWriteStream;
|
|
28471
28471
|
var fs$readFile = fs2.readFile;
|
|
28472
|
-
fs2.readFile =
|
|
28473
|
-
function
|
|
28472
|
+
fs2.readFile = readFile16;
|
|
28473
|
+
function readFile16(path3, options, cb) {
|
|
28474
28474
|
if (typeof options === "function")
|
|
28475
28475
|
cb = options, options = null;
|
|
28476
28476
|
return go$readFile(path3, options, cb);
|
|
@@ -28486,8 +28486,8 @@ var require_graceful_fs = __commonJS({
|
|
|
28486
28486
|
}
|
|
28487
28487
|
}
|
|
28488
28488
|
var fs$writeFile = fs2.writeFile;
|
|
28489
|
-
fs2.writeFile =
|
|
28490
|
-
function
|
|
28489
|
+
fs2.writeFile = writeFile12;
|
|
28490
|
+
function writeFile12(path3, data, options, cb) {
|
|
28491
28491
|
if (typeof options === "function")
|
|
28492
28492
|
cb = options, options = null;
|
|
28493
28493
|
return go$writeFile(path3, data, options, cb);
|
|
@@ -29094,7 +29094,7 @@ var require_signal_exit = __commonJS({
|
|
|
29094
29094
|
emitter.count -= 1;
|
|
29095
29095
|
};
|
|
29096
29096
|
module.exports.unload = unload;
|
|
29097
|
-
|
|
29097
|
+
emit7 = function emit8(event, code, signal) {
|
|
29098
29098
|
if (emitter.emitted[event]) {
|
|
29099
29099
|
return;
|
|
29100
29100
|
}
|
|
@@ -29110,8 +29110,8 @@ var require_signal_exit = __commonJS({
|
|
|
29110
29110
|
var listeners = process2.listeners(sig);
|
|
29111
29111
|
if (listeners.length === emitter.count) {
|
|
29112
29112
|
unload();
|
|
29113
|
-
|
|
29114
|
-
|
|
29113
|
+
emit7("exit", null, sig);
|
|
29114
|
+
emit7("afterexit", null, sig);
|
|
29115
29115
|
if (isWin && sig === "SIGHUP") {
|
|
29116
29116
|
sig = "SIGINT";
|
|
29117
29117
|
}
|
|
@@ -29148,8 +29148,8 @@ var require_signal_exit = __commonJS({
|
|
|
29148
29148
|
}
|
|
29149
29149
|
process2.exitCode = code || /* istanbul ignore next */
|
|
29150
29150
|
0;
|
|
29151
|
-
|
|
29152
|
-
|
|
29151
|
+
emit7("exit", process2.exitCode, null);
|
|
29152
|
+
emit7("afterexit", process2.exitCode, null);
|
|
29153
29153
|
originalProcessReallyExit.call(process2, process2.exitCode);
|
|
29154
29154
|
};
|
|
29155
29155
|
originalProcessEmit = process2.emit;
|
|
@@ -29159,8 +29159,8 @@ var require_signal_exit = __commonJS({
|
|
|
29159
29159
|
process2.exitCode = arg;
|
|
29160
29160
|
}
|
|
29161
29161
|
var ret = originalProcessEmit.apply(this, arguments);
|
|
29162
|
-
|
|
29163
|
-
|
|
29162
|
+
emit7("exit", process2.exitCode, null);
|
|
29163
|
+
emit7("afterexit", process2.exitCode, null);
|
|
29164
29164
|
return ret;
|
|
29165
29165
|
} else {
|
|
29166
29166
|
return originalProcessEmit.apply(this, arguments);
|
|
@@ -29173,7 +29173,7 @@ var require_signal_exit = __commonJS({
|
|
|
29173
29173
|
var EE;
|
|
29174
29174
|
var emitter;
|
|
29175
29175
|
var unload;
|
|
29176
|
-
var
|
|
29176
|
+
var emit7;
|
|
29177
29177
|
var sigListeners;
|
|
29178
29178
|
var loaded;
|
|
29179
29179
|
var load;
|
|
@@ -29591,6 +29591,48 @@ var {
|
|
|
29591
29591
|
|
|
29592
29592
|
// src/login.ts
|
|
29593
29593
|
init_esm_shims();
|
|
29594
|
+
|
|
29595
|
+
// src/output.ts
|
|
29596
|
+
init_esm_shims();
|
|
29597
|
+
var CliExit = class extends Error {
|
|
29598
|
+
/**
|
|
29599
|
+
* v8.3.3 PR-2c — carry the closed-set / stderr token through the thrown
|
|
29600
|
+
* error so callers can pattern-match without parsing stderr. Optional
|
|
29601
|
+
* because some callsites throw `new CliExit(code)` directly without a
|
|
29602
|
+
* named token (e.g. the harness EX_CONFIG paths).
|
|
29603
|
+
*/
|
|
29604
|
+
constructor(exitCode, code) {
|
|
29605
|
+
super(`CliExit(${exitCode}${code ? ` ${code}` : ""})`);
|
|
29606
|
+
this.exitCode = exitCode;
|
|
29607
|
+
this.code = code;
|
|
29608
|
+
this.name = "CliExit";
|
|
29609
|
+
}
|
|
29610
|
+
};
|
|
29611
|
+
function info(line) {
|
|
29612
|
+
process.stdout.write(`${line}
|
|
29613
|
+
`);
|
|
29614
|
+
}
|
|
29615
|
+
function fail(code, message, exitCode = 1) {
|
|
29616
|
+
process.stderr.write(`${JSON.stringify({ ok: false, code, message })}
|
|
29617
|
+
`);
|
|
29618
|
+
throw new CliExit(exitCode, code);
|
|
29619
|
+
}
|
|
29620
|
+
|
|
29621
|
+
// src/services/errors.ts
|
|
29622
|
+
init_esm_shims();
|
|
29623
|
+
var ComputerServiceError = class extends Error {
|
|
29624
|
+
code;
|
|
29625
|
+
cause;
|
|
29626
|
+
constructor(code, message, cause) {
|
|
29627
|
+
super(message);
|
|
29628
|
+
this.name = "ComputerServiceError";
|
|
29629
|
+
this.code = code;
|
|
29630
|
+
if (cause !== void 0) this.cause = cause;
|
|
29631
|
+
}
|
|
29632
|
+
};
|
|
29633
|
+
|
|
29634
|
+
// src/services/login.ts
|
|
29635
|
+
init_esm_shims();
|
|
29594
29636
|
import { mkdir, writeFile } from "fs/promises";
|
|
29595
29637
|
import { dirname } from "path";
|
|
29596
29638
|
|
|
@@ -29901,32 +29943,6 @@ function formatUpgradeLogTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
|
29901
29943
|
return iso.replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
29902
29944
|
}
|
|
29903
29945
|
|
|
29904
|
-
// src/output.ts
|
|
29905
|
-
init_esm_shims();
|
|
29906
|
-
var CliExit = class extends Error {
|
|
29907
|
-
/**
|
|
29908
|
-
* v8.3.3 PR-2c — carry the closed-set / stderr token through the thrown
|
|
29909
|
-
* error so callers can pattern-match without parsing stderr. Optional
|
|
29910
|
-
* because some callsites throw `new CliExit(code)` directly without a
|
|
29911
|
-
* named token (e.g. the harness EX_CONFIG paths).
|
|
29912
|
-
*/
|
|
29913
|
-
constructor(exitCode, code) {
|
|
29914
|
-
super(`CliExit(${exitCode}${code ? ` ${code}` : ""})`);
|
|
29915
|
-
this.exitCode = exitCode;
|
|
29916
|
-
this.code = code;
|
|
29917
|
-
this.name = "CliExit";
|
|
29918
|
-
}
|
|
29919
|
-
};
|
|
29920
|
-
function info(line) {
|
|
29921
|
-
process.stdout.write(`${line}
|
|
29922
|
-
`);
|
|
29923
|
-
}
|
|
29924
|
-
function fail(code, message, exitCode = 1) {
|
|
29925
|
-
process.stderr.write(`${JSON.stringify({ ok: false, code, message })}
|
|
29926
|
-
`);
|
|
29927
|
-
throw new CliExit(exitCode, code);
|
|
29928
|
-
}
|
|
29929
|
-
|
|
29930
29946
|
// src/serverUrl.ts
|
|
29931
29947
|
init_esm_shims();
|
|
29932
29948
|
var DEFAULT_SLOCK_SERVER_URL = "https://api.slock.ai";
|
|
@@ -29938,62 +29954,146 @@ function resolveServerUrl(...candidates) {
|
|
|
29938
29954
|
return DEFAULT_SLOCK_SERVER_URL;
|
|
29939
29955
|
}
|
|
29940
29956
|
|
|
29941
|
-
// src/login.ts
|
|
29942
|
-
function sleep(ms) {
|
|
29943
|
-
return new Promise((
|
|
29957
|
+
// src/services/login.ts
|
|
29958
|
+
function sleep(ms, signal) {
|
|
29959
|
+
return new Promise((resolve, reject) => {
|
|
29960
|
+
if (signal?.aborted) {
|
|
29961
|
+
reject(abortError(signal));
|
|
29962
|
+
return;
|
|
29963
|
+
}
|
|
29964
|
+
const t = setTimeout(() => {
|
|
29965
|
+
signal?.removeEventListener("abort", onAbort);
|
|
29966
|
+
resolve();
|
|
29967
|
+
}, ms);
|
|
29968
|
+
const onAbort = () => {
|
|
29969
|
+
clearTimeout(t);
|
|
29970
|
+
reject(abortError(signal));
|
|
29971
|
+
};
|
|
29972
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
29973
|
+
});
|
|
29944
29974
|
}
|
|
29945
|
-
|
|
29946
|
-
const
|
|
29975
|
+
function abortError(signal) {
|
|
29976
|
+
const reason = signal.reason;
|
|
29977
|
+
if (reason instanceof Error) return reason;
|
|
29978
|
+
const err = new Error("aborted");
|
|
29979
|
+
err.name = "AbortError";
|
|
29980
|
+
return err;
|
|
29981
|
+
}
|
|
29982
|
+
function emit(opts, event) {
|
|
29983
|
+
const cb = opts?.onEvent;
|
|
29984
|
+
if (!cb) return;
|
|
29985
|
+
try {
|
|
29986
|
+
cb(event);
|
|
29987
|
+
} catch {
|
|
29988
|
+
}
|
|
29989
|
+
}
|
|
29990
|
+
async function login(input, options = {}) {
|
|
29991
|
+
const baseUrl = resolveServerUrl(input.serverUrl, process.env.SLOCK_SERVER_URL);
|
|
29992
|
+
options.signal?.throwIfAborted?.();
|
|
29947
29993
|
const client = new DeviceAuthClient(baseUrl);
|
|
29948
29994
|
let grant;
|
|
29949
29995
|
try {
|
|
29950
29996
|
grant = await client.authorize("slock-computer");
|
|
29951
29997
|
} catch (err) {
|
|
29952
29998
|
const reason = err instanceof Error ? err.message : String(err);
|
|
29953
|
-
|
|
29999
|
+
throw new ComputerServiceError(
|
|
29954
30000
|
"DEVICE_AUTHORIZE_FAILED",
|
|
29955
|
-
`Could not start device login at ${baseUrl}: ${reason}. Check that the server URL is correct and reachable
|
|
30001
|
+
`Could not start device login at ${baseUrl}: ${reason}. Check that the server URL is correct and reachable.`,
|
|
30002
|
+
err
|
|
29956
30003
|
);
|
|
29957
30004
|
}
|
|
29958
30005
|
const verificationUri = grant.verificationUriComplete || grant.verificationUri;
|
|
29959
30006
|
const verifyUrl = new URL(verificationUri, baseUrl).toString();
|
|
29960
|
-
|
|
29961
|
-
|
|
29962
|
-
|
|
30007
|
+
const expiresAt = new Date(Date.now() + grant.expiresIn * 1e3).toISOString();
|
|
30008
|
+
emit(options, {
|
|
30009
|
+
type: "device-code",
|
|
30010
|
+
verifyUrl,
|
|
30011
|
+
userCode: grant.userCode,
|
|
30012
|
+
expiresAt,
|
|
30013
|
+
expiresInSeconds: grant.expiresIn
|
|
30014
|
+
});
|
|
29963
30015
|
const intervalMs = Math.max(1, grant.interval) * 1e3;
|
|
29964
30016
|
const deadline = Date.now() + grant.expiresIn * 1e3;
|
|
29965
30017
|
while (Date.now() < deadline) {
|
|
29966
|
-
|
|
30018
|
+
options.signal?.throwIfAborted?.();
|
|
30019
|
+
await sleep(intervalMs, options.signal);
|
|
30020
|
+
emit(options, { type: "polling" });
|
|
29967
30021
|
const r = await client.token(grant.deviceCode);
|
|
29968
30022
|
if (r.status === "pending") continue;
|
|
29969
|
-
if (r.status === "denied")
|
|
29970
|
-
|
|
29971
|
-
|
|
30023
|
+
if (r.status === "denied") {
|
|
30024
|
+
throw new ComputerServiceError("LOGIN_DENIED", "Login was denied in the approval page.");
|
|
30025
|
+
}
|
|
30026
|
+
if (r.status === "expired") {
|
|
30027
|
+
throw new ComputerServiceError(
|
|
30028
|
+
"LOGIN_EXPIRED",
|
|
30029
|
+
"Login request expired before approval. Re-run `slock-computer login`."
|
|
30030
|
+
);
|
|
30031
|
+
}
|
|
30032
|
+
if (r.status === "error") {
|
|
30033
|
+
throw new ComputerServiceError(
|
|
30034
|
+
"LOGIN_FAILED",
|
|
30035
|
+
`Login failed (${r.code}). Re-run \`slock-computer login\`.`
|
|
30036
|
+
);
|
|
30037
|
+
}
|
|
29972
30038
|
const slockHome = resolveSlockHome();
|
|
29973
30039
|
const file = userSessionPath(slockHome);
|
|
29974
30040
|
await mkdir(dirname(file), { recursive: true });
|
|
29975
30041
|
await writeFile(
|
|
29976
30042
|
file,
|
|
29977
30043
|
JSON.stringify(
|
|
29978
|
-
{
|
|
30044
|
+
{
|
|
30045
|
+
kind: "user-session",
|
|
30046
|
+
userId: r.userId,
|
|
30047
|
+
accessToken: r.accessToken,
|
|
30048
|
+
refreshToken: r.refreshToken,
|
|
30049
|
+
serverUrl: baseUrl,
|
|
30050
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30051
|
+
},
|
|
29979
30052
|
null,
|
|
29980
30053
|
2
|
|
29981
30054
|
),
|
|
29982
30055
|
{ mode: 384 }
|
|
29983
30056
|
);
|
|
29984
|
-
|
|
29985
|
-
|
|
29986
|
-
|
|
30057
|
+
emit(options, { type: "approved", userId: r.userId, sessionPath: file });
|
|
30058
|
+
return { userId: r.userId, sessionPath: file, serverUrl: baseUrl };
|
|
30059
|
+
}
|
|
30060
|
+
throw new ComputerServiceError(
|
|
30061
|
+
"LOGIN_EXPIRED",
|
|
30062
|
+
"Login request expired before approval. Re-run `slock-computer login`."
|
|
30063
|
+
);
|
|
30064
|
+
}
|
|
30065
|
+
|
|
30066
|
+
// src/login.ts
|
|
30067
|
+
async function runLogin(opts) {
|
|
30068
|
+
try {
|
|
30069
|
+
await login(
|
|
30070
|
+
{ serverUrl: opts.serverUrl },
|
|
30071
|
+
{
|
|
30072
|
+
onEvent: (event) => {
|
|
30073
|
+
if (event.type === "device-code") {
|
|
30074
|
+
info(`To finish login, open: ${event.verifyUrl}`);
|
|
30075
|
+
info(`and enter the code: ${event.userCode}`);
|
|
30076
|
+
info(`Waiting for approval (expires in ${event.expiresInSeconds}s)\u2026`);
|
|
30077
|
+
} else if (event.type === "approved") {
|
|
30078
|
+
info(`Logged in. User session written to ${event.sessionPath}`);
|
|
30079
|
+
if (!opts.orchestrated) {
|
|
30080
|
+
info(`Next: run \`slock-computer attach <serverSlug>\` to attach this machine.`);
|
|
30081
|
+
}
|
|
30082
|
+
}
|
|
30083
|
+
}
|
|
30084
|
+
}
|
|
30085
|
+
);
|
|
30086
|
+
} catch (err) {
|
|
30087
|
+
if (err instanceof CliExit) throw err;
|
|
30088
|
+
if (err instanceof ComputerServiceError) {
|
|
30089
|
+
fail(err.code, err.message);
|
|
29987
30090
|
}
|
|
29988
|
-
|
|
30091
|
+
throw err;
|
|
29989
30092
|
}
|
|
29990
|
-
fail("LOGIN_EXPIRED", "Login request expired before approval. Re-run `slock-computer login`.");
|
|
29991
30093
|
}
|
|
29992
30094
|
|
|
29993
30095
|
// src/attach.ts
|
|
29994
30096
|
init_esm_shims();
|
|
29995
|
-
import { chmod as chmod2, mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
29996
|
-
import { dirname as dirname3 } from "path";
|
|
29997
30097
|
|
|
29998
30098
|
// src/serverState.ts
|
|
29999
30099
|
init_esm_shims();
|
|
@@ -30102,77 +30202,95 @@ async function listManagedServerIds(slockHome) {
|
|
|
30102
30202
|
return out;
|
|
30103
30203
|
}
|
|
30104
30204
|
|
|
30105
|
-
// src/attach.ts
|
|
30106
|
-
|
|
30205
|
+
// src/services/attach.ts
|
|
30206
|
+
init_esm_shims();
|
|
30207
|
+
import { chmod as chmod2, mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
30208
|
+
import { dirname as dirname3 } from "path";
|
|
30209
|
+
function emit2(opts, event) {
|
|
30210
|
+
const cb = opts?.onEvent;
|
|
30211
|
+
if (!cb) return;
|
|
30212
|
+
try {
|
|
30213
|
+
cb(event);
|
|
30214
|
+
} catch {
|
|
30215
|
+
}
|
|
30216
|
+
}
|
|
30217
|
+
async function attach(input, options = {}) {
|
|
30218
|
+
options.signal?.throwIfAborted?.();
|
|
30107
30219
|
const slockHome = resolveSlockHome();
|
|
30108
30220
|
const sessionFile = userSessionPath(slockHome);
|
|
30109
30221
|
let session;
|
|
30110
30222
|
try {
|
|
30111
30223
|
session = JSON.parse(await readFile2(sessionFile, "utf8"));
|
|
30112
|
-
} catch {
|
|
30113
|
-
|
|
30224
|
+
} catch (err) {
|
|
30225
|
+
throw new ComputerServiceError(
|
|
30114
30226
|
"NO_USER_SESSION",
|
|
30115
|
-
`No user session at ${sessionFile}. Run \`slock-computer login\` first
|
|
30227
|
+
`No user session at ${sessionFile}. Run \`slock-computer login\` first.`,
|
|
30228
|
+
err
|
|
30116
30229
|
);
|
|
30117
30230
|
}
|
|
30118
30231
|
if (session.kind !== "user-session" || typeof session.accessToken !== "string" || !session.accessToken) {
|
|
30119
|
-
|
|
30232
|
+
throw new ComputerServiceError(
|
|
30120
30233
|
"INVALID_USER_SESSION",
|
|
30121
30234
|
`User session at ${sessionFile} is invalid. Re-run \`slock-computer login\`.`
|
|
30122
30235
|
);
|
|
30123
30236
|
}
|
|
30124
|
-
const baseUrl = resolveServerUrl(
|
|
30125
|
-
const
|
|
30126
|
-
const slugForServer = normalizeServerSlug(opts.serverSlug);
|
|
30237
|
+
const baseUrl = resolveServerUrl(input.serverUrl, session.serverUrl, process.env.SLOCK_SERVER_URL);
|
|
30238
|
+
const slugForServer = normalizeServerSlug(input.serverSlug);
|
|
30127
30239
|
if (!slugForServer) {
|
|
30128
|
-
|
|
30240
|
+
throw new ComputerServiceError("ATTACH_NOT_AUTHORIZED", "Server slug must not be empty.");
|
|
30129
30241
|
}
|
|
30130
|
-
const computerName =
|
|
30131
|
-
|
|
30242
|
+
const computerName = input.name?.trim() || deriveDefaultComputerName();
|
|
30243
|
+
options.signal?.throwIfAborted?.();
|
|
30244
|
+
emit2(options, { type: "attaching", serverSlug: slugForServer });
|
|
30245
|
+
const client = new ComputerAttachClient(baseUrl, session.accessToken);
|
|
30132
30246
|
const attached = await client.attach(slugForServer, computerName);
|
|
30133
30247
|
if (attached.status === "disabled") {
|
|
30134
|
-
|
|
30248
|
+
throw new ComputerServiceError(
|
|
30135
30249
|
"ATTACH_DISABLED",
|
|
30136
30250
|
"Computer attach is not enabled on this server (ask an admin to unset SLOCK_DEVICE_LOGIN_ENABLED or set it back to a non-false value; this surface is on by default since PR-G \u2014 upgrade the server if you are on an older build)."
|
|
30137
30251
|
);
|
|
30138
30252
|
}
|
|
30139
30253
|
if (attached.status === "not_authorized") {
|
|
30140
|
-
|
|
30254
|
+
throw new ComputerServiceError(
|
|
30141
30255
|
"ATTACH_NOT_AUTHORIZED",
|
|
30142
30256
|
"Not authorized to attach to that server. Check the server slug and that you're a member."
|
|
30143
30257
|
);
|
|
30144
30258
|
}
|
|
30145
30259
|
if (attached.status === "server_not_found") {
|
|
30146
|
-
|
|
30260
|
+
throw new ComputerServiceError(
|
|
30147
30261
|
"ATTACH_SERVER_NOT_FOUND",
|
|
30148
30262
|
`Server ${formatServerSlugDisplay(slugForServer)} was not found on ${baseUrl}. Check the slug spelling and --server-url, then retry.`
|
|
30149
30263
|
);
|
|
30150
30264
|
}
|
|
30151
30265
|
if (attached.status === "error") {
|
|
30152
30266
|
if (attached.code === "session_invalid") {
|
|
30153
|
-
|
|
30267
|
+
throw new ComputerServiceError(
|
|
30154
30268
|
"USER_SESSION_EXPIRED",
|
|
30155
30269
|
"Your user session is no longer valid. Re-run `slock-computer login`."
|
|
30156
30270
|
);
|
|
30157
30271
|
}
|
|
30158
30272
|
if (attached.code === "request_failed") {
|
|
30159
|
-
|
|
30273
|
+
throw new ComputerServiceError(
|
|
30160
30274
|
"ATTACH_REQUEST_FAILED",
|
|
30161
30275
|
`Could not reach ${baseUrl} while attaching to ${formatServerSlugDisplay(slugForServer)}. Check --server-url / network connectivity, then retry.`
|
|
30162
30276
|
);
|
|
30163
30277
|
}
|
|
30164
30278
|
if (attached.code === "COMPUTER_NAME_COLLISION") {
|
|
30165
|
-
|
|
30279
|
+
throw new ComputerServiceError(
|
|
30166
30280
|
"COMPUTER_NAME_COLLISION",
|
|
30167
30281
|
`A Computer named ${JSON.stringify(computerName)} already exists on that server. Re-run with --name <name>.`
|
|
30168
30282
|
);
|
|
30169
30283
|
}
|
|
30170
|
-
|
|
30284
|
+
throw new ComputerServiceError(
|
|
30285
|
+
"ATTACH_FAILED",
|
|
30286
|
+
`Attach failed (${attached.code}). Re-run after checking --server-url / server version.`
|
|
30287
|
+
);
|
|
30171
30288
|
}
|
|
30172
|
-
|
|
30289
|
+
emit2(options, { type: "preflight", resumed: attached.resumed });
|
|
30290
|
+
options.signal?.throwIfAborted?.();
|
|
30173
30291
|
const pre = await client.preflight(attached.apiKey);
|
|
30174
30292
|
if (!pre.ok) {
|
|
30175
|
-
|
|
30293
|
+
throw new ComputerServiceError(
|
|
30176
30294
|
"PREFLIGHT_FAILED",
|
|
30177
30295
|
`Server preflight failed (${pre.code}). The server's Computer surface is not aligned; nothing was written locally. Upgrade the server or retry.`
|
|
30178
30296
|
);
|
|
@@ -30197,9 +30315,57 @@ async function runAttach(opts) {
|
|
|
30197
30315
|
{ mode: 384 }
|
|
30198
30316
|
);
|
|
30199
30317
|
await chmod2(file, 384);
|
|
30200
|
-
|
|
30201
|
-
|
|
30202
|
-
|
|
30318
|
+
const apiKeyRedactedPrefix = attached.apiKey.slice(0, 8);
|
|
30319
|
+
emit2(options, {
|
|
30320
|
+
type: "attached",
|
|
30321
|
+
serverId: attached.serverId,
|
|
30322
|
+
serverMachineId: attached.serverMachineId,
|
|
30323
|
+
serverSlug: attached.serverSlug,
|
|
30324
|
+
attachmentPath: file,
|
|
30325
|
+
resumed: attached.resumed,
|
|
30326
|
+
apiKeyRedactedPrefix
|
|
30327
|
+
});
|
|
30328
|
+
return {
|
|
30329
|
+
serverId: attached.serverId,
|
|
30330
|
+
serverMachineId: attached.serverMachineId,
|
|
30331
|
+
serverSlug: attached.serverSlug,
|
|
30332
|
+
serverUrl: baseUrl,
|
|
30333
|
+
attachmentPath: file,
|
|
30334
|
+
resumed: attached.resumed,
|
|
30335
|
+
apiKeyRedactedPrefix
|
|
30336
|
+
};
|
|
30337
|
+
}
|
|
30338
|
+
|
|
30339
|
+
// src/attach.ts
|
|
30340
|
+
async function runAttach(opts) {
|
|
30341
|
+
try {
|
|
30342
|
+
await attach(
|
|
30343
|
+
{
|
|
30344
|
+
serverSlug: opts.serverSlug,
|
|
30345
|
+
serverUrl: opts.serverUrl,
|
|
30346
|
+
name: opts.name
|
|
30347
|
+
},
|
|
30348
|
+
{
|
|
30349
|
+
onEvent: (event) => {
|
|
30350
|
+
if (event.type === "attaching") {
|
|
30351
|
+
info(`Attaching this machine to server ${formatServerSlugDisplay(event.serverSlug)}\u2026`);
|
|
30352
|
+
} else if (event.type === "preflight") {
|
|
30353
|
+
info(event.resumed ? "Resumed existing attachment; running preflight\u2026" : "Attachment issued; running preflight\u2026");
|
|
30354
|
+
} else if (event.type === "attached") {
|
|
30355
|
+
info(`Attached. Computer state written to ${event.attachmentPath}`);
|
|
30356
|
+
info(` server: ${formatServerSlugDisplay(event.serverSlug)}`);
|
|
30357
|
+
info(` serverMachine: ${event.serverMachineId}`);
|
|
30358
|
+
}
|
|
30359
|
+
}
|
|
30360
|
+
}
|
|
30361
|
+
);
|
|
30362
|
+
} catch (err) {
|
|
30363
|
+
if (err instanceof CliExit) throw err;
|
|
30364
|
+
if (err instanceof ComputerServiceError) {
|
|
30365
|
+
fail(err.code, err.message);
|
|
30366
|
+
}
|
|
30367
|
+
throw err;
|
|
30368
|
+
}
|
|
30203
30369
|
if (opts.orchestrated) {
|
|
30204
30370
|
return;
|
|
30205
30371
|
}
|
|
@@ -30212,77 +30378,21 @@ async function runAttach(opts) {
|
|
|
30212
30378
|
|
|
30213
30379
|
// src/adopt.ts
|
|
30214
30380
|
init_esm_shims();
|
|
30381
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
30382
|
+
|
|
30383
|
+
// src/services/adoptLegacy.ts
|
|
30384
|
+
init_esm_shims();
|
|
30215
30385
|
import { chmod as chmod3, mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4, appendFile, stat } from "fs/promises";
|
|
30216
30386
|
import { createHash as createHash2 } from "crypto";
|
|
30217
30387
|
import { dirname as dirname4, join } from "path";
|
|
30218
30388
|
import { setTimeout as delay } from "timers/promises";
|
|
30219
|
-
|
|
30220
|
-
const
|
|
30221
|
-
if (
|
|
30222
|
-
|
|
30223
|
-
|
|
30224
|
-
|
|
30225
|
-
});
|
|
30226
|
-
}
|
|
30227
|
-
if (typeof inputs.legacyApiKeyFile === "string" && inputs.legacyApiKeyFile.length > 0) {
|
|
30228
|
-
sources.push({
|
|
30229
|
-
mode: "legacy_key_file",
|
|
30230
|
-
load: async () => {
|
|
30231
|
-
const contents = await readFile3(inputs.legacyApiKeyFile, "utf8");
|
|
30232
|
-
return contents.trim();
|
|
30233
|
-
}
|
|
30234
|
-
});
|
|
30235
|
-
}
|
|
30236
|
-
if (inputs.legacyApiKeyStdin) {
|
|
30237
|
-
sources.push({
|
|
30238
|
-
mode: "legacy_key_stdin",
|
|
30239
|
-
load: async () => readAllStdin()
|
|
30240
|
-
});
|
|
30241
|
-
}
|
|
30242
|
-
const envKey = env.SLOCK_LEGACY_API_KEY;
|
|
30243
|
-
if (typeof envKey === "string" && envKey.trim().length > 0) {
|
|
30244
|
-
sources.push({
|
|
30245
|
-
mode: "legacy_key_env",
|
|
30246
|
-
load: async () => envKey.trim()
|
|
30247
|
-
});
|
|
30248
|
-
}
|
|
30249
|
-
if (sources.length === 0) {
|
|
30250
|
-
fail(
|
|
30251
|
-
"LEGACY_KEY_REQUIRED",
|
|
30252
|
-
"No legacy api key provided. Pass exactly one of: --legacy-api-key <key>, --legacy-api-key-file <path>, --legacy-api-key-stdin, or SLOCK_LEGACY_API_KEY."
|
|
30253
|
-
);
|
|
30254
|
-
}
|
|
30255
|
-
if (sources.length > 1) {
|
|
30256
|
-
const modes = sources.map((s) => s.mode).join(", ");
|
|
30257
|
-
fail(
|
|
30258
|
-
"LEGACY_KEY_MULTIPLE_SOURCES",
|
|
30259
|
-
`Multiple legacy api key sources provided (${modes}). Choose exactly one.`
|
|
30260
|
-
);
|
|
30261
|
-
}
|
|
30262
|
-
const chosen = sources[0];
|
|
30263
|
-
const rawKey = await chosen.load();
|
|
30264
|
-
if (chosen.mode === "legacy_key_env") {
|
|
30265
|
-
delete env.SLOCK_LEGACY_API_KEY;
|
|
30266
|
-
}
|
|
30267
|
-
if (!rawKey.startsWith("sk_machine_") && !rawKey.startsWith("sk_daemon_")) {
|
|
30268
|
-
fail(
|
|
30269
|
-
"LEGACY_KEY_INVALID",
|
|
30270
|
-
"Provided key does not look like a legacy machine key (expected `sk_machine_*` or `sk_daemon_*`)."
|
|
30271
|
-
);
|
|
30389
|
+
function emit3(opts, event) {
|
|
30390
|
+
const cb = opts?.onEvent;
|
|
30391
|
+
if (!cb) return;
|
|
30392
|
+
try {
|
|
30393
|
+
cb(event);
|
|
30394
|
+
} catch {
|
|
30272
30395
|
}
|
|
30273
|
-
return {
|
|
30274
|
-
rawKey,
|
|
30275
|
-
mode: chosen.mode,
|
|
30276
|
-
redactedPrefix: rawKey.slice(0, 8)
|
|
30277
|
-
};
|
|
30278
|
-
}
|
|
30279
|
-
async function readAllStdin() {
|
|
30280
|
-
return new Promise((resolve, reject) => {
|
|
30281
|
-
const chunks = [];
|
|
30282
|
-
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
30283
|
-
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8").trim()));
|
|
30284
|
-
process.stdin.on("error", reject);
|
|
30285
|
-
});
|
|
30286
30396
|
}
|
|
30287
30397
|
var LEGACY_STOP_WAIT_MS = 1e4;
|
|
30288
30398
|
var LEGACY_STOP_POLL_MS = 200;
|
|
@@ -30371,162 +30481,164 @@ function formatLegacyOwnerEvidence(slockHome, evidence) {
|
|
|
30371
30481
|
if (evidence.reason) fields.push(`ownerStatus=${evidence.reason}`);
|
|
30372
30482
|
return fields.join(" ");
|
|
30373
30483
|
}
|
|
30374
|
-
async function
|
|
30484
|
+
async function adoptLegacy(input, options = {}) {
|
|
30485
|
+
options.signal?.throwIfAborted?.();
|
|
30375
30486
|
const slockHome = resolveSlockHome();
|
|
30376
30487
|
const sessionFile = userSessionPath(slockHome);
|
|
30377
30488
|
let session;
|
|
30378
30489
|
try {
|
|
30379
30490
|
session = JSON.parse(await readFile3(sessionFile, "utf8"));
|
|
30380
|
-
} catch {
|
|
30381
|
-
|
|
30491
|
+
} catch (err) {
|
|
30492
|
+
throw new ComputerServiceError(
|
|
30382
30493
|
"NO_USER_SESSION",
|
|
30383
|
-
`No user session at ${sessionFile}. Run \`slock-computer login\` first
|
|
30494
|
+
`No user session at ${sessionFile}. Run \`slock-computer login\` first.`,
|
|
30495
|
+
err
|
|
30384
30496
|
);
|
|
30385
30497
|
}
|
|
30386
30498
|
if (session.kind !== "user-session" || typeof session.accessToken !== "string" || !session.accessToken) {
|
|
30387
|
-
|
|
30499
|
+
throw new ComputerServiceError(
|
|
30388
30500
|
"INVALID_USER_SESSION",
|
|
30389
30501
|
`User session at ${sessionFile} is invalid. Re-run \`slock-computer login\`.`
|
|
30390
30502
|
);
|
|
30391
30503
|
}
|
|
30392
|
-
const baseUrl = resolveServerUrl(
|
|
30393
|
-
const slugForServer = normalizeServerSlug(
|
|
30504
|
+
const baseUrl = resolveServerUrl(input.serverUrl, session.serverUrl, process.env.SLOCK_SERVER_URL);
|
|
30505
|
+
const slugForServer = normalizeServerSlug(input.serverSlug);
|
|
30394
30506
|
if (!slugForServer) {
|
|
30395
|
-
|
|
30507
|
+
throw new ComputerServiceError("ADOPT_NOT_AUTHORIZED", "Server slug must not be empty.");
|
|
30396
30508
|
}
|
|
30397
|
-
|
|
30398
|
-
|
|
30399
|
-
`Adopting legacy daemon for ${formatServerSlugDisplay(slugForServer)} via ${legacy.mode}\u2026`
|
|
30400
|
-
);
|
|
30509
|
+
options.signal?.throwIfAborted?.();
|
|
30510
|
+
emit3(options, { type: "adopting", serverSlug: slugForServer, mode: input.mode });
|
|
30401
30511
|
const client = new ComputerAttachClient(baseUrl, session.accessToken);
|
|
30402
30512
|
const adoptStartedAt = /* @__PURE__ */ new Date();
|
|
30403
|
-
const legacyOwnerFile = legacyLockOwnerPath(slockHome,
|
|
30513
|
+
const legacyOwnerFile = legacyLockOwnerPath(slockHome, input.rawKey);
|
|
30404
30514
|
const ownerEvidence = await readLegacyOwnerEvidence(legacyOwnerFile);
|
|
30405
|
-
const result = await client.adoptLegacy(
|
|
30406
|
-
|
|
30515
|
+
const result = await client.adoptLegacy(input.rawKey, input.name);
|
|
30516
|
+
input.rawKey = void 0;
|
|
30407
30517
|
if (result.status === "disabled") {
|
|
30408
30518
|
await appendAdoptionLog(slockHome, {
|
|
30409
|
-
mode:
|
|
30410
|
-
redactedPrefix:
|
|
30519
|
+
mode: input.mode,
|
|
30520
|
+
redactedPrefix: input.redactedPrefix,
|
|
30411
30521
|
startedAt: adoptStartedAt,
|
|
30412
30522
|
outcome: "failed",
|
|
30413
30523
|
failureReason: "computer_adopt_disabled"
|
|
30414
30524
|
});
|
|
30415
|
-
|
|
30525
|
+
throw new ComputerServiceError(
|
|
30416
30526
|
"ADOPT_DISABLED",
|
|
30417
30527
|
"Computer legacy adoption is not enabled on this server. Upgrade the server or set SLOCK_DEVICE_LOGIN_ENABLED."
|
|
30418
30528
|
);
|
|
30419
30529
|
}
|
|
30420
30530
|
if (result.status === "legacy_key_invalid") {
|
|
30421
30531
|
await appendAdoptionLog(slockHome, {
|
|
30422
|
-
mode:
|
|
30423
|
-
redactedPrefix:
|
|
30532
|
+
mode: input.mode,
|
|
30533
|
+
redactedPrefix: input.redactedPrefix,
|
|
30424
30534
|
startedAt: adoptStartedAt,
|
|
30425
30535
|
outcome: "failed",
|
|
30426
30536
|
failureReason: "legacy_key_invalid"
|
|
30427
30537
|
});
|
|
30428
|
-
|
|
30538
|
+
throw new ComputerServiceError(
|
|
30429
30539
|
"LEGACY_KEY_INVALID",
|
|
30430
30540
|
`Server rejected the legacy api key (unknown / wrong server / malformed). Local legacy owner evidence for this key: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Verify the key came from this SLOCK_HOME and server, or use a fresh isolated SLOCK_HOME for a clean Computer setup.`
|
|
30431
30541
|
);
|
|
30432
30542
|
}
|
|
30433
30543
|
if (result.status === "legacy_machine_key_migrated") {
|
|
30434
30544
|
await appendAdoptionLog(slockHome, {
|
|
30435
|
-
mode:
|
|
30436
|
-
redactedPrefix:
|
|
30545
|
+
mode: input.mode,
|
|
30546
|
+
redactedPrefix: input.redactedPrefix,
|
|
30437
30547
|
startedAt: adoptStartedAt,
|
|
30438
30548
|
outcome: "failed",
|
|
30439
30549
|
failureReason: "legacy_machine_key_migrated"
|
|
30440
30550
|
});
|
|
30441
|
-
|
|
30551
|
+
throw new ComputerServiceError(
|
|
30442
30552
|
"LEGACY_MACHINE_KEY_MIGRATED",
|
|
30443
30553
|
`This machine has already been adopted; the legacy key is no longer accepted. Local legacy owner evidence for this key: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Use \`slock-computer attach\` to add another Computer attachment, or use a fresh isolated SLOCK_HOME for a clean setup.`
|
|
30444
30554
|
);
|
|
30445
30555
|
}
|
|
30446
30556
|
if (result.status === "auth_required") {
|
|
30447
30557
|
await appendAdoptionLog(slockHome, {
|
|
30448
|
-
mode:
|
|
30449
|
-
redactedPrefix:
|
|
30558
|
+
mode: input.mode,
|
|
30559
|
+
redactedPrefix: input.redactedPrefix,
|
|
30450
30560
|
startedAt: adoptStartedAt,
|
|
30451
30561
|
outcome: "failed",
|
|
30452
30562
|
failureReason: "auth_required"
|
|
30453
30563
|
});
|
|
30454
|
-
|
|
30564
|
+
throw new ComputerServiceError(
|
|
30455
30565
|
"ADOPT_AUTH_REQUIRED",
|
|
30456
30566
|
`Your Computer user session was rejected by the server before legacy adoption. Local legacy owner evidence for this key: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Re-run \`slock-computer login\` for this server (use the same \`--server-url\` if not on production), then retry the adopt command. No local Computer state was written.`
|
|
30457
30567
|
);
|
|
30458
30568
|
}
|
|
30459
30569
|
if (result.status === "not_authorized") {
|
|
30460
30570
|
await appendAdoptionLog(slockHome, {
|
|
30461
|
-
mode:
|
|
30462
|
-
redactedPrefix:
|
|
30571
|
+
mode: input.mode,
|
|
30572
|
+
redactedPrefix: input.redactedPrefix,
|
|
30463
30573
|
startedAt: adoptStartedAt,
|
|
30464
30574
|
outcome: "failed",
|
|
30465
30575
|
failureReason: "not_authorized"
|
|
30466
30576
|
});
|
|
30467
|
-
|
|
30577
|
+
throw new ComputerServiceError(
|
|
30468
30578
|
"ADOPT_NOT_AUTHORIZED",
|
|
30469
30579
|
"Not authorized to adopt this machine on this server. Check that you are a current member."
|
|
30470
30580
|
);
|
|
30471
30581
|
}
|
|
30472
30582
|
if (result.status === "unexpected_response") {
|
|
30473
30583
|
await appendAdoptionLog(slockHome, {
|
|
30474
|
-
mode:
|
|
30475
|
-
redactedPrefix:
|
|
30584
|
+
mode: input.mode,
|
|
30585
|
+
redactedPrefix: input.redactedPrefix,
|
|
30476
30586
|
startedAt: adoptStartedAt,
|
|
30477
30587
|
outcome: "failed",
|
|
30478
30588
|
failureReason: result.code ? `unexpected_response_${result.code}` : "unexpected_response_missing_code"
|
|
30479
30589
|
});
|
|
30480
30590
|
const responseDetail = result.code ? `status ${result.httpStatus}, code ${result.code}` : `status ${result.httpStatus}, missing error code`;
|
|
30481
30591
|
const authHint = result.httpStatus === 401 ? " If your server may be on an older release that does not emit `code: auth_required`, re-run `slock-computer login` first to refresh your user session, then retry. If the issue persists, report it as server contract drift." : "";
|
|
30482
|
-
|
|
30592
|
+
throw new ComputerServiceError(
|
|
30483
30593
|
"ADOPT_UNEXPECTED_RESPONSE",
|
|
30484
30594
|
`Server returned an unexpected legacy adoption response (${responseDetail}).${authHint} Local legacy owner evidence for this key: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Please report this with the command, server URL, and SLOCK_HOME; no local Computer state was written.`
|
|
30485
30595
|
);
|
|
30486
30596
|
}
|
|
30487
30597
|
if (result.status === "error") {
|
|
30488
30598
|
await appendAdoptionLog(slockHome, {
|
|
30489
|
-
mode:
|
|
30490
|
-
redactedPrefix:
|
|
30599
|
+
mode: input.mode,
|
|
30600
|
+
redactedPrefix: input.redactedPrefix,
|
|
30491
30601
|
startedAt: adoptStartedAt,
|
|
30492
30602
|
outcome: "failed",
|
|
30493
30603
|
failureReason: result.code
|
|
30494
30604
|
});
|
|
30495
|
-
|
|
30605
|
+
throw new ComputerServiceError(
|
|
30496
30606
|
"ADOPT_FAILED",
|
|
30497
30607
|
`Adoption failed at server exchange (${result.code}). Local legacy owner evidence for this key: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Confirm the user session is valid, the legacy key belongs to this server/SLOCK_HOME, and the server URL is correct. No local Computer state was written.`
|
|
30498
30608
|
);
|
|
30499
30609
|
}
|
|
30500
|
-
|
|
30610
|
+
emit3(options, { type: "preflight", resumed: result.resumed });
|
|
30611
|
+
options.signal?.throwIfAborted?.();
|
|
30501
30612
|
const pre = await client.preflight(result.apiKey);
|
|
30502
30613
|
if (!pre.ok) {
|
|
30503
30614
|
await appendAdoptionLog(slockHome, {
|
|
30504
|
-
mode:
|
|
30505
|
-
redactedPrefix:
|
|
30615
|
+
mode: input.mode,
|
|
30616
|
+
redactedPrefix: input.redactedPrefix,
|
|
30506
30617
|
startedAt: adoptStartedAt,
|
|
30507
30618
|
outcome: "failed",
|
|
30508
30619
|
failureReason: `preflight_${pre.code}`
|
|
30509
30620
|
});
|
|
30510
|
-
|
|
30621
|
+
throw new ComputerServiceError(
|
|
30511
30622
|
"PREFLIGHT_FAILED",
|
|
30512
30623
|
`Server preflight failed (${pre.code}); local state not written. Upgrade the server or retry.`
|
|
30513
30624
|
);
|
|
30514
30625
|
}
|
|
30515
|
-
|
|
30516
|
-
|
|
30626
|
+
options.signal?.throwIfAborted?.();
|
|
30627
|
+
const stop2 = await stopLegacyDaemonByOwnerFile(legacyOwnerFile);
|
|
30628
|
+
if (stop2.outcome === "timed_out" || stop2.outcome === "denied" || stop2.outcome === "error") {
|
|
30517
30629
|
await appendAdoptionLog(slockHome, {
|
|
30518
|
-
mode:
|
|
30519
|
-
redactedPrefix:
|
|
30630
|
+
mode: input.mode,
|
|
30631
|
+
redactedPrefix: input.redactedPrefix,
|
|
30520
30632
|
startedAt: adoptStartedAt,
|
|
30521
30633
|
outcome: "failed",
|
|
30522
|
-
failureReason: `legacy_stop_${
|
|
30634
|
+
failureReason: `legacy_stop_${stop2.outcome}`,
|
|
30523
30635
|
computerId: result.computerId,
|
|
30524
30636
|
machineId: result.machineId,
|
|
30525
30637
|
serverId: result.serverId,
|
|
30526
|
-
legacyStop:
|
|
30638
|
+
legacyStop: stop2
|
|
30527
30639
|
});
|
|
30528
|
-
const detail =
|
|
30529
|
-
|
|
30640
|
+
const detail = stop2.outcome === "timed_out" ? `pid ${stop2.pid} did not exit within ${LEGACY_STOP_WAIT_MS}ms` : stop2.outcome === "denied" ? `pid ${stop2.pid} cannot be stopped (permission denied)` : `stop attempt error (${stop2.reason ?? "unknown"})`;
|
|
30641
|
+
throw new ComputerServiceError(
|
|
30530
30642
|
"LEGACY_DAEMON_STOP_FAILED",
|
|
30531
30643
|
`Adoption succeeded server-side but the legacy daemon could not be stopped: ${detail}. Stop it manually and re-run \`slock-computer adopt-legacy\`, or run \`slock-computer attach\` after confirming the legacy process is gone. No local Computer state was written.`
|
|
30532
30644
|
);
|
|
@@ -30554,33 +30666,38 @@ async function runAdoptLegacy(inputs) {
|
|
|
30554
30666
|
);
|
|
30555
30667
|
await chmod3(file, 384);
|
|
30556
30668
|
await appendAdoptionLog(slockHome, {
|
|
30557
|
-
mode:
|
|
30558
|
-
redactedPrefix:
|
|
30669
|
+
mode: input.mode,
|
|
30670
|
+
redactedPrefix: input.redactedPrefix,
|
|
30559
30671
|
startedAt: adoptStartedAt,
|
|
30560
30672
|
outcome: "succeeded",
|
|
30561
30673
|
computerId: result.computerId,
|
|
30562
30674
|
machineId: result.machineId,
|
|
30563
30675
|
serverId: result.serverId,
|
|
30564
|
-
legacyStop:
|
|
30676
|
+
legacyStop: stop2
|
|
30565
30677
|
});
|
|
30566
|
-
|
|
30567
|
-
|
|
30568
|
-
|
|
30569
|
-
|
|
30570
|
-
|
|
30571
|
-
|
|
30572
|
-
|
|
30573
|
-
|
|
30574
|
-
|
|
30575
|
-
|
|
30576
|
-
|
|
30577
|
-
|
|
30578
|
-
|
|
30579
|
-
|
|
30580
|
-
|
|
30581
|
-
|
|
30582
|
-
|
|
30583
|
-
|
|
30678
|
+
const apiKeyRedactedPrefix = result.apiKey.slice(0, 8);
|
|
30679
|
+
emit3(options, {
|
|
30680
|
+
type: "adopted",
|
|
30681
|
+
serverId: result.serverId,
|
|
30682
|
+
serverMachineId: result.computerId,
|
|
30683
|
+
legacyMachineId: result.machineId,
|
|
30684
|
+
serverSlug: slugForServer,
|
|
30685
|
+
attachmentPath: file,
|
|
30686
|
+
resumed: result.resumed,
|
|
30687
|
+
apiKeyRedactedPrefix,
|
|
30688
|
+
legacyStop: stop2
|
|
30689
|
+
});
|
|
30690
|
+
return {
|
|
30691
|
+
serverId: result.serverId,
|
|
30692
|
+
serverMachineId: result.computerId,
|
|
30693
|
+
legacyMachineId: result.machineId,
|
|
30694
|
+
serverSlug: slugForServer,
|
|
30695
|
+
serverUrl: baseUrl,
|
|
30696
|
+
attachmentPath: file,
|
|
30697
|
+
resumed: result.resumed,
|
|
30698
|
+
apiKeyRedactedPrefix,
|
|
30699
|
+
legacyStop: stop2
|
|
30700
|
+
};
|
|
30584
30701
|
}
|
|
30585
30702
|
async function appendAdoptionLog(slockHome, line) {
|
|
30586
30703
|
try {
|
|
@@ -30616,22 +30733,178 @@ async function appendAdoptionLog(slockHome, line) {
|
|
|
30616
30733
|
}
|
|
30617
30734
|
}
|
|
30618
30735
|
|
|
30736
|
+
// src/adopt.ts
|
|
30737
|
+
async function resolveLegacyKey(inputs, env) {
|
|
30738
|
+
const sources = [];
|
|
30739
|
+
if (typeof inputs.legacyApiKey === "string" && inputs.legacyApiKey.length > 0) {
|
|
30740
|
+
sources.push({
|
|
30741
|
+
mode: "legacy_key_argv",
|
|
30742
|
+
load: async () => inputs.legacyApiKey.trim()
|
|
30743
|
+
});
|
|
30744
|
+
}
|
|
30745
|
+
if (typeof inputs.legacyApiKeyFile === "string" && inputs.legacyApiKeyFile.length > 0) {
|
|
30746
|
+
sources.push({
|
|
30747
|
+
mode: "legacy_key_file",
|
|
30748
|
+
load: async () => {
|
|
30749
|
+
const contents = await readFile4(inputs.legacyApiKeyFile, "utf8");
|
|
30750
|
+
return contents.trim();
|
|
30751
|
+
}
|
|
30752
|
+
});
|
|
30753
|
+
}
|
|
30754
|
+
if (inputs.legacyApiKeyStdin) {
|
|
30755
|
+
sources.push({
|
|
30756
|
+
mode: "legacy_key_stdin",
|
|
30757
|
+
load: async () => readAllStdin()
|
|
30758
|
+
});
|
|
30759
|
+
}
|
|
30760
|
+
const envKey = env.SLOCK_LEGACY_API_KEY;
|
|
30761
|
+
if (typeof envKey === "string" && envKey.trim().length > 0) {
|
|
30762
|
+
sources.push({
|
|
30763
|
+
mode: "legacy_key_env",
|
|
30764
|
+
load: async () => envKey.trim()
|
|
30765
|
+
});
|
|
30766
|
+
}
|
|
30767
|
+
if (sources.length === 0) {
|
|
30768
|
+
fail(
|
|
30769
|
+
"LEGACY_KEY_REQUIRED",
|
|
30770
|
+
"No legacy api key provided. Pass exactly one of: --legacy-api-key <key>, --legacy-api-key-file <path>, --legacy-api-key-stdin, or SLOCK_LEGACY_API_KEY."
|
|
30771
|
+
);
|
|
30772
|
+
}
|
|
30773
|
+
if (sources.length > 1) {
|
|
30774
|
+
const modes = sources.map((s) => s.mode).join(", ");
|
|
30775
|
+
fail(
|
|
30776
|
+
"LEGACY_KEY_MULTIPLE_SOURCES",
|
|
30777
|
+
`Multiple legacy api key sources provided (${modes}). Choose exactly one.`
|
|
30778
|
+
);
|
|
30779
|
+
}
|
|
30780
|
+
const chosen = sources[0];
|
|
30781
|
+
const rawKey = await chosen.load();
|
|
30782
|
+
if (chosen.mode === "legacy_key_env") {
|
|
30783
|
+
delete env.SLOCK_LEGACY_API_KEY;
|
|
30784
|
+
}
|
|
30785
|
+
if (!rawKey.startsWith("sk_machine_") && !rawKey.startsWith("sk_daemon_")) {
|
|
30786
|
+
fail(
|
|
30787
|
+
"LEGACY_KEY_INVALID",
|
|
30788
|
+
"Provided key does not look like a legacy machine key (expected `sk_machine_*` or `sk_daemon_*`)."
|
|
30789
|
+
);
|
|
30790
|
+
}
|
|
30791
|
+
return {
|
|
30792
|
+
rawKey,
|
|
30793
|
+
mode: chosen.mode,
|
|
30794
|
+
redactedPrefix: rawKey.slice(0, 8)
|
|
30795
|
+
};
|
|
30796
|
+
}
|
|
30797
|
+
async function readAllStdin() {
|
|
30798
|
+
return new Promise((resolve, reject) => {
|
|
30799
|
+
const chunks = [];
|
|
30800
|
+
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
30801
|
+
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8").trim()));
|
|
30802
|
+
process.stdin.on("error", reject);
|
|
30803
|
+
});
|
|
30804
|
+
}
|
|
30805
|
+
async function runAdoptLegacy(inputs) {
|
|
30806
|
+
const legacy = await resolveLegacyKey(inputs, process.env);
|
|
30807
|
+
try {
|
|
30808
|
+
await adoptLegacy(
|
|
30809
|
+
{
|
|
30810
|
+
serverSlug: inputs.serverSlug,
|
|
30811
|
+
serverUrl: inputs.serverUrl,
|
|
30812
|
+
name: inputs.name,
|
|
30813
|
+
rawKey: legacy.rawKey,
|
|
30814
|
+
mode: legacy.mode,
|
|
30815
|
+
redactedPrefix: legacy.redactedPrefix
|
|
30816
|
+
},
|
|
30817
|
+
{
|
|
30818
|
+
onEvent: (event) => {
|
|
30819
|
+
if (event.type === "adopting") {
|
|
30820
|
+
info(
|
|
30821
|
+
`Adopting legacy daemon for ${formatServerSlugDisplay(event.serverSlug)} via ${event.mode}\u2026`
|
|
30822
|
+
);
|
|
30823
|
+
} else if (event.type === "preflight") {
|
|
30824
|
+
info(event.resumed ? "Adopted (resumed prior attachment); running preflight\u2026" : "Adopted; running preflight\u2026");
|
|
30825
|
+
} else if (event.type === "adopted") {
|
|
30826
|
+
info(`Adopted. Computer state written to ${event.attachmentPath}`);
|
|
30827
|
+
info(` server: ${formatServerSlugDisplay(event.serverSlug)}`);
|
|
30828
|
+
info(` serverMachine: ${event.serverMachineId}`);
|
|
30829
|
+
info(` legacyMachine: ${event.legacyMachineId}`);
|
|
30830
|
+
switch (event.legacyStop.outcome) {
|
|
30831
|
+
case "absent":
|
|
30832
|
+
info(" legacy daemon: not detected on this Computer (no local lock file)");
|
|
30833
|
+
break;
|
|
30834
|
+
case "already_dead":
|
|
30835
|
+
info(` legacy daemon: already stopped (pid ${event.legacyStop.pid} not running)`);
|
|
30836
|
+
break;
|
|
30837
|
+
case "stopped":
|
|
30838
|
+
info(` legacy daemon: stopped (pid ${event.legacyStop.pid}, SIGTERM)`);
|
|
30839
|
+
break;
|
|
30840
|
+
}
|
|
30841
|
+
}
|
|
30842
|
+
}
|
|
30843
|
+
}
|
|
30844
|
+
);
|
|
30845
|
+
} catch (err) {
|
|
30846
|
+
if (err instanceof CliExit) throw err;
|
|
30847
|
+
if (err instanceof ComputerServiceError) {
|
|
30848
|
+
fail(err.code, err.message);
|
|
30849
|
+
}
|
|
30850
|
+
throw err;
|
|
30851
|
+
} finally {
|
|
30852
|
+
legacy.rawKey = void 0;
|
|
30853
|
+
}
|
|
30854
|
+
if (!inputs.orchestrated) {
|
|
30855
|
+
info(`Next: run \`slock-computer start\` to bring this server online under the Computer supervisor.`);
|
|
30856
|
+
}
|
|
30857
|
+
}
|
|
30858
|
+
|
|
30619
30859
|
// src/setup.ts
|
|
30620
30860
|
init_esm_shims();
|
|
30621
|
-
var
|
|
30622
|
-
import { chmod as chmod4, mkdir as
|
|
30623
|
-
import { dirname as
|
|
30861
|
+
var import_undici3 = __toESM(require_undici(), 1);
|
|
30862
|
+
import { chmod as chmod4, mkdir as mkdir9, readdir as readdir3, readFile as readFile8, rename as rename3, rm as rm2, writeFile as writeFile8 } from "fs/promises";
|
|
30863
|
+
import { dirname as dirname9, join as join3 } from "path";
|
|
30864
|
+
|
|
30865
|
+
// src/internal/process-primitives.ts
|
|
30866
|
+
init_esm_shims();
|
|
30867
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5, unlink as unlink2 } from "fs/promises";
|
|
30868
|
+
import { dirname as dirname5 } from "path";
|
|
30869
|
+
async function readPidfileAt(pidfilePath) {
|
|
30870
|
+
try {
|
|
30871
|
+
const raw = (await readFile5(pidfilePath, "utf8")).trim();
|
|
30872
|
+
const pid = Number.parseInt(raw, 10);
|
|
30873
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
30874
|
+
} catch {
|
|
30875
|
+
return null;
|
|
30876
|
+
}
|
|
30877
|
+
}
|
|
30878
|
+
function isProcessAlive2(pid) {
|
|
30879
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
30880
|
+
try {
|
|
30881
|
+
process.kill(pid, 0);
|
|
30882
|
+
return true;
|
|
30883
|
+
} catch (err) {
|
|
30884
|
+
return err.code === "EPERM";
|
|
30885
|
+
}
|
|
30886
|
+
}
|
|
30887
|
+
async function writePidfileAt(pidfilePath, pid) {
|
|
30888
|
+
await mkdir5(dirname5(pidfilePath), { recursive: true });
|
|
30889
|
+
await writeFile5(pidfilePath, String(pid), { mode: 384 });
|
|
30890
|
+
}
|
|
30891
|
+
async function clearPidfileAt(pidfilePath) {
|
|
30892
|
+
try {
|
|
30893
|
+
await unlink2(pidfilePath);
|
|
30894
|
+
} catch {
|
|
30895
|
+
}
|
|
30896
|
+
}
|
|
30624
30897
|
|
|
30625
30898
|
// src/supervisor.ts
|
|
30626
30899
|
init_esm_shims();
|
|
30627
30900
|
import { spawn as spawn2 } from "child_process";
|
|
30628
|
-
import { mkdir as
|
|
30629
|
-
import { dirname as
|
|
30901
|
+
import { mkdir as mkdir8, readFile as readFile7, writeFile as writeFile7, open, rename as rename2 } from "fs/promises";
|
|
30902
|
+
import { dirname as dirname8, join as joinPath } from "path";
|
|
30630
30903
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
30631
30904
|
|
|
30632
30905
|
// src/cleanup.ts
|
|
30633
30906
|
init_esm_shims();
|
|
30634
|
-
import { readdir as readdir2, stat as stat2, unlink as
|
|
30907
|
+
import { readdir as readdir2, stat as stat2, unlink as unlink3, rm, rmdir, rename, mkdir as mkdir6 } from "fs/promises";
|
|
30635
30908
|
import { spawn } from "child_process";
|
|
30636
30909
|
import { join as join2 } from "path";
|
|
30637
30910
|
function emptyCleanupReport() {
|
|
@@ -30649,7 +30922,7 @@ async function cleanupStalePidfile(pidfilePath) {
|
|
|
30649
30922
|
if (pid === null) return false;
|
|
30650
30923
|
if (isProcessAlive2(pid)) return false;
|
|
30651
30924
|
try {
|
|
30652
|
-
await
|
|
30925
|
+
await unlink3(pidfilePath);
|
|
30653
30926
|
return true;
|
|
30654
30927
|
} catch {
|
|
30655
30928
|
return false;
|
|
@@ -30734,11 +31007,11 @@ async function quarantineServerSubtree(slockHome, serverId) {
|
|
|
30734
31007
|
const src = join2(serversDir(slockHome), serverId);
|
|
30735
31008
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30736
31009
|
const dest = join2(quarantineDir(slockHome), `${stamp}-${serverId}`);
|
|
30737
|
-
await
|
|
31010
|
+
await mkdir6(dirname6(dest), { recursive: true });
|
|
30738
31011
|
await rename(src, dest);
|
|
30739
31012
|
return dest;
|
|
30740
31013
|
}
|
|
30741
|
-
function
|
|
31014
|
+
function dirname6(p) {
|
|
30742
31015
|
const idx = p.lastIndexOf("/");
|
|
30743
31016
|
return idx > 0 ? p.slice(0, idx) : "/";
|
|
30744
31017
|
}
|
|
@@ -30797,7 +31070,7 @@ async function cleanupTmpFiles(slockHome) {
|
|
|
30797
31070
|
try {
|
|
30798
31071
|
const s = await stat2(snap);
|
|
30799
31072
|
if (Date.now() - s.mtimeMs > TMP_MAX_AGE_MS) {
|
|
30800
|
-
await
|
|
31073
|
+
await unlink3(snap);
|
|
30801
31074
|
removed.push(snap);
|
|
30802
31075
|
}
|
|
30803
31076
|
} catch {
|
|
@@ -30841,14 +31114,14 @@ async function runFullCleanup(slockHome, options = {}) {
|
|
|
30841
31114
|
|
|
30842
31115
|
// src/health.ts
|
|
30843
31116
|
init_esm_shims();
|
|
30844
|
-
import { readFile as
|
|
30845
|
-
import { dirname as
|
|
31117
|
+
import { readFile as readFile6, writeFile as writeFile6, unlink as unlink4, mkdir as mkdir7 } from "fs/promises";
|
|
31118
|
+
import { dirname as dirname7 } from "path";
|
|
30846
31119
|
var CRASH_WINDOW_MS = 6e4;
|
|
30847
31120
|
var DEGRADED_THRESHOLD = 3;
|
|
30848
31121
|
async function readHealthFile(slockHome, serverId) {
|
|
30849
31122
|
if (!isValidServerId(serverId)) return { crashes: [] };
|
|
30850
31123
|
try {
|
|
30851
|
-
const raw = await
|
|
31124
|
+
const raw = await readFile6(serverHealthPath(slockHome, serverId), "utf8");
|
|
30852
31125
|
const parsed = JSON.parse(raw);
|
|
30853
31126
|
if (parsed && typeof parsed === "object" && Array.isArray(parsed.crashes)) {
|
|
30854
31127
|
return parsed;
|
|
@@ -30859,8 +31132,8 @@ async function readHealthFile(slockHome, serverId) {
|
|
|
30859
31132
|
}
|
|
30860
31133
|
async function writeHealthFile(slockHome, serverId, file) {
|
|
30861
31134
|
const path3 = serverHealthPath(slockHome, serverId);
|
|
30862
|
-
await
|
|
30863
|
-
await
|
|
31135
|
+
await mkdir7(dirname7(path3), { recursive: true });
|
|
31136
|
+
await writeFile6(path3, JSON.stringify(file), { mode: 384 });
|
|
30864
31137
|
}
|
|
30865
31138
|
async function recordCrash(slockHome, serverId, exitCode, signal, nowMs = Date.now()) {
|
|
30866
31139
|
if (!isValidServerId(serverId)) return;
|
|
@@ -30893,63 +31166,373 @@ async function isDegraded(slockHome, serverId, nowMs = Date.now()) {
|
|
|
30893
31166
|
const t = new Date(c.at).getTime();
|
|
30894
31167
|
return Number.isFinite(t) && t >= cutoffMs;
|
|
30895
31168
|
});
|
|
30896
|
-
return recent.length >= DEGRADED_THRESHOLD;
|
|
30897
|
-
}
|
|
30898
|
-
async function markFatalConfig(slockHome, serverId, exitCode, signal, nowMs = Date.now()) {
|
|
30899
|
-
if (!isValidServerId(serverId)) return;
|
|
30900
|
-
const file = await readHealthFile(slockHome, serverId);
|
|
30901
|
-
file.fatalConfig = {
|
|
30902
|
-
at: new Date(nowMs).toISOString(),
|
|
30903
|
-
exitCode,
|
|
30904
|
-
signal,
|
|
30905
|
-
reason: "ex_config"
|
|
31169
|
+
return recent.length >= DEGRADED_THRESHOLD;
|
|
31170
|
+
}
|
|
31171
|
+
async function markFatalConfig(slockHome, serverId, exitCode, signal, nowMs = Date.now()) {
|
|
31172
|
+
if (!isValidServerId(serverId)) return;
|
|
31173
|
+
const file = await readHealthFile(slockHome, serverId);
|
|
31174
|
+
file.fatalConfig = {
|
|
31175
|
+
at: new Date(nowMs).toISOString(),
|
|
31176
|
+
exitCode,
|
|
31177
|
+
signal,
|
|
31178
|
+
reason: "ex_config"
|
|
31179
|
+
};
|
|
31180
|
+
await writeHealthFile(slockHome, serverId, file);
|
|
31181
|
+
}
|
|
31182
|
+
async function resetHealth(slockHome, serverId) {
|
|
31183
|
+
if (!isValidServerId(serverId)) return;
|
|
31184
|
+
try {
|
|
31185
|
+
await unlink4(serverHealthPath(slockHome, serverId));
|
|
31186
|
+
} catch {
|
|
31187
|
+
}
|
|
31188
|
+
}
|
|
31189
|
+
|
|
31190
|
+
// src/services/start.ts
|
|
31191
|
+
init_esm_shims();
|
|
31192
|
+
var START_ENSURE_TIMEOUT_MS = 15e3;
|
|
31193
|
+
var START_ENSURE_POLL_INTERVAL_MS = 100;
|
|
31194
|
+
function emit4(opts, event) {
|
|
31195
|
+
const cb = opts?.onEvent;
|
|
31196
|
+
if (!cb) return;
|
|
31197
|
+
try {
|
|
31198
|
+
cb(event);
|
|
31199
|
+
} catch {
|
|
31200
|
+
}
|
|
31201
|
+
}
|
|
31202
|
+
async function waitForManagedDaemonPids(slockHome, serverIds, opts) {
|
|
31203
|
+
const readPidfile = opts.readPidfile ?? readPidfileAt;
|
|
31204
|
+
const isAlive = opts.isProcessAlive ?? isProcessAlive2;
|
|
31205
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
31206
|
+
const timeoutMs = opts.ensureTimeoutMs ?? START_ENSURE_TIMEOUT_MS;
|
|
31207
|
+
const pollIntervalMs = opts.ensurePollIntervalMs ?? START_ENSURE_POLL_INTERVAL_MS;
|
|
31208
|
+
const deadline = Date.now() + timeoutMs;
|
|
31209
|
+
const ready = /* @__PURE__ */ new Map();
|
|
31210
|
+
while (ready.size < serverIds.length) {
|
|
31211
|
+
for (const serverId of serverIds) {
|
|
31212
|
+
if (ready.has(serverId)) continue;
|
|
31213
|
+
const pid = await readPidfile(serverDaemonPidPath(slockHome, serverId));
|
|
31214
|
+
if (pid && isAlive(pid)) ready.set(serverId, pid);
|
|
31215
|
+
}
|
|
31216
|
+
if (ready.size === serverIds.length) return ready;
|
|
31217
|
+
const remaining = deadline - Date.now();
|
|
31218
|
+
if (remaining <= 0) return ready;
|
|
31219
|
+
await sleep2(Math.min(pollIntervalMs, remaining));
|
|
31220
|
+
}
|
|
31221
|
+
return ready;
|
|
31222
|
+
}
|
|
31223
|
+
function buildTimeoutMessage(slockHome, serverIds, ready, input) {
|
|
31224
|
+
const missing = serverIds.filter((id) => !ready.has(id));
|
|
31225
|
+
const target = input.serverId && missing.length === 1 ? `${input.serverLabel ?? input.serverId}` : `${missing.length} daemon(s): ${missing.join(", ")}`;
|
|
31226
|
+
return `Timed out waiting for ${target} to start. Run \`slock-computer status\` and inspect ${supervisorLogPath(slockHome)} plus per-server daemon logs under ~/.slock/computer/servers/<serverId>/daemon.log.`;
|
|
31227
|
+
}
|
|
31228
|
+
async function start(input, options = {}) {
|
|
31229
|
+
options.signal?.throwIfAborted?.();
|
|
31230
|
+
const slockHome = resolveSlockHome();
|
|
31231
|
+
const attached = await listAttachedServerIds(slockHome);
|
|
31232
|
+
if (attached.length === 0) {
|
|
31233
|
+
throw new ComputerServiceError(
|
|
31234
|
+
"NO_ATTACHMENT",
|
|
31235
|
+
"No server attachments yet. Run `slock-computer attach <serverId>` first."
|
|
31236
|
+
);
|
|
31237
|
+
}
|
|
31238
|
+
if (input.serverId && !attached.includes(input.serverId)) {
|
|
31239
|
+
throw new ComputerServiceError(
|
|
31240
|
+
"NOT_ATTACHED",
|
|
31241
|
+
`Not attached to server ${input.serverId}. Run \`slock-computer attach ${input.serverId}\` first or omit the argument.`
|
|
31242
|
+
);
|
|
31243
|
+
}
|
|
31244
|
+
const managedTargets = input.serverId ? [input.serverId] : attached;
|
|
31245
|
+
emit4(options, {
|
|
31246
|
+
type: "starting",
|
|
31247
|
+
managedTargets,
|
|
31248
|
+
attachedCount: attached.length,
|
|
31249
|
+
foreground: !!input.foreground
|
|
31250
|
+
});
|
|
31251
|
+
for (const id of managedTargets) {
|
|
31252
|
+
await setServerManaged(slockHome, id);
|
|
31253
|
+
}
|
|
31254
|
+
const existing = await readPidfileAt(supervisorPidPath(slockHome));
|
|
31255
|
+
if (existing && isProcessAlive2(existing)) {
|
|
31256
|
+
emit4(options, {
|
|
31257
|
+
type: "already_running",
|
|
31258
|
+
supervisorPid: existing,
|
|
31259
|
+
managedTargets,
|
|
31260
|
+
attachedCount: attached.length
|
|
31261
|
+
});
|
|
31262
|
+
const ready2 = await waitForManagedDaemonPids(slockHome, managedTargets, options);
|
|
31263
|
+
if (ready2.size !== managedTargets.length) {
|
|
31264
|
+
throw new ComputerServiceError(
|
|
31265
|
+
"START_DAEMON_TIMEOUT",
|
|
31266
|
+
buildTimeoutMessage(slockHome, managedTargets, ready2, input)
|
|
31267
|
+
);
|
|
31268
|
+
}
|
|
31269
|
+
emit4(options, { type: "ready", ready: ready2, managedTargets });
|
|
31270
|
+
return {
|
|
31271
|
+
status: "already_running",
|
|
31272
|
+
managedTargets,
|
|
31273
|
+
attachedCount: attached.length,
|
|
31274
|
+
ready: ready2,
|
|
31275
|
+
supervisorPid: existing,
|
|
31276
|
+
supervisorLogPath: supervisorLogPath(slockHome)
|
|
31277
|
+
};
|
|
31278
|
+
}
|
|
31279
|
+
if (input.foreground) {
|
|
31280
|
+
emit4(options, {
|
|
31281
|
+
type: "running",
|
|
31282
|
+
managedTargets,
|
|
31283
|
+
attachedCount: attached.length
|
|
31284
|
+
});
|
|
31285
|
+
const supervise = options.runSupervise ?? runSupervise;
|
|
31286
|
+
await supervise();
|
|
31287
|
+
return {
|
|
31288
|
+
status: "running",
|
|
31289
|
+
managedTargets,
|
|
31290
|
+
attachedCount: attached.length,
|
|
31291
|
+
ready: /* @__PURE__ */ new Map(),
|
|
31292
|
+
supervisorPid: null,
|
|
31293
|
+
supervisorLogPath: supervisorLogPath(slockHome)
|
|
31294
|
+
};
|
|
31295
|
+
}
|
|
31296
|
+
options.signal?.throwIfAborted?.();
|
|
31297
|
+
let pid;
|
|
31298
|
+
try {
|
|
31299
|
+
pid = await (options.spawnDetachedSupervisor ?? spawnDetachedSupervisor)(slockHome);
|
|
31300
|
+
} catch (err) {
|
|
31301
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
31302
|
+
throw new ComputerServiceError("SUPERVISOR_SPAWN_FAILED", msg, err);
|
|
31303
|
+
}
|
|
31304
|
+
emit4(options, {
|
|
31305
|
+
type: "spawned",
|
|
31306
|
+
supervisorPid: pid,
|
|
31307
|
+
managedTargets,
|
|
31308
|
+
attachedCount: attached.length
|
|
31309
|
+
});
|
|
31310
|
+
if (options.signal?.aborted) {
|
|
31311
|
+
const ready2 = await pollReadyOnce(slockHome, managedTargets, options);
|
|
31312
|
+
emit4(options, { type: "aborted", supervisorPid: pid, managedTargets, ready: ready2 });
|
|
31313
|
+
return {
|
|
31314
|
+
status: "aborted",
|
|
31315
|
+
managedTargets,
|
|
31316
|
+
attachedCount: attached.length,
|
|
31317
|
+
ready: ready2,
|
|
31318
|
+
supervisorPid: pid,
|
|
31319
|
+
supervisorLogPath: supervisorLogPath(slockHome)
|
|
31320
|
+
};
|
|
31321
|
+
}
|
|
31322
|
+
const ready = await waitForManagedDaemonPids(slockHome, managedTargets, options);
|
|
31323
|
+
if (ready.size !== managedTargets.length) {
|
|
31324
|
+
throw new ComputerServiceError(
|
|
31325
|
+
"START_DAEMON_TIMEOUT",
|
|
31326
|
+
buildTimeoutMessage(slockHome, managedTargets, ready, input)
|
|
31327
|
+
);
|
|
31328
|
+
}
|
|
31329
|
+
emit4(options, { type: "ready", ready, managedTargets });
|
|
31330
|
+
return {
|
|
31331
|
+
status: "spawned",
|
|
31332
|
+
managedTargets,
|
|
31333
|
+
attachedCount: attached.length,
|
|
31334
|
+
ready,
|
|
31335
|
+
supervisorPid: pid,
|
|
31336
|
+
supervisorLogPath: supervisorLogPath(slockHome)
|
|
30906
31337
|
};
|
|
30907
|
-
await writeHealthFile(slockHome, serverId, file);
|
|
30908
31338
|
}
|
|
30909
|
-
async function
|
|
30910
|
-
|
|
30911
|
-
|
|
30912
|
-
|
|
30913
|
-
|
|
31339
|
+
async function pollReadyOnce(slockHome, serverIds, opts) {
|
|
31340
|
+
const readPidfile = opts.readPidfile ?? readPidfileAt;
|
|
31341
|
+
const isAlive = opts.isProcessAlive ?? isProcessAlive2;
|
|
31342
|
+
const ready = /* @__PURE__ */ new Map();
|
|
31343
|
+
for (const serverId of serverIds) {
|
|
31344
|
+
const pid = await readPidfile(serverDaemonPidPath(slockHome, serverId));
|
|
31345
|
+
if (pid && isAlive(pid)) ready.set(serverId, pid);
|
|
30914
31346
|
}
|
|
31347
|
+
return ready;
|
|
30915
31348
|
}
|
|
30916
31349
|
|
|
30917
|
-
// src/
|
|
30918
|
-
|
|
31350
|
+
// src/services/stop.ts
|
|
31351
|
+
init_esm_shims();
|
|
31352
|
+
var STOP_POLL_INTERVAL_MS = 200;
|
|
31353
|
+
var STOP_TIMEOUT_MS = 5e3;
|
|
31354
|
+
function emit5(opts, event) {
|
|
31355
|
+
const cb = opts?.onEvent;
|
|
31356
|
+
if (!cb) return;
|
|
30919
31357
|
try {
|
|
30920
|
-
|
|
30921
|
-
const pid = Number.parseInt(raw, 10);
|
|
30922
|
-
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
31358
|
+
cb(event);
|
|
30923
31359
|
} catch {
|
|
30924
|
-
return null;
|
|
30925
31360
|
}
|
|
30926
31361
|
}
|
|
30927
|
-
function
|
|
30928
|
-
|
|
31362
|
+
async function stop(input = {}, options = {}) {
|
|
31363
|
+
void input;
|
|
31364
|
+
options.signal?.throwIfAborted?.();
|
|
31365
|
+
const slockHome = resolveSlockHome();
|
|
31366
|
+
const readPidfile = options.readPidfile ?? readPidfileAt;
|
|
31367
|
+
const isAlive = options.isProcessAlive ?? isProcessAlive2;
|
|
31368
|
+
const killer = options.killSupervisor ?? ((pid2) => {
|
|
31369
|
+
process.kill(pid2, "SIGTERM");
|
|
31370
|
+
});
|
|
31371
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
31372
|
+
const pollIntervalMs = options.pollIntervalMs ?? STOP_POLL_INTERVAL_MS;
|
|
31373
|
+
const timeoutMs = options.timeoutMs ?? STOP_TIMEOUT_MS;
|
|
31374
|
+
const pidfilePath = supervisorPidPath(slockHome);
|
|
31375
|
+
const pid = await readPidfile(pidfilePath);
|
|
31376
|
+
emit5(options, { type: "stopping", pid });
|
|
31377
|
+
if (pid === null) {
|
|
31378
|
+
emit5(options, { type: "not_running" });
|
|
31379
|
+
return { status: "not_running", pid: null, pidfilePath };
|
|
31380
|
+
}
|
|
31381
|
+
if (!isAlive(pid)) {
|
|
31382
|
+
await clearPidfileAt(pidfilePath);
|
|
31383
|
+
emit5(options, { type: "stale_pidfile_cleared", pid });
|
|
31384
|
+
return { status: "stale_pidfile_cleared", pid, pidfilePath };
|
|
31385
|
+
}
|
|
31386
|
+
options.signal?.throwIfAborted?.();
|
|
30929
31387
|
try {
|
|
30930
|
-
|
|
30931
|
-
return true;
|
|
31388
|
+
killer(pid);
|
|
30932
31389
|
} catch (err) {
|
|
30933
|
-
|
|
31390
|
+
const cause = err instanceof Error ? err : new Error(String(err));
|
|
31391
|
+
throw new ComputerServiceError(
|
|
31392
|
+
"STOP_SIGNAL_FAILED",
|
|
31393
|
+
`Failed to send SIGTERM to supervisor (pid ${pid}): ${cause.message}. Check process permissions or run: kill ${pid}`,
|
|
31394
|
+
err
|
|
31395
|
+
);
|
|
30934
31396
|
}
|
|
31397
|
+
emit5(options, { type: "signaled", pid });
|
|
31398
|
+
const deadline = Date.now() + timeoutMs;
|
|
31399
|
+
while (Date.now() < deadline) {
|
|
31400
|
+
options.signal?.throwIfAborted?.();
|
|
31401
|
+
if (!isAlive(pid)) {
|
|
31402
|
+
await clearPidfileAt(pidfilePath);
|
|
31403
|
+
emit5(options, { type: "stopped", pid });
|
|
31404
|
+
return { status: "stopped", pid, pidfilePath };
|
|
31405
|
+
}
|
|
31406
|
+
const remaining = deadline - Date.now();
|
|
31407
|
+
if (remaining <= 0) break;
|
|
31408
|
+
await sleep2(Math.min(pollIntervalMs, remaining));
|
|
31409
|
+
}
|
|
31410
|
+
throw new ComputerServiceError(
|
|
31411
|
+
"STOP_TIMEOUT",
|
|
31412
|
+
`Supervisor (pid ${pid}) did not exit within ${timeoutMs}ms after SIGTERM. Force-kill with: kill -9 ${pid}`
|
|
31413
|
+
);
|
|
30935
31414
|
}
|
|
30936
|
-
|
|
30937
|
-
|
|
30938
|
-
|
|
30939
|
-
|
|
30940
|
-
|
|
31415
|
+
|
|
31416
|
+
// src/services/detach.ts
|
|
31417
|
+
init_esm_shims();
|
|
31418
|
+
var import_undici2 = __toESM(require_undici(), 1);
|
|
31419
|
+
var REVOKE_TIMEOUT_MS = 3e3;
|
|
31420
|
+
function emit6(opts, event) {
|
|
31421
|
+
const cb = opts?.onEvent;
|
|
31422
|
+
if (!cb) return;
|
|
30941
31423
|
try {
|
|
30942
|
-
|
|
31424
|
+
cb(event);
|
|
30943
31425
|
} catch {
|
|
30944
31426
|
}
|
|
30945
31427
|
}
|
|
31428
|
+
async function bestEffortServerRevoke(fetchImpl, serverUrl, apiKey, serverId, timeoutMs) {
|
|
31429
|
+
const url = `${serverUrl.replace(/\/$/, "")}/internal/computer/attachment/revoke`;
|
|
31430
|
+
const controller = new AbortController();
|
|
31431
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
31432
|
+
try {
|
|
31433
|
+
const res = await fetchImpl(url, {
|
|
31434
|
+
method: "POST",
|
|
31435
|
+
headers: {
|
|
31436
|
+
"content-type": "application/json",
|
|
31437
|
+
authorization: `Bearer ${apiKey}`
|
|
31438
|
+
},
|
|
31439
|
+
body: JSON.stringify({ reason: "computer_detach" }),
|
|
31440
|
+
signal: controller.signal
|
|
31441
|
+
});
|
|
31442
|
+
if (!res.ok) {
|
|
31443
|
+
return {
|
|
31444
|
+
kind: "http_error",
|
|
31445
|
+
status: res.status,
|
|
31446
|
+
message: `server-side revoke for server ${serverId} returned HTTP ${res.status}; local detach proceeds, but the credential may remain valid until it expires.`
|
|
31447
|
+
};
|
|
31448
|
+
}
|
|
31449
|
+
return { kind: "success" };
|
|
31450
|
+
} catch (err) {
|
|
31451
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
31452
|
+
return {
|
|
31453
|
+
kind: "network_error",
|
|
31454
|
+
message: `server-side revoke for server ${serverId} failed: ${m}. Local detach proceeds, but the credential may remain valid until it expires.`
|
|
31455
|
+
};
|
|
31456
|
+
} finally {
|
|
31457
|
+
clearTimeout(timeout);
|
|
31458
|
+
}
|
|
31459
|
+
}
|
|
31460
|
+
async function detach(input, options = {}) {
|
|
31461
|
+
options.signal?.throwIfAborted?.();
|
|
31462
|
+
const serverId = assertValidServerId(input.serverId);
|
|
31463
|
+
const serverLabel = input.serverLabel ?? serverId;
|
|
31464
|
+
const slockHome = resolveSlockHome();
|
|
31465
|
+
const attachment = await readServerAttachment(slockHome, serverId);
|
|
31466
|
+
if (!attachment) {
|
|
31467
|
+
throw new ComputerServiceError(
|
|
31468
|
+
"NOT_ATTACHED",
|
|
31469
|
+
`Not attached to server ${serverLabel} (nothing to detach).`
|
|
31470
|
+
);
|
|
31471
|
+
}
|
|
31472
|
+
emit6(options, { type: "detaching", serverId, serverLabel });
|
|
31473
|
+
options.signal?.throwIfAborted?.();
|
|
31474
|
+
const fetchImpl = options.fetch ?? import_undici2.fetch;
|
|
31475
|
+
const timeoutMs = options.revokeTimeoutMs ?? REVOKE_TIMEOUT_MS;
|
|
31476
|
+
const outcome = await bestEffortServerRevoke(
|
|
31477
|
+
fetchImpl,
|
|
31478
|
+
attachment.serverUrl,
|
|
31479
|
+
attachment.apiKey,
|
|
31480
|
+
serverId,
|
|
31481
|
+
timeoutMs
|
|
31482
|
+
);
|
|
31483
|
+
let revokeOutcome;
|
|
31484
|
+
let revokeHttpStatus;
|
|
31485
|
+
if (outcome.kind === "success") {
|
|
31486
|
+
revokeOutcome = "success";
|
|
31487
|
+
emit6(options, { type: "revoke_succeeded", serverId, serverLabel });
|
|
31488
|
+
} else if (outcome.kind === "http_error") {
|
|
31489
|
+
revokeOutcome = "http_error";
|
|
31490
|
+
revokeHttpStatus = outcome.status;
|
|
31491
|
+
emit6(options, {
|
|
31492
|
+
type: "revoke_failed",
|
|
31493
|
+
serverId,
|
|
31494
|
+
serverLabel,
|
|
31495
|
+
reason: "http_error",
|
|
31496
|
+
httpStatus: outcome.status,
|
|
31497
|
+
message: outcome.message
|
|
31498
|
+
});
|
|
31499
|
+
} else {
|
|
31500
|
+
revokeOutcome = "network_error";
|
|
31501
|
+
emit6(options, {
|
|
31502
|
+
type: "revoke_failed",
|
|
31503
|
+
serverId,
|
|
31504
|
+
serverLabel,
|
|
31505
|
+
reason: "network_error",
|
|
31506
|
+
message: outcome.message
|
|
31507
|
+
});
|
|
31508
|
+
}
|
|
31509
|
+
options.signal?.throwIfAborted?.();
|
|
31510
|
+
await clearServerManaged(slockHome, serverId);
|
|
31511
|
+
const subtree = [
|
|
31512
|
+
serverAttachmentPath(slockHome, serverId),
|
|
31513
|
+
serverDaemonPidPath(slockHome, serverId),
|
|
31514
|
+
serverDaemonLogPath(slockHome, serverId)
|
|
31515
|
+
];
|
|
31516
|
+
for (const p of subtree) await clearPidfileAt(p);
|
|
31517
|
+
emit6(options, { type: "subtree_cleared", serverId, serverLabel });
|
|
31518
|
+
emit6(options, { type: "detached", serverId, serverLabel });
|
|
31519
|
+
return {
|
|
31520
|
+
status: "detached",
|
|
31521
|
+
serverId,
|
|
31522
|
+
serverLabel,
|
|
31523
|
+
revokeOutcome,
|
|
31524
|
+
revokeHttpStatus
|
|
31525
|
+
};
|
|
31526
|
+
}
|
|
31527
|
+
|
|
31528
|
+
// src/supervisor.ts
|
|
30946
31529
|
function buildResidentSpawn(mode, serverId, selfEntry = process.argv[1] ?? "", execArgv = process.execArgv) {
|
|
30947
31530
|
const tail = serverId ? [mode, serverId] : [mode];
|
|
30948
31531
|
return { command: process.execPath, args: [...execArgv, selfEntry, ...tail] };
|
|
30949
31532
|
}
|
|
30950
31533
|
var PARENT_LOCK_HELD_ENV_VAR = "SLOCK_COMPUTER_PARENT_MUTATION_LOCK_HELD";
|
|
30951
31534
|
async function spawnDetachedSupervisor(slockHome) {
|
|
30952
|
-
await
|
|
31535
|
+
await mkdir8(computerDir(slockHome), { recursive: true });
|
|
30953
31536
|
const supLogFd = await open(supervisorLogPath(slockHome), "a");
|
|
30954
31537
|
const { command, args } = buildResidentSpawn("__supervise", null);
|
|
30955
31538
|
const child = spawn2(command, args, {
|
|
@@ -31034,29 +31617,6 @@ async function runResident(serverId, deps = {}) {
|
|
|
31034
31617
|
}
|
|
31035
31618
|
var RECONCILE_INTERVAL_MS = 5e3;
|
|
31036
31619
|
var CHILD_RESTART_BACKOFF_MS = 2e3;
|
|
31037
|
-
var START_ENSURE_TIMEOUT_MS = 15e3;
|
|
31038
|
-
var START_ENSURE_POLL_INTERVAL_MS = 100;
|
|
31039
|
-
async function waitForManagedDaemonPids(slockHome, serverIds, deps) {
|
|
31040
|
-
const readPidfile = deps.readPidfile ?? readPidfileAt;
|
|
31041
|
-
const isAlive = deps.isProcessAlive ?? isProcessAlive2;
|
|
31042
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
31043
|
-
const timeoutMs = deps.ensureTimeoutMs ?? START_ENSURE_TIMEOUT_MS;
|
|
31044
|
-
const pollIntervalMs = deps.ensurePollIntervalMs ?? START_ENSURE_POLL_INTERVAL_MS;
|
|
31045
|
-
const deadline = Date.now() + timeoutMs;
|
|
31046
|
-
const ready = /* @__PURE__ */ new Map();
|
|
31047
|
-
while (ready.size < serverIds.length) {
|
|
31048
|
-
for (const serverId of serverIds) {
|
|
31049
|
-
if (ready.has(serverId)) continue;
|
|
31050
|
-
const pid = await readPidfile(serverDaemonPidPath(slockHome, serverId));
|
|
31051
|
-
if (pid && isAlive(pid)) ready.set(serverId, pid);
|
|
31052
|
-
}
|
|
31053
|
-
if (ready.size === serverIds.length) return ready;
|
|
31054
|
-
const remaining = deadline - Date.now();
|
|
31055
|
-
if (remaining <= 0) return ready;
|
|
31056
|
-
await sleep2(Math.min(pollIntervalMs, remaining));
|
|
31057
|
-
}
|
|
31058
|
-
return ready;
|
|
31059
|
-
}
|
|
31060
31620
|
function formatReadySummary(ready, serverIds, opts) {
|
|
31061
31621
|
if (opts.serverId && serverIds.length === 1) {
|
|
31062
31622
|
const pid = ready.get(opts.serverId);
|
|
@@ -31064,14 +31624,6 @@ function formatReadySummary(ready, serverIds, opts) {
|
|
|
31064
31624
|
}
|
|
31065
31625
|
return `Daemons for ${serverIds.length} managed server(s) are running.`;
|
|
31066
31626
|
}
|
|
31067
|
-
function failStartEnsureTimeout(slockHome, serverIds, ready, opts) {
|
|
31068
|
-
const missing = serverIds.filter((id) => !ready.has(id));
|
|
31069
|
-
const target = opts.serverId && missing.length === 1 ? `${opts.serverLabel ?? opts.serverId}` : `${missing.length} daemon(s): ${missing.join(", ")}`;
|
|
31070
|
-
fail(
|
|
31071
|
-
"START_DAEMON_TIMEOUT",
|
|
31072
|
-
`Timed out waiting for ${target} to start. Run \`slock-computer status\` and inspect ${supervisorLogPath(slockHome)} plus per-server daemon logs under ~/.slock/computer/servers/<serverId>/daemon.log.`
|
|
31073
|
-
);
|
|
31074
|
-
}
|
|
31075
31627
|
async function runSupervisorStartupRecovery(slockHome) {
|
|
31076
31628
|
const parentHoldsLock = process.env[PARENT_LOCK_HELD_ENV_VAR] === "1";
|
|
31077
31629
|
try {
|
|
@@ -31105,10 +31657,10 @@ async function runSupervisorStartupRecovery(slockHome) {
|
|
|
31105
31657
|
}
|
|
31106
31658
|
async function resolveSupervisorIdentity() {
|
|
31107
31659
|
const here = fileURLToPath2(import.meta.url);
|
|
31108
|
-
const installRoot =
|
|
31660
|
+
const installRoot = dirname8(dirname8(here));
|
|
31109
31661
|
let version = null;
|
|
31110
31662
|
try {
|
|
31111
|
-
const raw = await
|
|
31663
|
+
const raw = await readFile7(joinPath(installRoot, "package.json"), "utf8");
|
|
31112
31664
|
const parsed = JSON.parse(raw);
|
|
31113
31665
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
31114
31666
|
version = parsed.version;
|
|
@@ -31128,7 +31680,7 @@ async function writeSupervisorVersionEvidence(slockHome) {
|
|
|
31128
31680
|
};
|
|
31129
31681
|
const dest = supervisorVersionPath(slockHome);
|
|
31130
31682
|
const tmp = `${dest}.tmp`;
|
|
31131
|
-
await
|
|
31683
|
+
await writeFile7(tmp, JSON.stringify(payload) + "\n", { mode: 384 });
|
|
31132
31684
|
await rename2(tmp, dest);
|
|
31133
31685
|
} catch (err) {
|
|
31134
31686
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -31140,7 +31692,7 @@ async function writeSupervisorVersionEvidence(slockHome) {
|
|
|
31140
31692
|
}
|
|
31141
31693
|
async function readSupervisorVersionEvidence(slockHome) {
|
|
31142
31694
|
try {
|
|
31143
|
-
const raw = await
|
|
31695
|
+
const raw = await readFile7(supervisorVersionPath(slockHome), "utf8");
|
|
31144
31696
|
const parsed = JSON.parse(raw);
|
|
31145
31697
|
if (typeof parsed.installRoot !== "string" || typeof parsed.pid !== "number" || typeof parsed.writtenAt !== "string" || parsed.version !== null && typeof parsed.version !== "string") {
|
|
31146
31698
|
return null;
|
|
@@ -31157,7 +31709,7 @@ async function readSupervisorVersionEvidence(slockHome) {
|
|
|
31157
31709
|
}
|
|
31158
31710
|
async function runSupervise() {
|
|
31159
31711
|
const slockHome = resolveSlockHome();
|
|
31160
|
-
await
|
|
31712
|
+
await mkdir8(computerDir(slockHome), { recursive: true });
|
|
31161
31713
|
await runSupervisorStartupRecovery(slockHome);
|
|
31162
31714
|
await writePidfileAt(supervisorPidPath(slockHome), process.pid);
|
|
31163
31715
|
await writeSupervisorVersionEvidence(slockHome);
|
|
@@ -31165,7 +31717,7 @@ async function runSupervise() {
|
|
|
31165
31717
|
const { [PARENT_LOCK_HELD_ENV_VAR]: _parentLockMarker, ...childEnv } = process.env;
|
|
31166
31718
|
const spawnChild = async (serverId) => {
|
|
31167
31719
|
const logPath = serverDaemonLogPath(slockHome, serverId);
|
|
31168
|
-
await
|
|
31720
|
+
await mkdir8(dirname8(logPath), { recursive: true });
|
|
31169
31721
|
const logFd = await open(logPath, "a");
|
|
31170
31722
|
const { command, args } = buildResidentSpawn("__run", serverId);
|
|
31171
31723
|
const child = spawn2(command, args, {
|
|
@@ -31254,149 +31806,111 @@ async function runSupervise() {
|
|
|
31254
31806
|
});
|
|
31255
31807
|
}
|
|
31256
31808
|
async function runStart(opts = {}, deps = {}) {
|
|
31257
|
-
|
|
31258
|
-
|
|
31259
|
-
|
|
31260
|
-
|
|
31261
|
-
|
|
31262
|
-
|
|
31263
|
-
|
|
31264
|
-
|
|
31265
|
-
|
|
31266
|
-
|
|
31267
|
-
|
|
31268
|
-
|
|
31809
|
+
let spawnedBackground = null;
|
|
31810
|
+
try {
|
|
31811
|
+
await start(
|
|
31812
|
+
{
|
|
31813
|
+
foreground: opts.foreground,
|
|
31814
|
+
serverId: opts.serverId ?? null,
|
|
31815
|
+
serverLabel: opts.serverLabel ?? null
|
|
31816
|
+
},
|
|
31817
|
+
{
|
|
31818
|
+
spawnDetachedSupervisor: deps.spawnDetachedSupervisor,
|
|
31819
|
+
readPidfile: deps.readPidfile,
|
|
31820
|
+
isProcessAlive: deps.isProcessAlive,
|
|
31821
|
+
sleep: deps.sleep,
|
|
31822
|
+
ensureTimeoutMs: deps.ensureTimeoutMs,
|
|
31823
|
+
ensurePollIntervalMs: deps.ensurePollIntervalMs,
|
|
31824
|
+
onEvent: (event) => {
|
|
31825
|
+
if (event.type === "already_running") {
|
|
31826
|
+
info(`Supervisor already running (pid ${event.supervisorPid}).`);
|
|
31827
|
+
} else if (event.type === "running") {
|
|
31828
|
+
info(
|
|
31829
|
+
`Running supervisor in the foreground (managing ${event.managedTargets.length} of ${event.attachedCount} attached server(s)). Ctrl-C to stop.`
|
|
31830
|
+
);
|
|
31831
|
+
} else if (event.type === "spawned") {
|
|
31832
|
+
info(`Supervisor started (pid ${event.supervisorPid}); keeps running after this terminal closes.`);
|
|
31833
|
+
spawnedBackground = {
|
|
31834
|
+
managedCount: event.managedTargets.length,
|
|
31835
|
+
attachedCount: event.attachedCount,
|
|
31836
|
+
logPath: supervisorLogPath(resolveSlockHome())
|
|
31837
|
+
};
|
|
31838
|
+
} else if (event.type === "ready") {
|
|
31839
|
+
info(formatReadySummary(event.ready, event.managedTargets, opts));
|
|
31840
|
+
}
|
|
31841
|
+
}
|
|
31842
|
+
}
|
|
31269
31843
|
);
|
|
31270
|
-
}
|
|
31271
|
-
|
|
31272
|
-
|
|
31273
|
-
|
|
31274
|
-
}
|
|
31275
|
-
const existing = await readPidfileAt(supervisorPidPath(slockHome));
|
|
31276
|
-
if (existing && isProcessAlive2(existing)) {
|
|
31277
|
-
info(`Supervisor already running (pid ${existing}).`);
|
|
31278
|
-
const ready2 = await waitForManagedDaemonPids(slockHome, managedTargets, deps);
|
|
31279
|
-
if (ready2.size !== managedTargets.length) {
|
|
31280
|
-
failStartEnsureTimeout(slockHome, managedTargets, ready2, opts);
|
|
31844
|
+
} catch (err) {
|
|
31845
|
+
if (err instanceof CliExit) throw err;
|
|
31846
|
+
if (err instanceof ComputerServiceError) {
|
|
31847
|
+
fail(err.code, err.message);
|
|
31281
31848
|
}
|
|
31282
|
-
|
|
31283
|
-
return;
|
|
31849
|
+
throw err;
|
|
31284
31850
|
}
|
|
31285
|
-
if (
|
|
31851
|
+
if (spawnedBackground) {
|
|
31852
|
+
const sb = spawnedBackground;
|
|
31286
31853
|
info(
|
|
31287
|
-
`
|
|
31854
|
+
`Managing ${sb.managedCount} of ${sb.attachedCount} attached server(s). Logs: ${sb.logPath}`
|
|
31288
31855
|
);
|
|
31289
|
-
|
|
31290
|
-
|
|
31291
|
-
}
|
|
31292
|
-
let pid;
|
|
31293
|
-
try {
|
|
31294
|
-
pid = await (deps.spawnDetachedSupervisor ?? spawnDetachedSupervisor)(slockHome);
|
|
31295
|
-
} catch (err) {
|
|
31296
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
31297
|
-
fail("SUPERVISOR_SPAWN_FAILED", msg);
|
|
31298
|
-
}
|
|
31299
|
-
info(`Supervisor started (pid ${pid}); keeps running after this terminal closes.`);
|
|
31300
|
-
const ready = await waitForManagedDaemonPids(slockHome, managedTargets, deps);
|
|
31301
|
-
if (ready.size !== managedTargets.length) {
|
|
31302
|
-
failStartEnsureTimeout(slockHome, managedTargets, ready, opts);
|
|
31856
|
+
info(`Per-server daemon logs: ~/.slock/computer/servers/<serverId>/daemon.log`);
|
|
31857
|
+
info(`Check state with \`slock-computer status\`.`);
|
|
31303
31858
|
}
|
|
31304
|
-
info(formatReadySummary(ready, managedTargets, opts));
|
|
31305
|
-
info(
|
|
31306
|
-
`Managing ${managedTargets.length} of ${attached.length} attached server(s). Logs: ${supervisorLogPath(slockHome)}`
|
|
31307
|
-
);
|
|
31308
|
-
info(`Per-server daemon logs: ~/.slock/computer/servers/<serverId>/daemon.log`);
|
|
31309
|
-
info(`Check state with \`slock-computer status\`.`);
|
|
31310
31859
|
}
|
|
31311
31860
|
async function runStop(deps = {}) {
|
|
31312
|
-
const slockHome = resolveSlockHome();
|
|
31313
|
-
const readPidfile = deps.readPidfile ?? readPidfileAt;
|
|
31314
|
-
const isAlive = deps.isProcessAlive ?? isProcessAlive2;
|
|
31315
|
-
const killer = deps.killSupervisor ?? ((pid2) => {
|
|
31316
|
-
process.kill(pid2, "SIGTERM");
|
|
31317
|
-
});
|
|
31318
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
31319
|
-
const pollIntervalMs = deps.pollIntervalMs ?? 200;
|
|
31320
|
-
const timeoutMs = deps.timeoutMs ?? 5e3;
|
|
31321
|
-
const pidfilePath = supervisorPidPath(slockHome);
|
|
31322
|
-
const pid = await readPidfile(pidfilePath);
|
|
31323
|
-
if (pid === null) {
|
|
31324
|
-
info("Supervisor not running.");
|
|
31325
|
-
return;
|
|
31326
|
-
}
|
|
31327
|
-
if (!isAlive(pid)) {
|
|
31328
|
-
await clearPidfileAt(pidfilePath);
|
|
31329
|
-
info(`Supervisor not running (cleared stale pidfile for pid ${pid}).`);
|
|
31330
|
-
return;
|
|
31331
|
-
}
|
|
31332
31861
|
try {
|
|
31333
|
-
|
|
31334
|
-
|
|
31335
|
-
|
|
31336
|
-
|
|
31337
|
-
|
|
31862
|
+
await stop(
|
|
31863
|
+
{},
|
|
31864
|
+
{
|
|
31865
|
+
readPidfile: deps.readPidfile,
|
|
31866
|
+
isProcessAlive: deps.isProcessAlive,
|
|
31867
|
+
killSupervisor: deps.killSupervisor,
|
|
31868
|
+
sleep: deps.sleep,
|
|
31869
|
+
pollIntervalMs: deps.pollIntervalMs,
|
|
31870
|
+
timeoutMs: deps.timeoutMs,
|
|
31871
|
+
onEvent: (event) => {
|
|
31872
|
+
if (event.type === "not_running") {
|
|
31873
|
+
info("Supervisor not running.");
|
|
31874
|
+
} else if (event.type === "stale_pidfile_cleared") {
|
|
31875
|
+
info(`Supervisor not running (cleared stale pidfile for pid ${event.pid}).`);
|
|
31876
|
+
} else if (event.type === "stopped") {
|
|
31877
|
+
info(`Stopped supervisor (pid ${event.pid}).`);
|
|
31878
|
+
}
|
|
31879
|
+
}
|
|
31880
|
+
}
|
|
31338
31881
|
);
|
|
31339
|
-
}
|
|
31340
|
-
|
|
31341
|
-
|
|
31342
|
-
|
|
31343
|
-
await clearPidfileAt(pidfilePath);
|
|
31344
|
-
info(`Stopped supervisor (pid ${pid}).`);
|
|
31345
|
-
return;
|
|
31882
|
+
} catch (err) {
|
|
31883
|
+
if (err instanceof CliExit) throw err;
|
|
31884
|
+
if (err instanceof ComputerServiceError) {
|
|
31885
|
+
fail(err.code, err.message);
|
|
31346
31886
|
}
|
|
31347
|
-
|
|
31887
|
+
throw err;
|
|
31348
31888
|
}
|
|
31349
|
-
fail(
|
|
31350
|
-
"STOP_TIMEOUT",
|
|
31351
|
-
`Supervisor (pid ${pid}) did not exit within ${timeoutMs}ms after SIGTERM. Force-kill with: kill -9 ${pid}`
|
|
31352
|
-
);
|
|
31353
31889
|
}
|
|
31354
31890
|
async function runDetach(serverId, serverLabel = serverId) {
|
|
31355
|
-
assertValidServerId(serverId);
|
|
31356
|
-
const slockHome = resolveSlockHome();
|
|
31357
|
-
const a = await readServerAttachment(slockHome, serverId);
|
|
31358
|
-
if (!a) {
|
|
31359
|
-
fail("NOT_ATTACHED", `Not attached to server ${serverLabel} (nothing to detach).`);
|
|
31360
|
-
}
|
|
31361
|
-
await bestEffortServerRevoke(a.serverUrl, a.apiKey, serverId);
|
|
31362
|
-
await clearServerManaged(slockHome, serverId);
|
|
31363
|
-
const subtree = [
|
|
31364
|
-
serverAttachmentPath(slockHome, serverId),
|
|
31365
|
-
serverDaemonPidPath(slockHome, serverId),
|
|
31366
|
-
serverDaemonLogPath(slockHome, serverId)
|
|
31367
|
-
];
|
|
31368
|
-
for (const p of subtree) await clearPidfileAt(p);
|
|
31369
|
-
info(`Detached from server ${serverLabel}.`);
|
|
31370
|
-
info(`The supervisor (if running) will stop that server's daemon on its next reconcile tick.`);
|
|
31371
|
-
}
|
|
31372
|
-
async function bestEffortServerRevoke(serverUrl, apiKey, serverId) {
|
|
31373
|
-
const url = `${serverUrl.replace(/\/$/, "")}/internal/computer/attachment/revoke`;
|
|
31374
|
-
const controller = new AbortController();
|
|
31375
|
-
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
31376
31891
|
try {
|
|
31377
|
-
|
|
31378
|
-
|
|
31379
|
-
|
|
31380
|
-
|
|
31381
|
-
|
|
31382
|
-
|
|
31383
|
-
|
|
31384
|
-
|
|
31385
|
-
|
|
31386
|
-
|
|
31387
|
-
|
|
31388
|
-
|
|
31389
|
-
|
|
31390
|
-
|
|
31391
|
-
|
|
31392
|
-
} catch (err) {
|
|
31393
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
31394
|
-
process.stderr.write(
|
|
31395
|
-
`Warning: server-side revoke for server ${serverId} failed: ${msg}. Local detach proceeds, but the credential may remain valid until it expires.
|
|
31396
|
-
`
|
|
31892
|
+
await detach(
|
|
31893
|
+
{ serverId, serverLabel },
|
|
31894
|
+
{
|
|
31895
|
+
onEvent: (event) => {
|
|
31896
|
+
if (event.type === "revoke_failed") {
|
|
31897
|
+
process.stderr.write(`Warning: ${event.message}
|
|
31898
|
+
`);
|
|
31899
|
+
} else if (event.type === "detached") {
|
|
31900
|
+
info(`Detached from server ${event.serverLabel}.`);
|
|
31901
|
+
info(
|
|
31902
|
+
`The supervisor (if running) will stop that server's daemon on its next reconcile tick.`
|
|
31903
|
+
);
|
|
31904
|
+
}
|
|
31905
|
+
}
|
|
31906
|
+
}
|
|
31397
31907
|
);
|
|
31398
|
-
}
|
|
31399
|
-
|
|
31908
|
+
} catch (err) {
|
|
31909
|
+
if (err instanceof CliExit) throw err;
|
|
31910
|
+
if (err instanceof ComputerServiceError) {
|
|
31911
|
+
fail(err.code, err.message);
|
|
31912
|
+
}
|
|
31913
|
+
throw err;
|
|
31400
31914
|
}
|
|
31401
31915
|
}
|
|
31402
31916
|
|
|
@@ -31404,7 +31918,7 @@ async function bestEffortServerRevoke(serverUrl, apiKey, serverId) {
|
|
|
31404
31918
|
var USER_SESSION_EXPIRY_LEEWAY_MS = 3e4;
|
|
31405
31919
|
async function hasValidUserSession(slockHome) {
|
|
31406
31920
|
try {
|
|
31407
|
-
const parsed = JSON.parse(await
|
|
31921
|
+
const parsed = JSON.parse(await readFile8(userSessionPath(slockHome), "utf8"));
|
|
31408
31922
|
return parsed.kind === "user-session" && typeof parsed.accessToken === "string" && parsed.accessToken.length > 0 && !isJwtExpired(parsed.accessToken);
|
|
31409
31923
|
} catch {
|
|
31410
31924
|
return false;
|
|
@@ -31424,7 +31938,7 @@ async function refreshUserSession(slockHome, serverUrl) {
|
|
|
31424
31938
|
const file = userSessionPath(slockHome);
|
|
31425
31939
|
let session;
|
|
31426
31940
|
try {
|
|
31427
|
-
session = JSON.parse(await
|
|
31941
|
+
session = JSON.parse(await readFile8(file, "utf8"));
|
|
31428
31942
|
} catch {
|
|
31429
31943
|
return false;
|
|
31430
31944
|
}
|
|
@@ -31434,7 +31948,7 @@ async function refreshUserSession(slockHome, serverUrl) {
|
|
|
31434
31948
|
const baseUrl = resolveServerUrl(serverUrl, session.serverUrl, process.env.SLOCK_SERVER_URL);
|
|
31435
31949
|
const tmpFile = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
31436
31950
|
try {
|
|
31437
|
-
const res = await (0,
|
|
31951
|
+
const res = await (0, import_undici3.fetch)(new URL("/api/auth/refresh", baseUrl).toString(), {
|
|
31438
31952
|
method: "POST",
|
|
31439
31953
|
headers: { "Content-Type": "application/json" },
|
|
31440
31954
|
body: JSON.stringify({ refreshToken: session.refreshToken })
|
|
@@ -31443,8 +31957,8 @@ async function refreshUserSession(slockHome, serverUrl) {
|
|
|
31443
31957
|
if (res.status !== 200 || typeof body?.accessToken !== "string" || typeof body.refreshToken !== "string") {
|
|
31444
31958
|
return false;
|
|
31445
31959
|
}
|
|
31446
|
-
await
|
|
31447
|
-
await
|
|
31960
|
+
await mkdir9(dirname9(file), { recursive: true });
|
|
31961
|
+
await writeFile8(
|
|
31448
31962
|
tmpFile,
|
|
31449
31963
|
JSON.stringify(
|
|
31450
31964
|
{
|
|
@@ -31479,7 +31993,7 @@ async function hasLiveLegacyDaemon(slockHome) {
|
|
|
31479
31993
|
for (const name of machineDirs) {
|
|
31480
31994
|
if (!name.startsWith("machine-")) continue;
|
|
31481
31995
|
try {
|
|
31482
|
-
const raw = await
|
|
31996
|
+
const raw = await readFile8(join3(slockHome, "machines", name, "daemon.lock", "owner.json"), "utf8");
|
|
31483
31997
|
const owner = JSON.parse(raw);
|
|
31484
31998
|
if (typeof owner.pid === "number" && isProcessAlive2(owner.pid)) return true;
|
|
31485
31999
|
} catch {
|
|
@@ -31496,10 +32010,10 @@ function hasAmbientLegacyKeyInput() {
|
|
|
31496
32010
|
async function runSetup(opts, deps = {}) {
|
|
31497
32011
|
const slockHome = resolveSlockHome();
|
|
31498
32012
|
const isTty = deps.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
31499
|
-
const
|
|
31500
|
-
const
|
|
32013
|
+
const login2 = deps.runLogin ?? runLogin;
|
|
32014
|
+
const attach2 = deps.runAttach ?? runAttach;
|
|
31501
32015
|
const adopt = deps.runAdoptLegacy ?? runAdoptLegacy;
|
|
31502
|
-
const
|
|
32016
|
+
const start2 = deps.runStart ?? runStart;
|
|
31503
32017
|
const refreshSession = deps.refreshUserSession ?? refreshUserSession;
|
|
31504
32018
|
const legacyDaemonCheck = deps.hasLiveLegacyDaemon ?? hasLiveLegacyDaemon;
|
|
31505
32019
|
if (!isTty && !opts.yes) {
|
|
@@ -31527,7 +32041,7 @@ async function runSetup(opts, deps = {}) {
|
|
|
31527
32041
|
);
|
|
31528
32042
|
}
|
|
31529
32043
|
info("User session: missing or expired; starting login.");
|
|
31530
|
-
await
|
|
32044
|
+
await login2({ serverUrl: opts.serverUrl, orchestrated: true });
|
|
31531
32045
|
}
|
|
31532
32046
|
} else {
|
|
31533
32047
|
info("User session: already logged in.");
|
|
@@ -31564,7 +32078,7 @@ async function runSetup(opts, deps = {}) {
|
|
|
31564
32078
|
if (opts.adoptLegacy) {
|
|
31565
32079
|
info("Legacy key: not provided; falling back to fresh device-login attach.");
|
|
31566
32080
|
}
|
|
31567
|
-
await
|
|
32081
|
+
await attach2({
|
|
31568
32082
|
serverSlug: opts.serverSlug,
|
|
31569
32083
|
serverUrl: opts.serverUrl,
|
|
31570
32084
|
name: opts.name,
|
|
@@ -31584,7 +32098,7 @@ async function runSetup(opts, deps = {}) {
|
|
|
31584
32098
|
info(`Start: skipped (--no-start). Run \`slock-computer start ${label}\` when ready.`);
|
|
31585
32099
|
return;
|
|
31586
32100
|
}
|
|
31587
|
-
await
|
|
32101
|
+
await start2({
|
|
31588
32102
|
serverId: attachment.serverId,
|
|
31589
32103
|
serverLabel: opts.serverSlug,
|
|
31590
32104
|
foreground: opts.foreground
|
|
@@ -31593,10 +32107,10 @@ async function runSetup(opts, deps = {}) {
|
|
|
31593
32107
|
|
|
31594
32108
|
// src/status.ts
|
|
31595
32109
|
init_esm_shims();
|
|
31596
|
-
import { readFile as
|
|
32110
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
31597
32111
|
async function readUserSession(path3) {
|
|
31598
32112
|
try {
|
|
31599
|
-
const parsed = JSON.parse(await
|
|
32113
|
+
const parsed = JSON.parse(await readFile9(path3, "utf8"));
|
|
31600
32114
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
31601
32115
|
return { state: "present", session: parsed, error: null };
|
|
31602
32116
|
}
|
|
@@ -31976,14 +32490,14 @@ async function runDoctor(opts) {
|
|
|
31976
32490
|
|
|
31977
32491
|
// src/logs.ts
|
|
31978
32492
|
init_esm_shims();
|
|
31979
|
-
import { readFile as
|
|
32493
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
31980
32494
|
var DEFAULT_LINES = 200;
|
|
31981
32495
|
async function runLogs(opts) {
|
|
31982
32496
|
const home = resolveSlockHome();
|
|
31983
32497
|
const file = opts.supervisor ? supervisorLogPath(home) : serverDaemonLogPath(home, await resolveTargetServerId({ server: opts.server }));
|
|
31984
32498
|
let content;
|
|
31985
32499
|
try {
|
|
31986
|
-
content = await
|
|
32500
|
+
content = await readFile10(file, "utf8");
|
|
31987
32501
|
} catch {
|
|
31988
32502
|
fail(
|
|
31989
32503
|
"NO_DAEMON_LOG",
|
|
@@ -32000,13 +32514,13 @@ async function runLogs(opts) {
|
|
|
32000
32514
|
// src/concurrency.ts
|
|
32001
32515
|
init_esm_shims();
|
|
32002
32516
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
32003
|
-
import { mkdir as
|
|
32517
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
32004
32518
|
import { join as join4 } from "path";
|
|
32005
32519
|
var STALE_LOCK_THRESHOLD_MS = 6e4;
|
|
32006
32520
|
async function withMutationLock(fn) {
|
|
32007
32521
|
const slockHome = resolveSlockHome();
|
|
32008
32522
|
const lockTarget = computerDir(slockHome);
|
|
32009
|
-
await
|
|
32523
|
+
await mkdir10(lockTarget, { recursive: true });
|
|
32010
32524
|
const lockfilePath = join4(lockTarget, ".lock");
|
|
32011
32525
|
let release = null;
|
|
32012
32526
|
try {
|
|
@@ -32045,8 +32559,8 @@ async function withMutationLock(fn) {
|
|
|
32045
32559
|
|
|
32046
32560
|
// src/channel.ts
|
|
32047
32561
|
init_esm_shims();
|
|
32048
|
-
import { readFile as
|
|
32049
|
-
import { dirname as
|
|
32562
|
+
import { readFile as readFile11, writeFile as writeFile9, mkdir as mkdir11 } from "fs/promises";
|
|
32563
|
+
import { dirname as dirname10 } from "path";
|
|
32050
32564
|
var DEFAULT_CHANNEL = "latest";
|
|
32051
32565
|
var SEMVER_RE = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
|
|
32052
32566
|
function parseChannel(raw) {
|
|
@@ -32061,7 +32575,7 @@ function parseChannel(raw) {
|
|
|
32061
32575
|
}
|
|
32062
32576
|
async function readChannel(slockHome) {
|
|
32063
32577
|
try {
|
|
32064
|
-
const raw = await
|
|
32578
|
+
const raw = await readFile11(channelPath(slockHome), "utf8");
|
|
32065
32579
|
const parsed = parseChannel(raw);
|
|
32066
32580
|
if (parsed !== null) return parsed;
|
|
32067
32581
|
} catch {
|
|
@@ -32070,8 +32584,8 @@ async function readChannel(slockHome) {
|
|
|
32070
32584
|
}
|
|
32071
32585
|
async function writeChannel(slockHome, channel2) {
|
|
32072
32586
|
const p = channelPath(slockHome);
|
|
32073
|
-
await
|
|
32074
|
-
await
|
|
32587
|
+
await mkdir11(dirname10(p), { recursive: true });
|
|
32588
|
+
await writeFile9(p, `${channel2}
|
|
32075
32589
|
`, { mode: 384 });
|
|
32076
32590
|
}
|
|
32077
32591
|
async function runChannelShow(slockHome) {
|
|
@@ -32094,20 +32608,20 @@ async function runChannelSet(slockHome, raw) {
|
|
|
32094
32608
|
|
|
32095
32609
|
// src/upgradeCli.ts
|
|
32096
32610
|
init_esm_shims();
|
|
32097
|
-
import { readFile as
|
|
32611
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
32098
32612
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
32099
|
-
import { dirname as
|
|
32613
|
+
import { dirname as dirname11, join as join7 } from "path";
|
|
32100
32614
|
|
|
32101
32615
|
// src/upgrade.ts
|
|
32102
32616
|
init_esm_shims();
|
|
32103
32617
|
import { spawn as spawn4 } from "child_process";
|
|
32104
|
-
import { mkdir as
|
|
32618
|
+
import { mkdir as mkdir12, readFile as readFile13, writeFile as writeFile10, rm as rm3, rename as rename4 } from "fs/promises";
|
|
32105
32619
|
import { join as join6 } from "path";
|
|
32106
32620
|
import { createHash as createHash3 } from "crypto";
|
|
32107
32621
|
|
|
32108
32622
|
// src/preflightDepDrift.ts
|
|
32109
32623
|
init_esm_shims();
|
|
32110
|
-
import { readFile as
|
|
32624
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
32111
32625
|
import { spawn as spawn3 } from "child_process";
|
|
32112
32626
|
import { createRequire } from "module";
|
|
32113
32627
|
import { join as join5 } from "path";
|
|
@@ -32278,7 +32792,7 @@ async function defaultReadTarballPackageJson(tarballPath) {
|
|
|
32278
32792
|
return JSON.parse(raw);
|
|
32279
32793
|
}
|
|
32280
32794
|
async function defaultReadCurrentPackageJson(currentBinaryDir) {
|
|
32281
|
-
const raw = await
|
|
32795
|
+
const raw = await readFile12(join5(currentBinaryDir, "package.json"), "utf8");
|
|
32282
32796
|
return JSON.parse(raw);
|
|
32283
32797
|
}
|
|
32284
32798
|
async function defaultReadInstalledDaemonVersion(currentBinaryDir) {
|
|
@@ -32295,7 +32809,7 @@ async function defaultReadInstalledDaemonVersion(currentBinaryDir) {
|
|
|
32295
32809
|
for (const base of searchPaths) {
|
|
32296
32810
|
const candidate = join5(base, ...subPath, "package.json");
|
|
32297
32811
|
try {
|
|
32298
|
-
const raw = await
|
|
32812
|
+
const raw = await readFile12(candidate, "utf8");
|
|
32299
32813
|
const parsed = JSON.parse(raw);
|
|
32300
32814
|
if (parsed.name !== DAEMON_PACKAGE_NAME) continue;
|
|
32301
32815
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
@@ -32375,9 +32889,9 @@ function satisfiesTilde(ver, base) {
|
|
|
32375
32889
|
// src/upgrade.ts
|
|
32376
32890
|
async function stagePhase(slockHome, version, deps = {}) {
|
|
32377
32891
|
const npmPack = deps.npmPack ?? defaultNpmPack;
|
|
32378
|
-
const fsReadFile = deps.fsReadFile ??
|
|
32892
|
+
const fsReadFile = deps.fsReadFile ?? readFile13;
|
|
32379
32893
|
const stagedPath = upgradeStagingDir(slockHome, version);
|
|
32380
|
-
await
|
|
32894
|
+
await mkdir12(stagedPath, { recursive: true });
|
|
32381
32895
|
const packageRef = `@slock-ai/computer@${version}`;
|
|
32382
32896
|
const result = await npmPack(stagedPath, packageRef);
|
|
32383
32897
|
if (result.exitCode !== 0) {
|
|
@@ -32485,8 +32999,8 @@ async function cleanupStaged(slockHome, version) {
|
|
|
32485
32999
|
async function snapshotPhase(slockHome, snap) {
|
|
32486
33000
|
const path3 = upgradeSnapshotPath(slockHome);
|
|
32487
33001
|
const tmp = `${path3}.tmp`;
|
|
32488
|
-
await
|
|
32489
|
-
await
|
|
33002
|
+
await mkdir12(computerDir(slockHome), { recursive: true });
|
|
33003
|
+
await writeFile10(tmp, JSON.stringify(snap, null, 2), { mode: 384 });
|
|
32490
33004
|
await rename4(tmp, path3);
|
|
32491
33005
|
}
|
|
32492
33006
|
async function clearUpgradeSnapshot(slockHome) {
|
|
@@ -32498,7 +33012,7 @@ async function clearUpgradeSnapshot(slockHome) {
|
|
|
32498
33012
|
}
|
|
32499
33013
|
async function extractTarball(tarballPath, destDir, deps = {}) {
|
|
32500
33014
|
const tarSpawn = deps.tarSpawn ?? defaultTarSpawn;
|
|
32501
|
-
await
|
|
33015
|
+
await mkdir12(destDir, { recursive: true });
|
|
32502
33016
|
const result = await tarSpawn(tarballPath, destDir);
|
|
32503
33017
|
if (result.exitCode !== 0) {
|
|
32504
33018
|
const err = new Error(
|
|
@@ -32603,7 +33117,7 @@ async function rollbackSwap(currentBinaryDir, deps = {}) {
|
|
|
32603
33117
|
}
|
|
32604
33118
|
async function rollbackRestart(slockHome, currentBinaryDir, deps = {}) {
|
|
32605
33119
|
const readSupervisorPid = deps.readSupervisorPid ?? (() => defaultReadSupervisorPid(slockHome));
|
|
32606
|
-
const
|
|
33120
|
+
const isProcessAlive4 = deps.isProcessAlive ?? defaultIsProcessAlive;
|
|
32607
33121
|
const killSupervisor = deps.killSupervisor ?? defaultKillSupervisor;
|
|
32608
33122
|
const forceKillSupervisor = deps.forceKillSupervisor ?? defaultForceKillSupervisor;
|
|
32609
33123
|
const waitForExit = deps.waitForExit ?? defaultWaitForExit;
|
|
@@ -32614,7 +33128,7 @@ async function rollbackRestart(slockHome, currentBinaryDir, deps = {}) {
|
|
|
32614
33128
|
let supervisorStopped = false;
|
|
32615
33129
|
try {
|
|
32616
33130
|
const pid = await readSupervisorPid();
|
|
32617
|
-
if (pid === null || !
|
|
33131
|
+
if (pid === null || !isProcessAlive4(pid)) {
|
|
32618
33132
|
supervisorStopped = true;
|
|
32619
33133
|
} else {
|
|
32620
33134
|
await killSupervisor(pid);
|
|
@@ -32683,9 +33197,9 @@ async function rollbackRestart(slockHome, currentBinaryDir, deps = {}) {
|
|
|
32683
33197
|
reason: e instanceof Error ? e.message : String(e)
|
|
32684
33198
|
};
|
|
32685
33199
|
}
|
|
32686
|
-
const
|
|
33200
|
+
const start2 = Date.now();
|
|
32687
33201
|
let healthy = false;
|
|
32688
|
-
while (Date.now() -
|
|
33202
|
+
while (Date.now() - start2 < healthTimeoutMs) {
|
|
32689
33203
|
if (await healthCheck()) {
|
|
32690
33204
|
healthy = true;
|
|
32691
33205
|
break;
|
|
@@ -32746,10 +33260,10 @@ async function restartPhase(slockHome, deps = {}) {
|
|
|
32746
33260
|
return { ok: false, reason: "spawn_failed" };
|
|
32747
33261
|
}
|
|
32748
33262
|
}
|
|
32749
|
-
const
|
|
32750
|
-
while (Date.now() -
|
|
33263
|
+
const start2 = Date.now();
|
|
33264
|
+
while (Date.now() - start2 < healthTimeoutMs) {
|
|
32751
33265
|
if (await healthCheck()) {
|
|
32752
|
-
return { ok: true, healthAfterMs: Date.now() -
|
|
33266
|
+
return { ok: true, healthAfterMs: Date.now() - start2 };
|
|
32753
33267
|
}
|
|
32754
33268
|
await new Promise((r) => setTimeout(r, healthPollIntervalMs));
|
|
32755
33269
|
}
|
|
@@ -32757,7 +33271,7 @@ async function restartPhase(slockHome, deps = {}) {
|
|
|
32757
33271
|
}
|
|
32758
33272
|
async function defaultReadSupervisorPid(slockHome) {
|
|
32759
33273
|
try {
|
|
32760
|
-
const raw = (await
|
|
33274
|
+
const raw = (await readFile13(supervisorPidPath(slockHome), "utf8")).trim();
|
|
32761
33275
|
const pid = Number.parseInt(raw, 10);
|
|
32762
33276
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
32763
33277
|
} catch {
|
|
@@ -32784,8 +33298,8 @@ async function defaultIsDaemonBusy(_slockHome) {
|
|
|
32784
33298
|
return false;
|
|
32785
33299
|
}
|
|
32786
33300
|
async function defaultWaitForExit(pid, timeoutMs) {
|
|
32787
|
-
const
|
|
32788
|
-
while (Date.now() -
|
|
33301
|
+
const start2 = Date.now();
|
|
33302
|
+
while (Date.now() - start2 < timeoutMs) {
|
|
32789
33303
|
try {
|
|
32790
33304
|
process.kill(pid, 0);
|
|
32791
33305
|
} catch (err) {
|
|
@@ -33019,20 +33533,20 @@ async function runUpgrade(slockHome, opts) {
|
|
|
33019
33533
|
async function rollingDaemonHealthCheck(slockHome, deps = {}) {
|
|
33020
33534
|
const list = deps.listManagedServerIds ?? listManagedServerIds;
|
|
33021
33535
|
const readDaemonPid = deps.readDaemonPid ?? defaultReadDaemonPid;
|
|
33022
|
-
const
|
|
33536
|
+
const isProcessAlive4 = deps.isProcessAlive ?? defaultIsProcessAlive;
|
|
33023
33537
|
const perDaemonTimeoutMs = deps.perDaemonTimeoutMs ?? 3e4;
|
|
33024
33538
|
const pollIntervalMs = deps.pollIntervalMs ?? 500;
|
|
33025
33539
|
const managed = await list(slockHome);
|
|
33026
33540
|
const daemons = [];
|
|
33027
33541
|
for (const serverId of managed) {
|
|
33028
|
-
const
|
|
33542
|
+
const start2 = Date.now();
|
|
33029
33543
|
let lastReason = "timeout";
|
|
33030
33544
|
let healthy = false;
|
|
33031
|
-
while (Date.now() -
|
|
33545
|
+
while (Date.now() - start2 < perDaemonTimeoutMs) {
|
|
33032
33546
|
const pid = await readDaemonPid(slockHome, serverId);
|
|
33033
33547
|
if (pid === null) {
|
|
33034
33548
|
lastReason = "pidfile_missing";
|
|
33035
|
-
} else if (!
|
|
33549
|
+
} else if (!isProcessAlive4(pid)) {
|
|
33036
33550
|
lastReason = "process_dead";
|
|
33037
33551
|
} else {
|
|
33038
33552
|
healthy = true;
|
|
@@ -33040,7 +33554,7 @@ async function rollingDaemonHealthCheck(slockHome, deps = {}) {
|
|
|
33040
33554
|
}
|
|
33041
33555
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
33042
33556
|
}
|
|
33043
|
-
const entry = healthy ? { serverId, ok: true, healthAfterMs: Date.now() -
|
|
33557
|
+
const entry = healthy ? { serverId, ok: true, healthAfterMs: Date.now() - start2 } : { serverId, ok: false, reason: lastReason };
|
|
33044
33558
|
daemons.push(entry);
|
|
33045
33559
|
if (!healthy) {
|
|
33046
33560
|
return { ok: false, daemons };
|
|
@@ -33050,7 +33564,7 @@ async function rollingDaemonHealthCheck(slockHome, deps = {}) {
|
|
|
33050
33564
|
}
|
|
33051
33565
|
async function defaultReadDaemonPid(slockHome, serverId) {
|
|
33052
33566
|
try {
|
|
33053
|
-
const raw = (await
|
|
33567
|
+
const raw = (await readFile13(serverDaemonPidPath(slockHome, serverId), "utf8")).trim();
|
|
33054
33568
|
const pid = Number.parseInt(raw, 10);
|
|
33055
33569
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
33056
33570
|
} catch {
|
|
@@ -33078,7 +33592,7 @@ async function locateStagedTarball(stagedPath) {
|
|
|
33078
33592
|
|
|
33079
33593
|
// src/upgradeLog.ts
|
|
33080
33594
|
init_esm_shims();
|
|
33081
|
-
import { chmod as chmod5, mkdir as
|
|
33595
|
+
import { chmod as chmod5, mkdir as mkdir13, open as open2 } from "fs/promises";
|
|
33082
33596
|
var FILE_MODE = 384;
|
|
33083
33597
|
var UPGRADE_ERROR_CODES = [
|
|
33084
33598
|
"UPGRADE_DEPS_CHANGED",
|
|
@@ -33123,7 +33637,7 @@ function assertUpgradeLogEntry(entry) {
|
|
|
33123
33637
|
}
|
|
33124
33638
|
async function appendUpgradeLogEntry(slockHome, entry) {
|
|
33125
33639
|
assertUpgradeLogEntry(entry);
|
|
33126
|
-
await
|
|
33640
|
+
await mkdir13(computerDir(slockHome), { recursive: true });
|
|
33127
33641
|
const path3 = upgradeLogPath(slockHome);
|
|
33128
33642
|
const at = entry.at ?? formatUpgradeLogTimestamp();
|
|
33129
33643
|
const fullEntry = { ...entry, at };
|
|
@@ -33146,7 +33660,7 @@ function isEphemeralNpxContext(binaryDir) {
|
|
|
33146
33660
|
async function readBundledDaemonVersion(binaryDir) {
|
|
33147
33661
|
try {
|
|
33148
33662
|
const pkgPath = join7(binaryDir, "package.json");
|
|
33149
|
-
const raw = await
|
|
33663
|
+
const raw = await readFile14(pkgPath, "utf8");
|
|
33150
33664
|
const parsed = JSON.parse(raw);
|
|
33151
33665
|
const pinned = parsed.dependencies?.["@slock-ai/daemon"];
|
|
33152
33666
|
if (typeof pinned !== "string" || pinned.length === 0) return null;
|
|
@@ -33374,12 +33888,12 @@ async function defaultFetchDistTags() {
|
|
|
33374
33888
|
}
|
|
33375
33889
|
function defaultCurrentBinaryDir() {
|
|
33376
33890
|
const here = fileURLToPath3(import.meta.url);
|
|
33377
|
-
return
|
|
33891
|
+
return dirname11(dirname11(here));
|
|
33378
33892
|
}
|
|
33379
33893
|
async function defaultCurrentVersion() {
|
|
33380
33894
|
const pkgPath = join7(defaultCurrentBinaryDir(), "package.json");
|
|
33381
33895
|
try {
|
|
33382
|
-
const raw = await
|
|
33896
|
+
const raw = await readFile14(pkgPath, "utf8");
|
|
33383
33897
|
const parsed = JSON.parse(raw);
|
|
33384
33898
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
33385
33899
|
return parsed.version;
|
|
@@ -33391,7 +33905,7 @@ async function defaultCurrentVersion() {
|
|
|
33391
33905
|
|
|
33392
33906
|
// src/upgradeTestHarness.ts
|
|
33393
33907
|
init_esm_shims();
|
|
33394
|
-
import { mkdir as
|
|
33908
|
+
import { mkdir as mkdir14, readdir as readdir4, stat as stat3, writeFile as writeFile11 } from "fs/promises";
|
|
33395
33909
|
import { join as join8 } from "path";
|
|
33396
33910
|
import { createHash as createHash4 } from "crypto";
|
|
33397
33911
|
var PHASES = /* @__PURE__ */ new Set([
|
|
@@ -33450,7 +33964,7 @@ function buildSimulatedDeps(slockHome, opts) {
|
|
|
33450
33964
|
return { tarballPath: "", exitCode: 1, stderr: "simulated stage failure" };
|
|
33451
33965
|
}
|
|
33452
33966
|
const filename = `slock-ai-computer-${targetVersion}.tgz`;
|
|
33453
|
-
await
|
|
33967
|
+
await writeFile11(join8(cwd, filename), tarballBytes);
|
|
33454
33968
|
return { tarballPath: join8(cwd, filename), exitCode: 0, stderr: "" };
|
|
33455
33969
|
},
|
|
33456
33970
|
fetchAdvertisedHash: async () => {
|
|
@@ -33462,8 +33976,8 @@ function buildSimulatedDeps(slockHome, opts) {
|
|
|
33462
33976
|
if (opts.simulateFail === "extract") {
|
|
33463
33977
|
return { exitCode: 1, stderr: "simulated extract failure" };
|
|
33464
33978
|
}
|
|
33465
|
-
await
|
|
33466
|
-
await
|
|
33979
|
+
await mkdir14(join8(destDir, "package"), { recursive: true });
|
|
33980
|
+
await writeFile11(join8(destDir, "package", "marker.txt"), `NEW@${targetVersion}`);
|
|
33467
33981
|
return { exitCode: 0, stderr: "" };
|
|
33468
33982
|
},
|
|
33469
33983
|
npmInstall: async () => ({ exitCode: 0, stderr: "" }),
|
|
@@ -33524,7 +34038,7 @@ function buildSimulatedDeps(slockHome, opts) {
|
|
|
33524
34038
|
}
|
|
33525
34039
|
async function arrangeSnapshotFailure(slockHome) {
|
|
33526
34040
|
const snapshotPath = join8(slockHome, "computer", "upgrade-snapshot.json");
|
|
33527
|
-
await
|
|
34041
|
+
await mkdir14(snapshotPath, { recursive: true });
|
|
33528
34042
|
}
|
|
33529
34043
|
async function pathInfo(path3) {
|
|
33530
34044
|
try {
|
|
@@ -33579,12 +34093,12 @@ async function runUpgradeTestHarness(slockHome, opts, writer = (s) => process.st
|
|
|
33579
34093
|
process.exitCode = 1;
|
|
33580
34094
|
return;
|
|
33581
34095
|
}
|
|
33582
|
-
await
|
|
34096
|
+
await mkdir14(slockHome, { recursive: true });
|
|
33583
34097
|
if (opts.simulateFail === "snapshot") {
|
|
33584
34098
|
await arrangeSnapshotFailure(slockHome);
|
|
33585
34099
|
}
|
|
33586
34100
|
const { opts: upgradeOpts } = buildSimulatedDeps(slockHome, opts);
|
|
33587
|
-
await
|
|
34101
|
+
await mkdir14(upgradeOpts.currentBinaryDir, { recursive: true });
|
|
33588
34102
|
let outcome;
|
|
33589
34103
|
try {
|
|
33590
34104
|
outcome = await runUpgrade(slockHome, upgradeOpts);
|
|
@@ -33622,9 +34136,9 @@ async function runUpgradeTestHarness(slockHome, opts, writer = (s) => process.st
|
|
|
33622
34136
|
|
|
33623
34137
|
// src/upgradeInstallSmoke.ts
|
|
33624
34138
|
init_esm_shims();
|
|
33625
|
-
import { copyFile, mkdir as
|
|
34139
|
+
import { copyFile, mkdir as mkdir15, readFile as readFile15 } from "fs/promises";
|
|
33626
34140
|
import { createHash as createHash5 } from "crypto";
|
|
33627
|
-
import { dirname as
|
|
34141
|
+
import { dirname as dirname12, isAbsolute, join as join9, resolve as pathResolve } from "path";
|
|
33628
34142
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
33629
34143
|
async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
|
|
33630
34144
|
if (typeof opts.packageTarball !== "string" || opts.packageTarball.trim().length === 0) {
|
|
@@ -33633,7 +34147,7 @@ async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
|
|
|
33633
34147
|
const tarballPath = isAbsolute(opts.packageTarball) ? opts.packageTarball : pathResolve(opts.packageTarball);
|
|
33634
34148
|
let tarballBytes;
|
|
33635
34149
|
try {
|
|
33636
|
-
tarballBytes = await
|
|
34150
|
+
tarballBytes = await readFile15(tarballPath);
|
|
33637
34151
|
} catch (e) {
|
|
33638
34152
|
const msg = e instanceof Error ? e.message : String(e);
|
|
33639
34153
|
throw new Error(`__upgrade-install-smoke: cannot read --package-tarball ${tarballPath}: ${msg}`);
|
|
@@ -33645,7 +34159,7 @@ async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
|
|
|
33645
34159
|
const spawnFreshSupervisor = deps.spawnFreshSupervisor ?? (async (h) => {
|
|
33646
34160
|
await spawnDetachedSupervisor(h);
|
|
33647
34161
|
});
|
|
33648
|
-
await
|
|
34162
|
+
await mkdir15(slockHome, { recursive: true });
|
|
33649
34163
|
const outcome = await runUpgrade(slockHome, {
|
|
33650
34164
|
targetVersion: opts.targetVersion,
|
|
33651
34165
|
fromVersion,
|
|
@@ -33703,12 +34217,12 @@ async function runUpgradeInstallSmokeCli(slockHome, opts, writer = (s) => proces
|
|
|
33703
34217
|
}
|
|
33704
34218
|
function defaultCurrentBinaryDirLocal() {
|
|
33705
34219
|
const here = fileURLToPath4(import.meta.url);
|
|
33706
|
-
return
|
|
34220
|
+
return dirname12(dirname12(here));
|
|
33707
34221
|
}
|
|
33708
34222
|
async function defaultCurrentVersionLocal() {
|
|
33709
34223
|
const pkgPath = join9(defaultCurrentBinaryDirLocal(), "package.json");
|
|
33710
34224
|
try {
|
|
33711
|
-
const raw = await
|
|
34225
|
+
const raw = await readFile15(pkgPath, "utf8");
|
|
33712
34226
|
const parsed = JSON.parse(raw);
|
|
33713
34227
|
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
33714
34228
|
return parsed.version;
|