@slock-ai/computer 0.0.24 → 0.0.26

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
@@ -36,12 +36,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  mod
37
37
  ));
38
38
 
39
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js
39
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/esm_shims.js
40
40
  import path from "path";
41
41
  import { fileURLToPath } from "url";
42
42
  var getFilename, __filename;
43
43
  var init_esm_shims = __esm({
44
- "../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js"() {
44
+ "../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/esm_shims.js"() {
45
45
  "use strict";
46
46
  getFilename = () => fileURLToPath(import.meta.url);
47
47
  __filename = /* @__PURE__ */ getFilename();
@@ -29773,6 +29773,51 @@ var ComputerAttachClient = class {
29773
29773
  if (!code) return { status: "unexpected_response", httpStatus: res.status };
29774
29774
  return { status: "error", code };
29775
29775
  }
29776
+ /**
29777
+ * POST /api/computer/adopt-legacy — user-authed roster-selected adoption.
29778
+ * Used by setup after the operator selected a server-rostered legacy
29779
+ * candidate. The user session + serverSlug + legacyMachineId +
29780
+ * apiKeyFingerprint are the authority; no raw legacy key is required.
29781
+ */
29782
+ async adoptLegacyByFingerprint(input) {
29783
+ const res = await (0, import_undici.fetch)(this.url("/api/computer/adopt-legacy"), {
29784
+ method: "POST",
29785
+ headers: {
29786
+ "Content-Type": "application/json",
29787
+ Authorization: `Bearer ${this.accessToken}`
29788
+ },
29789
+ body: JSON.stringify({
29790
+ serverSlug: input.serverSlug,
29791
+ legacyMachineId: input.legacyMachineId,
29792
+ apiKeyFingerprint: input.apiKeyFingerprint,
29793
+ ...input.name ? { name: input.name } : {}
29794
+ })
29795
+ });
29796
+ const body = await res.json().catch(() => null);
29797
+ if (res.status === 201 && body && typeof body.apiKey === "string") {
29798
+ return {
29799
+ status: "success",
29800
+ apiKey: body.apiKey,
29801
+ computerId: String(body.computerId ?? ""),
29802
+ machineId: String(body.machineId ?? ""),
29803
+ serverId: String(body.serverId ?? ""),
29804
+ resumed: body.resumed === true
29805
+ };
29806
+ }
29807
+ const code = body && typeof body.code === "string" ? body.code : void 0;
29808
+ if (res.status === 401) {
29809
+ if (code === "legacy_key_invalid") return { status: "legacy_key_invalid" };
29810
+ if (code === "auth_required") return { status: "auth_required" };
29811
+ return { status: "unexpected_response", httpStatus: res.status, ...code ? { code } : {} };
29812
+ }
29813
+ if (res.status === 409 && code === "legacy_machine_key_migrated") {
29814
+ return { status: "legacy_machine_key_migrated" };
29815
+ }
29816
+ if (res.status === 403) return { status: "not_authorized" };
29817
+ if (res.status === 404) return { status: "disabled" };
29818
+ if (!code) return { status: "unexpected_response", httpStatus: res.status };
29819
+ return { status: "error", code };
29820
+ }
29776
29821
  /**
29777
29822
  * POST /internal/computer/preflight — §9 READ-ONLY, side-effect-free.
29778
29823
  * Authenticated with the freshly-issued sk_computer_* (NOT the user
@@ -30630,10 +30675,6 @@ function emit3(opts, event) {
30630
30675
  }
30631
30676
  var LEGACY_STOP_WAIT_MS = 1e4;
30632
30677
  var LEGACY_STOP_POLL_MS = 200;
30633
- function legacyLockOwnerPath(slockHome, rawKey) {
30634
- const fingerprint = createHash2("sha256").update(rawKey).digest("hex").slice(0, 16);
30635
- return join2(slockHome, "machines", `machine-${fingerprint}`, "daemon.lock", "owner.json");
30636
- }
30637
30678
  function isProcessAlive(pid) {
30638
30679
  if (!Number.isInteger(pid) || pid <= 0) return false;
30639
30680
  try {
@@ -30715,9 +30756,7 @@ function formatLegacyOwnerEvidence(slockHome, evidence) {
30715
30756
  if (evidence.reason) fields.push(`ownerStatus=${evidence.reason}`);
30716
30757
  return fields.join(" ");
30717
30758
  }
30718
- async function adoptLegacy(input, options = {}) {
30719
- options.signal?.throwIfAborted?.();
30720
- const slockHome = resolveSlockHome();
30759
+ async function readUserSessionContext(slockHome, serverUrl) {
30721
30760
  const sessionFile = userSessionPath(slockHome);
30722
30761
  let session;
30723
30762
  try {
@@ -30735,123 +30774,82 @@ async function adoptLegacy(input, options = {}) {
30735
30774
  `User session at ${sessionFile} is invalid. Re-run \`slock-computer login\`.`
30736
30775
  );
30737
30776
  }
30738
- const baseUrl = resolveServerUrl(input.serverUrl, session.serverUrl, process.env.SLOCK_SERVER_URL);
30739
- const slugForServer = normalizeServerSlug(input.serverSlug);
30740
- if (!slugForServer) {
30741
- throw new ComputerServiceError("ADOPT_NOT_AUTHORIZED", "Server slug must not be empty.");
30742
- }
30743
- options.signal?.throwIfAborted?.();
30744
- emit3(options, { type: "adopting", serverSlug: slugForServer, mode: input.mode });
30745
- const client = new ComputerAttachClient(baseUrl, session.accessToken);
30746
- const adoptStartedAt = /* @__PURE__ */ new Date();
30747
- const legacyOwnerFile = legacyLockOwnerPath(slockHome, input.rawKey);
30748
- const ownerEvidence = await readLegacyOwnerEvidence(legacyOwnerFile);
30749
- const result = await client.adoptLegacy(input.rawKey, input.name);
30750
- input.rawKey = void 0;
30777
+ return {
30778
+ accessToken: session.accessToken,
30779
+ baseUrl: resolveServerUrl(serverUrl, session.serverUrl, process.env.SLOCK_SERVER_URL)
30780
+ };
30781
+ }
30782
+ async function appendAdoptionFailureLog(slockHome, log, startedAt, failureReason, extra = {}) {
30783
+ await appendAdoptionLog(slockHome, {
30784
+ ...log,
30785
+ startedAt,
30786
+ outcome: "failed",
30787
+ failureReason,
30788
+ ...extra
30789
+ });
30790
+ }
30791
+ async function completeAdoptionExchange(input, options) {
30792
+ const {
30793
+ slockHome,
30794
+ client,
30795
+ baseUrl,
30796
+ slugForServer,
30797
+ ownerEvidence,
30798
+ legacyOwnerFile,
30799
+ result,
30800
+ startedAt,
30801
+ log,
30802
+ copy
30803
+ } = input;
30751
30804
  if (result.status === "disabled") {
30752
- await appendAdoptionLog(slockHome, {
30753
- mode: input.mode,
30754
- redactedPrefix: input.redactedPrefix,
30755
- startedAt: adoptStartedAt,
30756
- outcome: "failed",
30757
- failureReason: "computer_adopt_disabled"
30758
- });
30805
+ await appendAdoptionFailureLog(slockHome, log, startedAt, "computer_adopt_disabled");
30759
30806
  throw new ComputerServiceError(
30760
30807
  "ADOPT_DISABLED",
30761
30808
  "Computer legacy adoption is not enabled on this server. Upgrade the server or set SLOCK_DEVICE_LOGIN_ENABLED."
30762
30809
  );
30763
30810
  }
30764
30811
  if (result.status === "legacy_key_invalid") {
30765
- await appendAdoptionLog(slockHome, {
30766
- mode: input.mode,
30767
- redactedPrefix: input.redactedPrefix,
30768
- startedAt: adoptStartedAt,
30769
- outcome: "failed",
30770
- failureReason: "legacy_key_invalid"
30771
- });
30772
- throw new ComputerServiceError(
30773
- "LEGACY_KEY_INVALID",
30774
- `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.`
30775
- );
30812
+ await appendAdoptionFailureLog(slockHome, log, startedAt, copy.invalidFailureReason);
30813
+ throw new ComputerServiceError("LEGACY_KEY_INVALID", copy.invalidMessage);
30776
30814
  }
30777
30815
  if (result.status === "legacy_machine_key_migrated") {
30778
- await appendAdoptionLog(slockHome, {
30779
- mode: input.mode,
30780
- redactedPrefix: input.redactedPrefix,
30781
- startedAt: adoptStartedAt,
30782
- outcome: "failed",
30783
- failureReason: "legacy_machine_key_migrated"
30784
- });
30785
- throw new ComputerServiceError(
30786
- "LEGACY_MACHINE_KEY_MIGRATED",
30787
- `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.`
30788
- );
30816
+ await appendAdoptionFailureLog(slockHome, log, startedAt, "legacy_machine_key_migrated");
30817
+ throw new ComputerServiceError("LEGACY_MACHINE_KEY_MIGRATED", copy.migratedMessage);
30789
30818
  }
30790
30819
  if (result.status === "auth_required") {
30791
- await appendAdoptionLog(slockHome, {
30792
- mode: input.mode,
30793
- redactedPrefix: input.redactedPrefix,
30794
- startedAt: adoptStartedAt,
30795
- outcome: "failed",
30796
- failureReason: "auth_required"
30797
- });
30798
- throw new ComputerServiceError(
30799
- "ADOPT_AUTH_REQUIRED",
30800
- `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.`
30801
- );
30820
+ await appendAdoptionFailureLog(slockHome, log, startedAt, "auth_required");
30821
+ throw new ComputerServiceError("ADOPT_AUTH_REQUIRED", copy.authRequiredMessage);
30802
30822
  }
30803
30823
  if (result.status === "not_authorized") {
30804
- await appendAdoptionLog(slockHome, {
30805
- mode: input.mode,
30806
- redactedPrefix: input.redactedPrefix,
30807
- startedAt: adoptStartedAt,
30808
- outcome: "failed",
30809
- failureReason: "not_authorized"
30810
- });
30824
+ await appendAdoptionFailureLog(slockHome, log, startedAt, "not_authorized");
30811
30825
  throw new ComputerServiceError(
30812
30826
  "ADOPT_NOT_AUTHORIZED",
30813
30827
  "Not authorized to adopt this machine on this server. Check that you are a current member."
30814
30828
  );
30815
30829
  }
30816
30830
  if (result.status === "unexpected_response") {
30817
- await appendAdoptionLog(slockHome, {
30818
- mode: input.mode,
30819
- redactedPrefix: input.redactedPrefix,
30820
- startedAt: adoptStartedAt,
30821
- outcome: "failed",
30822
- failureReason: result.code ? `unexpected_response_${result.code}` : "unexpected_response_missing_code"
30823
- });
30831
+ await appendAdoptionFailureLog(
30832
+ slockHome,
30833
+ log,
30834
+ startedAt,
30835
+ result.code ? `unexpected_response_${result.code}` : "unexpected_response_missing_code"
30836
+ );
30824
30837
  const responseDetail = result.code ? `status ${result.httpStatus}, code ${result.code}` : `status ${result.httpStatus}, missing error code`;
30825
- 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." : "";
30838
+ const authHint = copy.unexpectedAuthHint?.(result.httpStatus) ?? "";
30826
30839
  throw new ComputerServiceError(
30827
30840
  "ADOPT_UNEXPECTED_RESPONSE",
30828
- `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.`
30841
+ `Server returned an unexpected legacy adoption response (${responseDetail}).${authHint} Local legacy owner evidence: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Please report this with the command, server URL, and SLOCK_HOME; no local Computer state was written.`
30829
30842
  );
30830
30843
  }
30831
30844
  if (result.status === "error") {
30832
- await appendAdoptionLog(slockHome, {
30833
- mode: input.mode,
30834
- redactedPrefix: input.redactedPrefix,
30835
- startedAt: adoptStartedAt,
30836
- outcome: "failed",
30837
- failureReason: result.code
30838
- });
30839
- throw new ComputerServiceError(
30840
- "ADOPT_FAILED",
30841
- `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.`
30842
- );
30845
+ await appendAdoptionFailureLog(slockHome, log, startedAt, result.code);
30846
+ throw new ComputerServiceError("ADOPT_FAILED", copy.failedMessage(result.code));
30843
30847
  }
30844
30848
  emit3(options, { type: "preflight", resumed: result.resumed });
30845
30849
  options.signal?.throwIfAborted?.();
30846
30850
  const pre = await client.preflight(result.apiKey);
30847
30851
  if (!pre.ok) {
30848
- await appendAdoptionLog(slockHome, {
30849
- mode: input.mode,
30850
- redactedPrefix: input.redactedPrefix,
30851
- startedAt: adoptStartedAt,
30852
- outcome: "failed",
30853
- failureReason: `preflight_${pre.code}`
30854
- });
30852
+ await appendAdoptionFailureLog(slockHome, log, startedAt, `preflight_${pre.code}`);
30855
30853
  throw new ComputerServiceError(
30856
30854
  "PREFLIGHT_FAILED",
30857
30855
  `Server preflight failed (${pre.code}); local state not written. Upgrade the server or retry.`
@@ -30860,12 +30858,7 @@ async function adoptLegacy(input, options = {}) {
30860
30858
  options.signal?.throwIfAborted?.();
30861
30859
  const stop2 = await stopLegacyDaemonByOwnerFile(legacyOwnerFile);
30862
30860
  if (stop2.outcome === "timed_out" || stop2.outcome === "denied" || stop2.outcome === "error") {
30863
- await appendAdoptionLog(slockHome, {
30864
- mode: input.mode,
30865
- redactedPrefix: input.redactedPrefix,
30866
- startedAt: adoptStartedAt,
30867
- outcome: "failed",
30868
- failureReason: `legacy_stop_${stop2.outcome}`,
30861
+ await appendAdoptionFailureLog(slockHome, log, startedAt, `legacy_stop_${stop2.outcome}`, {
30869
30862
  computerId: result.computerId,
30870
30863
  machineId: result.machineId,
30871
30864
  serverId: result.serverId,
@@ -30874,7 +30867,7 @@ async function adoptLegacy(input, options = {}) {
30874
30867
  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"})`;
30875
30868
  throw new ComputerServiceError(
30876
30869
  "LEGACY_DAEMON_STOP_FAILED",
30877
- `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.`
30870
+ `Adoption succeeded server-side but the legacy daemon could not be stopped: ${detail}. Stop it manually and re-run ${copy.stopFailureRetryCommand}, or run \`slock-computer attach\` after confirming the legacy process is gone. No local Computer state was written.`
30878
30871
  );
30879
30872
  }
30880
30873
  const file = serverAttachmentPath(slockHome, result.serverId);
@@ -30900,9 +30893,8 @@ async function adoptLegacy(input, options = {}) {
30900
30893
  );
30901
30894
  await chmod3(file, 384);
30902
30895
  await appendAdoptionLog(slockHome, {
30903
- mode: input.mode,
30904
- redactedPrefix: input.redactedPrefix,
30905
- startedAt: adoptStartedAt,
30896
+ ...log,
30897
+ startedAt,
30906
30898
  outcome: "succeeded",
30907
30899
  computerId: result.computerId,
30908
30900
  machineId: result.machineId,
@@ -30933,6 +30925,48 @@ async function adoptLegacy(input, options = {}) {
30933
30925
  legacyStop: stop2
30934
30926
  };
30935
30927
  }
30928
+ async function adoptLegacyByFingerprint(input, options = {}) {
30929
+ options.signal?.throwIfAborted?.();
30930
+ const slockHome = resolveSlockHome();
30931
+ const { accessToken, baseUrl } = await readUserSessionContext(slockHome, input.serverUrl);
30932
+ const slugForServer = normalizeServerSlug(input.serverSlug);
30933
+ if (!slugForServer) {
30934
+ throw new ComputerServiceError("ADOPT_NOT_AUTHORIZED", "Server slug must not be empty.");
30935
+ }
30936
+ options.signal?.throwIfAborted?.();
30937
+ emit3(options, { type: "adopting", serverSlug: slugForServer, mode: "legacy_fingerprint_roster" });
30938
+ const client = new ComputerAttachClient(baseUrl, accessToken);
30939
+ const adoptStartedAt = /* @__PURE__ */ new Date();
30940
+ const ownerEvidence = await readLegacyOwnerEvidence(input.legacyOwnerPath);
30941
+ const result = await client.adoptLegacyByFingerprint({
30942
+ serverSlug: slugForServer,
30943
+ legacyMachineId: input.legacyMachineId,
30944
+ apiKeyFingerprint: input.apiKeyFingerprint,
30945
+ ...input.name ? { name: input.name } : {}
30946
+ });
30947
+ return completeAdoptionExchange(
30948
+ {
30949
+ slockHome,
30950
+ client,
30951
+ baseUrl,
30952
+ slugForServer,
30953
+ ownerEvidence,
30954
+ legacyOwnerFile: input.legacyOwnerPath,
30955
+ result,
30956
+ startedAt: adoptStartedAt,
30957
+ log: { mode: "legacy_fingerprint_roster", legacyFingerprint: input.apiKeyFingerprint },
30958
+ copy: {
30959
+ invalidFailureReason: "legacy_fingerprint_invalid",
30960
+ invalidMessage: `Server rejected the selected legacy machine (unknown / wrong server / malformed fingerprint). Local legacy owner evidence for this candidate: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Re-run setup and pick a currently listed legacy daemon, or choose fresh attach.`,
30961
+ migratedMessage: `This machine has already been adopted. Local legacy owner evidence for this candidate: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Use \`slock-computer attach\` to add another Computer attachment, or choose fresh attach in setup.`,
30962
+ authRequiredMessage: `Your Computer user session was rejected by the server before legacy adoption. Local legacy owner evidence for this candidate: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Re-run \`slock-computer login\` for this server, then retry setup. No local Computer state was written.`,
30963
+ failedMessage: (code) => `Adoption failed at server exchange (${code}). Local legacy owner evidence for this candidate: ${formatLegacyOwnerEvidence(slockHome, ownerEvidence)}. Confirm the user session is valid and the server URL is correct. No local Computer state was written.`,
30964
+ stopFailureRetryCommand: `\`slock-computer setup ${formatServerSlugDisplay(slugForServer)}\``
30965
+ }
30966
+ },
30967
+ options
30968
+ );
30969
+ }
30936
30970
  async function appendAdoptionLog(slockHome, line) {
30937
30971
  try {
30938
30972
  await mkdir4(computerDir(slockHome), { recursive: true });
@@ -30940,9 +30974,10 @@ async function appendAdoptionLog(slockHome, line) {
30940
30974
  `ts=${(/* @__PURE__ */ new Date()).toISOString()}`,
30941
30975
  `started_at=${line.startedAt.toISOString()}`,
30942
30976
  `outcome=${line.outcome}`,
30943
- `credential_bridge_mode=${line.mode}`,
30944
- `legacy_key_prefix=${line.redactedPrefix}`
30977
+ `credential_bridge_mode=${line.mode}`
30945
30978
  ];
30979
+ if (line.redactedPrefix) fields.push(`legacy_key_prefix=${line.redactedPrefix}`);
30980
+ if (line.legacyFingerprint) fields.push(`legacy_fingerprint=${line.legacyFingerprint}`);
30946
30981
  if (line.failureReason) fields.push(`failure_reason=${line.failureReason}`);
30947
30982
  if (line.computerId) fields.push(`computer_id=${line.computerId}`);
30948
30983
  if (line.machineId) fields.push(`legacy_machine_id=${line.machineId}`);
@@ -32293,17 +32328,6 @@ async function defaultPickMigrationCandidate(candidates) {
32293
32328
  }
32294
32329
  );
32295
32330
  }
32296
- async function readLegacyApiKey(isTty) {
32297
- const fromEnv = process.env.SLOCK_LEGACY_API_KEY;
32298
- if (typeof fromEnv === "string" && fromEnv.length > 0) return fromEnv.trim();
32299
- if (!isTty) return "";
32300
- process.stdout.write(
32301
- "Paste legacy api key (sk_machine_* or sk_daemon_*); input is hidden: "
32302
- );
32303
- const { line } = await readLine(true);
32304
- process.stdout.write("\n");
32305
- return line.trim();
32306
- }
32307
32331
  async function readLine(masked) {
32308
32332
  const stdin = process.stdin;
32309
32333
  const wasRaw = stdin.isTTY === true && stdin.isRaw === true;
@@ -32351,33 +32375,21 @@ async function readLine(masked) {
32351
32375
  stdin.on("end", onEnd);
32352
32376
  });
32353
32377
  }
32354
- async function runMigrateAdoption(opts, candidate, isTty, adoptLegacyImpl, readLegacyApiKeyImpl) {
32378
+ async function runRosterAdoption(opts, candidate, adoptLegacyByFingerprintImpl) {
32355
32379
  const where = candidate.hostname ? ` (${candidate.hostname})` : "";
32356
32380
  const seen = candidate.lastSeenAt ? ` last seen ${candidate.lastSeenAt}` : "";
32357
- info(`Migration: adopting legacy daemon "${candidate.machineName}"${where}${seen}.`);
32381
+ info(`Migration: adopting legacy daemon "${candidate.machineName}"${where}${seen} using logged-in user authorization.`);
32358
32382
  info(` local owner.json: ${candidate.localPath}`);
32359
- const rawKey = await readLegacyApiKeyImpl(isTty);
32360
- if (rawKey.length === 0) {
32361
- fail(
32362
- "LEGACY_KEY_REQUIRED",
32363
- "No legacy api key provided. Set `SLOCK_LEGACY_API_KEY` or paste the legacy `sk_machine_*` / `sk_daemon_*` key when prompted."
32364
- );
32365
- }
32366
- if (!rawKey.startsWith("sk_machine_") && !rawKey.startsWith("sk_daemon_")) {
32367
- fail(
32368
- "LEGACY_KEY_INVALID",
32369
- "Provided key does not look like a legacy machine key (expected `sk_machine_*` or `sk_daemon_*`)."
32370
- );
32371
- }
32383
+ info(" legacy api key: not required for setup after user login.");
32372
32384
  try {
32373
- await adoptLegacyImpl(
32385
+ await adoptLegacyByFingerprintImpl(
32374
32386
  {
32375
32387
  serverSlug: opts.serverSlug,
32376
32388
  ...opts.serverUrl ? { serverUrl: opts.serverUrl } : {},
32377
32389
  ...opts.name ? { name: opts.name } : {},
32378
- rawKey,
32379
- mode: "legacy_key_stdin",
32380
- redactedPrefix: rawKey.slice(0, 8)
32390
+ legacyMachineId: candidate.daemonId,
32391
+ apiKeyFingerprint: candidate.apiKeyFingerprint,
32392
+ legacyOwnerPath: candidate.localPath
32381
32393
  },
32382
32394
  {
32383
32395
  onEvent: (event) => {
@@ -32438,8 +32450,7 @@ async function runSetup(opts, deps = {}) {
32438
32450
  const validateManualPath = deps.validateManualMigratePath ?? validateManualMigratePath;
32439
32451
  const buildRoster = deps.buildRosterClient ?? ((baseUrl, accessToken) => new LegacyMachinesClient(baseUrl, accessToken));
32440
32452
  const pickCandidate = deps.pickMigrationCandidate ?? defaultPickMigrationCandidate;
32441
- const adoptLegacy2 = deps.adoptLegacy ?? adoptLegacy;
32442
- const readLegacyKey = deps.readLegacyApiKey ?? readLegacyApiKey;
32453
+ const adoptLegacyByFingerprint2 = deps.adoptLegacyByFingerprint ?? adoptLegacyByFingerprint;
32443
32454
  if (!isTty && !opts.yes) {
32444
32455
  fail(
32445
32456
  "NON_INTERACTIVE_SETUP_REQUIRES_FLAGS",
@@ -32489,7 +32500,7 @@ async function runSetup(opts, deps = {}) {
32489
32500
  if (!validation.ok) {
32490
32501
  fail(validation.code, manualPathErrorMessage(validation.code, opts.migrateFrom));
32491
32502
  }
32492
- await runMigrateAdoption(opts, validation.candidate, isTty, adoptLegacy2, readLegacyKey);
32503
+ await runRosterAdoption(opts, validation.candidate, adoptLegacyByFingerprint2);
32493
32504
  migrated = true;
32494
32505
  } else {
32495
32506
  const detection = await detectMigration(slockHome, opts.serverSlug, rosterClient);
@@ -32513,7 +32524,7 @@ async function runSetup(opts, deps = {}) {
32513
32524
  `Picker returned candidate index ${selection.index} but only ${detection.candidates.length} candidate(s) were detected.`
32514
32525
  );
32515
32526
  }
32516
- await runMigrateAdoption(opts, candidate, isTty, adoptLegacy2, readLegacyKey);
32527
+ await runRosterAdoption(opts, candidate, adoptLegacyByFingerprint2);
32517
32528
  migrated = true;
32518
32529
  break pickerLoop;
32519
32530
  } else if (selection.kind === "manual") {
@@ -32535,13 +32546,7 @@ async function runSetup(opts, deps = {}) {
32535
32546
  );
32536
32547
  continue pickerLoop;
32537
32548
  }
32538
- await runMigrateAdoption(
32539
- opts,
32540
- validation.candidate,
32541
- isTty,
32542
- adoptLegacy2,
32543
- readLegacyKey
32544
- );
32549
+ await runRosterAdoption(opts, validation.candidate, adoptLegacyByFingerprint2);
32545
32550
  migrated = true;
32546
32551
  break pickerLoop;
32547
32552
  } else {
@@ -437,10 +437,11 @@ interface LegacyMachineCandidate {
437
437
  * Fingerprint discipline (Cody msg=ec68c27f redline):
438
438
  * - `apiKeyFingerprint` is treated as a sensitive identity. It is
439
439
  * scoped to this caller's authenticated request and never logged.
440
- * - The intersection is the trust-gate: a candidate proves the
441
- * server has a roster row keyed by the same fingerprint that
442
- * appears in the local owner.json. Final adoption still walks
443
- * `AdoptLegacyService.migrate()` credential validation.
440
+ * - The intersection is setup's adoption identity after user login:
441
+ * a candidate proves the server has a roster row keyed by the same
442
+ * fingerprint that appears in the local owner.json. The server
443
+ * still enforces ownership by requiring the session user to own the
444
+ * selected machine row; fingerprint is only the selector.
444
445
  */
445
446
  /**
446
447
  * Closed set of `MigrationDetection.kind` discriminator values. Mirrors
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/computer",
3
- "version": "0.0.24",
3
+ "version": "0.0.26",
4
4
  "description": "Slock Computer — standalone human/local-machine control-plane CLI (login + attach). Distinct from the agent-facing @slock-ai/cli.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,7 +28,7 @@
28
28
  "commander": "^12.1.0",
29
29
  "proper-lockfile": "^4.1.2",
30
30
  "undici": "^7.24.7",
31
- "@slock-ai/daemon": "0.55.5"
31
+ "@slock-ai/daemon": "0.56.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^25.5.0",