@slock-ai/computer 0.0.16 → 0.0.17

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 CHANGED
@@ -5729,9 +5729,9 @@ var require_dispatcher_base = __commonJS({
5729
5729
  }
5730
5730
  close(callback) {
5731
5731
  if (callback === void 0) {
5732
- return new Promise((resolve, reject) => {
5732
+ return new Promise((resolve2, reject) => {
5733
5733
  this.close((err, data) => {
5734
- return err ? reject(err) : resolve(data);
5734
+ return err ? reject(err) : resolve2(data);
5735
5735
  });
5736
5736
  });
5737
5737
  }
@@ -5769,9 +5769,9 @@ var require_dispatcher_base = __commonJS({
5769
5769
  err = null;
5770
5770
  }
5771
5771
  if (callback === void 0) {
5772
- return new Promise((resolve, reject) => {
5772
+ return new Promise((resolve2, reject) => {
5773
5773
  this.destroy(err, (err2, data) => {
5774
- return err2 ? reject(err2) : resolve(data);
5774
+ return err2 ? reject(err2) : resolve2(data);
5775
5775
  });
5776
5776
  });
5777
5777
  }
@@ -9264,8 +9264,8 @@ var require_promise = __commonJS({
9264
9264
  function createDeferredPromise() {
9265
9265
  let res;
9266
9266
  let rej;
9267
- const promise = new Promise((resolve, reject) => {
9268
- res = resolve;
9267
+ const promise = new Promise((resolve2, reject) => {
9268
+ res = resolve2;
9269
9269
  rej = reject;
9270
9270
  });
9271
9271
  return { promise, resolve: res, reject: rej };
@@ -10568,12 +10568,12 @@ upgrade: ${upgrade}\r
10568
10568
  cb();
10569
10569
  }
10570
10570
  }
10571
- const waitForDrain = () => new Promise((resolve, reject) => {
10571
+ const waitForDrain = () => new Promise((resolve2, reject) => {
10572
10572
  assert(callback === null);
10573
10573
  if (socket[kError]) {
10574
10574
  reject(socket[kError]);
10575
10575
  } else {
10576
- callback = resolve;
10576
+ callback = resolve2;
10577
10577
  }
10578
10578
  });
10579
10579
  socket.on("close", onDrain).on("drain", onDrain);
@@ -11419,12 +11419,12 @@ var require_client_h2 = __commonJS({
11419
11419
  cb();
11420
11420
  }
11421
11421
  }
11422
- const waitForDrain = () => new Promise((resolve, reject) => {
11422
+ const waitForDrain = () => new Promise((resolve2, reject) => {
11423
11423
  assert(callback === null);
11424
11424
  if (socket[kError]) {
11425
11425
  reject(socket[kError]);
11426
11426
  } else {
11427
- callback = resolve;
11427
+ callback = resolve2;
11428
11428
  }
11429
11429
  });
11430
11430
  h2stream.on("close", onDrain).on("drain", onDrain);
@@ -11736,16 +11736,16 @@ var require_client = __commonJS({
11736
11736
  return this[kNeedDrain] < 2;
11737
11737
  }
11738
11738
  [kClose]() {
11739
- return new Promise((resolve) => {
11739
+ return new Promise((resolve2) => {
11740
11740
  if (this[kSize]) {
11741
- this[kClosedResolve] = resolve;
11741
+ this[kClosedResolve] = resolve2;
11742
11742
  } else {
11743
- resolve(null);
11743
+ resolve2(null);
11744
11744
  }
11745
11745
  });
11746
11746
  }
11747
11747
  [kDestroy](err) {
11748
- return new Promise((resolve) => {
11748
+ return new Promise((resolve2) => {
11749
11749
  const requests = this[kQueue].splice(this[kPendingIdx]);
11750
11750
  for (let i = 0; i < requests.length; i++) {
11751
11751
  const request = requests[i];
@@ -11756,7 +11756,7 @@ var require_client = __commonJS({
11756
11756
  this[kClosedResolve]();
11757
11757
  this[kClosedResolve] = null;
11758
11758
  }
11759
- resolve(null);
11759
+ resolve2(null);
11760
11760
  };
11761
11761
  if (this[kHTTPContext]) {
11762
11762
  this[kHTTPContext].destroy(err, callback);
@@ -12163,8 +12163,8 @@ var require_pool_base = __commonJS({
12163
12163
  }
12164
12164
  return Promise.all(closeAll);
12165
12165
  } else {
12166
- return new Promise((resolve) => {
12167
- this[kClosedResolve] = resolve;
12166
+ return new Promise((resolve2) => {
12167
+ this[kClosedResolve] = resolve2;
12168
12168
  });
12169
12169
  }
12170
12170
  }
@@ -13263,10 +13263,10 @@ var require_socks5_proxy_agent = __commonJS({
13263
13263
  const proxyHost = this[kProxyUrl].hostname;
13264
13264
  const proxyPort = parseInt(this[kProxyUrl].port) || 1080;
13265
13265
  debug("creating SOCKS5 connection to", proxyHost, proxyPort);
13266
- const socket = await new Promise((resolve, reject) => {
13266
+ const socket = await new Promise((resolve2, reject) => {
13267
13267
  const onConnect = () => {
13268
13268
  socket2.removeListener("error", onError);
13269
- resolve(socket2);
13269
+ resolve2(socket2);
13270
13270
  };
13271
13271
  const onError = (err) => {
13272
13272
  socket2.removeListener("connect", onConnect);
@@ -13285,14 +13285,14 @@ var require_socks5_proxy_agent = __commonJS({
13285
13285
  socket.destroy();
13286
13286
  });
13287
13287
  await socks5Client.handshake();
13288
- await new Promise((resolve, reject) => {
13288
+ await new Promise((resolve2, reject) => {
13289
13289
  const timeout = setTimeout(() => {
13290
13290
  reject(new Error("SOCKS5 authentication timeout"));
13291
13291
  }, 5e3);
13292
13292
  const onAuthenticated = () => {
13293
13293
  clearTimeout(timeout);
13294
13294
  socks5Client.removeListener("error", onError);
13295
- resolve();
13295
+ resolve2();
13296
13296
  };
13297
13297
  const onError = (err) => {
13298
13298
  clearTimeout(timeout);
@@ -13301,14 +13301,14 @@ var require_socks5_proxy_agent = __commonJS({
13301
13301
  };
13302
13302
  if (socks5Client.state === "authenticated") {
13303
13303
  clearTimeout(timeout);
13304
- resolve();
13304
+ resolve2();
13305
13305
  } else {
13306
13306
  socks5Client.once("authenticated", onAuthenticated);
13307
13307
  socks5Client.once("error", onError);
13308
13308
  }
13309
13309
  });
13310
13310
  await socks5Client.connect(targetHost, targetPort);
13311
- await new Promise((resolve, reject) => {
13311
+ await new Promise((resolve2, reject) => {
13312
13312
  const timeout = setTimeout(() => {
13313
13313
  reject(new Error("SOCKS5 connection timeout"));
13314
13314
  }, 5e3);
@@ -13316,7 +13316,7 @@ var require_socks5_proxy_agent = __commonJS({
13316
13316
  debug("SOCKS5 tunnel established to", targetHost, targetPort, "via", info2);
13317
13317
  clearTimeout(timeout);
13318
13318
  socks5Client.removeListener("error", onError);
13319
- resolve();
13319
+ resolve2();
13320
13320
  };
13321
13321
  const onError = (err) => {
13322
13322
  clearTimeout(timeout);
@@ -13357,8 +13357,8 @@ var require_socks5_proxy_agent = __commonJS({
13357
13357
  servername: targetHost,
13358
13358
  ...connectOpts.tls || {}
13359
13359
  });
13360
- await new Promise((resolve, reject) => {
13361
- finalSocket.once("secureConnect", resolve);
13360
+ await new Promise((resolve2, reject) => {
13361
+ finalSocket.once("secureConnect", resolve2);
13362
13362
  finalSocket.once("error", reject);
13363
13363
  });
13364
13364
  }
@@ -14389,7 +14389,7 @@ var require_readable = __commonJS({
14389
14389
  if (this._readableState.closeEmitted) {
14390
14390
  return Promise.resolve(null);
14391
14391
  }
14392
- return new Promise((resolve, reject) => {
14392
+ return new Promise((resolve2, reject) => {
14393
14393
  if (this[kContentLength] && this[kContentLength] > limit || this[kBytesRead] > limit) {
14394
14394
  this.destroy(new AbortError());
14395
14395
  }
@@ -14403,11 +14403,11 @@ var require_readable = __commonJS({
14403
14403
  if (signal.aborted) {
14404
14404
  reject(signal.reason ?? new AbortError());
14405
14405
  } else {
14406
- resolve(null);
14406
+ resolve2(null);
14407
14407
  }
14408
14408
  });
14409
14409
  } else {
14410
- this.on("close", resolve);
14410
+ this.on("close", resolve2);
14411
14411
  }
14412
14412
  this.on("error", noop).on("data", () => {
14413
14413
  if (this[kBytesRead] > limit) {
@@ -14435,7 +14435,7 @@ var require_readable = __commonJS({
14435
14435
  }
14436
14436
  function consume(stream, type) {
14437
14437
  assert(!stream[kConsume]);
14438
- return new Promise((resolve, reject) => {
14438
+ return new Promise((resolve2, reject) => {
14439
14439
  if (isUnusable(stream)) {
14440
14440
  const rState = stream._readableState;
14441
14441
  if (rState.destroyed && rState.closeEmitted === false) {
@@ -14450,7 +14450,7 @@ var require_readable = __commonJS({
14450
14450
  stream[kConsume] = {
14451
14451
  type,
14452
14452
  stream,
14453
- resolve,
14453
+ resolve: resolve2,
14454
14454
  reject,
14455
14455
  length: 0,
14456
14456
  body: []
@@ -14524,18 +14524,18 @@ var require_readable = __commonJS({
14524
14524
  return buffer;
14525
14525
  }
14526
14526
  function consumeEnd(consume2, encoding) {
14527
- const { type, body, resolve, stream, length } = consume2;
14527
+ const { type, body, resolve: resolve2, stream, length } = consume2;
14528
14528
  try {
14529
14529
  if (type === "text") {
14530
- resolve(chunksDecode(body, length, encoding));
14530
+ resolve2(chunksDecode(body, length, encoding));
14531
14531
  } else if (type === "json") {
14532
- resolve(JSON.parse(chunksDecode(body, length, encoding)));
14532
+ resolve2(JSON.parse(chunksDecode(body, length, encoding)));
14533
14533
  } else if (type === "arrayBuffer") {
14534
- resolve(chunksConcat(body, length).buffer);
14534
+ resolve2(chunksConcat(body, length).buffer);
14535
14535
  } else if (type === "blob") {
14536
- resolve(new Blob(body, { type: stream[kContentType] }));
14536
+ resolve2(new Blob(body, { type: stream[kContentType] }));
14537
14537
  } else if (type === "bytes") {
14538
- resolve(chunksConcat(body, length));
14538
+ resolve2(chunksConcat(body, length));
14539
14539
  }
14540
14540
  consumeFinish(consume2);
14541
14541
  } catch (err) {
@@ -14726,9 +14726,9 @@ var require_api_request = __commonJS({
14726
14726
  };
14727
14727
  function request(opts, callback) {
14728
14728
  if (callback === void 0) {
14729
- return new Promise((resolve, reject) => {
14729
+ return new Promise((resolve2, reject) => {
14730
14730
  request.call(this, opts, (err, data) => {
14731
- return err ? reject(err) : resolve(data);
14731
+ return err ? reject(err) : resolve2(data);
14732
14732
  });
14733
14733
  });
14734
14734
  }
@@ -14942,9 +14942,9 @@ var require_api_stream = __commonJS({
14942
14942
  };
14943
14943
  function stream(opts, factory, callback) {
14944
14944
  if (callback === void 0) {
14945
- return new Promise((resolve, reject) => {
14945
+ return new Promise((resolve2, reject) => {
14946
14946
  stream.call(this, opts, factory, (err, data) => {
14947
- return err ? reject(err) : resolve(data);
14947
+ return err ? reject(err) : resolve2(data);
14948
14948
  });
14949
14949
  });
14950
14950
  }
@@ -15234,9 +15234,9 @@ var require_api_upgrade = __commonJS({
15234
15234
  };
15235
15235
  function upgrade(opts, callback) {
15236
15236
  if (callback === void 0) {
15237
- return new Promise((resolve, reject) => {
15237
+ return new Promise((resolve2, reject) => {
15238
15238
  upgrade.call(this, opts, (err, data) => {
15239
- return err ? reject(err) : resolve(data);
15239
+ return err ? reject(err) : resolve2(data);
15240
15240
  });
15241
15241
  });
15242
15242
  }
@@ -15330,9 +15330,9 @@ var require_api_connect = __commonJS({
15330
15330
  };
15331
15331
  function connect(opts, callback) {
15332
15332
  if (callback === void 0) {
15333
- return new Promise((resolve, reject) => {
15333
+ return new Promise((resolve2, reject) => {
15334
15334
  connect.call(this, opts, (err, data) => {
15335
- return err ? reject(err) : resolve(data);
15335
+ return err ? reject(err) : resolve2(data);
15336
15336
  });
15337
15337
  });
15338
15338
  }
@@ -16618,7 +16618,7 @@ var require_snapshot_recorder = __commonJS({
16618
16618
  "use strict";
16619
16619
  init_esm_shims();
16620
16620
  var { writeFile: writeFile13, readFile: readFile17, mkdir: mkdir17 } = __require("fs/promises");
16621
- var { dirname: dirname14, resolve } = __require("path");
16621
+ var { dirname: dirname14, resolve: resolve2 } = __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 readFile17(resolve(path3), "utf8");
16822
+ const data = await readFile17(resolve2(path3), "utf8");
16823
16823
  const parsed = JSON.parse(data);
16824
16824
  if (Array.isArray(parsed)) {
16825
16825
  this.#snapshots.clear();
@@ -16848,7 +16848,7 @@ var require_snapshot_recorder = __commonJS({
16848
16848
  if (!path3) {
16849
16849
  throw new InvalidArgumentError2("Snapshot path is required");
16850
16850
  }
16851
- const resolvedPath = resolve(path3);
16851
+ const resolvedPath = resolve2(path3);
16852
16852
  await mkdir17(dirname14(resolvedPath), { recursive: true });
16853
16853
  const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
16854
16854
  hash,
@@ -23714,7 +23714,7 @@ var require_fetch = __commonJS({
23714
23714
  const agent = fetchParams.controller.dispatcher;
23715
23715
  const path3 = url.pathname + url.search;
23716
23716
  const hasTrailingQuestionMark = url.search.length === 0 && url.href[url.href.length - url.hash.length - 1] === "?";
23717
- return new Promise((resolve, reject) => agent.dispatch(
23717
+ return new Promise((resolve2, reject) => agent.dispatch(
23718
23718
  {
23719
23719
  path: hasTrailingQuestionMark ? `${path3}?` : path3,
23720
23720
  origin: url.origin,
@@ -23802,7 +23802,7 @@ var require_fetch = __commonJS({
23802
23802
  }
23803
23803
  }
23804
23804
  const onError = this.onError.bind(this);
23805
- resolve({
23805
+ resolve2({
23806
23806
  status,
23807
23807
  statusText,
23808
23808
  headersList,
@@ -23855,7 +23855,7 @@ var require_fetch = __commonJS({
23855
23855
  headersList.append(headerName, String(value), true);
23856
23856
  }
23857
23857
  }
23858
- resolve({
23858
+ resolve2({
23859
23859
  status,
23860
23860
  statusText: STATUS_CODES[status],
23861
23861
  headersList,
@@ -23879,7 +23879,7 @@ var require_fetch = __commonJS({
23879
23879
  headersList.append(nameStr, value.toString("latin1"), true);
23880
23880
  }
23881
23881
  }
23882
- resolve({
23882
+ resolve2({
23883
23883
  status,
23884
23884
  statusText: STATUS_CODES[status],
23885
23885
  headersList,
@@ -29193,11 +29193,11 @@ var require_mtime_precision = __commonJS({
29193
29193
  function probe(file, fs, callback) {
29194
29194
  const cachedPrecision = fs[cacheSymbol];
29195
29195
  if (cachedPrecision) {
29196
- return fs.stat(file, (err, stat4) => {
29196
+ return fs.stat(file, (err, stat5) => {
29197
29197
  if (err) {
29198
29198
  return callback(err);
29199
29199
  }
29200
- callback(null, stat4.mtime, cachedPrecision);
29200
+ callback(null, stat5.mtime, cachedPrecision);
29201
29201
  });
29202
29202
  }
29203
29203
  const mtime = new Date(Math.ceil(Date.now() / 1e3) * 1e3 + 5);
@@ -29205,13 +29205,13 @@ var require_mtime_precision = __commonJS({
29205
29205
  if (err) {
29206
29206
  return callback(err);
29207
29207
  }
29208
- fs.stat(file, (err2, stat4) => {
29208
+ fs.stat(file, (err2, stat5) => {
29209
29209
  if (err2) {
29210
29210
  return callback(err2);
29211
29211
  }
29212
- const precision = stat4.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
29212
+ const precision = stat5.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
29213
29213
  Object.defineProperty(fs, cacheSymbol, { value: precision });
29214
- callback(null, stat4.mtime, precision);
29214
+ callback(null, stat5.mtime, precision);
29215
29215
  });
29216
29216
  });
29217
29217
  }
@@ -29266,14 +29266,14 @@ var require_lockfile = __commonJS({
29266
29266
  if (options.stale <= 0) {
29267
29267
  return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
29268
29268
  }
29269
- options.fs.stat(lockfilePath, (err2, stat4) => {
29269
+ options.fs.stat(lockfilePath, (err2, stat5) => {
29270
29270
  if (err2) {
29271
29271
  if (err2.code === "ENOENT") {
29272
29272
  return acquireLock(file, { ...options, stale: 0 }, callback);
29273
29273
  }
29274
29274
  return callback(err2);
29275
29275
  }
29276
- if (!isLockStale(stat4, options)) {
29276
+ if (!isLockStale(stat5, options)) {
29277
29277
  return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
29278
29278
  }
29279
29279
  removeLock(file, options, (err3) => {
@@ -29285,8 +29285,8 @@ var require_lockfile = __commonJS({
29285
29285
  });
29286
29286
  });
29287
29287
  }
29288
- function isLockStale(stat4, options) {
29289
- return stat4.mtime.getTime() < Date.now() - options.stale;
29288
+ function isLockStale(stat5, options) {
29289
+ return stat5.mtime.getTime() < Date.now() - options.stale;
29290
29290
  }
29291
29291
  function removeLock(file, options, callback) {
29292
29292
  options.fs.rmdir(getLockFile(file, options), (err) => {
@@ -29304,7 +29304,7 @@ var require_lockfile = __commonJS({
29304
29304
  lock2.updateDelay = lock2.updateDelay || options.update;
29305
29305
  lock2.updateTimeout = setTimeout(() => {
29306
29306
  lock2.updateTimeout = null;
29307
- options.fs.stat(lock2.lockfilePath, (err, stat4) => {
29307
+ options.fs.stat(lock2.lockfilePath, (err, stat5) => {
29308
29308
  const isOverThreshold = lock2.lastUpdate + options.stale < Date.now();
29309
29309
  if (err) {
29310
29310
  if (err.code === "ENOENT" || isOverThreshold) {
@@ -29313,7 +29313,7 @@ var require_lockfile = __commonJS({
29313
29313
  lock2.updateDelay = 1e3;
29314
29314
  return updateLock(file, options);
29315
29315
  }
29316
- const isMtimeOurs = lock2.mtime.getTime() === stat4.mtime.getTime();
29316
+ const isMtimeOurs = lock2.mtime.getTime() === stat5.mtime.getTime();
29317
29317
  if (!isMtimeOurs) {
29318
29318
  return setLockAsCompromised(
29319
29319
  file,
@@ -29438,11 +29438,11 @@ var require_lockfile = __commonJS({
29438
29438
  if (err) {
29439
29439
  return callback(err);
29440
29440
  }
29441
- options.fs.stat(getLockFile(file2, options), (err2, stat4) => {
29441
+ options.fs.stat(getLockFile(file2, options), (err2, stat5) => {
29442
29442
  if (err2) {
29443
29443
  return err2.code === "ENOENT" ? callback(null, false) : callback(err2);
29444
29444
  }
29445
- return callback(null, !isLockStale(stat4, options));
29445
+ return callback(null, !isLockStale(stat5, options));
29446
29446
  });
29447
29447
  });
29448
29448
  }
@@ -29489,12 +29489,12 @@ var require_adapter = __commonJS({
29489
29489
  return newFs;
29490
29490
  }
29491
29491
  function toPromise(method) {
29492
- return (...args) => new Promise((resolve, reject) => {
29492
+ return (...args) => new Promise((resolve2, reject) => {
29493
29493
  args.push((err, result) => {
29494
29494
  if (err) {
29495
29495
  reject(err);
29496
29496
  } else {
29497
- resolve(result);
29497
+ resolve2(result);
29498
29498
  }
29499
29499
  });
29500
29500
  method(...args);
@@ -29799,6 +29799,52 @@ var ComputerAttachClient = class {
29799
29799
  return { ok: false, code };
29800
29800
  }
29801
29801
  };
29802
+ var LegacyMachinesClient = class {
29803
+ constructor(baseUrl, accessToken) {
29804
+ this.baseUrl = baseUrl;
29805
+ this.accessToken = accessToken;
29806
+ }
29807
+ url(p) {
29808
+ return new URL(p, this.baseUrl).toString();
29809
+ }
29810
+ async list(serverSlug) {
29811
+ let res;
29812
+ try {
29813
+ res = await (0, import_undici.fetch)(
29814
+ this.url(`/api/computer/legacy-machines?serverSlug=${encodeURIComponent(serverSlug)}`),
29815
+ {
29816
+ method: "GET",
29817
+ headers: { Authorization: `Bearer ${this.accessToken}` }
29818
+ }
29819
+ );
29820
+ } catch {
29821
+ return { status: "error", code: "request_failed" };
29822
+ }
29823
+ const body = await res.json().catch(() => null);
29824
+ if (res.status === 200 && body && Array.isArray(body.entries)) {
29825
+ const entries = body.entries.map((raw) => {
29826
+ if (!raw || typeof raw !== "object") return null;
29827
+ const e = raw;
29828
+ if (typeof e.daemonId !== "string" || typeof e.apiKeyFingerprint !== "string" || typeof e.machineName !== "string") {
29829
+ return null;
29830
+ }
29831
+ return {
29832
+ daemonId: e.daemonId,
29833
+ apiKeyFingerprint: e.apiKeyFingerprint,
29834
+ machineName: e.machineName,
29835
+ hostname: typeof e.hostname === "string" ? e.hostname : null,
29836
+ lastSeenAt: typeof e.lastSeenAt === "string" ? e.lastSeenAt : null
29837
+ };
29838
+ }).filter((entry) => entry !== null);
29839
+ return { status: "success", entries };
29840
+ }
29841
+ if (res.status === 401) return { status: "auth_required" };
29842
+ if (res.status === 403) return { status: "not_authorized" };
29843
+ if (res.status === 404) return { status: "disabled" };
29844
+ const code = body && typeof body.code === "string" ? body.code : `http_${res.status}`;
29845
+ return { status: "error", code };
29846
+ }
29847
+ };
29802
29848
  var RunnersClient = class {
29803
29849
  constructor(baseUrl, computerApiKey) {
29804
29850
  this.baseUrl = baseUrl;
@@ -29975,14 +30021,14 @@ function resolveServerUrl(...candidates) {
29975
30021
 
29976
30022
  // src/services/login.ts
29977
30023
  function sleep(ms, signal) {
29978
- return new Promise((resolve, reject) => {
30024
+ return new Promise((resolve2, reject) => {
29979
30025
  if (signal?.aborted) {
29980
30026
  reject(abortError(signal));
29981
30027
  return;
29982
30028
  }
29983
30029
  const t = setTimeout(() => {
29984
30030
  signal?.removeEventListener("abort", onAbort);
29985
- resolve();
30031
+ resolve2();
29986
30032
  }, ms);
29987
30033
  const onAbort = () => {
29988
30034
  clearTimeout(t);
@@ -30403,68 +30449,153 @@ import { dirname as dirname9 } from "path";
30403
30449
 
30404
30450
  // src/lib/migration.ts
30405
30451
  init_esm_shims();
30406
- import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
30407
- import { join } from "path";
30452
+ import { readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
30453
+ import { join, resolve } from "path";
30408
30454
  var MACHINE_DIR_PREFIX = "machine-";
30409
- async function readOwnerEvidence(installRoot, machineDirName) {
30410
- const ownerFile = join(
30411
- installRoot,
30412
- "machines",
30413
- machineDirName,
30414
- "daemon.lock",
30415
- "owner.json"
30416
- );
30455
+ var FINGERPRINT_HEX_RE = /^[0-9a-f]{16}$/;
30456
+ async function readLocalOwners(installRoot) {
30457
+ const machinesDir = join(installRoot, "machines");
30458
+ let entries;
30459
+ try {
30460
+ entries = await readdir2(machinesDir);
30461
+ } catch {
30462
+ return [];
30463
+ }
30464
+ const owners = [];
30465
+ for (const name of entries) {
30466
+ if (!name.startsWith(MACHINE_DIR_PREFIX)) continue;
30467
+ const ownerPath = join(machinesDir, name, "daemon.lock", "owner.json");
30468
+ let raw;
30469
+ try {
30470
+ raw = await readFile3(ownerPath, "utf8");
30471
+ } catch {
30472
+ continue;
30473
+ }
30474
+ let parsed;
30475
+ try {
30476
+ parsed = JSON.parse(raw);
30477
+ } catch {
30478
+ continue;
30479
+ }
30480
+ const fp = parsed.apiKeyFingerprint;
30481
+ if (typeof fp !== "string" || !FINGERPRINT_HEX_RE.test(fp)) continue;
30482
+ owners.push({ apiKeyFingerprint: fp, localPath: ownerPath });
30483
+ }
30484
+ return owners;
30485
+ }
30486
+ function indexLocalByFingerprint(owners) {
30487
+ const map = /* @__PURE__ */ new Map();
30488
+ for (const owner of owners) {
30489
+ if (!map.has(owner.apiKeyFingerprint)) {
30490
+ map.set(owner.apiKeyFingerprint, owner);
30491
+ }
30492
+ }
30493
+ return map;
30494
+ }
30495
+ function intersect(localByFp, roster) {
30496
+ const out = [];
30497
+ for (const entry of roster) {
30498
+ const local = localByFp.get(entry.apiKeyFingerprint);
30499
+ if (!local) continue;
30500
+ out.push({
30501
+ apiKeyFingerprint: entry.apiKeyFingerprint,
30502
+ daemonId: entry.daemonId,
30503
+ localPath: local.localPath,
30504
+ machineName: entry.machineName,
30505
+ ...entry.hostname ? { hostname: entry.hostname } : {},
30506
+ ...entry.lastSeenAt ? { lastSeenAt: entry.lastSeenAt } : {}
30507
+ });
30508
+ }
30509
+ out.sort((a, b) => {
30510
+ const aSeen = a.lastSeenAt ?? "";
30511
+ const bSeen = b.lastSeenAt ?? "";
30512
+ if (aSeen !== bSeen) {
30513
+ if (aSeen === "") return 1;
30514
+ if (bSeen === "") return -1;
30515
+ return aSeen < bSeen ? 1 : -1;
30516
+ }
30517
+ return a.apiKeyFingerprint < b.apiKeyFingerprint ? -1 : a.apiKeyFingerprint > b.apiKeyFingerprint ? 1 : 0;
30518
+ });
30519
+ return out;
30520
+ }
30521
+ async function detectLegacyMigration(installRoot, serverSlug, client) {
30522
+ const localOwners = await readLocalOwners(installRoot);
30523
+ if (localOwners.length === 0) {
30524
+ return { kind: "candidates", candidates: [] };
30525
+ }
30526
+ const result = await client.list(serverSlug);
30527
+ if (result.status !== "success") {
30528
+ return { kind: "server-unavailable" };
30529
+ }
30530
+ const localByFp = indexLocalByFingerprint(localOwners);
30531
+ return { kind: "candidates", candidates: intersect(localByFp, result.entries) };
30532
+ }
30533
+ async function validateManualMigratePath(inputPath, roster) {
30534
+ const absInput = resolve(inputPath);
30535
+ let stats;
30536
+ try {
30537
+ stats = await stat(absInput);
30538
+ } catch {
30539
+ return { ok: false, code: "MIGRATE_FROM_NOT_FOUND" };
30540
+ }
30541
+ const ownerPath = stats.isDirectory() ? join(absInput, "daemon.lock", "owner.json") : absInput;
30542
+ const candidateOwnerPath = await firstReadableOwner([
30543
+ ownerPath,
30544
+ join(absInput, "owner.json"),
30545
+ absInput
30546
+ ]);
30547
+ if (!candidateOwnerPath) {
30548
+ return { ok: false, code: "MIGRATE_FROM_INVALID" };
30549
+ }
30417
30550
  let raw;
30418
30551
  try {
30419
- raw = await readFile3(ownerFile, "utf8");
30552
+ raw = await readFile3(candidateOwnerPath, "utf8");
30420
30553
  } catch {
30421
- return {};
30554
+ return { ok: false, code: "MIGRATE_FROM_INVALID" };
30422
30555
  }
30423
30556
  let parsed;
30424
30557
  try {
30425
30558
  parsed = JSON.parse(raw);
30426
30559
  } catch {
30427
- return {};
30560
+ return { ok: false, code: "MIGRATE_FROM_INVALID" };
30428
30561
  }
30429
- const machineName = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : void 0;
30430
- const serverUrl = typeof parsed.serverUrl === "string" && parsed.serverUrl.length > 0 ? parsed.serverUrl : void 0;
30431
- return { machineName, serverUrl };
30432
- }
30433
- async function detectLegacyMigration(installRoot, loggedInUserId) {
30434
- void loggedInUserId;
30435
- const machinesDir = join(installRoot, "machines");
30436
- let entries;
30437
- try {
30438
- entries = await readdir2(machinesDir);
30439
- } catch {
30440
- return { kind: "no-match" };
30562
+ const fp = parsed.apiKeyFingerprint;
30563
+ if (typeof fp !== "string" || !FINGERPRINT_HEX_RE.test(fp)) {
30564
+ return { ok: false, code: "MIGRATE_FROM_INVALID" };
30441
30565
  }
30442
- const candidates = [];
30443
- for (const name of entries) {
30444
- if (!name.startsWith(MACHINE_DIR_PREFIX)) continue;
30445
- const evidence = await readOwnerEvidence(installRoot, name);
30446
- candidates.push({
30447
- machineId: name,
30448
- machineName: evidence.machineName,
30449
- serverUrl: evidence.serverUrl
30450
- });
30566
+ const match = roster.find((r) => r.apiKeyFingerprint === fp);
30567
+ if (!match) {
30568
+ return { ok: false, code: "MIGRATE_FROM_NOT_OWNED" };
30451
30569
  }
30452
- if (candidates.length === 0) return { kind: "no-match" };
30453
- if (candidates.length === 1) {
30454
- const only = candidates[0];
30455
- return {
30456
- kind: "match",
30457
- machineId: only.machineId,
30458
- machineName: only.machineName,
30459
- serverUrl: only.serverUrl
30460
- };
30570
+ return {
30571
+ ok: true,
30572
+ candidate: {
30573
+ apiKeyFingerprint: match.apiKeyFingerprint,
30574
+ daemonId: match.daemonId,
30575
+ localPath: candidateOwnerPath,
30576
+ machineName: match.machineName,
30577
+ ...match.hostname ? { hostname: match.hostname } : {},
30578
+ ...match.lastSeenAt ? { lastSeenAt: match.lastSeenAt } : {}
30579
+ }
30580
+ };
30581
+ }
30582
+ async function firstReadableOwner(paths) {
30583
+ const seen = /* @__PURE__ */ new Set();
30584
+ for (const p of paths) {
30585
+ if (seen.has(p)) continue;
30586
+ seen.add(p);
30587
+ try {
30588
+ const st = await stat(p);
30589
+ if (st.isFile()) return p;
30590
+ } catch {
30591
+ }
30461
30592
  }
30462
- return { kind: "ambiguous", candidates };
30593
+ return null;
30463
30594
  }
30464
30595
 
30465
30596
  // src/services/adoptLegacy.ts
30466
30597
  init_esm_shims();
30467
- import { chmod as chmod3, mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4, appendFile, stat } from "fs/promises";
30598
+ import { chmod as chmod3, mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4, appendFile, stat as stat2 } from "fs/promises";
30468
30599
  import { createHash as createHash2 } from "crypto";
30469
30600
  import { dirname as dirname4, join as join2 } from "path";
30470
30601
  import { setTimeout as delay } from "timers/promises";
@@ -30807,7 +30938,7 @@ async function appendAdoptionLog(slockHome, line) {
30807
30938
  const path3 = adoptionLogPath(slockHome);
30808
30939
  await appendFile(path3, fields.join(" ") + "\n", { mode: 384 });
30809
30940
  try {
30810
- const st = await stat(path3);
30941
+ const st = await stat2(path3);
30811
30942
  if ((st.mode & 63) !== 0) await chmod3(path3, 384);
30812
30943
  } catch {
30813
30944
  }
@@ -30824,7 +30955,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
30824
30955
 
30825
30956
  // src/cleanup.ts
30826
30957
  init_esm_shims();
30827
- import { readdir as readdir3, stat as stat2, unlink as unlink3, rm, rmdir, rename, mkdir as mkdir6 } from "fs/promises";
30958
+ import { readdir as readdir3, stat as stat3, unlink as unlink3, rm, rmdir, rename, mkdir as mkdir6 } from "fs/promises";
30828
30959
  import { spawn } from "child_process";
30829
30960
  import { join as join3 } from "path";
30830
30961
 
@@ -30918,7 +31049,7 @@ async function readPsTable(psSpawn = defaultPsSpawn) {
30918
31049
  }
30919
31050
  }
30920
31051
  function defaultPsSpawn() {
30921
- return new Promise((resolve) => {
31052
+ return new Promise((resolve2) => {
30922
31053
  const child = spawn("ps", ["-o", "pid,ppid,comm", "-A"], {
30923
31054
  stdio: ["ignore", "pipe", "ignore"]
30924
31055
  });
@@ -30926,8 +31057,8 @@ function defaultPsSpawn() {
30926
31057
  child.stdout.on("data", (chunk) => {
30927
31058
  out += String(chunk);
30928
31059
  });
30929
- child.on("error", () => resolve({ stdout: "", exitCode: 1 }));
30930
- child.on("close", (code) => resolve({ stdout: out, exitCode: code ?? 1 }));
31060
+ child.on("error", () => resolve2({ stdout: "", exitCode: 1 }));
31061
+ child.on("close", (code) => resolve2({ stdout: out, exitCode: code ?? 1 }));
30931
31062
  });
30932
31063
  }
30933
31064
  async function cleanupOrphanProcesses(slockHome, psSpawn) {
@@ -31006,7 +31137,7 @@ async function cleanupTmpFiles(slockHome) {
31006
31137
  for (const v of versions) {
31007
31138
  const vdir = join3(stagingDir, v);
31008
31139
  try {
31009
- const s = await stat2(vdir);
31140
+ const s = await stat3(vdir);
31010
31141
  if (Date.now() - s.mtimeMs > TMP_MAX_AGE_MS) {
31011
31142
  await rm(vdir, { recursive: true, force: true });
31012
31143
  removed.push(vdir);
@@ -31023,7 +31154,7 @@ async function cleanupTmpFiles(slockHome) {
31023
31154
  }
31024
31155
  const snap = join3(cdir, "upgrade-snapshot.json");
31025
31156
  try {
31026
- const s = await stat2(snap);
31157
+ const s = await stat3(snap);
31027
31158
  if (Date.now() - s.mtimeMs > TMP_MAX_AGE_MS) {
31028
31159
  await unlink3(snap);
31029
31160
  removed.push(snap);
@@ -31035,7 +31166,7 @@ async function cleanupTmpFiles(slockHome) {
31035
31166
  async function cleanupStaleLock(slockHome) {
31036
31167
  const lockDir = join3(computerDir(slockHome), ".lock");
31037
31168
  try {
31038
- const s = await stat2(lockDir);
31169
+ const s = await stat3(lockDir);
31039
31170
  if (Date.now() - s.mtimeMs > 60 * 1e3) {
31040
31171
  await rm(lockDir, { recursive: true, force: true });
31041
31172
  return [lockDir];
@@ -31047,7 +31178,7 @@ async function cleanupStaleLock(slockHome) {
31047
31178
  async function forceReleaseLock(slockHome) {
31048
31179
  const lockDir = join3(computerDir(slockHome), ".lock");
31049
31180
  try {
31050
- await stat2(lockDir);
31181
+ await stat3(lockDir);
31051
31182
  await rm(lockDir, { recursive: true, force: true });
31052
31183
  return [lockDir];
31053
31184
  } catch {
@@ -31238,7 +31369,7 @@ function emit4(opts, event) {
31238
31369
  async function waitForManagedDaemonPids(slockHome, serverIds, opts) {
31239
31370
  const readPidfile = opts.readPidfile ?? readPidfileAt;
31240
31371
  const isAlive = opts.isProcessAlive ?? isProcessAlive2;
31241
- const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
31372
+ const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
31242
31373
  const timeoutMs = opts.ensureTimeoutMs ?? START_ENSURE_TIMEOUT_MS;
31243
31374
  const pollIntervalMs = opts.ensurePollIntervalMs ?? START_ENSURE_POLL_INTERVAL_MS;
31244
31375
  const deadline = Date.now() + timeoutMs;
@@ -31414,7 +31545,7 @@ async function stop(input = {}, options = {}) {
31414
31545
  const killer = options.killService ?? ((pid2) => {
31415
31546
  process.kill(pid2, "SIGTERM");
31416
31547
  });
31417
- const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
31548
+ const sleep2 = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
31418
31549
  const pollIntervalMs = options.pollIntervalMs ?? STOP_POLL_INTERVAL_MS;
31419
31550
  const timeoutMs = options.timeoutMs ?? STOP_TIMEOUT_MS;
31420
31551
  const { pid, pidfilePath, firstStalePidfile, firstStalePid } = await findLiveServicePid(slockHome, {
@@ -31967,12 +32098,15 @@ async function runDetach(serverId, serverLabel = serverId) {
31967
32098
 
31968
32099
  // src/setup.ts
31969
32100
  var USER_SESSION_EXPIRY_LEEWAY_MS = 3e4;
31970
- async function readUserSessionUserId(slockHome) {
32101
+ async function readUserSessionAuth(slockHome) {
31971
32102
  try {
31972
32103
  const parsed = JSON.parse(await readFile8(userSessionPath(slockHome), "utf8"));
31973
- return typeof parsed.userId === "string" ? parsed.userId : "";
32104
+ return {
32105
+ accessToken: typeof parsed.accessToken === "string" ? parsed.accessToken : "",
32106
+ ...typeof parsed.serverUrl === "string" ? { serverUrl: parsed.serverUrl } : {}
32107
+ };
31974
32108
  } catch {
31975
- return "";
32109
+ return { accessToken: "" };
31976
32110
  }
31977
32111
  }
31978
32112
  async function hasValidUserSession(slockHome) {
@@ -32042,15 +32176,60 @@ async function refreshUserSession(slockHome, serverUrl) {
32042
32176
  return false;
32043
32177
  }
32044
32178
  }
32045
- async function defaultConfirmMigrate(prompt) {
32046
- process.stdout.write(prompt);
32047
- const line = await readLine(false);
32048
- const norm = line.trim().toLowerCase();
32049
- return norm === "y" || norm === "yes";
32179
+ async function pickMigrationCandidateFromInput(candidates, read, write) {
32180
+ write(
32181
+ "Migration: detected legacy daemon(s) on this Computer that match the target server.\n"
32182
+ );
32183
+ candidates.forEach((c, i) => {
32184
+ const host = c.hostname ? ` (${c.hostname})` : "";
32185
+ const seen = c.lastSeenAt ? ` last seen ${c.lastSeenAt}` : "";
32186
+ write(` ${i + 1}. ${c.machineName}${host}${seen}
32187
+ `);
32188
+ write(` local: ${c.localPath}
32189
+ `);
32190
+ });
32191
+ write(" 0. Fresh attach (skip migration)\n");
32192
+ write(" m. Migrate from a different on-disk path\n");
32193
+ while (true) {
32194
+ write("Choose [1]: ");
32195
+ const { line, eof } = await read();
32196
+ if (eof) return { kind: "fresh" };
32197
+ const trimmed = line.trim();
32198
+ const norm = trimmed.toLowerCase();
32199
+ if (norm === "m") {
32200
+ write("Path to legacy machine dir or owner.json: ");
32201
+ const { line: pathLine } = await read();
32202
+ return { kind: "manual", path: pathLine.trim() };
32203
+ }
32204
+ if (norm.length === 0) return { kind: "candidate", index: 0 };
32205
+ if (norm === "0") return { kind: "fresh" };
32206
+ const n = Number.parseInt(norm, 10);
32207
+ if (Number.isFinite(n) && String(n) === norm && n >= 1 && n <= candidates.length) {
32208
+ return { kind: "candidate", index: n - 1 };
32209
+ }
32210
+ write(
32211
+ `Invalid selection "${trimmed}". Enter 1..${candidates.length}, 0, or m.
32212
+ `
32213
+ );
32214
+ }
32050
32215
  }
32051
- async function defaultReadMigrateSecret(prompt) {
32052
- process.stdout.write(prompt);
32053
- const line = await readLine(true);
32216
+ async function defaultPickMigrationCandidate(candidates) {
32217
+ return pickMigrationCandidateFromInput(
32218
+ candidates,
32219
+ () => readLine(false),
32220
+ (s) => {
32221
+ process.stdout.write(s);
32222
+ }
32223
+ );
32224
+ }
32225
+ async function readLegacyApiKey(isTty) {
32226
+ const fromEnv = process.env.SLOCK_LEGACY_API_KEY;
32227
+ if (typeof fromEnv === "string" && fromEnv.length > 0) return fromEnv.trim();
32228
+ if (!isTty) return "";
32229
+ process.stdout.write(
32230
+ "Paste legacy api key (sk_machine_* or sk_daemon_*); input is hidden: "
32231
+ );
32232
+ const { line } = await readLine(true);
32054
32233
  process.stdout.write("\n");
32055
32234
  return line.trim();
32056
32235
  }
@@ -32060,7 +32239,7 @@ async function readLine(masked) {
32060
32239
  const enterRaw = masked && stdin.isTTY === true && typeof stdin.setRawMode === "function";
32061
32240
  if (enterRaw) stdin.setRawMode(true);
32062
32241
  stdin.resume();
32063
- return await new Promise((resolve) => {
32242
+ return await new Promise((resolve2) => {
32064
32243
  let buf = "";
32065
32244
  const cleanup = () => {
32066
32245
  stdin.off("data", onData);
@@ -32073,12 +32252,17 @@ async function readLine(masked) {
32073
32252
  for (const ch of text) {
32074
32253
  if (ch === "\n" || ch === "\r") {
32075
32254
  cleanup();
32076
- resolve(buf);
32255
+ resolve2({ line: buf, eof: false });
32256
+ return;
32257
+ }
32258
+ if (ch === "") {
32259
+ cleanup();
32260
+ resolve2({ line: buf, eof: true });
32077
32261
  return;
32078
32262
  }
32079
32263
  if (ch === "") {
32080
32264
  cleanup();
32081
- resolve("");
32265
+ resolve2({ line: "", eof: false });
32082
32266
  return;
32083
32267
  }
32084
32268
  if (ch === "\x7F" || ch === "\b") {
@@ -32090,12 +32274,88 @@ async function readLine(masked) {
32090
32274
  };
32091
32275
  const onEnd = () => {
32092
32276
  cleanup();
32093
- resolve(buf);
32277
+ resolve2({ line: buf, eof: true });
32094
32278
  };
32095
32279
  stdin.on("data", onData);
32096
32280
  stdin.on("end", onEnd);
32097
32281
  });
32098
32282
  }
32283
+ async function runMigrateAdoption(opts, candidate, isTty, adoptLegacyImpl, readLegacyApiKeyImpl) {
32284
+ const where = candidate.hostname ? ` (${candidate.hostname})` : "";
32285
+ const seen = candidate.lastSeenAt ? ` last seen ${candidate.lastSeenAt}` : "";
32286
+ info(`Migration: adopting legacy daemon "${candidate.machineName}"${where}${seen}.`);
32287
+ info(` local owner.json: ${candidate.localPath}`);
32288
+ const rawKey = await readLegacyApiKeyImpl(isTty);
32289
+ if (rawKey.length === 0) {
32290
+ fail(
32291
+ "LEGACY_KEY_REQUIRED",
32292
+ "No legacy api key provided. Set `SLOCK_LEGACY_API_KEY` or paste the legacy `sk_machine_*` / `sk_daemon_*` key when prompted."
32293
+ );
32294
+ }
32295
+ if (!rawKey.startsWith("sk_machine_") && !rawKey.startsWith("sk_daemon_")) {
32296
+ fail(
32297
+ "LEGACY_KEY_INVALID",
32298
+ "Provided key does not look like a legacy machine key (expected `sk_machine_*` or `sk_daemon_*`)."
32299
+ );
32300
+ }
32301
+ try {
32302
+ await adoptLegacyImpl(
32303
+ {
32304
+ serverSlug: opts.serverSlug,
32305
+ ...opts.serverUrl ? { serverUrl: opts.serverUrl } : {},
32306
+ ...opts.name ? { name: opts.name } : {},
32307
+ rawKey,
32308
+ mode: "legacy_key_stdin",
32309
+ redactedPrefix: rawKey.slice(0, 8)
32310
+ },
32311
+ {
32312
+ onEvent: (event) => {
32313
+ if (event.type === "adopting") {
32314
+ info(
32315
+ `Adopting legacy daemon for ${formatServerSlugDisplay(event.serverSlug)} via ${event.mode}\u2026`
32316
+ );
32317
+ } else if (event.type === "preflight") {
32318
+ info(
32319
+ event.resumed ? "Adopted (resumed prior attachment); running preflight\u2026" : "Adopted; running preflight\u2026"
32320
+ );
32321
+ } else if (event.type === "adopted") {
32322
+ info(`Adopted. Computer state written to ${event.attachmentPath}`);
32323
+ info(` server: ${formatServerSlugDisplay(event.serverSlug)}`);
32324
+ info(` serverMachine: ${event.serverMachineId}`);
32325
+ info(` legacyMachine: ${event.legacyMachineId}`);
32326
+ switch (event.legacyStop.outcome) {
32327
+ case "absent":
32328
+ info(" legacy daemon: not detected on this Computer (no local lock file)");
32329
+ break;
32330
+ case "already_dead":
32331
+ info(` legacy daemon: already stopped (pid ${event.legacyStop.pid} not running)`);
32332
+ break;
32333
+ case "stopped":
32334
+ info(` legacy daemon: stopped (pid ${event.legacyStop.pid}, SIGTERM)`);
32335
+ break;
32336
+ }
32337
+ }
32338
+ }
32339
+ }
32340
+ );
32341
+ } catch (err) {
32342
+ if (err instanceof CliExit) throw err;
32343
+ if (err instanceof ComputerServiceError) {
32344
+ fail(err.code, err.message);
32345
+ }
32346
+ throw err;
32347
+ }
32348
+ }
32349
+ function manualPathErrorMessage(code, inputPath) {
32350
+ switch (code) {
32351
+ case "MIGRATE_FROM_NOT_FOUND":
32352
+ return `--migrate-from path not found: ${inputPath}. Pass the legacy machine directory, its daemon.lock/, or its owner.json file.`;
32353
+ case "MIGRATE_FROM_INVALID":
32354
+ return `--migrate-from path is not a valid legacy daemon install: ${inputPath}. Expected an owner.json with a 16-hex-char apiKeyFingerprint field.`;
32355
+ case "MIGRATE_FROM_NOT_OWNED":
32356
+ return `--migrate-from path's apiKeyFingerprint is not in this server's legacy machine roster for the logged-in user: ${inputPath}. Either the daemon is already migrated, the row is unowned by this user, or the fingerprint is from a different server.`;
32357
+ }
32358
+ }
32099
32359
  async function runSetup(opts, deps = {}) {
32100
32360
  const slockHome = resolveSlockHome();
32101
32361
  const isTty = deps.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
@@ -32104,8 +32364,11 @@ async function runSetup(opts, deps = {}) {
32104
32364
  const start2 = deps.runStart ?? runStart;
32105
32365
  const refreshSession = deps.refreshUserSession ?? refreshUserSession;
32106
32366
  const detectMigration = deps.detectLegacyMigration ?? detectLegacyMigration;
32107
- const confirmMigrate = deps.confirmMigrate ?? defaultConfirmMigrate;
32108
- const readMigrateSecret = deps.readMigrateSecret ?? defaultReadMigrateSecret;
32367
+ const validateManualPath = deps.validateManualMigratePath ?? validateManualMigratePath;
32368
+ const buildRoster = deps.buildRosterClient ?? ((baseUrl, accessToken) => new LegacyMachinesClient(baseUrl, accessToken));
32369
+ const pickCandidate = deps.pickMigrationCandidate ?? defaultPickMigrationCandidate;
32370
+ const adoptLegacy2 = deps.adoptLegacy ?? adoptLegacy;
32371
+ const readLegacyKey = deps.readLegacyApiKey ?? readLegacyApiKey;
32109
32372
  if (!isTty && !opts.yes) {
32110
32373
  fail(
32111
32374
  "NON_INTERACTIVE_SETUP_REQUIRES_FLAGS",
@@ -32134,78 +32397,85 @@ async function runSetup(opts, deps = {}) {
32134
32397
  if (attachment) {
32135
32398
  info(`Attachment: already attached to ${label}.`);
32136
32399
  } else {
32400
+ const session = await readUserSessionAuth(slockHome);
32401
+ const baseUrl = resolveServerUrl(
32402
+ opts.serverUrl,
32403
+ session.serverUrl,
32404
+ process.env.SLOCK_SERVER_URL
32405
+ );
32406
+ const rosterClient = buildRoster(baseUrl, session.accessToken);
32137
32407
  let migrated = false;
32138
- if (isTty) {
32139
- const detection = await detectMigration(slockHome, await readUserSessionUserId(slockHome));
32140
- if (detection.kind === "match") {
32141
- const where = detection.serverUrl ? ` attached to ${detection.serverUrl}` : "";
32142
- const who = detection.machineName ? ` "${detection.machineName}"` : "";
32143
- const accepted = await confirmMigrate(
32144
- `Migration: detected legacy daemon machine${who}${where}. Migrate it under Computer instead of creating a fresh attachment? [y/N] `
32408
+ if (typeof opts.migrateFrom === "string" && opts.migrateFrom.length > 0) {
32409
+ const rosterResult = await rosterClient.list(opts.serverSlug);
32410
+ if (rosterResult.status !== "success") {
32411
+ fail(
32412
+ "MIGRATE_FROM_ROSTER_UNAVAILABLE",
32413
+ `Cannot validate --migrate-from path: legacy machine roster unavailable (${rosterResult.status}). Retry when the server is reachable, or remove --migrate-from to fall through to fresh attach.`
32145
32414
  );
32146
- if (accepted) {
32147
- const rawKey = await readMigrateSecret(
32148
- "Paste legacy api key (sk_machine_* or sk_daemon_*); input is hidden: "
32149
- );
32150
- if (rawKey.length === 0) {
32151
- info("Migration: no key provided; falling back to fresh attach.");
32152
- } else if (!rawKey.startsWith("sk_machine_") && !rawKey.startsWith("sk_daemon_")) {
32153
- fail(
32154
- "LEGACY_KEY_INVALID",
32155
- "Provided key does not look like a legacy machine key (expected `sk_machine_*` or `sk_daemon_*`)."
32156
- );
32157
- } else {
32158
- try {
32159
- await adoptLegacy(
32160
- {
32161
- serverSlug: opts.serverSlug,
32162
- serverUrl: opts.serverUrl,
32163
- name: opts.name,
32164
- rawKey,
32165
- mode: "legacy_key_stdin",
32166
- redactedPrefix: rawKey.slice(0, 8)
32167
- },
32168
- {
32169
- onEvent: (event) => {
32170
- if (event.type === "adopting") {
32171
- info(
32172
- `Adopting legacy daemon for ${formatServerSlugDisplay(event.serverSlug)} via ${event.mode}\u2026`
32173
- );
32174
- } else if (event.type === "preflight") {
32175
- info(
32176
- event.resumed ? "Adopted (resumed prior attachment); running preflight\u2026" : "Adopted; running preflight\u2026"
32177
- );
32178
- } else if (event.type === "adopted") {
32179
- info(`Adopted. Computer state written to ${event.attachmentPath}`);
32180
- info(` server: ${formatServerSlugDisplay(event.serverSlug)}`);
32181
- info(` serverMachine: ${event.serverMachineId}`);
32182
- info(` legacyMachine: ${event.legacyMachineId}`);
32183
- switch (event.legacyStop.outcome) {
32184
- case "absent":
32185
- info(" legacy daemon: not detected on this Computer (no local lock file)");
32186
- break;
32187
- case "already_dead":
32188
- info(` legacy daemon: already stopped (pid ${event.legacyStop.pid} not running)`);
32189
- break;
32190
- case "stopped":
32191
- info(` legacy daemon: stopped (pid ${event.legacyStop.pid}, SIGTERM)`);
32192
- break;
32193
- }
32194
- }
32195
- }
32196
- }
32415
+ }
32416
+ const validation = await validateManualPath(opts.migrateFrom, rosterResult.entries);
32417
+ if (!validation.ok) {
32418
+ fail(validation.code, manualPathErrorMessage(validation.code, opts.migrateFrom));
32419
+ }
32420
+ await runMigrateAdoption(opts, validation.candidate, isTty, adoptLegacy2, readLegacyKey);
32421
+ migrated = true;
32422
+ } else {
32423
+ const detection = await detectMigration(slockHome, opts.serverSlug, rosterClient);
32424
+ if (detection.kind === "server-unavailable") {
32425
+ info(
32426
+ "Migration: legacy machine roster unavailable (fresh trigger: server-unavailable); falling back to fresh attach."
32427
+ );
32428
+ } else if (detection.candidates.length === 0) {
32429
+ } else if (!isTty) {
32430
+ info(
32431
+ `Migration: ${detection.candidates.length} legacy daemon(s) match this server but setup is non-interactive (fresh trigger: non-tty); falling back to fresh attach. Re-run on a TTY or pass --migrate-from <path> to adopt a specific install.`
32432
+ );
32433
+ } else {
32434
+ pickerLoop: while (true) {
32435
+ const selection = await pickCandidate(detection.candidates);
32436
+ if (selection.kind === "candidate") {
32437
+ const candidate = detection.candidates[selection.index];
32438
+ if (!candidate) {
32439
+ fail(
32440
+ "MIGRATE_PICKER_OUT_OF_RANGE",
32441
+ `Picker returned candidate index ${selection.index} but only ${detection.candidates.length} candidate(s) were detected.`
32197
32442
  );
32198
- migrated = true;
32199
- } catch (err) {
32200
- if (err instanceof CliExit) throw err;
32201
- if (err instanceof ComputerServiceError) {
32202
- fail(err.code, err.message);
32203
- }
32204
- throw err;
32205
32443
  }
32444
+ await runMigrateAdoption(opts, candidate, isTty, adoptLegacy2, readLegacyKey);
32445
+ migrated = true;
32446
+ break pickerLoop;
32447
+ } else if (selection.kind === "manual") {
32448
+ if (selection.path.length === 0) {
32449
+ info("Migration: no path provided; returning to picker.");
32450
+ continue pickerLoop;
32451
+ }
32452
+ const rosterResult = await rosterClient.list(opts.serverSlug);
32453
+ if (rosterResult.status !== "success") {
32454
+ info(
32455
+ `Migration: MIGRATE_FROM_ROSTER_UNAVAILABLE \u2014 legacy machine roster unavailable (${rosterResult.status}); returning to picker. Pick "0" to fall through to fresh attach if the server stays unreachable.`
32456
+ );
32457
+ continue pickerLoop;
32458
+ }
32459
+ const validation = await validateManualPath(selection.path, rosterResult.entries);
32460
+ if (!validation.ok) {
32461
+ info(
32462
+ `Migration: ${validation.code} \u2014 ${manualPathErrorMessage(validation.code, selection.path)} Returning to picker.`
32463
+ );
32464
+ continue pickerLoop;
32465
+ }
32466
+ await runMigrateAdoption(
32467
+ opts,
32468
+ validation.candidate,
32469
+ isTty,
32470
+ adoptLegacy2,
32471
+ readLegacyKey
32472
+ );
32473
+ migrated = true;
32474
+ break pickerLoop;
32475
+ } else {
32476
+ info("Migration: fresh attach selected.");
32477
+ break pickerLoop;
32206
32478
  }
32207
- } else {
32208
- info("Migration: declined; falling back to fresh attach.");
32209
32479
  }
32210
32480
  }
32211
32481
  }
@@ -33096,7 +33366,7 @@ function errMsg(e) {
33096
33366
  return e instanceof Error ? e.message : String(e);
33097
33367
  }
33098
33368
  async function defaultReadTarballPackageJson(tarballPath) {
33099
- const raw = await new Promise((resolve, reject) => {
33369
+ const raw = await new Promise((resolve2, reject) => {
33100
33370
  const child = spawn3("tar", ["-xzOf", tarballPath, "package/package.json"], {
33101
33371
  stdio: ["ignore", "pipe", "pipe"]
33102
33372
  });
@@ -33114,7 +33384,7 @@ async function defaultReadTarballPackageJson(tarballPath) {
33114
33384
  reject(new Error(`tar -xzOf exited ${code}: ${stderrBuf.slice(0, 300)}`));
33115
33385
  return;
33116
33386
  }
33117
- resolve(stdoutBuf);
33387
+ resolve2(stdoutBuf);
33118
33388
  });
33119
33389
  });
33120
33390
  return JSON.parse(raw);
@@ -33242,7 +33512,7 @@ async function stagePhase(slockHome, version, deps = {}) {
33242
33512
  return { stagedPath, version, tarballSha1 };
33243
33513
  }
33244
33514
  function defaultNpmPack(cwd, packageRef) {
33245
- return new Promise((resolve) => {
33515
+ return new Promise((resolve2) => {
33246
33516
  const child = spawn4("npm", ["pack", packageRef, "--json"], {
33247
33517
  cwd,
33248
33518
  stdio: ["ignore", "pipe", "pipe"]
@@ -33256,23 +33526,23 @@ function defaultNpmPack(cwd, packageRef) {
33256
33526
  stderrBuf += String(chunk);
33257
33527
  });
33258
33528
  child.on("error", (err) => {
33259
- resolve({ tarballPath: "", exitCode: 1, stderr: String(err) });
33529
+ resolve2({ tarballPath: "", exitCode: 1, stderr: String(err) });
33260
33530
  });
33261
33531
  child.on("close", (code) => {
33262
33532
  if (code !== 0) {
33263
- resolve({ tarballPath: "", exitCode: code ?? 1, stderr: stderrBuf });
33533
+ resolve2({ tarballPath: "", exitCode: code ?? 1, stderr: stderrBuf });
33264
33534
  return;
33265
33535
  }
33266
33536
  try {
33267
33537
  const parsed = JSON.parse(stdoutBuf);
33268
33538
  const filename = parsed[0]?.filename;
33269
33539
  if (!filename) {
33270
- resolve({ tarballPath: "", exitCode: 1, stderr: "npm pack returned no filename" });
33540
+ resolve2({ tarballPath: "", exitCode: 1, stderr: "npm pack returned no filename" });
33271
33541
  return;
33272
33542
  }
33273
- resolve({ tarballPath: join6(cwd, filename), exitCode: 0, stderr: stderrBuf });
33543
+ resolve2({ tarballPath: join6(cwd, filename), exitCode: 0, stderr: stderrBuf });
33274
33544
  } catch (e) {
33275
- resolve({ tarballPath: "", exitCode: 1, stderr: `parse npm pack output: ${e}` });
33545
+ resolve2({ tarballPath: "", exitCode: 1, stderr: `parse npm pack output: ${e}` });
33276
33546
  }
33277
33547
  });
33278
33548
  });
@@ -33352,7 +33622,7 @@ async function extractTarball(tarballPath, destDir, deps = {}) {
33352
33622
  return { extractedPackageDir: join6(destDir, "package") };
33353
33623
  }
33354
33624
  function defaultTarSpawn(tarballPath, destDir) {
33355
- return new Promise((resolve) => {
33625
+ return new Promise((resolve2) => {
33356
33626
  const child = spawn4("tar", ["-xzf", tarballPath, "-C", destDir], {
33357
33627
  stdio: ["ignore", "ignore", "pipe"]
33358
33628
  });
@@ -33361,10 +33631,10 @@ function defaultTarSpawn(tarballPath, destDir) {
33361
33631
  stderrBuf += String(chunk);
33362
33632
  });
33363
33633
  child.on("error", (err) => {
33364
- resolve({ exitCode: 1, stderr: String(err) });
33634
+ resolve2({ exitCode: 1, stderr: String(err) });
33365
33635
  });
33366
33636
  child.on("close", (code) => {
33367
- resolve({ exitCode: code ?? 1, stderr: stderrBuf });
33637
+ resolve2({ exitCode: code ?? 1, stderr: stderrBuf });
33368
33638
  });
33369
33639
  });
33370
33640
  }
@@ -33380,7 +33650,7 @@ async function hydrateStagedDependencies(extractedPackageDir, deps = {}) {
33380
33650
  }
33381
33651
  }
33382
33652
  function defaultNpmInstallProductionDeps(cwd) {
33383
- return new Promise((resolve) => {
33653
+ return new Promise((resolve2) => {
33384
33654
  const child = spawn4(
33385
33655
  "npm",
33386
33656
  [
@@ -33401,10 +33671,10 @@ function defaultNpmInstallProductionDeps(cwd) {
33401
33671
  stderrBuf += String(chunk);
33402
33672
  });
33403
33673
  child.on("error", (err) => {
33404
- resolve({ exitCode: 1, stderr: String(err) });
33674
+ resolve2({ exitCode: 1, stderr: String(err) });
33405
33675
  });
33406
33676
  child.on("close", (code) => {
33407
- resolve({ exitCode: code ?? 1, stderr: stderrBuf });
33677
+ resolve2({ exitCode: code ?? 1, stderr: stderrBuf });
33408
33678
  });
33409
33679
  });
33410
33680
  }
@@ -34264,7 +34534,7 @@ async function defaultCurrentVersion() {
34264
34534
 
34265
34535
  // src/upgradeTestHarness.ts
34266
34536
  init_esm_shims();
34267
- import { mkdir as mkdir15, readdir as readdir4, stat as stat3, writeFile as writeFile12 } from "fs/promises";
34537
+ import { mkdir as mkdir15, readdir as readdir4, stat as stat4, writeFile as writeFile12 } from "fs/promises";
34268
34538
  import { join as join8 } from "path";
34269
34539
  import { createHash as createHash4 } from "crypto";
34270
34540
  var PHASES = /* @__PURE__ */ new Set([
@@ -34401,7 +34671,7 @@ async function arrangeSnapshotFailure(slockHome) {
34401
34671
  }
34402
34672
  async function pathInfo(path3) {
34403
34673
  try {
34404
- const s = await stat3(path3);
34674
+ const s = await stat4(path3);
34405
34675
  if (s.isDirectory()) return { kind: "dir", size: 0 };
34406
34676
  if (s.isFile()) return { kind: "file", size: s.size };
34407
34677
  return null;