@scalequality/cli 0.4.21 → 0.4.22

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "sdkVersion": "0.3.281",
3
- "sha256": "64b584e7f307f8bed9bee151ca7dd57c5fe8e54c8adf6e9fd76ba11dbfba93c1",
4
- "sourceCommit": "a4093e270f574ff0830763c574a17585cf269fe6"
3
+ "sha256": "46fe6b09a8c69a92a0f54cc420a3596c6f13b82177d3aec1d46456ae508a4517",
4
+ "sourceCommit": "2e2e401c42e98d17e1c0dd8286bdc6d15e44f77b"
5
5
  }
package/dist/connect.cjs CHANGED
@@ -563,10 +563,15 @@ async function writeScrubbedTranscript(source, target) {
563
563
 
564
564
  // src/application/services/workspaceSandbox/backgroundService.ts
565
565
  var import_child_process2 = require("child_process");
566
- var import_crypto3 = require("crypto");
566
+ var import_crypto4 = require("crypto");
567
567
  var import_fs5 = require("fs");
568
568
  var import_path6 = require("path");
569
569
 
570
+ // src/application/services/workspaceSandbox/HttpSessionTransport.ts
571
+ var import_crypto2 = require("crypto");
572
+ var import_util2 = require("util");
573
+ var import_zlib = require("zlib");
574
+
570
575
  // src/application/services/workspaceSandbox/reasoning.ts
571
576
  var REASONING_LEVELS = ["off", "low", "medium", "high", "xhigh", "max"];
572
577
  function isReasoningLevel(value) {
@@ -729,6 +734,15 @@ function serializeCheckpoints(map) {
729
734
  }
730
735
  return Object.keys(repos).length ? JSON.stringify({ version: CHECKPOINT_MAP_VERSION, repos }) : "";
731
736
  }
737
+ var CHECKPOINT_INLINE_MAX_BYTES = 6 * 1024 * 1024;
738
+ var CHECKPOINT_PART_BYTES = 4 * 1024 * 1024;
739
+ var CHECKPOINT_MAX_PARTS = 32;
740
+ function checkpointPartsOf(raw) {
741
+ const p = raw;
742
+ if (!p || typeof p !== "object" || typeof p.sha256 !== "string" || !/^[0-9a-f]{64}$/.test(p.sha256)) return null;
743
+ if (!Number.isInteger(p.count) || p.count < 1 || p.count > CHECKPOINT_MAX_PARTS || !Number.isInteger(p.bytes) || p.bytes < 1) return null;
744
+ return { sha256: p.sha256, count: p.count, bytes: p.bytes };
745
+ }
732
746
 
733
747
  // src/application/services/workspaceSandbox/workspaceGit.ts
734
748
  var run = (0, import_util.promisify)(import_child_process.execFile);
@@ -856,11 +870,36 @@ async function computeDiff(root, base, caps = DIFF_CAPS, scope) {
856
870
  async function worktreeTreeId(root, scope) {
857
871
  return withWorktreeIndex(root, async (env) => (await git(["write-tree"], { cwd: root, env })).trim(), scope);
858
872
  }
859
- async function checkpointPatch(root, base, scope) {
873
+ var CHECKPOINT_KEPT_HEADER = "ScaleQuality-Kept:";
874
+ var CHECKPOINT_KEPT_TEXT = { bytes: 1024 * 1024, lines: 2e4 };
875
+ async function checkpointPatch(root, base, scope, opts = {}) {
860
876
  return withWorktreeIndex(root, async (env) => {
861
- const patch = await git(["diff", "--cached", "--no-renames", "--binary", base, ...scopeSpec(scope)], { cwd: root, env, maxBuffer: 512 * 1024 * 1024 });
862
- return patch ? `${CHECKPOINT_BASE_HEADER} ${base}
863
- ${patch}` : "";
877
+ const head = `${CHECKPOINT_BASE_HEADER} ${base}
878
+ `;
879
+ const whole = async () => {
880
+ const patch2 = await git(["diff", "--cached", "--no-renames", "--binary", base, ...scopeSpec(scope)], { cwd: root, env, maxBuffer: 512 * 1024 * 1024 });
881
+ return patch2 ? head + patch2 : "";
882
+ };
883
+ if (!opts.keepInFolder) return whole();
884
+ const changes = await listChangesWithEnv(root, base, env, scope);
885
+ const kept = [];
886
+ const carried = [];
887
+ for (const c of changes) {
888
+ const size = c.binary || c.status === "deleted" ? 0 : (await (0, import_promises2.stat)((0, import_path2.join)(root, c.path)).catch(() => null))?.size ?? 0;
889
+ const large = size > CHECKPOINT_KEPT_TEXT.bytes || c.additions + c.deletions > CHECKPOINT_KEPT_TEXT.lines;
890
+ if (c.binary || large) kept.push(c);
891
+ else carried.push(c.path);
892
+ }
893
+ if (!kept.length) return whole();
894
+ let patch = "";
895
+ for (let i = 0; i < carried.length; i += 200) {
896
+ patch += await git(
897
+ ["diff", "--cached", "--no-renames", "--binary", base, "--", ...carried.slice(i, i + 200)],
898
+ { cwd: root, env: { ...env, GIT_LITERAL_PATHSPECS: "1" }, maxBuffer: 512 * 1024 * 1024 }
899
+ );
900
+ }
901
+ return head + kept.map((c) => `${CHECKPOINT_KEPT_HEADER} ${c.status} ${JSON.stringify(c.path)}
902
+ `).join("") + patch;
864
903
  }, scope);
865
904
  }
866
905
  var PR_FILE_MAX_BYTES = 2 * 1024 * 1024;
@@ -1931,6 +1970,8 @@ var TransportError = class extends Error {
1931
1970
  };
1932
1971
 
1933
1972
  // src/application/services/workspaceSandbox/HttpSessionTransport.ts
1973
+ var gzipAsync = (0, import_util2.promisify)(import_zlib.gzip);
1974
+ var gunzipAsync = (0, import_util2.promisify)(import_zlib.gunzip);
1934
1975
  var GONE = [401, 403, 404, 410];
1935
1976
  var HttpSessionTransport = class {
1936
1977
  constructor(opts) {
@@ -1946,6 +1987,8 @@ var HttpSessionTransport = class {
1946
1987
  gone;
1947
1988
  async bootstrap() {
1948
1989
  const raw = await this.get(`/bootstrap?features=${ENGINE_FEATURES.join(",")}`, this.opts.requestTimeoutMs ?? 3e4);
1990
+ const parts = checkpointPartsOf(raw?.checkpointParts);
1991
+ if (parts && typeof raw?.checkpointPatch !== "string") raw.checkpointPatch = await this.checkpointFromParts(parts);
1949
1992
  return toSessionBootstrap(raw);
1950
1993
  }
1951
1994
  async pollCommands(waitSeconds, signal) {
@@ -1972,7 +2015,31 @@ var HttpSessionTransport = class {
1972
2015
  return this.post("/pull-request", req, this.opts.toolTimeoutMs ?? 18e4);
1973
2016
  }
1974
2017
  async checkpoint(req) {
1975
- await this.post("/checkpoint", req, this.opts.requestTimeoutMs ?? 6e4);
2018
+ const timeout = this.opts.requestTimeoutMs ?? 6e4;
2019
+ if (Buffer.byteLength(JSON.stringify(req), "utf8") <= CHECKPOINT_INLINE_MAX_BYTES) {
2020
+ await this.post("/checkpoint", req, timeout);
2021
+ return;
2022
+ }
2023
+ const packed = await gzipAsync(Buffer.from(req.patch, "utf8"));
2024
+ const sha256 = (0, import_crypto2.createHash)("sha256").update(req.patch, "utf8").digest("hex");
2025
+ const count = Math.ceil(packed.length / CHECKPOINT_PART_BYTES);
2026
+ if (count > CHECKPOINT_MAX_PARTS) throw new TransportError(`checkpoint of ${packed.length} compressed bytes is over the limit`, 413, false);
2027
+ for (let index = 0; index < count; index++) {
2028
+ const data = packed.subarray(index * CHECKPOINT_PART_BYTES, (index + 1) * CHECKPOINT_PART_BYTES).toString("base64");
2029
+ await this.post("/checkpoint/parts", { sha256, index, count, data }, timeout);
2030
+ }
2031
+ await this.post("/checkpoint", { parts: { sha256, count, bytes: packed.length }, sdkSessionId: req.sdkSessionId }, timeout);
2032
+ }
2033
+ async checkpointFromParts(parts) {
2034
+ const chunks = [];
2035
+ for (let index = 0; index < parts.count; index++) {
2036
+ const r = await this.get(`/checkpoint/parts/${index}`, this.opts.requestTimeoutMs ?? 6e4);
2037
+ if (r?.sha256 !== parts.sha256 || typeof r.data !== "string") throw new TransportError("checkpoint part does not match", 502, false);
2038
+ chunks.push(Buffer.from(r.data, "base64"));
2039
+ }
2040
+ const patch = (await gunzipAsync(Buffer.concat(chunks)).catch(() => Buffer.alloc(0))).toString("utf8");
2041
+ if ((0, import_crypto2.createHash)("sha256").update(patch, "utf8").digest("hex") !== parts.sha256) throw new TransportError("checkpoint parts do not match", 502, false);
2042
+ return patch;
1976
2043
  }
1977
2044
  async openRepository(repoFullName) {
1978
2045
  const raw = await this.post("/repositories/open", { repoFullName }, this.opts.requestTimeoutMs ?? 3e4);
@@ -2277,7 +2344,7 @@ var import_os3 = require("os");
2277
2344
  var import_path5 = require("path");
2278
2345
 
2279
2346
  // src/application/services/workspaceSandbox/localTracking.ts
2280
- var import_crypto2 = require("crypto");
2347
+ var import_crypto3 = require("crypto");
2281
2348
  var import_fs4 = require("fs");
2282
2349
  var import_promises5 = require("fs/promises");
2283
2350
  var import_os2 = require("os");
@@ -2305,7 +2372,7 @@ function gitTracker(root, base, prefix) {
2305
2372
  },
2306
2373
  snapshot: () => worktreeTreeId(root, scope),
2307
2374
  restore: (tree) => restoreTree(root, tree, scope),
2308
- checkpoint: () => checkpointPatch(root, base, scope),
2375
+ checkpoint: (opts) => checkpointPatch(root, base, scope, opts),
2309
2376
  prFiles: (published) => filesForPullRequest(root, base, published, scope),
2310
2377
  treeId: () => worktreeTreeId(root, scope)
2311
2378
  };
@@ -2389,7 +2456,7 @@ async function mapLimit(items, n, fn) {
2389
2456
  async function repoFingerprint(root) {
2390
2457
  const head = (await git(["rev-parse", "-q", "--verify", "HEAD"], { cwd: root }).catch(() => "")).trim();
2391
2458
  const status = await git(["--no-optional-locks", "status", "--porcelain", "-z", "--untracked-files=all"], { cwd: root, timeoutMs: 6e4 }).catch(() => "?");
2392
- const h = (0, import_crypto2.createHash)("sha256").update(head).update("\0").update(status);
2459
+ const h = (0, import_crypto3.createHash)("sha256").update(head).update("\0").update(status);
2393
2460
  const recs = status.split("\0");
2394
2461
  let n = 0;
2395
2462
  for (let i = 0; i < recs.length && n < 2e3; i++) {
@@ -2561,10 +2628,10 @@ var LooseFileTracker = class {
2561
2628
  }
2562
2629
  await restoreTree(this.o.root, target, this.scope());
2563
2630
  }
2564
- async checkpoint() {
2631
+ async checkpoint(opts) {
2565
2632
  await this.chain;
2566
2633
  if (!this.entries.length) return "";
2567
- return checkpointPatch(this.o.root, await this.baseTree(), this.scope());
2634
+ return checkpointPatch(this.o.root, await this.baseTree(), this.scope(), opts);
2568
2635
  }
2569
2636
  async prFiles(published) {
2570
2637
  await this.chain;
@@ -3530,7 +3597,7 @@ function serviceSlug(api) {
3530
3597
  } catch {
3531
3598
  }
3532
3599
  const slug = host.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "custom";
3533
- return `${slug}-${(0, import_crypto3.createHash)("sha256").update(api).digest("hex").slice(0, 6)}`;
3600
+ return `${slug}-${(0, import_crypto4.createHash)("sha256").update(api).digest("hex").slice(0, 6)}`;
3534
3601
  }
3535
3602
  function serviceNames(api) {
3536
3603
  const slug = serviceSlug(api);
@@ -3669,7 +3736,7 @@ function installStableCopy(o) {
3669
3736
  const bundle = (0, import_path6.join)(o.packageRoot, "dist", "connect.cjs");
3670
3737
  const bin = (dir2) => (0, import_path6.join)(dir2, "node_modules", "@scalequality", "cli", "bin", "scalequality.mjs");
3671
3738
  if (!(0, import_fs5.existsSync)(bundle)) throw new ServiceError("NOT_AN_INSTALLED_CLI", "This installation of @scalequality/cli is incomplete (dist/connect.cjs is missing).");
3672
- const sha = (0, import_crypto3.createHash)("sha256").update((0, import_fs5.readFileSync)(bundle)).digest("hex");
3739
+ const sha = (0, import_crypto4.createHash)("sha256").update((0, import_fs5.readFileSync)(bundle)).digest("hex");
3673
3740
  const base = (0, import_path6.join)(sqHome(o.home), "cli");
3674
3741
  const dir = (0, import_path6.join)(base, pkg.version);
3675
3742
  if (inside2(dir, o.packageRoot)) return { dir, bin: bin(dir), version: pkg.version, reused: true };
@@ -4817,7 +4884,7 @@ async function addFolder(credentials2, api, path, home) {
4817
4884
  var import_child_process3 = require("child_process");
4818
4885
  var import_promises14 = require("fs/promises");
4819
4886
  var import_fs9 = require("fs");
4820
- var import_crypto7 = require("crypto");
4887
+ var import_crypto8 = require("crypto");
4821
4888
  var import_path15 = require("path");
4822
4889
 
4823
4890
  // src/application/services/execution/LanguageAdapter.ts
@@ -5261,7 +5328,7 @@ function limitInEngineText(text3) {
5261
5328
  }
5262
5329
 
5263
5330
  // src/application/services/workspaceSandbox/scalequalityTools.ts
5264
- var import_crypto4 = require("crypto");
5331
+ var import_crypto5 = require("crypto");
5265
5332
 
5266
5333
  // node_modules/zod/v3/external.js
5267
5334
  var external_exports = {};
@@ -9381,7 +9448,7 @@ function buildScaleQualityServer(sdk, host) {
9381
9448
  const tools = REMOTE_TOOLS.map(
9382
9449
  (spec) => sdk.tool(spec.name, spec.description, spec.shape, async (args) => {
9383
9450
  const a = { ...args ?? {} };
9384
- if (spec.idempotent && typeof a.idempotencyKey !== "string") a.idempotencyKey = (0, import_crypto4.randomUUID)();
9451
+ if (spec.idempotent && typeof a.idempotencyKey !== "string") a.idempotencyKey = (0, import_crypto5.randomUUID)();
9385
9452
  return callScaleQualityTool(host, spec.name, a);
9386
9453
  })
9387
9454
  );
@@ -9956,7 +10023,7 @@ function blockText(content) {
9956
10023
  }
9957
10024
 
9958
10025
  // src/application/services/workspaceSandbox/inputChannel.ts
9959
- var import_crypto5 = require("crypto");
10026
+ var import_crypto6 = require("crypto");
9960
10027
  var InputChannel = class {
9961
10028
  buffer = [];
9962
10029
  waiting = null;
@@ -10014,7 +10081,7 @@ var InputChannel = class {
10014
10081
  var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10015
10082
  function messageUuid(id) {
10016
10083
  if (UUID.test(id)) return id.toLowerCase();
10017
- const h = (0, import_crypto5.createHash)("sha256").update(`sq-message:${id}`).digest("hex");
10084
+ const h = (0, import_crypto6.createHash)("sha256").update(`sq-message:${id}`).digest("hex");
10018
10085
  return `${h.slice(0, 8)}-${h.slice(8, 12)}-5${h.slice(13, 16)}-${(parseInt(h[16], 16) & 3 | 8).toString(16)}${h.slice(17, 20)}-${h.slice(20, 32)}`;
10019
10086
  }
10020
10087
  function userMessage(text3, images, uuid2, priority) {
@@ -10346,11 +10413,11 @@ async function detectTestCommandFromManifests(dir) {
10346
10413
  }
10347
10414
 
10348
10415
  // src/application/services/workspaceSandbox/transcript.ts
10349
- var import_crypto6 = require("crypto");
10416
+ var import_crypto7 = require("crypto");
10350
10417
  var import_fs8 = require("fs");
10351
10418
  var import_promises11 = require("fs/promises");
10352
10419
  var import_path12 = require("path");
10353
- var import_zlib = require("zlib");
10420
+ var import_zlib2 = require("zlib");
10354
10421
  var TRANSCRIPT_CAPS = { maxRawBytes: 64 * 1024 * 1024, maxCompressedBytes: 4 * 1024 * 1024 };
10355
10422
  function engineProjectDir(cwd) {
10356
10423
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
@@ -10389,10 +10456,10 @@ async function packTranscript(configDir, sessionId, redactor, caps = TRANSCRIPT_
10389
10456
  }
10390
10457
  const text3 = lines2.length ? `${lines2.join("\n")}
10391
10458
  ` : "";
10392
- const zipped = (0, import_zlib.gzipSync)(Buffer.from(text3, "utf8"), { level: 9 });
10459
+ const zipped = (0, import_zlib2.gzipSync)(Buffer.from(text3, "utf8"), { level: 9 });
10393
10460
  if (zipped.length > caps.maxCompressedBytes) return { tooLarge: true, bytes: zipped.length };
10394
10461
  return {
10395
- payload: { sdkSessionId: sessionId, encoding: "gzip-base64", data: zipped.toString("base64"), bytes: Buffer.byteLength(text3), sha256: (0, import_crypto6.createHash)("sha256").update(text3).digest("hex") },
10462
+ payload: { sdkSessionId: sessionId, encoding: "gzip-base64", data: zipped.toString("base64"), bytes: Buffer.byteLength(text3), sha256: (0, import_crypto7.createHash)("sha256").update(text3).digest("hex") },
10396
10463
  secretsRemoved: removed
10397
10464
  };
10398
10465
  }
@@ -10401,11 +10468,11 @@ async function restoreTranscript(configDir, cwd, t) {
10401
10468
  if (await findTranscript(configDir, t.sdkSessionId)) return true;
10402
10469
  let text3;
10403
10470
  try {
10404
- text3 = (0, import_zlib.gunzipSync)(Buffer.from(t.data, "base64"), { maxOutputLength: TRANSCRIPT_CAPS.maxRawBytes });
10471
+ text3 = (0, import_zlib2.gunzipSync)(Buffer.from(t.data, "base64"), { maxOutputLength: TRANSCRIPT_CAPS.maxRawBytes });
10405
10472
  } catch {
10406
10473
  return false;
10407
10474
  }
10408
- if (t.sha256 && (0, import_crypto6.createHash)("sha256").update(text3).digest("hex") !== t.sha256) return false;
10475
+ if (t.sha256 && (0, import_crypto7.createHash)("sha256").update(text3).digest("hex") !== t.sha256) return false;
10409
10476
  const dir = (0, import_path12.join)(configDir, "projects", engineProjectDir(cwd));
10410
10477
  await (0, import_promises11.mkdir)(dir, { recursive: true, mode: 448 });
10411
10478
  const target = (0, import_path12.join)(dir, `${t.sdkSessionId}.jsonl`);
@@ -12014,7 +12081,7 @@ ${String(item.content)}` : String(item.content);
12014
12081
  if (files === "unavailable") this.error("REWIND_FAILED", { turnId });
12015
12082
  this.emit({ type: "rewind", data: { turnId, files, conversation, ...untouched.length ? { untouched } : {} } });
12016
12083
  await this.diffNow();
12017
- await this.saveCheckpoint().catch(() => void 0);
12084
+ await this.saveCheckpoint().catch((e) => this.deps.log.warn("checkpoint failed", { error: this.redactor.text(e.message) }));
12018
12085
  this.setState("READY", "REWOUND");
12019
12086
  await this.sink.flush();
12020
12087
  }
@@ -12575,7 +12642,7 @@ ${text3}`;
12575
12642
  this.turnWatch = null;
12576
12643
  this.previousTrouble = stopped || !watch ? null : watch.errored ? "follows a request that ended with an error" : watch.lastExit !== null && watch.lastExit !== 0 ? "follows a failed verification" : null;
12577
12644
  await this.diffNow();
12578
- await this.saveCheckpoint().catch(() => void 0);
12645
+ await this.saveCheckpoint().catch((e) => this.deps.log.warn("checkpoint failed", { error: this.redactor.text(e.message) }));
12579
12646
  this.setState("READY", stopped ? "STOPPED" : void 0);
12580
12647
  await this.sink.flush();
12581
12648
  }
@@ -12960,9 +13027,18 @@ ${text3}`;
12960
13027
  * The checkpoint: every open repository's change against its base, plus the
12961
13028
  * saved changes of repositories not open in this run, as one map. In the
12962
13029
  * cloud workspace the engine's transcript goes with it (v4), so a resumed
12963
- * session continues the same conversation.
13030
+ * session continues the same conversation. On the person's computer the
13031
+ * folder keeps its binary and very large files, which the map only names.
13032
+ * The transcript is kept even when the change could not be.
12964
13033
  */
12965
13034
  async saveCheckpoint() {
13035
+ try {
13036
+ await this.saveChange();
13037
+ } finally {
13038
+ await this.saveTranscript().catch((e) => this.deps.log.warn("transcript not kept", { error: e.message }));
13039
+ }
13040
+ }
13041
+ async saveChange() {
12966
13042
  const map = new Map(this.pendingCheckpoints);
12967
13043
  for (const repo2 of this.repos.values()) {
12968
13044
  const key2 = repo2.repoFullName ?? repo2.localId ?? LOCAL_FOLDER_KEY;
@@ -12970,7 +13046,7 @@ ${text3}`;
12970
13046
  if (repo2.unsaved) map.set(key2, repo2.unsaved);
12971
13047
  continue;
12972
13048
  }
12973
- const patch2 = await repo2.tracker.checkpoint();
13049
+ const patch2 = await repo2.tracker.checkpoint(this.local ? { keepInFolder: true } : void 0);
12974
13050
  if (patch2) map.set(key2, patch2);
12975
13051
  else if (repo2.unsaved) map.set(key2, repo2.unsaved);
12976
13052
  else map.delete(key2);
@@ -12982,7 +13058,6 @@ ${patch}`;
12982
13058
  await this.deps.transport.checkpoint({ patch, sdkSessionId: this.sdkSessionId });
12983
13059
  this.lastCheckpoint = key;
12984
13060
  }
12985
- await this.saveTranscript().catch((e) => this.deps.log.warn("transcript not kept", { error: e.message }));
12986
13061
  }
12987
13062
  /** The engine conversation, without secrets, kept by the API (only when it changed). */
12988
13063
  async saveTranscript() {
@@ -13027,8 +13102,7 @@ ${patch}`;
13027
13102
  if (running) await Promise.race([running, new Promise((r) => setTimeout(r, 2e4))]);
13028
13103
  if (opts.checkpoint) {
13029
13104
  await this.saveCheckpoint().catch((e) => {
13030
- this.deps.log.warn("checkpoint failed", { error: e.message });
13031
- this.error("CHECKPOINT_FAILED");
13105
+ this.deps.log.warn("checkpoint failed", { error: this.redactor.text(e.message) });
13032
13106
  });
13033
13107
  }
13034
13108
  if (reason === "shutdown" && this.state && this.state !== "FAILED") this.setState("PAUSED", pausedCode);
@@ -13060,7 +13134,7 @@ ${patch}`;
13060
13134
  this.pendingCheckpoints.delete(repo2.localId);
13061
13135
  repo2.dormant = !repo2.unsaved;
13062
13136
  }
13063
- const stateDir = this.deps.stateDir ?? (0, import_path15.join)(this.deps.scratch, `folder-${(0, import_crypto7.createHash)("sha256").update(local.root).digest("hex").slice(0, 16)}`);
13137
+ const stateDir = this.deps.stateDir ?? (0, import_path15.join)(this.deps.scratch, `folder-${(0, import_crypto8.createHash)("sha256").update(local.root).digest("hex").slice(0, 16)}`);
13064
13138
  const tracker = new LooseFileTracker({ root: local.root, stateDir, excluded: () => local.repos.map((r) => r.root) });
13065
13139
  const tracked = local.gitAvailable && await tracker.init().then(() => true, (e) => {
13066
13140
  this.deps.log.warn("loose files not tracked", { error: e.message });
@@ -13677,7 +13751,7 @@ function buildQueryOptions(o) {
13677
13751
  }
13678
13752
  function spawnKey(i) {
13679
13753
  const { orgMcpServers, ...data } = i;
13680
- return (0, import_crypto7.createHash)("sha256").update(JSON.stringify({ ...data, orgMcpServers: Object.keys(orgMcpServers).sort() })).digest("hex");
13754
+ return (0, import_crypto8.createHash)("sha256").update(JSON.stringify({ ...data, orgMcpServers: Object.keys(orgMcpServers).sort() })).digest("hex");
13681
13755
  }
13682
13756
  function timedGate(gate, took) {
13683
13757
  const timed = async (run2) => {
@@ -13703,7 +13777,7 @@ async function hasLocalTranscript(configDir, sessionId) {
13703
13777
  }
13704
13778
 
13705
13779
  // src/application/services/workspaceSandbox/selfUpdate.ts
13706
- var import_crypto8 = require("crypto");
13780
+ var import_crypto9 = require("crypto");
13707
13781
  var import_fs10 = require("fs");
13708
13782
  var import_path16 = require("path");
13709
13783
  var REGISTRY_LATEST = "https://registry.npmjs.org/@scalequality%2fcli/latest";
@@ -13886,7 +13960,7 @@ async function installVersion(o) {
13886
13960
  if (pkg?.name !== "@scalequality/cli" || pkg?.version !== o.version) throw new UpdateError("WRONG_PACKAGE", `npm installed ${String(pkg?.name)}@${String(pkg?.version)} instead of ${o.version}.`);
13887
13961
  const build = readJson2((0, import_path16.join)(root, "dist", "connect.build.json"));
13888
13962
  const bundle = (0, import_path16.join)(root, "dist", "connect.cjs");
13889
- const sha = (0, import_fs10.existsSync)(bundle) ? (0, import_crypto8.createHash)("sha256").update((0, import_fs10.readFileSync)(bundle)).digest("hex") : null;
13963
+ const sha = (0, import_fs10.existsSync)(bundle) ? (0, import_crypto9.createHash)("sha256").update((0, import_fs10.readFileSync)(bundle)).digest("hex") : null;
13890
13964
  if (!sha || typeof build?.sha256 !== "string" || build.sha256 !== sha) throw new UpdateError("BUNDLE_MISMATCH", `The engine of ${o.version} does not match the sha256 it declares.`);
13891
13965
  let copy;
13892
13966
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalequality/cli",
3
- "version": "0.4.21",
3
+ "version": "0.4.22",
4
4
  "description": "ScaleQuality CLI. Connect your computer to the ScaleQuality AI Workspace (`scalequality login`), run its coding engine in any folder of your computer, and import your Claude Code and Codex conversations.",
5
5
  "type": "module",
6
6
  "bin": {