@slock-ai/computer 0.0.2 → 0.0.4

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.
Files changed (2) hide show
  1. package/dist/index.js +97 -23
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -121,15 +121,18 @@ var ComputerAttachClient = class {
121
121
  resumed: body.resumed === true
122
122
  };
123
123
  }
124
- const code = body && typeof body.code === "string" ? body.code : `http_${res.status}`;
125
- if (res.status === 401 && code === "legacy_key_invalid") {
126
- return { status: "legacy_key_invalid" };
124
+ const code = body && typeof body.code === "string" ? body.code : void 0;
125
+ if (res.status === 401) {
126
+ if (code === "legacy_key_invalid") return { status: "legacy_key_invalid" };
127
+ if (code === "auth_required") return { status: "auth_required" };
128
+ return { status: "unexpected_response", httpStatus: res.status, ...code ? { code } : {} };
127
129
  }
128
130
  if (res.status === 409 && code === "legacy_machine_key_migrated") {
129
131
  return { status: "legacy_machine_key_migrated" };
130
132
  }
131
133
  if (res.status === 403) return { status: "not_authorized" };
132
134
  if (res.status === 404) return { status: "disabled" };
135
+ if (!code) return { status: "unexpected_response", httpStatus: res.status };
133
136
  return { status: "error", code };
134
137
  }
135
138
  /**
@@ -306,13 +309,22 @@ function fail(code, message, exitCode = 1) {
306
309
  throw new CliExit(exitCode);
307
310
  }
308
311
 
312
+ // src/serverUrl.ts
313
+ var DEFAULT_SLOCK_SERVER_URL = "https://api.slock.ai";
314
+ function resolveServerUrl(...candidates) {
315
+ for (const candidate of candidates) {
316
+ const value = candidate?.trim();
317
+ if (value) return value;
318
+ }
319
+ return DEFAULT_SLOCK_SERVER_URL;
320
+ }
321
+
309
322
  // src/login.ts
310
323
  function sleep(ms) {
311
324
  return new Promise((r) => setTimeout(r, ms));
312
325
  }
313
326
  async function runLogin(opts) {
314
- const baseUrl = (opts.serverUrl ?? process.env.SLOCK_SERVER_URL ?? "").trim();
315
- if (!baseUrl) fail("MISSING_SERVER_URL", "Set SLOCK_SERVER_URL or pass --server-url <url>.");
327
+ const baseUrl = resolveServerUrl(opts.serverUrl, process.env.SLOCK_SERVER_URL);
316
328
  const client = new DeviceAuthClient(baseUrl);
317
329
  let grant;
318
330
  try {
@@ -482,10 +494,7 @@ async function runAttach(opts) {
482
494
  `User session at ${sessionFile} is invalid. Re-run \`slock-computer login\`.`
483
495
  );
484
496
  }
485
- const baseUrl = (opts.serverUrl ?? session.serverUrl ?? process.env.SLOCK_SERVER_URL ?? "").trim();
486
- if (!baseUrl) {
487
- fail("MISSING_SERVER_URL", "Set SLOCK_SERVER_URL or pass --server-url <url>.");
488
- }
497
+ const baseUrl = resolveServerUrl(opts.serverUrl, session.serverUrl, process.env.SLOCK_SERVER_URL);
489
498
  const client = new ComputerAttachClient(baseUrl, session.accessToken);
490
499
  const slugForServer = normalizeServerSlug(opts.serverSlug);
491
500
  if (!slugForServer) {
@@ -682,6 +691,43 @@ async function stopLegacyDaemonByOwnerFile(ownerFile) {
682
691
  }
683
692
  return { attempted: true, pid, outcome: "timed_out" };
684
693
  }
694
+ async function readLegacyOwnerEvidence(ownerFile) {
695
+ let raw;
696
+ try {
697
+ raw = await readFile3(ownerFile, "utf8");
698
+ } catch {
699
+ return { ownerFile, reason: "owner_file_absent" };
700
+ }
701
+ let owner;
702
+ try {
703
+ owner = JSON.parse(raw);
704
+ } catch {
705
+ return { ownerFile, reason: "owner_json_unparseable" };
706
+ }
707
+ const pid = typeof owner.pid === "number" ? owner.pid : void 0;
708
+ return {
709
+ ownerFile,
710
+ pid,
711
+ alive: typeof pid === "number" ? isProcessAlive(pid) : void 0,
712
+ startedAt: typeof owner.startedAt === "string" ? owner.startedAt : void 0,
713
+ serverUrl: typeof owner.serverUrl === "string" ? owner.serverUrl : void 0,
714
+ reason: typeof pid === "number" ? void 0 : "owner_json_missing_pid"
715
+ };
716
+ }
717
+ function formatLegacyOwnerEvidence(slockHome, evidence) {
718
+ const fields = [
719
+ `SLOCK_HOME=${slockHome}`,
720
+ `owner=${evidence.ownerFile}`
721
+ ];
722
+ if (typeof evidence.pid === "number") {
723
+ fields.push(`pid=${evidence.pid}`);
724
+ if (typeof evidence.alive === "boolean") fields.push(`alive=${evidence.alive}`);
725
+ }
726
+ if (evidence.startedAt) fields.push(`startedAt=${evidence.startedAt}`);
727
+ if (evidence.serverUrl) fields.push(`serverUrl=${evidence.serverUrl}`);
728
+ if (evidence.reason) fields.push(`ownerStatus=${evidence.reason}`);
729
+ return fields.join(" ");
730
+ }
685
731
  async function runAdoptLegacy(inputs) {
686
732
  const slockHome = resolveSlockHome();
687
733
  const sessionFile = userSessionPath(slockHome);
@@ -700,10 +746,7 @@ async function runAdoptLegacy(inputs) {
700
746
  `User session at ${sessionFile} is invalid. Re-run \`slock-computer login\`.`
701
747
  );
702
748
  }
703
- const baseUrl = (inputs.serverUrl ?? session.serverUrl ?? process.env.SLOCK_SERVER_URL ?? "").trim();
704
- if (!baseUrl) {
705
- fail("MISSING_SERVER_URL", "Set SLOCK_SERVER_URL or pass --server-url <url>.");
706
- }
749
+ const baseUrl = resolveServerUrl(inputs.serverUrl, session.serverUrl, process.env.SLOCK_SERVER_URL);
707
750
  const slugForServer = normalizeServerSlug(inputs.serverSlug);
708
751
  if (!slugForServer) {
709
752
  fail("ADOPT_NOT_AUTHORIZED", "Server slug must not be empty.");
@@ -715,6 +758,7 @@ async function runAdoptLegacy(inputs) {
715
758
  const client = new ComputerAttachClient(baseUrl, session.accessToken);
716
759
  const adoptStartedAt = /* @__PURE__ */ new Date();
717
760
  const legacyOwnerFile = legacyLockOwnerPath(slockHome, legacy.rawKey);
761
+ const ownerEvidence = await readLegacyOwnerEvidence(legacyOwnerFile);
718
762
  const result = await client.adoptLegacy(legacy.rawKey, inputs.name);
719
763
  legacy.rawKey = void 0;
720
764
  if (result.status === "disabled") {
@@ -740,7 +784,7 @@ async function runAdoptLegacy(inputs) {
740
784
  });
741
785
  fail(
742
786
  "LEGACY_KEY_INVALID",
743
- "Server rejected the legacy api key (unknown / wrong server / malformed). Verify the key and --server-url."
787
+ `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.`
744
788
  );
745
789
  }
746
790
  if (result.status === "legacy_machine_key_migrated") {
@@ -753,7 +797,20 @@ async function runAdoptLegacy(inputs) {
753
797
  });
754
798
  fail(
755
799
  "LEGACY_MACHINE_KEY_MIGRATED",
756
- "This machine has already been adopted; the legacy key is no longer accepted. Use `slock-computer attach` to add another Computer attachment."
800
+ `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.`
801
+ );
802
+ }
803
+ if (result.status === "auth_required") {
804
+ await appendAdoptionLog(slockHome, {
805
+ mode: legacy.mode,
806
+ redactedPrefix: legacy.redactedPrefix,
807
+ startedAt: adoptStartedAt,
808
+ outcome: "failed",
809
+ failureReason: "auth_required"
810
+ });
811
+ fail(
812
+ "ADOPT_AUTH_REQUIRED",
813
+ `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.`
757
814
  );
758
815
  }
759
816
  if (result.status === "not_authorized") {
@@ -769,6 +826,20 @@ async function runAdoptLegacy(inputs) {
769
826
  "Not authorized to adopt this machine on this server. Check that you are a current member."
770
827
  );
771
828
  }
829
+ if (result.status === "unexpected_response") {
830
+ await appendAdoptionLog(slockHome, {
831
+ mode: legacy.mode,
832
+ redactedPrefix: legacy.redactedPrefix,
833
+ startedAt: adoptStartedAt,
834
+ outcome: "failed",
835
+ failureReason: result.code ? `unexpected_response_${result.code}` : "unexpected_response_missing_code"
836
+ });
837
+ const responseDetail = result.code ? `status ${result.httpStatus}, code ${result.code}` : `status ${result.httpStatus}, missing error code`;
838
+ fail(
839
+ "ADOPT_UNEXPECTED_RESPONSE",
840
+ `Server returned an unexpected legacy adoption response (${responseDetail}). 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.`
841
+ );
842
+ }
772
843
  if (result.status === "error") {
773
844
  await appendAdoptionLog(slockHome, {
774
845
  mode: legacy.mode,
@@ -777,7 +848,10 @@ async function runAdoptLegacy(inputs) {
777
848
  outcome: "failed",
778
849
  failureReason: result.code
779
850
  });
780
- fail("ADOPT_FAILED", `Adoption failed (${result.code}).`);
851
+ fail(
852
+ "ADOPT_FAILED",
853
+ `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.`
854
+ );
781
855
  }
782
856
  info(result.resumed ? "Adopted (resumed prior attachment); running preflight\u2026" : "Adopted; running preflight\u2026");
783
857
  const pre = await client.preflight(result.apiKey);
@@ -1663,7 +1737,7 @@ async function runSetup(opts, deps = {}) {
1663
1737
  if (!opts.adoptLegacy && liveLegacy) {
1664
1738
  fail(
1665
1739
  "LEGACY_DAEMON_RUNNING",
1666
- "A legacy daemon appears to be running in this SLOCK_HOME. Re-run with `--adopt-legacy` and a legacy key, or stop the legacy daemon before fresh setup."
1740
+ `A legacy daemon appears to be running in ${slockHome}, so Computer setup stopped before creating a second attachment. To proceed: try the Computer beta in an isolated home with \`SLOCK_HOME=$HOME/.slock-computer-beta slock-computer setup ${label}\`; or stop the legacy daemon with \`pkill -f slock-daemon && rm -f ~/.slock/daemon.pid\`; or migrate the existing daemon with \`--adopt-legacy --legacy-api-key <key>\`.`
1667
1741
  );
1668
1742
  }
1669
1743
  if (opts.adoptLegacy && hasLegacyKey) {
@@ -1678,7 +1752,7 @@ async function runSetup(opts, deps = {}) {
1678
1752
  } else if (opts.adoptLegacy && liveLegacy) {
1679
1753
  fail(
1680
1754
  "LEGACY_DAEMON_RUNNING",
1681
- "A legacy daemon is running, but no legacy key was provided. Provide --legacy-api-key-file/--legacy-api-key/--legacy-api-key-stdin, or stop the legacy daemon before falling back to a fresh attach."
1755
+ `A legacy daemon is running in ${slockHome}, but no legacy key was provided. To proceed: provide --legacy-api-key-file/--legacy-api-key/--legacy-api-key-stdin to adopt it; or stop the legacy daemon with \`pkill -f slock-daemon && rm -f ~/.slock/daemon.pid\`; or use an isolated home with \`SLOCK_HOME=$HOME/.slock-computer-beta slock-computer setup ${label}\` for a clean beta trial.`
1682
1756
  );
1683
1757
  } else {
1684
1758
  if (opts.adoptLegacy) {
@@ -3634,16 +3708,16 @@ function withCliExit(fn) {
3634
3708
  };
3635
3709
  }
3636
3710
  var program = new Command();
3637
- program.name("slock-computer").description("Slock Computer \u2014 local-machine control plane (login + N per-server attachments).").version("0.0.1");
3638
- program.command("login").description("Log in via device-code (one user identity per Computer / SLOCK_HOME).").option("--server-url <url>", "Slock API base URL; defaults to SLOCK_SERVER_URL").action(withCliExit(async (opts) => {
3711
+ program.name("slock-computer").description("Slock Computer \u2014 local-machine control plane (login + N per-server attachments).").version("0.0.3");
3712
+ program.command("login").description("Log in via device-code (one user identity per Computer / SLOCK_HOME).").option("--server-url <url>", `Slock API base URL; defaults to SLOCK_SERVER_URL or ${DEFAULT_SLOCK_SERVER_URL}`).action(withCliExit(async (opts) => {
3639
3713
  await runLogin({ serverUrl: opts.serverUrl });
3640
3714
  }));
3641
- program.command("attach").argument("<serverSlug>", "target Slock server slug (canonical form `/myserver`; bare `myserver` accepted)").description("Attach this Computer to one Slock server (add-not-replace; multi-server OK).").option("--server-url <url>", "Slock API base URL; defaults to SLOCK_SERVER_URL").option("--name <name>", "Computer display name; defaults to a sanitized hostname").option("--no-run", "only authorize + write local state; do not start the supervisor").option("--foreground", "run the supervisor in this terminal instead of the background").action(withCliExit(async (serverSlug, opts) => {
3715
+ program.command("attach").argument("<serverSlug>", "target Slock server slug (canonical form `/myserver`; bare `myserver` accepted)").description("Attach this Computer to one Slock server (add-not-replace; multi-server OK).").option("--server-url <url>", `Slock API base URL; defaults to the saved user session, SLOCK_SERVER_URL, or ${DEFAULT_SLOCK_SERVER_URL}`).option("--name <name>", "Computer display name; defaults to a sanitized hostname").option("--no-run", "only authorize + write local state; do not start the supervisor").option("--foreground", "run the supervisor in this terminal instead of the background").action(withCliExit(async (serverSlug, opts) => {
3642
3716
  await withMutationLock(
3643
3717
  () => runAttach({ serverSlug, serverUrl: opts.serverUrl, name: opts.name, run: opts.run, foreground: opts.foreground })
3644
3718
  );
3645
3719
  }));
3646
- program.command("setup").argument("<serverSlug>", "target Slock server slug (canonical form `/myserver`; bare `myserver` accepted)").description("Set up this Computer for one server: login if needed, attach/adopt if needed, then start.").option("--server-url <url>", "Slock API base URL; defaults to SLOCK_SERVER_URL or the saved user session").option("--name <name>", "Computer display name for a new attachment/adoption; defaults to a sanitized hostname").option("--adopt-legacy", "attempt one-time migration from a legacy daemon before fresh attach fallback").option("--legacy-api-key <key>", "legacy key for --adopt-legacy (migration-only; prefer file/stdin)").option("--legacy-api-key-file <path>", "path to a file containing the legacy key for --adopt-legacy").option("--legacy-api-key-stdin", "read the legacy key from stdin for --adopt-legacy").option("--no-start", "stop after login + attach/adopt; do not start the supervisor").option("--foreground", "run the supervisor in this terminal instead of the background").option("-y, --yes", "allow non-interactive setup after confirming the planned actions").action(
3720
+ program.command("setup").argument("<serverSlug>", "target Slock server slug (canonical form `/myserver`; bare `myserver` accepted)").description("Set up this Computer for one server: login if needed, attach/adopt if needed, then start.").option("--server-url <url>", `Slock API base URL; defaults to the saved user session, SLOCK_SERVER_URL, or ${DEFAULT_SLOCK_SERVER_URL}`).option("--name <name>", "Computer display name for a new attachment/adoption; defaults to a sanitized hostname").option("--adopt-legacy", "attempt one-time migration from a legacy daemon before fresh attach fallback").option("--legacy-api-key <key>", "legacy key for --adopt-legacy (migration-only; prefer file/stdin)").option("--legacy-api-key-file <path>", "path to a file containing the legacy key for --adopt-legacy").option("--legacy-api-key-stdin", "read the legacy key from stdin for --adopt-legacy").option("--no-start", "stop after login + attach/adopt; do not start the supervisor").option("--foreground", "run the supervisor in this terminal instead of the background").option("-y, --yes", "allow non-interactive setup after confirming the planned actions").action(
3647
3721
  withCliExit(async (serverSlug, opts) => {
3648
3722
  await withMutationLock(
3649
3723
  () => runSetup({
@@ -3663,7 +3737,7 @@ program.command("setup").argument("<serverSlug>", "target Slock server slug (can
3663
3737
  );
3664
3738
  program.command("adopt-legacy").argument("<serverSlug>", "target Slock server slug (the legacy machine's server)").description(
3665
3739
  "One-time migration: trade a legacy sk_machine_* / sk_daemon_* key for a fresh Computer attachment (sk_computer_*)."
3666
- ).option("--server-url <url>", "Slock API base URL; defaults to SLOCK_SERVER_URL").option("--name <name>", "name to record on the new Computer attachment (default: existing machine name)").option("--legacy-api-key <key>", "raw legacy key on argv (insecure on shared shells; prefer --legacy-api-key-file or stdin)").option("--legacy-api-key-file <path>", "path to a 0600 file containing the legacy key (one line)").option("--legacy-api-key-stdin", "read the legacy key from stdin").action(
3740
+ ).option("--server-url <url>", `Slock API base URL; defaults to the saved user session, SLOCK_SERVER_URL, or ${DEFAULT_SLOCK_SERVER_URL}`).option("--name <name>", "name to record on the new Computer attachment (default: existing machine name)").option("--legacy-api-key <key>", "raw legacy key on argv (insecure on shared shells; prefer --legacy-api-key-file or stdin)").option("--legacy-api-key-file <path>", "path to a 0600 file containing the legacy key (one line)").option("--legacy-api-key-stdin", "read the legacy key from stdin").action(
3667
3741
  withCliExit(async (serverSlug, opts) => {
3668
3742
  await withMutationLock(
3669
3743
  () => runAdoptLegacy({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/computer",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
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": {
@@ -21,7 +21,7 @@
21
21
  "commander": "^12.1.0",
22
22
  "proper-lockfile": "^4.1.2",
23
23
  "undici": "^7.24.7",
24
- "@slock-ai/daemon": "0.52.2"
24
+ "@slock-ai/daemon": "0.53.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^25.5.0",