@slock-ai/computer 0.0.25 → 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 +154 -149
- package/dist/lib/index.d.ts +5 -4
- package/package.json +2 -2
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
30739
|
-
|
|
30740
|
-
|
|
30741
|
-
|
|
30742
|
-
|
|
30743
|
-
|
|
30744
|
-
|
|
30745
|
-
|
|
30746
|
-
|
|
30747
|
-
|
|
30748
|
-
|
|
30749
|
-
|
|
30750
|
-
|
|
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
|
|
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
|
|
30766
|
-
|
|
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
|
|
30779
|
-
|
|
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
|
|
30792
|
-
|
|
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
|
|
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
|
|
30818
|
-
|
|
30819
|
-
|
|
30820
|
-
startedAt
|
|
30821
|
-
|
|
30822
|
-
|
|
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
|
|
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
|
|
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
|
|
30833
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
30904
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
32379
|
-
|
|
32380
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -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
|
|
441
|
-
* server has a roster row keyed by the same
|
|
442
|
-
* appears in the local owner.json.
|
|
443
|
-
*
|
|
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.
|
|
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.
|
|
31
|
+
"@slock-ai/daemon": "0.56.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^25.5.0",
|