@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.
- package/dist/connect.build.json +2 -2
- package/dist/connect.cjs +109 -35
- package/package.json +1 -1
package/dist/connect.build.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sdkVersion": "0.3.281",
|
|
3
|
-
"sha256": "
|
|
4
|
-
"sourceCommit": "
|
|
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
|
|
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
|
-
|
|
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
|
|
862
|
-
|
|
863
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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(() =>
|
|
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(() =>
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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.
|
|
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": {
|