@slock-ai/computer 0.0.4 → 0.0.5

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 +161 -59
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -68,15 +68,21 @@ var ComputerAttachClient = class {
68
68
  }
69
69
  /** POST /api/computer/attach — user-authed; issues sk_computer_*. */
70
70
  async attach(serverSlug, name) {
71
- const res = await fetch2(this.url("/api/computer/attach"), {
72
- method: "POST",
73
- headers: {
74
- "Content-Type": "application/json",
75
- Authorization: `Bearer ${this.accessToken}`
76
- },
77
- body: JSON.stringify({ serverSlug, name })
78
- });
71
+ let res;
72
+ try {
73
+ res = await fetch2(this.url("/api/computer/attach"), {
74
+ method: "POST",
75
+ headers: {
76
+ "Content-Type": "application/json",
77
+ Authorization: `Bearer ${this.accessToken}`
78
+ },
79
+ body: JSON.stringify({ serverSlug, name })
80
+ });
81
+ } catch {
82
+ return { status: "error", code: "request_failed" };
83
+ }
79
84
  const body = await res.json().catch(() => null);
85
+ const code = body && typeof body.code === "string" ? body.code : void 0;
80
86
  if (res.status === 201 && body && typeof body.apiKey === "string") {
81
87
  return {
82
88
  status: "success",
@@ -89,9 +95,11 @@ var ComputerAttachClient = class {
89
95
  }
90
96
  if (res.status === 401) return { status: "error", code: "session_invalid" };
91
97
  if (res.status === 403) return { status: "not_authorized" };
92
- if (res.status === 404) return { status: "disabled" };
93
- const code = body && typeof body.code === "string" ? body.code : `http_${res.status}`;
94
- return { status: "error", code };
98
+ if (res.status === 404) {
99
+ if (code === "not_authorized" || code === "server_not_found") return { status: "server_not_found" };
100
+ if (!code || code === "computer_attach_disabled") return { status: "disabled" };
101
+ }
102
+ return { status: "error", code: code ?? `http_${res.status}` };
95
103
  }
96
104
  /**
97
105
  * POST /api/computer/adopt-legacy — task #39 PR-J1 (RFC v8.2 §5.11).
@@ -515,6 +523,12 @@ async function runAttach(opts) {
515
523
  "Not authorized to attach to that server. Check the server slug and that you're a member."
516
524
  );
517
525
  }
526
+ if (attached.status === "server_not_found") {
527
+ fail(
528
+ "ATTACH_SERVER_NOT_FOUND",
529
+ `Server ${formatServerSlugDisplay(slugForServer)} was not found on ${baseUrl}. Check the slug spelling and --server-url, then retry.`
530
+ );
531
+ }
518
532
  if (attached.status === "error") {
519
533
  if (attached.code === "session_invalid") {
520
534
  fail(
@@ -522,6 +536,12 @@ async function runAttach(opts) {
522
536
  "Your user session is no longer valid. Re-run `slock-computer login`."
523
537
  );
524
538
  }
539
+ if (attached.code === "request_failed") {
540
+ fail(
541
+ "ATTACH_REQUEST_FAILED",
542
+ `Could not reach ${baseUrl} while attaching to ${formatServerSlugDisplay(slugForServer)}. Check --server-url / network connectivity, then retry.`
543
+ );
544
+ }
525
545
  if (attached.code === "COMPUTER_NAME_COLLISION") {
526
546
  fail(
527
547
  "COMPUTER_NAME_COLLISION",
@@ -835,9 +855,10 @@ async function runAdoptLegacy(inputs) {
835
855
  failureReason: result.code ? `unexpected_response_${result.code}` : "unexpected_response_missing_code"
836
856
  });
837
857
  const responseDetail = result.code ? `status ${result.httpStatus}, code ${result.code}` : `status ${result.httpStatus}, missing error code`;
858
+ 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." : "";
838
859
  fail(
839
860
  "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.`
861
+ `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.`
841
862
  );
842
863
  }
843
864
  if (result.status === "error") {
@@ -971,8 +992,9 @@ async function appendAdoptionLog(slockHome, line) {
971
992
  }
972
993
 
973
994
  // src/setup.ts
974
- import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
975
- import { join as join3 } from "path";
995
+ import { chmod as chmod4, mkdir as mkdir8, readdir as readdir3, readFile as readFile6, rename as rename3, rm as rm2, writeFile as writeFile7 } from "fs/promises";
996
+ import { dirname as dirname8, join as join3 } from "path";
997
+ import { fetch as fetch3 } from "undici";
976
998
 
977
999
  // src/supervisor.ts
978
1000
  import { spawn as spawn2 } from "child_process";
@@ -1662,14 +1684,74 @@ async function bestEffortServerRevoke(serverUrl, apiKey, serverId) {
1662
1684
  }
1663
1685
 
1664
1686
  // src/setup.ts
1687
+ var USER_SESSION_EXPIRY_LEEWAY_MS = 3e4;
1665
1688
  async function hasValidUserSession(slockHome) {
1666
1689
  try {
1667
1690
  const parsed = JSON.parse(await readFile6(userSessionPath(slockHome), "utf8"));
1668
- return parsed.kind === "user-session" && typeof parsed.accessToken === "string" && parsed.accessToken.length > 0;
1691
+ return parsed.kind === "user-session" && typeof parsed.accessToken === "string" && parsed.accessToken.length > 0 && !isJwtExpired(parsed.accessToken);
1669
1692
  } catch {
1670
1693
  return false;
1671
1694
  }
1672
1695
  }
1696
+ function isJwtExpired(token, nowMs = Date.now()) {
1697
+ const [, payload] = token.split(".");
1698
+ if (!payload) return false;
1699
+ try {
1700
+ const parsed = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
1701
+ return typeof parsed.exp === "number" && parsed.exp * 1e3 <= nowMs + USER_SESSION_EXPIRY_LEEWAY_MS;
1702
+ } catch {
1703
+ return false;
1704
+ }
1705
+ }
1706
+ async function refreshUserSession(slockHome, serverUrl) {
1707
+ const file = userSessionPath(slockHome);
1708
+ let session;
1709
+ try {
1710
+ session = JSON.parse(await readFile6(file, "utf8"));
1711
+ } catch {
1712
+ return false;
1713
+ }
1714
+ if (session.kind !== "user-session" || typeof session.refreshToken !== "string" || session.refreshToken.length === 0) {
1715
+ return false;
1716
+ }
1717
+ const baseUrl = resolveServerUrl(serverUrl, session.serverUrl, process.env.SLOCK_SERVER_URL);
1718
+ const tmpFile = `${file}.${process.pid}.${Date.now()}.tmp`;
1719
+ try {
1720
+ const res = await fetch3(new URL("/api/auth/refresh", baseUrl).toString(), {
1721
+ method: "POST",
1722
+ headers: { "Content-Type": "application/json" },
1723
+ body: JSON.stringify({ refreshToken: session.refreshToken })
1724
+ });
1725
+ const body = await res.json().catch(() => null);
1726
+ if (res.status !== 200 || typeof body?.accessToken !== "string" || typeof body.refreshToken !== "string") {
1727
+ return false;
1728
+ }
1729
+ await mkdir8(dirname8(file), { recursive: true });
1730
+ await writeFile7(
1731
+ tmpFile,
1732
+ JSON.stringify(
1733
+ {
1734
+ kind: "user-session",
1735
+ userId: session.userId,
1736
+ accessToken: body.accessToken,
1737
+ refreshToken: body.refreshToken,
1738
+ serverUrl: baseUrl,
1739
+ createdAt: session.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1740
+ refreshedAt: (/* @__PURE__ */ new Date()).toISOString()
1741
+ },
1742
+ null,
1743
+ 2
1744
+ ),
1745
+ { mode: 384 }
1746
+ );
1747
+ await chmod4(tmpFile, 384);
1748
+ await rename3(tmpFile, file);
1749
+ return true;
1750
+ } catch {
1751
+ await rm2(tmpFile, { force: true }).catch(() => void 0);
1752
+ return false;
1753
+ }
1754
+ }
1673
1755
  async function hasLiveLegacyDaemon(slockHome) {
1674
1756
  let machineDirs;
1675
1757
  try {
@@ -1701,6 +1783,7 @@ async function runSetup(opts, deps = {}) {
1701
1783
  const attach = deps.runAttach ?? runAttach;
1702
1784
  const adopt = deps.runAdoptLegacy ?? runAdoptLegacy;
1703
1785
  const start = deps.runStart ?? runStart;
1786
+ const refreshSession = deps.refreshUserSession ?? refreshUserSession;
1704
1787
  const legacyDaemonCheck = deps.hasLiveLegacyDaemon ?? hasLiveLegacyDaemon;
1705
1788
  if (!isTty && !opts.yes) {
1706
1789
  fail(
@@ -1717,13 +1800,18 @@ async function runSetup(opts, deps = {}) {
1717
1800
  const label = formatServerSlugDisplay(opts.serverSlug);
1718
1801
  info(`Setting up Slock Computer for ${label}\u2026`);
1719
1802
  if (!await hasValidUserSession(slockHome)) {
1720
- if (!isTty) {
1721
- fail(
1722
- "NON_INTERACTIVE_SETUP_REQUIRES_FLAGS",
1723
- "No user session is available and setup is running non-interactively. Run `slock-computer login` in a terminal first, then re-run setup with --yes."
1724
- );
1803
+ if (await refreshSession(slockHome, opts.serverUrl)) {
1804
+ info("User session: refreshed.");
1805
+ } else {
1806
+ if (!isTty) {
1807
+ fail(
1808
+ "NON_INTERACTIVE_SETUP_REQUIRES_FLAGS",
1809
+ "No valid user session is available and setup is running non-interactively. Run `slock-computer login` in a terminal first, then re-run setup with --yes."
1810
+ );
1811
+ }
1812
+ info("User session: missing or expired; starting login.");
1813
+ await login({ serverUrl: opts.serverUrl });
1725
1814
  }
1726
- await login({ serverUrl: opts.serverUrl });
1727
1815
  } else {
1728
1816
  info("User session: already logged in.");
1729
1817
  }
@@ -1786,11 +1874,22 @@ async function runSetup(opts, deps = {}) {
1786
1874
 
1787
1875
  // src/status.ts
1788
1876
  import { readFile as readFile7 } from "fs/promises";
1789
- async function readJsonSafe(path2) {
1877
+ async function readUserSession(path2) {
1790
1878
  try {
1791
- return JSON.parse(await readFile7(path2, "utf8"));
1792
- } catch {
1793
- return null;
1879
+ const parsed = JSON.parse(await readFile7(path2, "utf8"));
1880
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1881
+ return { state: "present", session: parsed, error: null };
1882
+ }
1883
+ return { state: "invalid", session: null, error: "not a JSON object" };
1884
+ } catch (err) {
1885
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
1886
+ return { state: "missing", session: null, error: null };
1887
+ }
1888
+ return {
1889
+ state: "invalid",
1890
+ session: null,
1891
+ error: err instanceof Error ? err.message : String(err)
1892
+ };
1794
1893
  }
1795
1894
  }
1796
1895
  function str(v) {
@@ -1807,7 +1906,8 @@ async function deriveHealth(slockHome, serverId, daemon) {
1807
1906
  }
1808
1907
  async function buildStatusReport() {
1809
1908
  const slockHome = resolveSlockHome();
1810
- const session = await readJsonSafe(userSessionPath(slockHome));
1909
+ const sessionRead = await readUserSession(userSessionPath(slockHome));
1910
+ const session = sessionRead.session;
1811
1911
  const attachments = await listServerAttachments(slockHome);
1812
1912
  const supervisor = {
1813
1913
  ...await pidStatus(supervisorPidPath(slockHome)),
@@ -1833,6 +1933,7 @@ async function buildStatusReport() {
1833
1933
  loggedIn,
1834
1934
  userId: session ? str(session.userId) : null,
1835
1935
  loginServerUrl: session ? str(session.serverUrl) : null,
1936
+ userSessionError: sessionRead.state === "invalid" ? sessionRead.error : null,
1836
1937
  supervisor,
1837
1938
  servers
1838
1939
  };
@@ -1848,8 +1949,9 @@ async function runStatus(opts) {
1848
1949
  }
1849
1950
  info("");
1850
1951
  info(`SLOCK_HOME: ${report.slockHome}`);
1952
+ const loginDetail = report.userSessionError ? "no \u2014 user session file is invalid; re-run `slock-computer login`" : report.loggedIn ? `yes (user ${report.userId ?? "?"})` : "no \u2014 run `slock-computer login`";
1851
1953
  info(
1852
- `Logged in: ${report.loggedIn ? `yes (user ${report.userId ?? "?"})` : "no \u2014 run `slock-computer login`"}`
1954
+ `Logged in: ${loginDetail}`
1853
1955
  );
1854
1956
  if (report.loginServerUrl) info(`Login server: ${report.loginServerUrl}`);
1855
1957
  info(
@@ -2028,7 +2130,7 @@ async function runDoctorChecks() {
2028
2130
  const checks = [];
2029
2131
  checks.push({ name: "SLOCK_HOME", ok: true, detail: report.slockHome });
2030
2132
  checks.push(
2031
- report.loggedIn ? { name: "user session", ok: true, detail: `logged in (user ${report.userId ?? "?"})` } : { name: "user session", ok: false, detail: "not logged in \u2014 run `slock-computer login`" }
2133
+ report.userSessionError ? { name: "user session", ok: false, detail: "invalid user session file \u2014 re-run `slock-computer login`" } : report.loggedIn ? { name: "user session", ok: true, detail: `logged in (user ${report.userId ?? "?"})` } : { name: "user session", ok: false, detail: "not logged in \u2014 run `slock-computer login`" }
2032
2134
  );
2033
2135
  checks.push(
2034
2136
  report.supervisor.running ? { name: "supervisor", ok: true, detail: `running (pid ${report.supervisor.pid})` } : {
@@ -2171,13 +2273,13 @@ async function runLogs(opts) {
2171
2273
 
2172
2274
  // src/concurrency.ts
2173
2275
  import lockfile from "proper-lockfile";
2174
- import { mkdir as mkdir8 } from "fs/promises";
2276
+ import { mkdir as mkdir9 } from "fs/promises";
2175
2277
  import { join as join4 } from "path";
2176
2278
  var STALE_LOCK_THRESHOLD_MS = 6e4;
2177
2279
  async function withMutationLock(fn) {
2178
2280
  const slockHome = resolveSlockHome();
2179
2281
  const lockTarget = computerDir(slockHome);
2180
- await mkdir8(lockTarget, { recursive: true });
2282
+ await mkdir9(lockTarget, { recursive: true });
2181
2283
  const lockfilePath = join4(lockTarget, ".lock");
2182
2284
  let release = null;
2183
2285
  try {
@@ -2215,8 +2317,8 @@ async function withMutationLock(fn) {
2215
2317
  }
2216
2318
 
2217
2319
  // src/channel.ts
2218
- import { readFile as readFile9, writeFile as writeFile7, mkdir as mkdir9 } from "fs/promises";
2219
- import { dirname as dirname8 } from "path";
2320
+ import { readFile as readFile9, writeFile as writeFile8, mkdir as mkdir10 } from "fs/promises";
2321
+ import { dirname as dirname9 } from "path";
2220
2322
  var DEFAULT_CHANNEL = "latest";
2221
2323
  var SEMVER_RE = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
2222
2324
  function parseChannel(raw) {
@@ -2240,8 +2342,8 @@ async function readChannel(slockHome) {
2240
2342
  }
2241
2343
  async function writeChannel(slockHome, channel2) {
2242
2344
  const p = channelPath(slockHome);
2243
- await mkdir9(dirname8(p), { recursive: true });
2244
- await writeFile7(p, `${channel2}
2345
+ await mkdir10(dirname9(p), { recursive: true });
2346
+ await writeFile8(p, `${channel2}
2245
2347
  `, { mode: 384 });
2246
2348
  }
2247
2349
  async function runChannelShow(slockHome) {
@@ -2265,11 +2367,11 @@ async function runChannelSet(slockHome, raw) {
2265
2367
  // src/upgradeCli.ts
2266
2368
  import { readFile as readFile12 } from "fs/promises";
2267
2369
  import { fileURLToPath as fileURLToPath2 } from "url";
2268
- import { dirname as dirname9, join as join7 } from "path";
2370
+ import { dirname as dirname10, join as join7 } from "path";
2269
2371
 
2270
2372
  // src/upgrade.ts
2271
2373
  import { spawn as spawn4 } from "child_process";
2272
- import { mkdir as mkdir10, readFile as readFile11, writeFile as writeFile8, rm as rm2, rename as rename3 } from "fs/promises";
2374
+ import { mkdir as mkdir11, readFile as readFile11, writeFile as writeFile9, rm as rm3, rename as rename4 } from "fs/promises";
2273
2375
  import { join as join6 } from "path";
2274
2376
  import { createHash as createHash3 } from "crypto";
2275
2377
 
@@ -2547,7 +2649,7 @@ async function stagePhase(slockHome, version, deps = {}) {
2547
2649
  const npmPack = deps.npmPack ?? defaultNpmPack;
2548
2650
  const fsReadFile = deps.fsReadFile ?? readFile11;
2549
2651
  const stagedPath = upgradeStagingDir(slockHome, version);
2550
- await mkdir10(stagedPath, { recursive: true });
2652
+ await mkdir11(stagedPath, { recursive: true });
2551
2653
  const packageRef = `@slock-ai/computer@${version}`;
2552
2654
  const result = await npmPack(stagedPath, packageRef);
2553
2655
  if (result.exitCode !== 0) {
@@ -2648,7 +2750,7 @@ async function defaultFetchAdvertisedHash(version) {
2648
2750
  }
2649
2751
  async function cleanupStaged(slockHome, version) {
2650
2752
  try {
2651
- await rm2(upgradeStagingDir(slockHome, version), { recursive: true, force: true });
2753
+ await rm3(upgradeStagingDir(slockHome, version), { recursive: true, force: true });
2652
2754
  } catch {
2653
2755
  }
2654
2756
  }
@@ -2658,9 +2760,9 @@ function upgradeSnapshotPath(slockHome) {
2658
2760
  async function snapshotPhase(slockHome, snap) {
2659
2761
  const path2 = upgradeSnapshotPath(slockHome);
2660
2762
  const tmp = `${path2}.tmp`;
2661
- await mkdir10(computerDir(slockHome), { recursive: true });
2662
- await writeFile8(tmp, JSON.stringify(snap, null, 2), { mode: 384 });
2663
- await rename3(tmp, path2);
2763
+ await mkdir11(computerDir(slockHome), { recursive: true });
2764
+ await writeFile9(tmp, JSON.stringify(snap, null, 2), { mode: 384 });
2765
+ await rename4(tmp, path2);
2664
2766
  }
2665
2767
  async function clearUpgradeSnapshot(slockHome) {
2666
2768
  try {
@@ -2671,7 +2773,7 @@ async function clearUpgradeSnapshot(slockHome) {
2671
2773
  }
2672
2774
  async function extractTarball(tarballPath, destDir, deps = {}) {
2673
2775
  const tarSpawn = deps.tarSpawn ?? defaultTarSpawn;
2674
- await mkdir10(destDir, { recursive: true });
2776
+ await mkdir11(destDir, { recursive: true });
2675
2777
  const result = await tarSpawn(tarballPath, destDir);
2676
2778
  if (result.exitCode !== 0) {
2677
2779
  const err = new Error(
@@ -2700,8 +2802,8 @@ function defaultTarSpawn(tarballPath, destDir) {
2700
2802
  });
2701
2803
  }
2702
2804
  async function swapPhase(currentBinaryDir, stagedBinaryDir, deps = {}) {
2703
- const fsRename = deps.fsRename ?? rename3;
2704
- const fsRm = deps.fsRm ?? ((p) => rm2(p, { recursive: true, force: true }));
2805
+ const fsRename = deps.fsRename ?? rename4;
2806
+ const fsRm = deps.fsRm ?? ((p) => rm3(p, { recursive: true, force: true }));
2705
2807
  const prevBinaryDir = `${currentBinaryDir}.prev`;
2706
2808
  try {
2707
2809
  await fsRm(prevBinaryDir);
@@ -2723,8 +2825,8 @@ async function swapPhase(currentBinaryDir, stagedBinaryDir, deps = {}) {
2723
2825
  return { prevBinaryDir, newBinaryDir: currentBinaryDir };
2724
2826
  }
2725
2827
  async function rollbackSwap(currentBinaryDir, deps = {}) {
2726
- const fsRename = deps.fsRename ?? rename3;
2727
- const fsRm = deps.fsRm ?? ((p) => rm2(p, { recursive: true, force: true }));
2828
+ const fsRename = deps.fsRename ?? rename4;
2829
+ const fsRm = deps.fsRm ?? ((p) => rm3(p, { recursive: true, force: true }));
2728
2830
  const prevBinaryDir = `${currentBinaryDir}.prev`;
2729
2831
  try {
2730
2832
  await fsRm(currentBinaryDir);
@@ -2942,7 +3044,7 @@ async function cleanupSuccessPhase(slockHome, version, prevBinaryDir) {
2942
3044
  await cleanupStaged(slockHome, version);
2943
3045
  await clearUpgradeSnapshot(slockHome);
2944
3046
  try {
2945
- await rm2(prevBinaryDir, { recursive: true, force: true });
3047
+ await rm3(prevBinaryDir, { recursive: true, force: true });
2946
3048
  } catch {
2947
3049
  }
2948
3050
  }
@@ -3355,7 +3457,7 @@ async function defaultFetchDistTags() {
3355
3457
  }
3356
3458
  function defaultCurrentBinaryDir() {
3357
3459
  const here = fileURLToPath2(import.meta.url);
3358
- return dirname9(dirname9(here));
3460
+ return dirname10(dirname10(here));
3359
3461
  }
3360
3462
  async function defaultCurrentVersion() {
3361
3463
  const pkgPath = join7(defaultCurrentBinaryDir(), "package.json");
@@ -3371,7 +3473,7 @@ async function defaultCurrentVersion() {
3371
3473
  }
3372
3474
 
3373
3475
  // src/upgradeTestHarness.ts
3374
- import { mkdir as mkdir11, readdir as readdir4, stat as stat3, writeFile as writeFile9 } from "fs/promises";
3476
+ import { mkdir as mkdir12, readdir as readdir4, stat as stat3, writeFile as writeFile10 } from "fs/promises";
3375
3477
  import { join as join8 } from "path";
3376
3478
  import { createHash as createHash4 } from "crypto";
3377
3479
  var PHASES = /* @__PURE__ */ new Set([
@@ -3430,7 +3532,7 @@ function buildSimulatedDeps(slockHome, opts) {
3430
3532
  return { tarballPath: "", exitCode: 1, stderr: "simulated stage failure" };
3431
3533
  }
3432
3534
  const filename = `slock-ai-computer-${targetVersion}.tgz`;
3433
- await writeFile9(join8(cwd, filename), tarballBytes);
3535
+ await writeFile10(join8(cwd, filename), tarballBytes);
3434
3536
  return { tarballPath: join8(cwd, filename), exitCode: 0, stderr: "" };
3435
3537
  },
3436
3538
  fetchAdvertisedHash: async () => {
@@ -3442,8 +3544,8 @@ function buildSimulatedDeps(slockHome, opts) {
3442
3544
  if (opts.simulateFail === "extract") {
3443
3545
  return { exitCode: 1, stderr: "simulated extract failure" };
3444
3546
  }
3445
- await mkdir11(join8(destDir, "package"), { recursive: true });
3446
- await writeFile9(join8(destDir, "package", "marker.txt"), `NEW@${targetVersion}`);
3547
+ await mkdir12(join8(destDir, "package"), { recursive: true });
3548
+ await writeFile10(join8(destDir, "package", "marker.txt"), `NEW@${targetVersion}`);
3447
3549
  return { exitCode: 0, stderr: "" };
3448
3550
  },
3449
3551
  fsRename: opts.simulateFail === "swap" ? async () => {
@@ -3503,7 +3605,7 @@ function buildSimulatedDeps(slockHome, opts) {
3503
3605
  }
3504
3606
  async function arrangeSnapshotFailure(slockHome) {
3505
3607
  const snapshotPath = join8(slockHome, "computer", "upgrade-snapshot.json");
3506
- await mkdir11(snapshotPath, { recursive: true });
3608
+ await mkdir12(snapshotPath, { recursive: true });
3507
3609
  }
3508
3610
  async function pathInfo(path2) {
3509
3611
  try {
@@ -3558,12 +3660,12 @@ async function runUpgradeTestHarness(slockHome, opts, writer = (s) => process.st
3558
3660
  process.exitCode = 1;
3559
3661
  return;
3560
3662
  }
3561
- await mkdir11(slockHome, { recursive: true });
3663
+ await mkdir12(slockHome, { recursive: true });
3562
3664
  if (opts.simulateFail === "snapshot") {
3563
3665
  await arrangeSnapshotFailure(slockHome);
3564
3666
  }
3565
3667
  const { opts: upgradeOpts } = buildSimulatedDeps(slockHome, opts);
3566
- await mkdir11(upgradeOpts.currentBinaryDir, { recursive: true });
3668
+ await mkdir12(upgradeOpts.currentBinaryDir, { recursive: true });
3567
3669
  let outcome;
3568
3670
  try {
3569
3671
  outcome = await runUpgrade(slockHome, upgradeOpts);
@@ -3600,9 +3702,9 @@ async function runUpgradeTestHarness(slockHome, opts, writer = (s) => process.st
3600
3702
  }
3601
3703
 
3602
3704
  // src/upgradeInstallSmoke.ts
3603
- import { copyFile, mkdir as mkdir12, readFile as readFile13 } from "fs/promises";
3705
+ import { copyFile, mkdir as mkdir13, readFile as readFile13 } from "fs/promises";
3604
3706
  import { createHash as createHash5 } from "crypto";
3605
- import { dirname as dirname10, isAbsolute, join as join9, resolve as pathResolve } from "path";
3707
+ import { dirname as dirname11, isAbsolute, join as join9, resolve as pathResolve } from "path";
3606
3708
  import { fileURLToPath as fileURLToPath3 } from "url";
3607
3709
  async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
3608
3710
  if (typeof opts.packageTarball !== "string" || opts.packageTarball.trim().length === 0) {
@@ -3623,7 +3725,7 @@ async function runUpgradeInstallSmoke(slockHome, opts, deps = {}) {
3623
3725
  const spawnFreshSupervisor = deps.spawnFreshSupervisor ?? (async (h) => {
3624
3726
  await spawnDetachedSupervisor(h);
3625
3727
  });
3626
- await mkdir12(slockHome, { recursive: true });
3728
+ await mkdir13(slockHome, { recursive: true });
3627
3729
  const outcome = await runUpgrade(slockHome, {
3628
3730
  targetVersion: opts.targetVersion,
3629
3731
  fromVersion,
@@ -3676,7 +3778,7 @@ async function runUpgradeInstallSmokeCli(slockHome, opts, writer = (s) => proces
3676
3778
  }
3677
3779
  function defaultCurrentBinaryDirLocal() {
3678
3780
  const here = fileURLToPath3(import.meta.url);
3679
- return dirname10(dirname10(here));
3781
+ return dirname11(dirname11(here));
3680
3782
  }
3681
3783
  async function defaultCurrentVersionLocal() {
3682
3784
  const pkgPath = join9(defaultCurrentBinaryDirLocal(), "package.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/computer",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
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": {