@centralu/darwin-arm64 0.1.0-beta.21 → 0.1.0-beta.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/Centralu.app/Contents/Info.plist +2 -2
- package/Centralu.app/Contents/MacOS/centralu +0 -0
- package/Centralu.app/Contents/MacOS/centralu-keeper +0 -0
- package/Centralu.app/Contents/Resources/content/centralu-keeper +0 -0
- package/Centralu.app/Contents/Resources/content/content-manifest.json +5 -5
- package/Centralu.app/Contents/Resources/content/content-manifest.json.sig +1 -1
- package/Centralu.app/Contents/Resources/content/host/bundle-info.json +2 -2
- package/Centralu.app/Contents/Resources/content/host/main.mjs +349 -96
- package/Centralu.app/Contents/Resources/resources/host/bundle-info.json +2 -2
- package/Centralu.app/Contents/Resources/resources/host/main.mjs +349 -96
- package/Centralu.app/Contents/_CodeSignature/CodeResources +16 -16
- package/package.json +1 -1
|
@@ -3235,7 +3235,7 @@ var require_stream = __commonJS({
|
|
|
3235
3235
|
};
|
|
3236
3236
|
duplex._final = function(callback) {
|
|
3237
3237
|
if (ws2.readyState === ws2.CONNECTING) {
|
|
3238
|
-
ws2.once("open", function
|
|
3238
|
+
ws2.once("open", function open6() {
|
|
3239
3239
|
duplex._final(callback);
|
|
3240
3240
|
});
|
|
3241
3241
|
return;
|
|
@@ -3256,7 +3256,7 @@ var require_stream = __commonJS({
|
|
|
3256
3256
|
};
|
|
3257
3257
|
duplex._write = function(chunk, encoding, callback) {
|
|
3258
3258
|
if (ws2.readyState === ws2.CONNECTING) {
|
|
3259
|
-
ws2.once("open", function
|
|
3259
|
+
ws2.once("open", function open6() {
|
|
3260
3260
|
duplex._write(chunk, encoding, callback);
|
|
3261
3261
|
});
|
|
3262
3262
|
return;
|
|
@@ -42965,7 +42965,7 @@ import { mkdirSync as mkdirSync11, writeSync as writeSync4 } from "node:fs";
|
|
|
42965
42965
|
|
|
42966
42966
|
// packages/protocol/src/brand.ts
|
|
42967
42967
|
var APP_NAME = "Centralu";
|
|
42968
|
-
var APP_VERSION = "0.1.0-beta.
|
|
42968
|
+
var APP_VERSION = "0.1.0-beta.22";
|
|
42969
42969
|
var APP_SLUG = "centralu";
|
|
42970
42970
|
var CLIENT_INFO = { name: APP_SLUG, title: APP_NAME, version: APP_VERSION };
|
|
42971
42971
|
var DATA_DIR = ".centralu";
|
|
@@ -62908,17 +62908,17 @@ function portInUseMessage(port2, servedBy) {
|
|
|
62908
62908
|
var VIEW_HANDOVER_KEY = "views.handover";
|
|
62909
62909
|
var VIEW_HANDOVER_MAX_AGE_MS = 10 * 6e4;
|
|
62910
62910
|
function recordViewHandover(settings, views2, inline, now = Date.now()) {
|
|
62911
|
-
const
|
|
62911
|
+
const open6 = views2.list().map((v) => {
|
|
62912
62912
|
const owner = inline?.owner(v.id);
|
|
62913
62913
|
return owner ? { ...v, inline: { sessionId: owner.sessionId, callId: owner.callId, tool: owner.tool } } : v;
|
|
62914
62914
|
});
|
|
62915
|
-
if (
|
|
62915
|
+
if (open6.length === 0) {
|
|
62916
62916
|
settings.deleteAppSetting(VIEW_HANDOVER_KEY);
|
|
62917
62917
|
return 0;
|
|
62918
62918
|
}
|
|
62919
|
-
const record2 = { at: now, views:
|
|
62919
|
+
const record2 = { at: now, views: open6 };
|
|
62920
62920
|
settings.setAppSetting(VIEW_HANDOVER_KEY, JSON.stringify(record2));
|
|
62921
|
-
return
|
|
62921
|
+
return open6.length;
|
|
62922
62922
|
}
|
|
62923
62923
|
function restoreViewHandover(settings, views2, inline, now = Date.now()) {
|
|
62924
62924
|
const raw = settings.appSetting(VIEW_HANDOVER_KEY);
|
|
@@ -63639,9 +63639,9 @@ import { join as join15 } from "node:path";
|
|
|
63639
63639
|
import { lstatSync as lstatSync2, watch } from "node:fs";
|
|
63640
63640
|
|
|
63641
63641
|
// packages/agent-host/src/dev-services/fs.ts
|
|
63642
|
-
import { spawn } from "node:child_process";
|
|
63642
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
63643
63643
|
import { constants as constants3 } from "node:fs";
|
|
63644
|
-
import { lstat as lstat2, mkdir, open, readdir, readlink, realpath as realpath2, rename, rm, stat as stat2, writeFile } from "node:fs/promises";
|
|
63644
|
+
import { lstat as lstat2, mkdir, open as open2, readdir, readlink, realpath as realpath2, rename, rm, stat as stat2, writeFile } from "node:fs/promises";
|
|
63645
63645
|
import { basename, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as join5, relative as relative2, resolve, sep as sep2 } from "node:path";
|
|
63646
63646
|
|
|
63647
63647
|
// packages/agent-host/src/dev-services/path-guard.ts
|
|
@@ -63776,7 +63776,8 @@ function isMissingFsPath(error51) {
|
|
|
63776
63776
|
}
|
|
63777
63777
|
|
|
63778
63778
|
// packages/agent-host/src/dev-services/git-exec.ts
|
|
63779
|
-
import { execFile } from "node:child_process";
|
|
63779
|
+
import { execFile, spawn } from "node:child_process";
|
|
63780
|
+
import { open } from "node:fs/promises";
|
|
63780
63781
|
import { devNull } from "node:os";
|
|
63781
63782
|
import { promisify } from "node:util";
|
|
63782
63783
|
|
|
@@ -64002,6 +64003,40 @@ async function gitArgv(cwd, args, allowRepoPrograms = false) {
|
|
|
64002
64003
|
}
|
|
64003
64004
|
async function runGit(cwd, args, opts = {}) {
|
|
64004
64005
|
const argv = await gitArgv(cwd, args, opts.allowRepoPrograms);
|
|
64006
|
+
if (opts.outputFile) {
|
|
64007
|
+
const output = await open(opts.outputFile, "w", 384);
|
|
64008
|
+
try {
|
|
64009
|
+
await new Promise((resolve12, reject) => {
|
|
64010
|
+
const child = spawn(programPath("git"), argv, {
|
|
64011
|
+
cwd,
|
|
64012
|
+
stdio: ["pipe", output.fd, "pipe"],
|
|
64013
|
+
timeout: opts.timeout ?? DEFAULTS.timeout,
|
|
64014
|
+
...opts.env ? { env: { ...process.env, ...opts.env } } : {}
|
|
64015
|
+
});
|
|
64016
|
+
let stderr = "";
|
|
64017
|
+
let overflow = false;
|
|
64018
|
+
child.stderr.on("data", (chunk) => {
|
|
64019
|
+
if (overflow) return;
|
|
64020
|
+
stderr += chunk.toString();
|
|
64021
|
+
if (Buffer.byteLength(stderr) > (opts.maxBuffer ?? DEFAULTS.maxBuffer)) {
|
|
64022
|
+
overflow = true;
|
|
64023
|
+
child.kill();
|
|
64024
|
+
}
|
|
64025
|
+
});
|
|
64026
|
+
child.on("error", reject);
|
|
64027
|
+
child.on("close", (code2, signal) => {
|
|
64028
|
+
if (code2 === 0 && !overflow) resolve12();
|
|
64029
|
+
else reject(Object.assign(new Error(overflow ? "Git stderr exceeded maxBuffer" : stderr || `Git exited with ${code2 ?? signal}`), { code: code2, stderr }));
|
|
64030
|
+
});
|
|
64031
|
+
child.stdin.on("error", () => {
|
|
64032
|
+
});
|
|
64033
|
+
child.stdin.end(opts.input);
|
|
64034
|
+
});
|
|
64035
|
+
return "";
|
|
64036
|
+
} finally {
|
|
64037
|
+
await output.close();
|
|
64038
|
+
}
|
|
64039
|
+
}
|
|
64005
64040
|
const run = exec(programPath("git"), argv, {
|
|
64006
64041
|
cwd,
|
|
64007
64042
|
timeout: opts.timeout ?? DEFAULTS.timeout,
|
|
@@ -64162,7 +64197,7 @@ async function readTextFile(root3, rel) {
|
|
|
64162
64197
|
const file2 = await resolveExisting(root3, rel);
|
|
64163
64198
|
const pathInfo = await stat2(file2, { bigint: true });
|
|
64164
64199
|
if (!pathInfo.isFile()) fail2("Path is not a regular file");
|
|
64165
|
-
const handle = await
|
|
64200
|
+
const handle = await open2(file2, READ_TEXT_FLAGS);
|
|
64166
64201
|
try {
|
|
64167
64202
|
const opened = await handle.stat({ bigint: true });
|
|
64168
64203
|
if (!opened.isFile()) fail2("Path is not a regular file");
|
|
@@ -64228,7 +64263,7 @@ async function copyTree(src, dst, clone2 = cloneTree) {
|
|
|
64228
64263
|
async function cloneTree(src, dst) {
|
|
64229
64264
|
if (!clonesFiles()) return false;
|
|
64230
64265
|
return new Promise((done) => {
|
|
64231
|
-
const p =
|
|
64266
|
+
const p = spawn2("/bin/cp", ["-Rc", src, dst], { stdio: "ignore" });
|
|
64232
64267
|
p.on("error", () => done(false));
|
|
64233
64268
|
p.on("close", (code2) => done(code2 === 0));
|
|
64234
64269
|
});
|
|
@@ -64385,7 +64420,7 @@ function isSymlink(path2) {
|
|
|
64385
64420
|
}
|
|
64386
64421
|
|
|
64387
64422
|
// packages/agent-host/src/apps/external/app-process.ts
|
|
64388
|
-
import { spawn as
|
|
64423
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
64389
64424
|
import { closeSync as closeSync2, existsSync as existsSync5, mkdirSync, openSync as openSync2, renameSync as renameSync3, statSync as statSync3, writeSync as writeSync3 } from "node:fs";
|
|
64390
64425
|
import { dirname as dirname3 } from "node:path";
|
|
64391
64426
|
|
|
@@ -79941,7 +79976,7 @@ var AppProcess = class _AppProcess {
|
|
|
79941
79976
|
log.note(`starting: ${spec.command} ${spec.args.join(" ")}`);
|
|
79942
79977
|
const launch2 = resolveCommand(spec.command, spec.env);
|
|
79943
79978
|
const spawnedAt = Date.now();
|
|
79944
|
-
const child =
|
|
79979
|
+
const child = spawn3(launch2.command, [...launch2.args, ...spec.args], {
|
|
79945
79980
|
cwd: spec.cwd,
|
|
79946
79981
|
env: spec.env,
|
|
79947
79982
|
/*
|
|
@@ -94045,7 +94080,7 @@ var RUN_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
|
94045
94080
|
var FAILURES_KEPT = 20;
|
|
94046
94081
|
var SUMMARY_CHARS = 200;
|
|
94047
94082
|
function announcingLedger(inner, announce) {
|
|
94048
|
-
const
|
|
94083
|
+
const open6 = /* @__PURE__ */ new Map();
|
|
94049
94084
|
const tell = (apps) => {
|
|
94050
94085
|
for (const a of apps ?? []) announce(a);
|
|
94051
94086
|
};
|
|
@@ -94054,20 +94089,20 @@ function announcingLedger(inner, announce) {
|
|
|
94054
94089
|
begin(row) {
|
|
94055
94090
|
inner.begin(row);
|
|
94056
94091
|
const apps = [{ projectId: row.projectId, appId: row.appId }];
|
|
94057
|
-
for (const up2 of row.parentRunId ?
|
|
94092
|
+
for (const up2 of row.parentRunId ? open6.get(row.parentRunId) ?? [] : []) {
|
|
94058
94093
|
if (!apps.some((a) => a.projectId === up2.projectId && a.appId === up2.appId)) apps.push(up2);
|
|
94059
94094
|
}
|
|
94060
|
-
|
|
94095
|
+
open6.set(row.id, apps);
|
|
94061
94096
|
tell(apps);
|
|
94062
94097
|
},
|
|
94063
94098
|
link(id2, sessionId) {
|
|
94064
94099
|
inner.link(id2, sessionId);
|
|
94065
|
-
tell(
|
|
94100
|
+
tell(open6.get(id2));
|
|
94066
94101
|
},
|
|
94067
94102
|
end(id2, e) {
|
|
94068
94103
|
inner.end(id2, e);
|
|
94069
|
-
tell(
|
|
94070
|
-
|
|
94104
|
+
tell(open6.get(id2));
|
|
94105
|
+
open6.delete(id2);
|
|
94071
94106
|
}
|
|
94072
94107
|
};
|
|
94073
94108
|
}
|
|
@@ -96600,8 +96635,8 @@ var ExternalApps = class {
|
|
|
96600
96635
|
return { runId, status, result, error: error51, durationMs };
|
|
96601
96636
|
};
|
|
96602
96637
|
if (!e.manifest) return done("rejected", null, `This app's manifest is invalid: ${e.error}`);
|
|
96603
|
-
const
|
|
96604
|
-
if (!e.scope.trusted && !
|
|
96638
|
+
const open6 = caller.kind === "session" && this.sessionBoundaryOpen(caller.sessionId);
|
|
96639
|
+
if (!e.scope.trusted && !open6) return done("rejected", null, "This app's project is not trusted, so Centralu does not call its apps");
|
|
96605
96640
|
const held3 = this.held(e);
|
|
96606
96641
|
if (held3) return done("rejected", null, held3);
|
|
96607
96642
|
if (e.life.gaveUp) return done("rejected", null, `This app stopped after failing ${this.timing.maxFailures} times in a row`);
|
|
@@ -96649,7 +96684,7 @@ var ExternalApps = class {
|
|
|
96649
96684
|
abort.abort();
|
|
96650
96685
|
for (const sig of upstream) sig.removeEventListener("abort", onUp);
|
|
96651
96686
|
}
|
|
96652
|
-
},
|
|
96687
|
+
}, open6);
|
|
96653
96688
|
} catch (err) {
|
|
96654
96689
|
return done("error", null, err.message);
|
|
96655
96690
|
}
|
|
@@ -97960,9 +97995,9 @@ var InlineViews = class {
|
|
|
97960
97995
|
* just opened is never the one closed.
|
|
97961
97996
|
*/
|
|
97962
97997
|
capLive(sessionId, keep) {
|
|
97963
|
-
const
|
|
97998
|
+
const open6 = [...this.bySession.get(sessionId)?.values() ?? []].filter((x) => x.instanceId).sort((a, b2) => a.openedAt - b2.openedAt);
|
|
97964
97999
|
const n = this.limits.livePerSession;
|
|
97965
|
-
for (const old of
|
|
98000
|
+
for (const old of open6.slice(0, Math.max(0, open6.length - n))) {
|
|
97966
98001
|
if (old === keep) continue;
|
|
97967
98002
|
this.shut(old);
|
|
97968
98003
|
this.deps.emit({
|
|
@@ -98172,8 +98207,9 @@ var OriginPorts = class {
|
|
|
98172
98207
|
|
|
98173
98208
|
// packages/agent-host/src/dev-services/worktree-recovery.ts
|
|
98174
98209
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
98175
|
-
import { existsSync as existsSync13 } from "node:fs";
|
|
98176
|
-
import { chmod, lstat as lstat3, mkdir as mkdir2, readFile, readdir as readdir2, realpath as realpath3, rm as rm2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
|
|
98210
|
+
import { createReadStream, existsSync as existsSync13 } from "node:fs";
|
|
98211
|
+
import { chmod, lstat as lstat3, mkdir as mkdir2, mkdtemp, readFile, readdir as readdir2, realpath as realpath3, rm as rm2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
|
|
98212
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
98177
98213
|
import { join as join16, resolve as resolve2 } from "node:path";
|
|
98178
98214
|
var WORKTREE_RECOVERY_MAX_BYTES = 256 * 1024 * 1024;
|
|
98179
98215
|
var RECOVERY_TIMEOUT = 12e4;
|
|
@@ -98184,6 +98220,25 @@ function worktreeRecoveryDir(sessionId) {
|
|
|
98184
98220
|
async function clearWorktreeRecovery(sessionId) {
|
|
98185
98221
|
await rm2(worktreeRecoveryDir(sessionId), { recursive: true, force: true });
|
|
98186
98222
|
}
|
|
98223
|
+
async function* gitRecords(repo, args, input) {
|
|
98224
|
+
const dir = await mkdtemp(join16(tmpdir2(), "cc-recovery-git-"));
|
|
98225
|
+
const file2 = join16(dir, "records");
|
|
98226
|
+
try {
|
|
98227
|
+
await runGit(repo, args, { outputFile: file2, timeout: RECOVERY_TIMEOUT, input });
|
|
98228
|
+
let pending = "";
|
|
98229
|
+
for await (const chunk of createReadStream(file2, { encoding: "utf8" })) {
|
|
98230
|
+
pending += chunk;
|
|
98231
|
+
let end;
|
|
98232
|
+
while ((end = pending.indexOf("\0")) !== -1) {
|
|
98233
|
+
yield pending.slice(0, end);
|
|
98234
|
+
pending = pending.slice(end + 1);
|
|
98235
|
+
}
|
|
98236
|
+
}
|
|
98237
|
+
if (pending) yield pending;
|
|
98238
|
+
} finally {
|
|
98239
|
+
await rm2(dir, { recursive: true, force: true });
|
|
98240
|
+
}
|
|
98241
|
+
}
|
|
98187
98242
|
async function saveWorktreeRecovery(sessionId, cwd, trunk, branch) {
|
|
98188
98243
|
const plans = [];
|
|
98189
98244
|
let bytes2 = 0;
|
|
@@ -98193,7 +98248,7 @@ async function saveWorktreeRecovery(sessionId, cwd, trunk, branch) {
|
|
|
98193
98248
|
throw new Error(`Worktree recovery exceeds the 256 MiB input limit (tracked and non-ignored untracked files plus staged changes, including submodules). No files were staged or removed; save the work elsewhere before deleting.`);
|
|
98194
98249
|
}
|
|
98195
98250
|
};
|
|
98196
|
-
const prepare = async (repo, path2) => {
|
|
98251
|
+
const prepare = async (repo, path2, gitlinks = []) => {
|
|
98197
98252
|
const tip = (await runGit(repo, ["rev-parse", "--verify", "HEAD"])).trim();
|
|
98198
98253
|
const refs = ["HEAD"];
|
|
98199
98254
|
if (!path2 && branch) {
|
|
@@ -98201,20 +98256,85 @@ async function saveWorktreeRecovery(sessionId, cwd, trunk, branch) {
|
|
|
98201
98256
|
if (await runGit(repo, ["rev-parse", "--verify", ref]).then(() => true).catch(() => false)) refs.push(ref);
|
|
98202
98257
|
} else if (path2) {
|
|
98203
98258
|
refs.push(...(await runGit(repo, ["for-each-ref", "--format=%(refname)", "refs/heads/"])).trim().split("\n").filter(Boolean));
|
|
98259
|
+
if (gitlinks.length) {
|
|
98260
|
+
const available = await runGit(repo, ["cat-file", "--batch-check=%(objectname) %(objecttype)"], {
|
|
98261
|
+
input: gitlinks.map((oid) => `${oid}^{commit}`).join("\n") + "\n",
|
|
98262
|
+
timeout: RECOVERY_TIMEOUT,
|
|
98263
|
+
// Older Git ignores the lazy-fetch switch; disallow transports there too.
|
|
98264
|
+
env: { GIT_NO_LAZY_FETCH: "1", GIT_ALLOW_PROTOCOL: "" }
|
|
98265
|
+
});
|
|
98266
|
+
for (const row of available.trim().split("\n")) {
|
|
98267
|
+
const [oid, type] = row.split(" ");
|
|
98268
|
+
if (type === "commit") refs.push(oid);
|
|
98269
|
+
}
|
|
98270
|
+
}
|
|
98204
98271
|
}
|
|
98205
98272
|
const baselineOid = !path2 ? await runGit(repo, ["rev-parse", "--verify", `${trunk}^{commit}`]).then((s) => s.trim()).catch(() => null) : null;
|
|
98206
|
-
const exclude = path2 ? ["--
|
|
98207
|
-
const
|
|
98273
|
+
const exclude = path2 ? (await runGit(repo, ["for-each-ref", "--format=^%(objectname)", "refs/remotes/"])).trim().split("\n").filter(Boolean) : baselineOid ? [`^${baselineOid}`] : [];
|
|
98274
|
+
const revisions = [...refs, ...exclude].join("\n") + "\n";
|
|
98275
|
+
const commits = Number((await runGit(repo, ["rev-list", "--count", "--stdin"], { input: revisions, timeout: RECOVERY_TIMEOUT })).trim());
|
|
98208
98276
|
plans.push({ repo, path: path2, tip, refs, commits });
|
|
98209
98277
|
const stagedNames = new Set((await runGit(repo, ["diff", "--cached", "--name-only", "-z", "--no-renames", tip], { timeout: RECOVERY_TIMEOUT })).split("\0"));
|
|
98210
98278
|
const modules = [];
|
|
98279
|
+
const moduleCommits = /* @__PURE__ */ new Map();
|
|
98280
|
+
const keepGitlink = (rel, oid) => {
|
|
98281
|
+
modules.push(rel);
|
|
98282
|
+
const tips = moduleCommits.get(rel) ?? /* @__PURE__ */ new Set();
|
|
98283
|
+
tips.add(oid);
|
|
98284
|
+
moduleCommits.set(rel, tips);
|
|
98285
|
+
};
|
|
98286
|
+
const manifests = /* @__PURE__ */ new Set();
|
|
98287
|
+
const keepManifest = (rel, oid) => {
|
|
98288
|
+
if (rel === ".gitmodules" && !/^0+$/.test(oid)) manifests.add(oid);
|
|
98289
|
+
};
|
|
98290
|
+
let change = null;
|
|
98291
|
+
for await (const row of gitRecords(repo, ["log", "--raw", "-z", "--no-renames", "--ignore-submodules=none", "--no-abbrev", "--format=", "--root", "--diff-merges=separate", "--stdin"], revisions)) {
|
|
98292
|
+
if (change) {
|
|
98293
|
+
const [, oldMode, newMode, oldOid, newOid] = change;
|
|
98294
|
+
if (oldMode === "160000") keepGitlink(row, oldOid);
|
|
98295
|
+
if (newMode === "160000") keepGitlink(row, newOid);
|
|
98296
|
+
keepManifest(row, oldOid);
|
|
98297
|
+
keepManifest(row, newOid);
|
|
98298
|
+
change = null;
|
|
98299
|
+
} else change = row.match(/^\n*:(\d{6}) (\d{6}) ([a-f0-9]+) ([a-f0-9]+) [A-Z]$/);
|
|
98300
|
+
}
|
|
98301
|
+
for await (const row of gitRecords(repo, ["ls-tree", "-r", "-z", "HEAD"])) {
|
|
98302
|
+
const tab = row.indexOf(" ");
|
|
98303
|
+
const [mode, , oid] = row.slice(0, tab).split(" ");
|
|
98304
|
+
const rel = row.slice(tab + 1);
|
|
98305
|
+
if (mode === "160000") keepGitlink(rel, oid);
|
|
98306
|
+
keepManifest(rel, oid);
|
|
98307
|
+
}
|
|
98211
98308
|
const stagedObjects = [];
|
|
98212
|
-
for (const row of (
|
|
98309
|
+
for await (const row of gitRecords(repo, ["ls-files", "--stage", "-z"])) {
|
|
98213
98310
|
if (!row) continue;
|
|
98214
98311
|
const tab = row.indexOf(" ");
|
|
98215
98312
|
const rel = row.slice(tab + 1);
|
|
98216
|
-
|
|
98217
|
-
|
|
98313
|
+
const oid = row.split(" ")[1];
|
|
98314
|
+
if (row.startsWith("160000 ")) keepGitlink(rel, oid);
|
|
98315
|
+
else if (stagedNames.has(rel)) stagedObjects.push(oid);
|
|
98316
|
+
keepManifest(rel, oid);
|
|
98317
|
+
}
|
|
98318
|
+
const modulePaths = /* @__PURE__ */ new Map();
|
|
98319
|
+
for (const oid of manifests) {
|
|
98320
|
+
const config2 = await runGit(repo, ["config", "--blob", oid, "-z", "--get-regexp", "^submodule\\..*\\.path$"]).catch((e) => {
|
|
98321
|
+
if (e.code === 1) return "";
|
|
98322
|
+
throw e;
|
|
98323
|
+
});
|
|
98324
|
+
for (const row of config2.split("\0").filter(Boolean)) {
|
|
98325
|
+
const newline = row.indexOf("\n");
|
|
98326
|
+
const name = row.slice("submodule.".length, newline - ".path".length);
|
|
98327
|
+
const paths = modulePaths.get(name) ?? /* @__PURE__ */ new Set();
|
|
98328
|
+
paths.add(row.slice(newline + 1));
|
|
98329
|
+
modulePaths.set(name, paths);
|
|
98330
|
+
}
|
|
98331
|
+
}
|
|
98332
|
+
for (const paths of modulePaths.values()) {
|
|
98333
|
+
const tips = new Set([...paths].flatMap((rel) => [...moduleCommits.get(rel) ?? []]));
|
|
98334
|
+
for (const rel of paths) {
|
|
98335
|
+
modules.push(rel);
|
|
98336
|
+
moduleCommits.set(rel, /* @__PURE__ */ new Set([...moduleCommits.get(rel) ?? [], ...tips]));
|
|
98337
|
+
}
|
|
98218
98338
|
}
|
|
98219
98339
|
if (stagedObjects.length) {
|
|
98220
98340
|
const sizes = await runGit(repo, ["cat-file", "--batch-check=%(objectsize)"], { input: stagedObjects.join("\n") + "\n" });
|
|
@@ -98236,7 +98356,7 @@ async function saveWorktreeRecovery(sessionId, cwd, trunk, branch) {
|
|
|
98236
98356
|
await assertExistingPath(repo, rel).catch((e) => {
|
|
98237
98357
|
if (existsSync13(join16(repo, rel))) throw e;
|
|
98238
98358
|
});
|
|
98239
|
-
if (existsSync13(join16(repo, rel, ".git"))) await prepare(join16(repo, rel), path2 ? `${path2}/${rel}` : rel);
|
|
98359
|
+
if (existsSync13(join16(repo, rel, ".git"))) await prepare(join16(repo, rel), path2 ? `${path2}/${rel}` : rel, [...moduleCommits.get(rel) ?? []]);
|
|
98240
98360
|
}
|
|
98241
98361
|
};
|
|
98242
98362
|
await prepare(cwd, "");
|
|
@@ -98257,7 +98377,26 @@ async function saveWorktreeRecovery(sessionId, cwd, trunk, branch) {
|
|
|
98257
98377
|
const entry = { path: path2, tip, refs, patch: false, staged: false, bundle: commits > 0 };
|
|
98258
98378
|
snapshot2.repositories.push(entry);
|
|
98259
98379
|
snapshot2.commits += commits;
|
|
98260
|
-
if (entry.bundle)
|
|
98380
|
+
if (entry.bundle) {
|
|
98381
|
+
const temporaryRefs = [];
|
|
98382
|
+
const updates2 = [];
|
|
98383
|
+
try {
|
|
98384
|
+
entry.refs = [];
|
|
98385
|
+
for (const ref of refs) {
|
|
98386
|
+
if (ref === "HEAD" || ref.startsWith("refs/")) entry.refs.push(ref);
|
|
98387
|
+
else {
|
|
98388
|
+
const name = `refs/centralu-recovery/${snapshotId}/gitlink-${temporaryRefs.length}`;
|
|
98389
|
+
temporaryRefs.push(name);
|
|
98390
|
+
updates2.push(`create ${name} ${ref}`);
|
|
98391
|
+
entry.refs.push(name);
|
|
98392
|
+
}
|
|
98393
|
+
}
|
|
98394
|
+
if (updates2.length) await runGit(repo, ["update-ref", "--stdin"], { input: updates2.join("\n") + "\n", timeout: RECOVERY_TIMEOUT });
|
|
98395
|
+
await runGit(repo, ["bundle", "create", join16(dir, `${n}.bundle`), "--stdin"], { input: entry.refs.join("\n") + "\n", timeout: RECOVERY_TIMEOUT });
|
|
98396
|
+
} finally {
|
|
98397
|
+
if (temporaryRefs.length) await runGit(repo, ["update-ref", "--stdin"], { input: temporaryRefs.map((ref) => `delete ${ref}`).join("\n") + "\n", timeout: RECOVERY_TIMEOUT });
|
|
98398
|
+
}
|
|
98399
|
+
}
|
|
98261
98400
|
entry.staged = await patchFile(repo, tip, join16(dir, `${n}.staged.patch`));
|
|
98262
98401
|
const stagedNames = await runGit(repo, ["diff", "--cached", "--name-only", "-z", "--no-renames", tip], { timeout: RECOVERY_TIMEOUT });
|
|
98263
98402
|
const env = { GIT_INDEX_FILE: join16(dir, `${n}.index`) };
|
|
@@ -98292,7 +98431,28 @@ async function restoreWorktreeRecovery(sessionId, repo, path2, branch, recovery,
|
|
|
98292
98431
|
const dir = join16(worktreeRecoveryDir(sessionId), recovery.snapshot);
|
|
98293
98432
|
const snapshot2 = JSON.parse(await readFile(join16(dir, "snapshot.json"), "utf8"));
|
|
98294
98433
|
const root3 = snapshot2.repositories[0];
|
|
98434
|
+
let extraNotice = "";
|
|
98435
|
+
if (root3.bundle) {
|
|
98436
|
+
const heads = (await runGit(repo, ["bundle", "list-heads", join16(dir, "0.bundle")])).trim().split("\n");
|
|
98437
|
+
for (const row of heads) {
|
|
98438
|
+
const [tip, ref] = row.split(" ");
|
|
98439
|
+
if (!tip || !ref?.startsWith("refs/heads/") || tip === recovery.tip) continue;
|
|
98440
|
+
const base2 = `recovered/${sessionId}-stored-branch`;
|
|
98441
|
+
let recovered = base2;
|
|
98442
|
+
for (let suffix = 1; ; suffix++) {
|
|
98443
|
+
const existing = await runGit(repo, ["rev-parse", "--verify", `refs/heads/${recovered}`]).then((s) => s.trim()).catch(() => null);
|
|
98444
|
+
if (existing === tip) break;
|
|
98445
|
+
if (!existing) {
|
|
98446
|
+
await runGit(repo, ["fetch", join16(dir, "0.bundle"), `${ref}:refs/heads/${recovered}`], { allowRepoPrograms: trust.trusted === true });
|
|
98447
|
+
break;
|
|
98448
|
+
}
|
|
98449
|
+
recovered = `${base2}-${suffix}`;
|
|
98450
|
+
}
|
|
98451
|
+
extraNotice += `Stored branch ${ref.slice("refs/heads/".length)} recovered as ${recovered}. `;
|
|
98452
|
+
}
|
|
98453
|
+
}
|
|
98295
98454
|
if (!existsSync13(path2)) {
|
|
98455
|
+
await rm2(join16(dir, "patches-applied"), { force: true });
|
|
98296
98456
|
if (await runGit(repo, ["show-ref", "--verify", `refs/heads/${branch}`]).then(() => true).catch(() => false)) {
|
|
98297
98457
|
branch = `recovered/${sessionId}`;
|
|
98298
98458
|
}
|
|
@@ -98302,18 +98462,26 @@ async function restoreWorktreeRecovery(sessionId, repo, path2, branch, recovery,
|
|
|
98302
98462
|
try {
|
|
98303
98463
|
if (root3.staged) await runGit(path2, ["apply", "--cached", "--binary", join16(dir, "0.staged.patch")]);
|
|
98304
98464
|
if (root3.patch) await runGit(path2, ["apply", "--binary", join16(dir, "0.patch")]);
|
|
98465
|
+
await writeFile2(join16(dir, "patches-applied"), await realpath3(path2), { mode: 384 });
|
|
98305
98466
|
} catch {
|
|
98306
|
-
return { branch, notice: `Restored branch ${branch}. Patches could not all be applied. Recovery files remain at ${dir}; apply numbered .patch files manually with git apply and .staged.patch files with git apply --cached.` };
|
|
98467
|
+
return { branch, notice: `Restored branch ${branch}. ${extraNotice}Patches could not all be applied. Recovery files remain at ${dir}; apply numbered .patch files manually with git apply and .staged.patch files with git apply --cached.` };
|
|
98307
98468
|
}
|
|
98308
98469
|
}
|
|
98309
98470
|
} else {
|
|
98310
98471
|
const common = async (cwd) => realpath3(resolve2(cwd, (await runGit(cwd, ["rev-parse", "--git-common-dir"])).trim()));
|
|
98311
98472
|
if (await common(path2) !== await common(repo)) throw new Error(`The worktree path belongs to another repository. Recovery files remain at ${dir}`);
|
|
98312
98473
|
branch = (await runGit(path2, ["symbolic-ref", "--quiet", "--short", "HEAD"])).trim();
|
|
98474
|
+
if (root3.patch || root3.staged) {
|
|
98475
|
+
const appliedTo = await readFile(join16(dir, "patches-applied"), "utf8").catch((e) => {
|
|
98476
|
+
if (e.code === "ENOENT") return null;
|
|
98477
|
+
throw e;
|
|
98478
|
+
});
|
|
98479
|
+
extraNotice += appliedTo === await realpath3(path2) ? "Saved patches were applied during the previous restore attempt; they were not reapplied on this retry. " : "The worktree already existed; saved patches were not applied on this retry and may still need manual recovery. ";
|
|
98480
|
+
}
|
|
98313
98481
|
}
|
|
98314
98482
|
return {
|
|
98315
98483
|
branch,
|
|
98316
|
-
notice: `Restored branch ${branch}. Recovery files remain at ${dir}: ${recovery.changedFiles} uncommitted files, ${recovery.commits} commits absent from the trunk or submodule remotes. Numbered .patch files can be applied with git apply; .bundle files list their saved refs with git bundle list-heads and can be fetched with git fetch <bundle> <ref>:<branch>. snapshot.json names each repository and its refs. Submodule recovery is manual. These files are removed when this session is deleted for good.`
|
|
98484
|
+
notice: `Restored branch ${branch}. ${extraNotice}Recovery files remain at ${dir}: ${recovery.changedFiles} uncommitted files, ${recovery.commits} commits absent from the trunk or submodule remotes. Numbered .patch files can be applied with git apply; .bundle files list their saved refs with git bundle list-heads and can be fetched with git fetch <bundle> <ref>:<branch>. snapshot.json names each repository and its refs. Submodule recovery is manual. These files are removed when this session is deleted for good.`
|
|
98317
98485
|
};
|
|
98318
98486
|
}
|
|
98319
98487
|
|
|
@@ -98406,8 +98574,12 @@ and approved skills, and whether every session can look at its own project), App
|
|
|
98406
98574
|
Shortcuts (the shortcut list), and Updates.`,
|
|
98407
98575
|
updates: `# Updates
|
|
98408
98576
|
Check from Settings \u2192 Updates. It reports a new version based on the npm registry, and the person
|
|
98409
|
-
clicks to install it.
|
|
98410
|
-
|
|
98577
|
+
clicks to install it. With automatic checking on, the check runs on startup, every six hours, when
|
|
98578
|
+
Updates opens, and on focus when the last check is over an hour old. With it off, use Check now.
|
|
98579
|
+
Ordinary checks follow npm, including dist-tag rollbacks. Installing reuses a successful check
|
|
98580
|
+
from the last minute or checks again. Only while installing can a higher stored version seen less
|
|
98581
|
+
than an hour ago win over the fresh tag. Older observations yield to npm. If the check fails, it
|
|
98582
|
+
uses the stored version and says so. Checks preserve install errors until the next install attempt.
|
|
98411
98583
|
Once installed, the desktop app offers "Apply now": it relaunches into the new version, and running
|
|
98412
98584
|
agents, terminals and commands keep going. "Apply updates automatically when idle" (off by default)
|
|
98413
98585
|
installs a new version without a click and applies it once no session is working or waiting, no
|
|
@@ -98780,6 +98952,7 @@ function agentRunText(r) {
|
|
|
98780
98952
|
case "stopped":
|
|
98781
98953
|
return { text: `Stopped ${who}.` };
|
|
98782
98954
|
case "deleted":
|
|
98955
|
+
if (r.branch === void 0) return { text: `Moved ${who} to the trash. Tell the person in one line.` };
|
|
98783
98956
|
return {
|
|
98784
98957
|
text: (r.warning ? `Moved ${who} to the trash. Warning: ${r.warning} ` : `Cleaned up ${who}: its worktree and branch "${r.branch}" were removed and the session went to the trash. `) + (r.setAside.changedFiles || r.setAside.commits ? `${r.setAside.changedFiles} uncommitted files, ${r.setAside.commits} commits not on the trunk, kept in the trash. ` : "Nothing needed setting aside. ") + "Tell the person in one line."
|
|
98785
98958
|
};
|
|
@@ -101604,7 +101777,7 @@ async function readClaudeModels(q2) {
|
|
|
101604
101777
|
}
|
|
101605
101778
|
|
|
101606
101779
|
// packages/agent-host/src/adapters/local-process.ts
|
|
101607
|
-
import { spawn as
|
|
101780
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
101608
101781
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
101609
101782
|
var EXIT_AFTER_OUTPUT_MS3 = 1e3;
|
|
101610
101783
|
var STDERR_TAIL = 8 * 1024;
|
|
@@ -101634,7 +101807,7 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
|
|
|
101634
101807
|
exitReported = false;
|
|
101635
101808
|
spawnedAt = Date.now();
|
|
101636
101809
|
static spawn(spec, os2) {
|
|
101637
|
-
const child =
|
|
101810
|
+
const child = spawn5(spec.command, spec.args, {
|
|
101638
101811
|
cwd: spec.cwd,
|
|
101639
101812
|
env: spec.env,
|
|
101640
101813
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -102818,10 +102991,19 @@ function readTarget(toolName, input) {
|
|
|
102818
102991
|
const p = toolName === "Read" ? input.file_path : input.path;
|
|
102819
102992
|
return typeof p === "string" && p ? p : null;
|
|
102820
102993
|
}
|
|
102821
|
-
function permissionOptionsFor(
|
|
102822
|
-
if (
|
|
102823
|
-
if (
|
|
102824
|
-
return {
|
|
102994
|
+
async function permissionOptionsFor(opts) {
|
|
102995
|
+
if (opts.permissionPreset === "auto") return { permissionMode: "bypassPermissions" };
|
|
102996
|
+
if (opts.permissionPreset === "safe") return { permissionMode: "default" };
|
|
102997
|
+
if (opts.noSettingFiles) return { permissionMode: await personsDefaultMode() ?? "default" };
|
|
102998
|
+
try {
|
|
102999
|
+
const resolved = await D9t({ cwd: opts.cwd, ...settingSourcesFor(opts) });
|
|
103000
|
+
const mode = qtt(resolved).permissions?.defaultMode;
|
|
103001
|
+
if (typeof mode === "string" && PERMISSION_MODES.includes(mode)) {
|
|
103002
|
+
return { permissionMode: mode };
|
|
103003
|
+
}
|
|
103004
|
+
} catch {
|
|
103005
|
+
}
|
|
103006
|
+
return { permissionMode: "default" };
|
|
102825
103007
|
}
|
|
102826
103008
|
var PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"];
|
|
102827
103009
|
async function personsDefaultMode() {
|
|
@@ -102961,7 +103143,7 @@ var ClaudeSession = class {
|
|
|
102961
103143
|
this.orchestratorServer = orchestratorMcp(this.opts.orchestratorTools, this.opts.toolProfile, this.opts.sessionId);
|
|
102962
103144
|
}
|
|
102963
103145
|
const servers = this.mcpServers();
|
|
102964
|
-
const
|
|
103146
|
+
const permissionOptions = await permissionOptionsFor(this.opts);
|
|
102965
103147
|
if (!this.opts.processSource?.adopt) await this.launch.gate.turn();
|
|
102966
103148
|
this.runKey = run?.key ?? null;
|
|
102967
103149
|
this.launch.links.acquire(this.runKey);
|
|
@@ -103054,7 +103236,7 @@ var ClaudeSession = class {
|
|
|
103054
103236
|
*/
|
|
103055
103237
|
...this.opts.systemPromptAppend ? { systemPrompt: { type: "preset", preset: "claude_code", append: this.opts.systemPromptAppend } } : {}
|
|
103056
103238
|
} : {},
|
|
103057
|
-
...
|
|
103239
|
+
...permissionOptions,
|
|
103058
103240
|
/*
|
|
103059
103241
|
* The agent an app hands a schema and asks for output (M4 D-1). It is a per-query option,
|
|
103060
103242
|
* so it can only be given when the session starts. Measured (SDK 0.3.263, CLI 2.1.282,
|
|
@@ -103296,8 +103478,8 @@ var ClaudeSession = class {
|
|
|
103296
103478
|
* turn released, rather than left hanging; the person can send the message again.
|
|
103297
103479
|
*/
|
|
103298
103480
|
releaseLostCalls(q2) {
|
|
103299
|
-
const
|
|
103300
|
-
const lost =
|
|
103481
|
+
const open6 = this.opts.processSource?.adopt?.openCalls ?? [];
|
|
103482
|
+
const lost = open6.filter((c) => isOrchestratorTool(c.tool) || appToolOf(c.tool) !== null);
|
|
103301
103483
|
if (lost.length === 0) return;
|
|
103302
103484
|
this.turnOpen = true;
|
|
103303
103485
|
this.stream.stopped();
|
|
@@ -103428,11 +103610,11 @@ var ClaudeSession = class {
|
|
|
103428
103610
|
* the model receives the answers in the CLI's own words (see the AskUserQuestion branch of `canUseTool`).
|
|
103429
103611
|
*/
|
|
103430
103612
|
answerQuestion(requestId, answers) {
|
|
103431
|
-
const
|
|
103432
|
-
if (!
|
|
103613
|
+
const open6 = this.questions.get(requestId);
|
|
103614
|
+
if (!open6) return false;
|
|
103433
103615
|
this.questions.delete(requestId);
|
|
103434
103616
|
const byQuestion = Object.fromEntries(answers.map((a) => [a.question, a.answers.join(", ")]));
|
|
103435
|
-
|
|
103617
|
+
open6.resolve({ behavior: "allow", updatedInput: { ...open6.input, answers: byQuestion } });
|
|
103436
103618
|
this.emit({ type: "question_resolved", sessionId: this.sessionId, requestId });
|
|
103437
103619
|
return true;
|
|
103438
103620
|
}
|
|
@@ -103708,8 +103890,8 @@ var ClaudeSession = class {
|
|
|
103708
103890
|
}
|
|
103709
103891
|
/** Releases a choice that was still waiting on an answer — **never released silently**, for the same reason as approval. */
|
|
103710
103892
|
releaseQuestions(why) {
|
|
103711
|
-
for (const [id2,
|
|
103712
|
-
|
|
103893
|
+
for (const [id2, open6] of this.questions) {
|
|
103894
|
+
open6.resolve({ behavior: "deny", message: why });
|
|
103713
103895
|
this.emit({ type: "question_resolved", sessionId: this.sessionId, requestId: id2 });
|
|
103714
103896
|
}
|
|
103715
103897
|
this.questions.clear();
|
|
@@ -103815,7 +103997,7 @@ var ClaudeAdapter = class _ClaudeAdapter {
|
|
|
103815
103997
|
sleep: true
|
|
103816
103998
|
};
|
|
103817
103999
|
launchSettingsFingerprint(settings) {
|
|
103818
|
-
return JSON.stringify({ v: settings.permissionPreset === "
|
|
104000
|
+
return JSON.stringify({ v: settings.permissionPreset === "safe" ? 1 : 2, cwd: settings.cwd, permission: settings.permissionPreset, trusted: !!settings.projectTrusted, noSettings: !!settings.noSettingFiles });
|
|
103819
104001
|
}
|
|
103820
104002
|
async detect() {
|
|
103821
104003
|
const path2 = whichTool("claude");
|
|
@@ -104163,7 +104345,7 @@ var CodexClient = class {
|
|
|
104163
104345
|
};
|
|
104164
104346
|
|
|
104165
104347
|
// packages/agent-host/src/adapters/codex/rollout.ts
|
|
104166
|
-
import { createReadStream as
|
|
104348
|
+
import { createReadStream as createReadStream3 } from "node:fs";
|
|
104167
104349
|
import { readdir as readdir5 } from "node:fs/promises";
|
|
104168
104350
|
import { createInterface as createInterface2 } from "node:readline";
|
|
104169
104351
|
import { homedir as homedir6 } from "node:os";
|
|
@@ -104199,7 +104381,7 @@ async function lastCompactSummary(threadId, sessionsDir) {
|
|
|
104199
104381
|
if (!path2) return null;
|
|
104200
104382
|
try {
|
|
104201
104383
|
let last = null;
|
|
104202
|
-
const rl2 = createInterface2({ input:
|
|
104384
|
+
const rl2 = createInterface2({ input: createReadStream3(path2, "utf8"), crlfDelay: Infinity });
|
|
104203
104385
|
for await (const line of rl2) {
|
|
104204
104386
|
if (!line.includes('"compacted"')) continue;
|
|
104205
104387
|
try {
|
|
@@ -106321,10 +106503,10 @@ var CodexSession = class {
|
|
|
106321
106503
|
return this.opts.outputSchema ? { outputSchema: this.opts.outputSchema } : {};
|
|
106322
106504
|
}
|
|
106323
106505
|
respondApproval(requestId, decision, _scope, matcher) {
|
|
106324
|
-
const
|
|
106325
|
-
if (
|
|
106506
|
+
const open6 = this.approvals.get(requestId);
|
|
106507
|
+
if (open6 === void 0) return false;
|
|
106326
106508
|
this.approvals.delete(requestId);
|
|
106327
|
-
const serverId =
|
|
106509
|
+
const serverId = open6.serverId;
|
|
106328
106510
|
if (this.elicitations.delete(requestId)) {
|
|
106329
106511
|
this.client.respond(serverId, {
|
|
106330
106512
|
action: decision === "deny" ? "decline" : "accept",
|
|
@@ -110100,7 +110282,7 @@ async function sweepAttachments(maxBytes = ATTACHMENTS_MAX_BYTES) {
|
|
|
110100
110282
|
|
|
110101
110283
|
// packages/agent-host/src/dev-services/message-image.ts
|
|
110102
110284
|
import { constants as constants7 } from "node:fs";
|
|
110103
|
-
import { open as
|
|
110285
|
+
import { open as open5, realpath as realpath7 } from "node:fs/promises";
|
|
110104
110286
|
import { homedir as homedir11 } from "node:os";
|
|
110105
110287
|
import { posix as posix3, sep as sep10, win32 as win326 } from "node:path";
|
|
110106
110288
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
@@ -110165,7 +110347,7 @@ async function readMessageImage(abs, max = IMAGE_PREVIEW_MAX_BYTES) {
|
|
|
110165
110347
|
}
|
|
110166
110348
|
let handle;
|
|
110167
110349
|
try {
|
|
110168
|
-
handle = await
|
|
110350
|
+
handle = await open5(file2, OPEN_FLAGS);
|
|
110169
110351
|
} catch (e) {
|
|
110170
110352
|
const code2 = e.code;
|
|
110171
110353
|
if (code2 === "EACCES" || code2 === "EPERM") return refuse5("unreadable", `Centralu may not read ${file2}`, file2);
|
|
@@ -111451,14 +111633,14 @@ var SessionManager = class {
|
|
|
111451
111633
|
}
|
|
111452
111634
|
/** Tool calls the store holds for this session with no result after them (the newest 200 rows) */
|
|
111453
111635
|
openToolCalls(sessionId) {
|
|
111454
|
-
const
|
|
111636
|
+
const open6 = /* @__PURE__ */ new Map();
|
|
111455
111637
|
for (const r of this.store.loadMessages(sessionId, 200)) {
|
|
111456
111638
|
const p = r.payload;
|
|
111457
111639
|
if (typeof p?.callId !== "string") continue;
|
|
111458
|
-
if (r.kind === "tool_call" && typeof p.summary?.tool === "string")
|
|
111459
|
-
else if (r.kind === "tool_result")
|
|
111640
|
+
if (r.kind === "tool_call" && typeof p.summary?.tool === "string") open6.set(p.callId, p.summary.tool);
|
|
111641
|
+
else if (r.kind === "tool_result") open6.delete(p.callId);
|
|
111460
111642
|
}
|
|
111461
|
-
return [...
|
|
111643
|
+
return [...open6].map(([callId, tool]) => ({ callId, tool }));
|
|
111462
111644
|
}
|
|
111463
111645
|
/**
|
|
111464
111646
|
* Re-attaches the tool processes a previous host left running in the keeper (#280 step 2), so
|
|
@@ -111713,6 +111895,7 @@ var SessionManager = class {
|
|
|
111713
111895
|
this.leaving.delete(sessionId);
|
|
111714
111896
|
}
|
|
111715
111897
|
}
|
|
111898
|
+
trashing = /* @__PURE__ */ new Map();
|
|
111716
111899
|
/**
|
|
111717
111900
|
* Moves a session to the trash (#204) — what deleting a session means now. Nothing is destroyed: the process
|
|
111718
111901
|
* stops, and the session leaves every list (sidebar, inbox, palette, grid, search, the agents' session tools and
|
|
@@ -111732,6 +111915,17 @@ var SessionManager = class {
|
|
|
111732
111915
|
* tool, and **+ → Past conversations**, still have it.
|
|
111733
111916
|
*/
|
|
111734
111917
|
async trashSession(sessionId, removeWorktree = false, removeExternal = false, recovery) {
|
|
111918
|
+
const pending = this.trashing.get(sessionId);
|
|
111919
|
+
if (pending) return pending;
|
|
111920
|
+
const deleting = this.trashSessionOnce(sessionId, removeWorktree, removeExternal, recovery);
|
|
111921
|
+
this.trashing.set(sessionId, deleting);
|
|
111922
|
+
try {
|
|
111923
|
+
await deleting;
|
|
111924
|
+
} finally {
|
|
111925
|
+
this.trashing.delete(sessionId);
|
|
111926
|
+
}
|
|
111927
|
+
}
|
|
111928
|
+
async trashSessionOnce(sessionId, removeWorktree, removeExternal, recovery) {
|
|
111735
111929
|
if (this.worktreeCleanup.has(sessionId) && !recovery) throw Object.assign(new Error("The session worktree is being removed"), { code: "internal" });
|
|
111736
111930
|
const m = this.meta.get(sessionId);
|
|
111737
111931
|
if (!m) {
|
|
@@ -114342,7 +114536,7 @@ ${list()}` : `${target.name} has no ordinary session. The person has to open one
|
|
|
114342
114536
|
* send gives it more, including during the caller's turn on adapters that accept mid-turn input; refused
|
|
114343
114537
|
* while it works on the person's own message or has a higher preset than the caller
|
|
114344
114538
|
* stop stops the turn the caller gave it; a turn the person started there is theirs
|
|
114345
|
-
* delete
|
|
114539
|
+
* delete trashes a child without a worktree, stopping it first; worktree cleanup requires an idle child (#608)
|
|
114346
114540
|
*/
|
|
114347
114541
|
async agentSession(callerId, opts, signal) {
|
|
114348
114542
|
if (opts.sessionId === callerId && opts.action !== "status") {
|
|
@@ -114350,7 +114544,9 @@ ${list()}` : `${target.name} has no ordinary session. The person has to open one
|
|
|
114350
114544
|
}
|
|
114351
114545
|
const caller = this.meta.get(callerId);
|
|
114352
114546
|
const child = this.meta.get(opts.sessionId);
|
|
114353
|
-
|
|
114547
|
+
const deletingWithoutWorktree = opts.action === "delete" && !child?.worktree;
|
|
114548
|
+
const startedHere = child?.askedBy === callerId || deletingWithoutWorktree && child?.parentSessionId === callerId;
|
|
114549
|
+
if (!caller?.projectId || !child || !startedHere && (deletingWithoutWorktree || !this.autoBoundaryOpen(callerId, "session ownership")) || child.projectId !== caller.projectId) {
|
|
114354
114550
|
return { ok: false, error: `${opts.sessionId} is not a session this session started. agent_session works only on sessions start_agent started here.` };
|
|
114355
114551
|
}
|
|
114356
114552
|
if (opts.sessionId === callerId) return this.agentAtRest(child);
|
|
@@ -114383,6 +114579,15 @@ ${list()}` : `${target.name} has no ordinary session. The person has to open one
|
|
|
114383
114579
|
return { ok: true, state: "stopped", sessionId: child.id, sessionName: child.name, tool: child.tool };
|
|
114384
114580
|
}
|
|
114385
114581
|
case "delete": {
|
|
114582
|
+
if (!child.worktree) {
|
|
114583
|
+
const busy = child.state === "working" || child.state === "waiting_approval" || !!child.pendingApproval || child.pendingQuestions.length > 0 || this.agentRuns.has(child.id) && !this.agentRuns.get(child.id).finished;
|
|
114584
|
+
if (!this.trashing.has(child.id) && busy && (!d2 || d2.settled || d2.callerId !== callerId)) {
|
|
114585
|
+
return { ok: false, error: `"${child.name}" is busy with something else; the person may be using it. Wait for that turn and its pending approval or question to finish before deleting it.` };
|
|
114586
|
+
}
|
|
114587
|
+
await this.trashSession(child.id);
|
|
114588
|
+
if (d2) this.delegations.delete(key);
|
|
114589
|
+
return { ok: true, state: "deleted", sessionId: child.id, sessionName: child.name, tool: child.tool };
|
|
114590
|
+
}
|
|
114386
114591
|
if (d2 && !d2.settled) return { ok: false, error: `"${child.name}" is still working on what you gave it \u2014 stop it or wait first.` };
|
|
114387
114592
|
const r = await this.cleanUpWorktreeChild(child);
|
|
114388
114593
|
if (!r.ok) return r;
|
|
@@ -114468,6 +114673,9 @@ ${list()}` : `${target.name} has no ordinary session. The person has to open one
|
|
|
114468
114673
|
if (target.state === "working" || target.state === "waiting_approval" || this.agentRuns.has(target.id) || sessionBusy({ ...target, live: this.handles.has(target.id) })) {
|
|
114469
114674
|
return { ok: false, error: `"${target.name}" is still working \u2014 stop it or wait first` };
|
|
114470
114675
|
}
|
|
114676
|
+
if (!existsSync19(target.worktree.path)) {
|
|
114677
|
+
return { ok: false, error: `The worktree folder for "${target.name}" is missing: ${target.worktree.path}. Restore the folder before cleanup; its branch has been left in place.` };
|
|
114678
|
+
}
|
|
114471
114679
|
if (this.resuming.has(target.id) || this.deferredRestarts.has(target.id)) return { ok: false, error: `"${target.name}" is waking or restarting \u2014 wait for it first` };
|
|
114472
114680
|
if (this.worktreeCleanup.has(target.id)) return { ok: false, error: "This worktree is already being removed" };
|
|
114473
114681
|
this.worktreeCleanup.add(target.id);
|
|
@@ -115355,8 +115563,8 @@ ${list()}` : `${target.name} has no ordinary session. The person has to open one
|
|
|
115355
115563
|
return readMessageImage(abs);
|
|
115356
115564
|
}
|
|
115357
115565
|
repliesName(sessionId, written) {
|
|
115358
|
-
const
|
|
115359
|
-
if (
|
|
115566
|
+
const open6 = this.streams.get(sessionId);
|
|
115567
|
+
if (open6?.kind === "text" && mentions(open6.text, written)) return true;
|
|
115360
115568
|
return writtenForms(written).some((form) => this.store.replyTextsContaining(sessionId, form).some((text2) => mentions(text2, written)));
|
|
115361
115569
|
}
|
|
115362
115570
|
/**
|
|
@@ -116558,9 +116766,9 @@ function broadcastAppChanges(send, windowMs = APP_CHANGE_WINDOW_MS) {
|
|
|
116558
116766
|
return {
|
|
116559
116767
|
emit(ref, cause2 = null) {
|
|
116560
116768
|
const key = JSON.stringify([ref.projectId, ref.appId]);
|
|
116561
|
-
const
|
|
116562
|
-
if (
|
|
116563
|
-
if (!sameCause(
|
|
116769
|
+
const open6 = pending.get(key);
|
|
116770
|
+
if (open6) {
|
|
116771
|
+
if (!sameCause(open6.cause, cause2)) open6.cause = null;
|
|
116564
116772
|
return;
|
|
116565
116773
|
}
|
|
116566
116774
|
const timer = setTimeout(() => {
|
|
@@ -117026,6 +117234,8 @@ var REGISTRY_URL = `https://registry.npmjs.org/${APP_SLUG}/latest`;
|
|
|
117026
117234
|
var CHECK_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
117027
117235
|
var SCHEDULED_TIMEOUT_MS = 3e3;
|
|
117028
117236
|
var FORCED_TIMEOUT_MS = 8e3;
|
|
117237
|
+
var INSTALL_CHECK_MAX_AGE_MS = 6e4;
|
|
117238
|
+
var STORED_RELEASE_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
117029
117239
|
var INSTALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
117030
117240
|
var NPM_RETRY_DELAY_MS = 3e4;
|
|
117031
117241
|
var NPM_RETRY_WINDOW_MS = 10 * 60 * 1e3;
|
|
@@ -117106,6 +117316,8 @@ var UpdateService = class {
|
|
|
117106
117316
|
}
|
|
117107
117317
|
publish;
|
|
117108
117318
|
status;
|
|
117319
|
+
stopped = false;
|
|
117320
|
+
installing = false;
|
|
117109
117321
|
timer = null;
|
|
117110
117322
|
deps;
|
|
117111
117323
|
/** One check at a time — the timer and the button can land together */
|
|
@@ -117121,16 +117333,22 @@ var UpdateService = class {
|
|
|
117121
117333
|
* closed while releases happened.
|
|
117122
117334
|
*/
|
|
117123
117335
|
start() {
|
|
117336
|
+
this.stopped = false;
|
|
117124
117337
|
if (!this.status.auto) return;
|
|
117125
117338
|
void this.check(false);
|
|
117126
117339
|
this.startTimer();
|
|
117127
117340
|
}
|
|
117341
|
+
/** Shutdown (including a host swap): no asynchronous continuation may start more work. */
|
|
117128
117342
|
stop() {
|
|
117343
|
+
this.stopped = true;
|
|
117344
|
+
this.stopTimer();
|
|
117345
|
+
}
|
|
117346
|
+
stopTimer() {
|
|
117129
117347
|
if (this.timer) clearInterval(this.timer);
|
|
117130
117348
|
this.timer = null;
|
|
117131
117349
|
}
|
|
117132
117350
|
startTimer() {
|
|
117133
|
-
this.
|
|
117351
|
+
this.stopTimer();
|
|
117134
117352
|
this.timer = setInterval(() => void this.check(false), CHECK_INTERVAL_MS);
|
|
117135
117353
|
this.timer.unref?.();
|
|
117136
117354
|
}
|
|
@@ -117146,7 +117364,7 @@ var UpdateService = class {
|
|
|
117146
117364
|
this.status = { ...this.status, auto: enabled };
|
|
117147
117365
|
this.deps.writeAuto(enabled);
|
|
117148
117366
|
if (!enabled) {
|
|
117149
|
-
this.
|
|
117367
|
+
this.stopTimer();
|
|
117150
117368
|
this.emit();
|
|
117151
117369
|
return this.current();
|
|
117152
117370
|
}
|
|
@@ -117183,32 +117401,36 @@ var UpdateService = class {
|
|
|
117183
117401
|
* failed, nothing about a background failure interrupts anyone.
|
|
117184
117402
|
*/
|
|
117185
117403
|
async check(force) {
|
|
117404
|
+
if (this.stopped || this.installing) return this.current();
|
|
117186
117405
|
if (this.inFlight) return this.inFlight;
|
|
117187
117406
|
if (!force && !this.status.auto) return this.current();
|
|
117188
117407
|
if (!force && this.status.checkedAt !== null && !this.isDue()) return this.current();
|
|
117189
117408
|
if (this.status.phase === "updating" || this.status.phase === "waiting_for_npm" || this.status.phase === "restart_required") return this.current();
|
|
117190
|
-
this.status
|
|
117409
|
+
const failure3 = this.status.phase === "failed" ? { error: this.status.error } : null;
|
|
117410
|
+
if (!failure3) this.status = { ...this.status, phase: "checking" };
|
|
117191
117411
|
this.emit();
|
|
117192
|
-
this.inFlight = this.runCheck(force);
|
|
117412
|
+
this.inFlight = this.runCheck(force, failure3);
|
|
117193
117413
|
try {
|
|
117194
117414
|
return await this.inFlight;
|
|
117195
117415
|
} finally {
|
|
117196
117416
|
this.inFlight = null;
|
|
117197
117417
|
}
|
|
117198
117418
|
}
|
|
117199
|
-
async runCheck(force) {
|
|
117419
|
+
async runCheck(force, failure3) {
|
|
117200
117420
|
const res = await this.deps.fetchLatest(force ? FORCED_TIMEOUT_MS : SCHEDULED_TIMEOUT_MS);
|
|
117421
|
+
if (this.stopped || this.installing) return this.current();
|
|
117201
117422
|
if (!res.ok) {
|
|
117202
|
-
this.status = { ...this.status, phase: "idle", error: res.reason };
|
|
117423
|
+
this.status = { ...this.status, phase: failure3 ? "failed" : "idle", error: failure3 ? failure3.error : res.reason };
|
|
117203
117424
|
this.emit();
|
|
117204
117425
|
return this.current();
|
|
117205
117426
|
}
|
|
117427
|
+
const latest = res.version;
|
|
117206
117428
|
this.status = {
|
|
117207
117429
|
...this.status,
|
|
117208
|
-
latest
|
|
117209
|
-
newer: isNewerVersion(
|
|
117210
|
-
phase: "idle",
|
|
117211
|
-
error: null,
|
|
117430
|
+
latest,
|
|
117431
|
+
newer: isNewerVersion(latest, this.status.current),
|
|
117432
|
+
phase: failure3 ? "failed" : "idle",
|
|
117433
|
+
error: failure3?.error ?? null,
|
|
117212
117434
|
checkedAt: this.deps.now()
|
|
117213
117435
|
};
|
|
117214
117436
|
this.emit();
|
|
@@ -117224,13 +117446,14 @@ var UpdateService = class {
|
|
|
117224
117446
|
* rest of the story arrives as `update_status` events.
|
|
117225
117447
|
*/
|
|
117226
117448
|
apply() {
|
|
117227
|
-
if (this.
|
|
117449
|
+
if (this.stopped || this.installing || this.status.phase === "restart_required") return this.current();
|
|
117228
117450
|
const latest = this.status.latest;
|
|
117229
117451
|
if (!this.status.newer || !latest) {
|
|
117230
117452
|
this.status = { ...this.status, phase: "failed", error: "There is no newer version to install" };
|
|
117231
117453
|
this.emit();
|
|
117232
117454
|
return this.current();
|
|
117233
117455
|
}
|
|
117456
|
+
this.installing = true;
|
|
117234
117457
|
this.status = { ...this.status, phase: "updating", error: null };
|
|
117235
117458
|
this.emit();
|
|
117236
117459
|
void this.runApply(latest);
|
|
@@ -117240,16 +117463,42 @@ var UpdateService = class {
|
|
|
117240
117463
|
let packageInstalled = false;
|
|
117241
117464
|
const copy = this.deps.installedCopy();
|
|
117242
117465
|
try {
|
|
117466
|
+
await this.inFlight;
|
|
117467
|
+
if (this.stopped) return;
|
|
117468
|
+
const age2 = this.status.checkedAt === null ? Infinity : this.deps.now() - this.status.checkedAt;
|
|
117469
|
+
if (age2 < 0 || age2 >= INSTALL_CHECK_MAX_AGE_MS) {
|
|
117470
|
+
const fresh = await this.deps.fetchLatest(FORCED_TIMEOUT_MS);
|
|
117471
|
+
if (this.stopped) return;
|
|
117472
|
+
if (fresh.ok) {
|
|
117473
|
+
const now = this.deps.now();
|
|
117474
|
+
const storedAge = this.status.checkedAt === null ? Infinity : now - this.status.checkedAt;
|
|
117475
|
+
const keepStored = storedAge >= 0 && storedAge < STORED_RELEASE_MAX_AGE_MS && isNewerVersion(version2, fresh.version);
|
|
117476
|
+
if (!keepStored) version2 = fresh.version;
|
|
117477
|
+
this.status = { ...this.status, latest: version2, newer: isNewerVersion(version2, this.status.current), checkedAt: keepStored ? this.status.checkedAt : now };
|
|
117478
|
+
if (!this.status.newer) {
|
|
117479
|
+
this.status = { ...this.status, phase: "idle", error: null };
|
|
117480
|
+
return;
|
|
117481
|
+
}
|
|
117482
|
+
} else {
|
|
117483
|
+
this.status = { ...this.status, error: `Could not refresh latest (${fresh.reason}); using previously checked ${version2}.` };
|
|
117484
|
+
}
|
|
117485
|
+
}
|
|
117486
|
+
this.emit();
|
|
117243
117487
|
await this.installPackage(version2);
|
|
117488
|
+
if (this.stopped) return;
|
|
117244
117489
|
packageInstalled = true;
|
|
117245
117490
|
if (copy) await this.deps.run(APP_SLUG, ["install"]);
|
|
117246
|
-
|
|
117491
|
+
if (this.stopped) return;
|
|
117492
|
+
this.status = { ...this.status, phase: "restart_required" };
|
|
117247
117493
|
} catch (e) {
|
|
117494
|
+
if (this.stopped) return;
|
|
117248
117495
|
const reason = e.message;
|
|
117249
117496
|
const error51 = packageInstalled ? `npm installed ${version2}, but the copy at ${copy} was not replaced (${reason}). Quit ${APP_NAME}, then run "${APP_SLUG} install" in a terminal.` : reason;
|
|
117250
117497
|
this.status = { ...this.status, phase: "failed", error: error51 };
|
|
117498
|
+
} finally {
|
|
117499
|
+
this.installing = false;
|
|
117500
|
+
if (!this.stopped) this.emit();
|
|
117251
117501
|
}
|
|
117252
|
-
this.emit();
|
|
117253
117502
|
}
|
|
117254
117503
|
/**
|
|
117255
117504
|
* A just-published release can be visible from `latest` before its package is on the npm edge
|
|
@@ -117259,16 +117508,20 @@ var UpdateService = class {
|
|
|
117259
117508
|
*/
|
|
117260
117509
|
async installPackage(version2) {
|
|
117261
117510
|
const started = this.deps.now();
|
|
117511
|
+
const notice = this.status.error;
|
|
117262
117512
|
for (; ; ) {
|
|
117513
|
+
if (this.stopped) return;
|
|
117263
117514
|
try {
|
|
117264
117515
|
await this.deps.run("npm", ["i", "-g", "--prefer-online", `${APP_SLUG}@${version2}`]);
|
|
117265
117516
|
return;
|
|
117266
117517
|
} catch (err) {
|
|
117518
|
+
if (this.stopped) return;
|
|
117267
117519
|
if (!npmHasNotServedVersion(err) || this.deps.now() - started >= NPM_RETRY_WINDOW_MS) throw err;
|
|
117268
|
-
this.status = { ...this.status, phase: "waiting_for_npm", error: `Waiting for npm to serve ${version2}` };
|
|
117520
|
+
this.status = { ...this.status, phase: "waiting_for_npm", error: `Waiting for npm to serve ${version2}${notice ? `. ${notice}` : ""}` };
|
|
117269
117521
|
this.emit();
|
|
117270
117522
|
await this.deps.wait(NPM_RETRY_DELAY_MS);
|
|
117271
|
-
|
|
117523
|
+
if (this.stopped) return;
|
|
117524
|
+
this.status = { ...this.status, phase: "updating", error: notice };
|
|
117272
117525
|
this.emit();
|
|
117273
117526
|
}
|
|
117274
117527
|
}
|
|
@@ -118159,7 +118412,7 @@ async function verifiedPackage(name, version2, opts = {}) {
|
|
|
118159
118412
|
}
|
|
118160
118413
|
|
|
118161
118414
|
// packages/agent-host/src/links/tunnel.ts
|
|
118162
|
-
import { spawn as
|
|
118415
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
118163
118416
|
import { createConnection as createConnection2, createServer as createServer3 } from "node:net";
|
|
118164
118417
|
function parseConnectionLine(stdout) {
|
|
118165
118418
|
const lines = stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
@@ -118423,7 +118676,7 @@ var SshTunnel = class {
|
|
|
118423
118676
|
}
|
|
118424
118677
|
run(args, stdin) {
|
|
118425
118678
|
return new Promise((resolve12, reject) => {
|
|
118426
|
-
const child =
|
|
118679
|
+
const child = spawn6(this.opts.ssh ?? "ssh", args, { env: this.opts.env ?? process.env, stdio: [stdin === void 0 ? "ignore" : "pipe", "pipe", "pipe"] });
|
|
118427
118680
|
if (stdin !== void 0) {
|
|
118428
118681
|
child.stdin.on("error", () => {
|
|
118429
118682
|
});
|
|
@@ -118482,7 +118735,7 @@ var SshTunnel = class {
|
|
|
118482
118735
|
this.opts.target,
|
|
118483
118736
|
...keepAlive ? [keepAlive] : []
|
|
118484
118737
|
];
|
|
118485
|
-
const child =
|
|
118738
|
+
const child = spawn6(this.opts.ssh ?? "ssh", args, { env: this.opts.env ?? process.env, stdio: ["ignore", "ignore", "pipe"] });
|
|
118486
118739
|
this.child = child;
|
|
118487
118740
|
let stderr = "";
|
|
118488
118741
|
child.stderr.on("data", (d2) => stderr += String(d2));
|
|
@@ -118834,7 +119087,7 @@ async function removeAutostart(c) {
|
|
|
118834
119087
|
}
|
|
118835
119088
|
|
|
118836
119089
|
// packages/agent-host/src/links/probe.ts
|
|
118837
|
-
import { spawn as
|
|
119090
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
118838
119091
|
var PROBE_MARK = "CENTRALU-PROBE";
|
|
118839
119092
|
var PROBE_PS = [
|
|
118840
119093
|
"$ErrorActionPreference = 'SilentlyContinue'",
|
|
@@ -118889,7 +119142,7 @@ function probeTarget(target, opts = {}) {
|
|
|
118889
119142
|
clearTimeout(timer);
|
|
118890
119143
|
resolve12(parseProbe(t, run));
|
|
118891
119144
|
};
|
|
118892
|
-
const child =
|
|
119145
|
+
const child = spawn7(opts.ssh ?? "ssh", args, { env: opts.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
118893
119146
|
const timer = setTimeout(() => {
|
|
118894
119147
|
child.kill("SIGTERM");
|
|
118895
119148
|
finish2({ code: 255, stdout: "", stderr: `ssh to ${t} did not answer in time` });
|
|
@@ -118902,7 +119155,7 @@ function probeTarget(target, opts = {}) {
|
|
|
118902
119155
|
}
|
|
118903
119156
|
|
|
118904
119157
|
// packages/agent-host/src/links/ssh-config.ts
|
|
118905
|
-
import { spawn as
|
|
119158
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
118906
119159
|
import { readdirSync as readdirSync8, readFileSync as readFileSync15, statSync as statSync13 } from "node:fs";
|
|
118907
119160
|
import { homedir as homedir13 } from "node:os";
|
|
118908
119161
|
import { isAbsolute as isAbsolute10, join as join32, normalize, parse as parse6, sep as sep12 } from "node:path";
|
|
@@ -119077,7 +119330,7 @@ function resolveAlias(alias, opts = {}) {
|
|
|
119077
119330
|
resolve12(r);
|
|
119078
119331
|
};
|
|
119079
119332
|
const none = (error51) => ({ hostName: null, user: null, port: null, error: error51 });
|
|
119080
|
-
const child =
|
|
119333
|
+
const child = spawn8(opts.ssh ?? "ssh", args, { env: opts.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
119081
119334
|
const timer = setTimeout(() => {
|
|
119082
119335
|
child.kill("SIGKILL");
|
|
119083
119336
|
finish2(none("ssh -G did not answer"));
|
|
@@ -121198,7 +121451,7 @@ function storeMirror(store2) {
|
|
|
121198
121451
|
var start = new StartSequence();
|
|
121199
121452
|
start.at("launch variables");
|
|
121200
121453
|
var launch = takeLaunchEnv();
|
|
121201
|
-
var BUILD = true ? "
|
|
121454
|
+
var BUILD = true ? "8dd78b6" : "dev";
|
|
121202
121455
|
var { values } = parseArgs({
|
|
121203
121456
|
options: {
|
|
121204
121457
|
port: { type: "string", default: "5175" },
|