toyon 0.5.0 → 0.6.0
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/README.md +2 -0
- package/dist/cli.js +5132 -24
- package/dist/daemon.js +1016 -316
- package/dist/shell/assets/{Editor-DR-GTFmf.js → Editor-D5UJRxLO.js} +4 -4
- package/dist/shell/assets/{XTerm-B3kT_naP.js → XTerm-l6IDHxt8.js} +1 -1
- package/dist/shell/assets/{cssMode-CB4CczRa.js → cssMode-DIt1Onbo.js} +1 -1
- package/dist/shell/assets/{freemarker2-Wjq3dVyB.js → freemarker2-BlnMjxhl.js} +1 -1
- package/dist/shell/assets/{grammar-yCr7BdNP.js → grammar-xCgG7hS1.js} +1 -1
- package/dist/shell/assets/{handlebars-Bjvi6pTu.js → handlebars-CgcpCzmy.js} +1 -1
- package/dist/shell/assets/{html-iB2mKeN2.js → html-yqzA9zjA.js} +1 -1
- package/dist/shell/assets/{htmlMode-DtZVhckD.js → htmlMode-AyUHvgCk.js} +1 -1
- package/dist/shell/assets/index-BGLn9_S8.js +241 -0
- package/dist/shell/assets/index-Cm07OHxH.css +1 -0
- package/dist/shell/assets/{javascript-BpuIkItm.js → javascript-CYZ-NuGG.js} +1 -1
- package/dist/shell/assets/{jsonMode-QCLe5ypz.js → jsonMode-BpybSxrR.js} +1 -1
- package/dist/shell/assets/{liquid-CQI6mzhA.js → liquid-BHHjN7T9.js} +1 -1
- package/dist/shell/assets/{lspLanguageFeatures-DqXzGsHQ.js → lspLanguageFeatures-CHyCsA1P.js} +1 -1
- package/dist/shell/assets/{mdx-D2Unh-By.js → mdx-CCgYQauJ.js} +1 -1
- package/dist/shell/assets/{python-LLSqiJ4D.js → python-C1JulLv8.js} +1 -1
- package/dist/shell/assets/{razor-CJ2qsTB8.js → razor-Dj4sWgNm.js} +1 -1
- package/dist/shell/assets/{tsMode-CwjjHUH5.js → tsMode-D1gKgsY6.js} +1 -1
- package/dist/shell/assets/{xml-BzqCyEOW.js → xml-BHUNgL0o.js} +1 -1
- package/dist/shell/assets/{yaml-N1Bfi3ds.js → yaml-CetvwW8L.js} +1 -1
- package/dist/shell/index.html +2 -2
- package/package.json +1 -1
- package/dist/shell/assets/index-DVg10dTN.js +0 -237
- package/dist/shell/assets/index-wt4cLIYn.css +0 -1
package/dist/daemon.js
CHANGED
|
@@ -42,6 +42,21 @@ var LOGIN_STREAM = "login";
|
|
|
42
42
|
function streamKey(worktreeId, stream) {
|
|
43
43
|
return `${worktreeId}/${stream}`;
|
|
44
44
|
}
|
|
45
|
+
function applyLog(lines, proc, line, retract = 0) {
|
|
46
|
+
let out = lines;
|
|
47
|
+
for (let left = retract;left > 0; left--) {
|
|
48
|
+
let i = out.length - 1;
|
|
49
|
+
while (i >= 0 && out[i]?.proc !== proc)
|
|
50
|
+
i--;
|
|
51
|
+
if (i < 0)
|
|
52
|
+
break;
|
|
53
|
+
out = out === lines ? [...out] : out;
|
|
54
|
+
out.splice(i, 1);
|
|
55
|
+
}
|
|
56
|
+
if (!line)
|
|
57
|
+
return out;
|
|
58
|
+
return [...out, { proc, line }];
|
|
59
|
+
}
|
|
45
60
|
|
|
46
61
|
// ../shared/src/worktree-caps.ts
|
|
47
62
|
function isMain(wt) {
|
|
@@ -302,62 +317,8 @@ function landPolicy(config) {
|
|
|
302
317
|
...config.land?.method ? { merge: config.land.method } : {}
|
|
303
318
|
};
|
|
304
319
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
var PAIR_TTL_MS = 120000;
|
|
308
|
-
var pairLink = (host, code) => `https://${host}/#pair=${code}`;
|
|
309
|
-
|
|
310
|
-
// ../shared/src/protocol/limits.ts
|
|
311
|
-
var PROTOCOL_VERSION = 65;
|
|
312
|
-
var FILE_MAX_CHARS = 5000000;
|
|
313
|
-
var IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
314
|
-
var IMAGE_MAX_EDGE = 2576;
|
|
315
|
-
var IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
316
|
-
var PASTE_MAX_CHARS = 1e5;
|
|
317
|
-
|
|
318
|
-
// ../shared/src/paste.ts
|
|
319
|
-
var PREVIEW_CHARS = 80;
|
|
320
|
-
function pasteSummary(text) {
|
|
321
|
-
const lines = text.split(`
|
|
322
|
-
`);
|
|
323
|
-
const first = lines.find((l) => l.trim().length > 0)?.trim() ?? "";
|
|
324
|
-
return { chars: text.length, lines: lines.length, preview: first.slice(0, PREVIEW_CHARS) };
|
|
325
|
-
}
|
|
326
|
-
function lineSpan({ startLine, endLine }) {
|
|
327
|
-
return startLine === endLine ? `${startLine}` : `${startLine}-${endLine}`;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// ../shared/src/ports.ts
|
|
331
|
-
var DAEMON_DEFAULT_PORT = 4141;
|
|
332
|
-
var SHELL_DEV_PORT = 4142;
|
|
333
|
-
|
|
334
|
-
// ../shared/src/project.ts
|
|
335
|
-
var PROJECTS_FOLDER = "Projects";
|
|
336
|
-
function hasControlChar(s) {
|
|
337
|
-
for (const ch of s) {
|
|
338
|
-
const code = ch.codePointAt(0) ?? 0;
|
|
339
|
-
if (code < 32 || code === 127)
|
|
340
|
-
return true;
|
|
341
|
-
}
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
function projectNameError(raw) {
|
|
345
|
-
const name = raw.trim();
|
|
346
|
-
if (!name)
|
|
347
|
-
return "a project name is required";
|
|
348
|
-
if (name.length > 100)
|
|
349
|
-
return "that name is too long: keep it under 100 characters";
|
|
350
|
-
if (name.includes("/") || name.includes("\\"))
|
|
351
|
-
return "a project name cannot contain a slash";
|
|
352
|
-
if (/\s/.test(name))
|
|
353
|
-
return "a project name cannot contain spaces";
|
|
354
|
-
if (hasControlChar(name))
|
|
355
|
-
return "a project name cannot contain control characters";
|
|
356
|
-
if (name === "." || name === "..")
|
|
357
|
-
return `"${name}" is not a project name`;
|
|
358
|
-
if (name.startsWith("-") || name.startsWith("."))
|
|
359
|
-
return "a project name cannot start with a dot or a dash";
|
|
360
|
-
return null;
|
|
320
|
+
function shipNoun(op) {
|
|
321
|
+
return { land: "a land", commit: "a commit", "sync-main": "a sync", "pull-main": "a pull" }[op];
|
|
361
322
|
}
|
|
362
323
|
|
|
363
324
|
// ../../node_modules/.bun/zod@4.5.4/node_modules/zod/v4/core/util.js
|
|
@@ -6254,6 +6215,221 @@ function refine(fn, _params = {}) {
|
|
|
6254
6215
|
function superRefine(fn, params) {
|
|
6255
6216
|
return _superRefine(fn, params);
|
|
6256
6217
|
}
|
|
6218
|
+
// ../shared/src/managed.ts
|
|
6219
|
+
var MANAGED_REMOTE = ["off", "tailscale", "any"];
|
|
6220
|
+
var MANAGED_DEFAULTS = {
|
|
6221
|
+
updates: true,
|
|
6222
|
+
deploy: true,
|
|
6223
|
+
remote: "any",
|
|
6224
|
+
agents: null,
|
|
6225
|
+
customAgents: true,
|
|
6226
|
+
brandedListener: true,
|
|
6227
|
+
planSignIn: true
|
|
6228
|
+
};
|
|
6229
|
+
var MANAGED_STRICTEST = {
|
|
6230
|
+
updates: false,
|
|
6231
|
+
deploy: false,
|
|
6232
|
+
remote: "off",
|
|
6233
|
+
agents: null,
|
|
6234
|
+
customAgents: false,
|
|
6235
|
+
brandedListener: false,
|
|
6236
|
+
planSignIn: false
|
|
6237
|
+
};
|
|
6238
|
+
var managedFileSchema = object({
|
|
6239
|
+
updates: boolean2().optional(),
|
|
6240
|
+
deploy: boolean2().optional(),
|
|
6241
|
+
remote: _enum(MANAGED_REMOTE).optional(),
|
|
6242
|
+
agents: array(string2()).optional(),
|
|
6243
|
+
customAgents: boolean2().optional(),
|
|
6244
|
+
brandedListener: boolean2().optional(),
|
|
6245
|
+
planSignIn: boolean2().optional()
|
|
6246
|
+
});
|
|
6247
|
+
var MANAGED_NONE = { ...MANAGED_DEFAULTS, source: null, problem: null };
|
|
6248
|
+
function managedSources(platform, user) {
|
|
6249
|
+
if (platform === "darwin") {
|
|
6250
|
+
return [
|
|
6251
|
+
{ path: "/Library/Managed Preferences/dev.toyon.plist", kind: "plist" },
|
|
6252
|
+
...user ? [{ path: `/Library/Managed Preferences/${user}/dev.toyon.plist`, kind: "plist" }] : [],
|
|
6253
|
+
{ path: "/Library/Application Support/toyon/policy.json", kind: "json" }
|
|
6254
|
+
];
|
|
6255
|
+
}
|
|
6256
|
+
if (platform === "linux")
|
|
6257
|
+
return [{ path: "/etc/toyon/policy.json", kind: "json" }];
|
|
6258
|
+
return [];
|
|
6259
|
+
}
|
|
6260
|
+
var KEYS = ["updates", "deploy", "remote", "agents", "customAgents", "brandedListener", "planSignIn"];
|
|
6261
|
+
function stricter(key, a, b) {
|
|
6262
|
+
if (key === "remote") {
|
|
6263
|
+
const ra = MANAGED_REMOTE.indexOf(a);
|
|
6264
|
+
const rb = MANAGED_REMOTE.indexOf(b);
|
|
6265
|
+
return ra <= rb ? a : b;
|
|
6266
|
+
}
|
|
6267
|
+
if (key === "agents") {
|
|
6268
|
+
const la = a;
|
|
6269
|
+
const lb = b;
|
|
6270
|
+
if (la === null)
|
|
6271
|
+
return b;
|
|
6272
|
+
if (lb === null)
|
|
6273
|
+
return a;
|
|
6274
|
+
return la.filter((id) => lb.includes(id));
|
|
6275
|
+
}
|
|
6276
|
+
return a && b;
|
|
6277
|
+
}
|
|
6278
|
+
function merge2(into, from) {
|
|
6279
|
+
const out = { ...into };
|
|
6280
|
+
for (const key of KEYS) {
|
|
6281
|
+
const v = from[key];
|
|
6282
|
+
if (v === undefined)
|
|
6283
|
+
continue;
|
|
6284
|
+
out[key] = stricter(key, into[key], v);
|
|
6285
|
+
}
|
|
6286
|
+
return out;
|
|
6287
|
+
}
|
|
6288
|
+
function textHash(text) {
|
|
6289
|
+
let h = 2166136261;
|
|
6290
|
+
for (let i = 0;i < text.length; i++) {
|
|
6291
|
+
h ^= text.charCodeAt(i);
|
|
6292
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
6293
|
+
}
|
|
6294
|
+
return h.toString(16).padStart(8, "0");
|
|
6295
|
+
}
|
|
6296
|
+
function describeIssue(e) {
|
|
6297
|
+
const issue = e.issues[0];
|
|
6298
|
+
if (!issue)
|
|
6299
|
+
return "not a policy";
|
|
6300
|
+
const at = issue.path.map(String).join(".");
|
|
6301
|
+
return at ? `"${at}" ${issue.message.toLowerCase()}` : issue.message.toLowerCase();
|
|
6302
|
+
}
|
|
6303
|
+
function resolveManaged(reads) {
|
|
6304
|
+
let policy = MANAGED_DEFAULTS;
|
|
6305
|
+
const sources = [];
|
|
6306
|
+
const applied = [];
|
|
6307
|
+
let source = null;
|
|
6308
|
+
let problem = null;
|
|
6309
|
+
for (const r of reads) {
|
|
6310
|
+
const spec = { path: r.path, kind: r.kind };
|
|
6311
|
+
let why = r.problem ?? (r.owned === false ? "not owned by root, or writable by others" : null);
|
|
6312
|
+
if (why === null && r.text === undefined) {
|
|
6313
|
+
sources.push({ ...spec, state: "absent" });
|
|
6314
|
+
continue;
|
|
6315
|
+
}
|
|
6316
|
+
let parsed = null;
|
|
6317
|
+
if (why === null) {
|
|
6318
|
+
try {
|
|
6319
|
+
const json = JSON.parse(r.text ?? "");
|
|
6320
|
+
const result = managedFileSchema.safeParse(json);
|
|
6321
|
+
if (result.success)
|
|
6322
|
+
parsed = result.data;
|
|
6323
|
+
else
|
|
6324
|
+
why = describeIssue(result.error);
|
|
6325
|
+
} catch {
|
|
6326
|
+
why = r.kind === "plist" ? "not a plist plutil can read" : "not valid JSON";
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
if (why !== null || parsed === null) {
|
|
6330
|
+
sources.push({ ...spec, state: "invalid", problem: why ?? "unreadable" });
|
|
6331
|
+
if (problem === null) {
|
|
6332
|
+
problem = `${r.path}: ${why ?? "unreadable"}`;
|
|
6333
|
+
source = r.path;
|
|
6334
|
+
}
|
|
6335
|
+
continue;
|
|
6336
|
+
}
|
|
6337
|
+
sources.push({ ...spec, state: "applied" });
|
|
6338
|
+
applied.push(r.text ?? "");
|
|
6339
|
+
if (problem === null && source === null)
|
|
6340
|
+
source = r.path;
|
|
6341
|
+
policy = merge2(policy, parsed);
|
|
6342
|
+
}
|
|
6343
|
+
if (problem !== null)
|
|
6344
|
+
policy = MANAGED_STRICTEST;
|
|
6345
|
+
return {
|
|
6346
|
+
policy,
|
|
6347
|
+
sources,
|
|
6348
|
+
hash: applied.length ? textHash(applied.join(`
|
|
6349
|
+
`)) : null,
|
|
6350
|
+
source,
|
|
6351
|
+
problem
|
|
6352
|
+
};
|
|
6353
|
+
}
|
|
6354
|
+
function managedView(r) {
|
|
6355
|
+
return { ...r.policy, source: r.source, problem: r.problem };
|
|
6356
|
+
}
|
|
6357
|
+
function describeManaged(p) {
|
|
6358
|
+
const out = [];
|
|
6359
|
+
if (!p.updates)
|
|
6360
|
+
out.push("updates off");
|
|
6361
|
+
if (!p.deploy)
|
|
6362
|
+
out.push("deploy off");
|
|
6363
|
+
if (p.remote !== "any")
|
|
6364
|
+
out.push(`remote ${p.remote}`);
|
|
6365
|
+
if (p.agents !== null)
|
|
6366
|
+
out.push(`agents ${p.agents.join(", ") || "none"}`);
|
|
6367
|
+
if (!p.customAgents)
|
|
6368
|
+
out.push("custom agents off");
|
|
6369
|
+
if (!p.brandedListener)
|
|
6370
|
+
out.push("toyon.localhost listener off");
|
|
6371
|
+
if (!p.planSignIn)
|
|
6372
|
+
out.push("plan sign-in off");
|
|
6373
|
+
return out;
|
|
6374
|
+
}
|
|
6375
|
+
|
|
6376
|
+
// ../shared/src/pair.ts
|
|
6377
|
+
var PAIR_TTL_MS = 120000;
|
|
6378
|
+
var pairLink = (host, code) => `https://${host}/#pair=${code}`;
|
|
6379
|
+
|
|
6380
|
+
// ../shared/src/protocol/limits.ts
|
|
6381
|
+
var PROTOCOL_VERSION = 69;
|
|
6382
|
+
var FILE_MAX_CHARS = 5000000;
|
|
6383
|
+
var IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
6384
|
+
var IMAGE_MAX_EDGE = 2576;
|
|
6385
|
+
var IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
6386
|
+
var PASTE_MAX_CHARS = 1e5;
|
|
6387
|
+
|
|
6388
|
+
// ../shared/src/paste.ts
|
|
6389
|
+
var PREVIEW_CHARS = 80;
|
|
6390
|
+
function pasteSummary(text) {
|
|
6391
|
+
const lines = text.split(`
|
|
6392
|
+
`);
|
|
6393
|
+
const first = lines.find((l) => l.trim().length > 0)?.trim() ?? "";
|
|
6394
|
+
return { chars: text.length, lines: lines.length, preview: first.slice(0, PREVIEW_CHARS) };
|
|
6395
|
+
}
|
|
6396
|
+
function lineSpan({ startLine, endLine }) {
|
|
6397
|
+
return startLine === endLine ? `${startLine}` : `${startLine}-${endLine}`;
|
|
6398
|
+
}
|
|
6399
|
+
|
|
6400
|
+
// ../shared/src/ports.ts
|
|
6401
|
+
var DAEMON_DEFAULT_PORT = 4141;
|
|
6402
|
+
var SHELL_DEV_PORT = 4142;
|
|
6403
|
+
|
|
6404
|
+
// ../shared/src/project.ts
|
|
6405
|
+
var PROJECTS_FOLDER = "Projects";
|
|
6406
|
+
function hasControlChar(s) {
|
|
6407
|
+
for (const ch of s) {
|
|
6408
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
6409
|
+
if (code < 32 || code === 127)
|
|
6410
|
+
return true;
|
|
6411
|
+
}
|
|
6412
|
+
return false;
|
|
6413
|
+
}
|
|
6414
|
+
function projectNameError(raw) {
|
|
6415
|
+
const name = raw.trim();
|
|
6416
|
+
if (!name)
|
|
6417
|
+
return "a project name is required";
|
|
6418
|
+
if (name.length > 100)
|
|
6419
|
+
return "that name is too long: keep it under 100 characters";
|
|
6420
|
+
if (name.includes("/") || name.includes("\\"))
|
|
6421
|
+
return "a project name cannot contain a slash";
|
|
6422
|
+
if (/\s/.test(name))
|
|
6423
|
+
return "a project name cannot contain spaces";
|
|
6424
|
+
if (hasControlChar(name))
|
|
6425
|
+
return "a project name cannot contain control characters";
|
|
6426
|
+
if (name === "." || name === "..")
|
|
6427
|
+
return `"${name}" is not a project name`;
|
|
6428
|
+
if (name.startsWith("-") || name.startsWith("."))
|
|
6429
|
+
return "a project name cannot start with a dot or a dash";
|
|
6430
|
+
return null;
|
|
6431
|
+
}
|
|
6432
|
+
|
|
6257
6433
|
// ../shared/src/protocol/pick.ts
|
|
6258
6434
|
var pickMetaSchema = object({
|
|
6259
6435
|
component: string2().nullable(),
|
|
@@ -7559,6 +7735,10 @@ var VIEWERS = [[/\.(png|jpe?g|gif|webp|avif|bmp|ico)$/i, "image"]];
|
|
|
7559
7735
|
function viewerOf(path) {
|
|
7560
7736
|
return VIEWERS.find(([re]) => re.test(path))?.[1] ?? null;
|
|
7561
7737
|
}
|
|
7738
|
+
var PAGE_ASSETS = /\.(css|svg|woff2?|ttf|otf|mp4|webm|mp3|ogg|wav)$/i;
|
|
7739
|
+
function isPageAsset(path) {
|
|
7740
|
+
return PAGE_ASSETS.test(path);
|
|
7741
|
+
}
|
|
7562
7742
|
|
|
7563
7743
|
// ../shared/src/vscode-theme.ts
|
|
7564
7744
|
var workbenchKeys = {
|
|
@@ -7736,10 +7916,59 @@ function pickSyntax(tokenColors) {
|
|
|
7736
7916
|
function slug(s) {
|
|
7737
7917
|
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "theme";
|
|
7738
7918
|
}
|
|
7919
|
+
|
|
7920
|
+
// ../shared/src/managed-load.ts
|
|
7921
|
+
import { readFileSync, statSync } from "fs";
|
|
7922
|
+
import { userInfo } from "os";
|
|
7923
|
+
async function plutilJson(path) {
|
|
7924
|
+
const p = Bun.spawn(["plutil", "-convert", "json", "-o", "-", path], { stdout: "pipe", stderr: "pipe" });
|
|
7925
|
+
const [out, err] = await Promise.all([new Response(p.stdout).text(), new Response(p.stderr).text()]);
|
|
7926
|
+
await p.exited;
|
|
7927
|
+
if (p.exitCode !== 0)
|
|
7928
|
+
throw new Error(err.trim().split(`
|
|
7929
|
+
`)[0] || `plutil exited ${p.exitCode}`);
|
|
7930
|
+
return out;
|
|
7931
|
+
}
|
|
7932
|
+
function ownedByRoot(st) {
|
|
7933
|
+
return st.uid === 0 && (st.mode & 18) === 0;
|
|
7934
|
+
}
|
|
7935
|
+
async function loadManaged(opts = {}) {
|
|
7936
|
+
const platform = opts.platform ?? process.platform;
|
|
7937
|
+
const user = opts.user ?? safeUser();
|
|
7938
|
+
const sources = opts.sources ?? managedSources(platform, user);
|
|
7939
|
+
const reads = [];
|
|
7940
|
+
for (const s of sources) {
|
|
7941
|
+
let st;
|
|
7942
|
+
try {
|
|
7943
|
+
st = statSync(s.path);
|
|
7944
|
+
} catch {
|
|
7945
|
+
reads.push(s);
|
|
7946
|
+
continue;
|
|
7947
|
+
}
|
|
7948
|
+
if (s.kind === "json" && !ownedByRoot(st)) {
|
|
7949
|
+
reads.push({ ...s, owned: false });
|
|
7950
|
+
continue;
|
|
7951
|
+
}
|
|
7952
|
+
try {
|
|
7953
|
+
const text = s.kind === "plist" ? await (opts.plist ?? plutilJson)(s.path) : readFileSync(s.path, "utf8");
|
|
7954
|
+
reads.push({ ...s, text });
|
|
7955
|
+
} catch (e) {
|
|
7956
|
+
reads.push({ ...s, problem: e instanceof Error ? e.message : String(e) });
|
|
7957
|
+
}
|
|
7958
|
+
}
|
|
7959
|
+
return resolveManaged(reads);
|
|
7960
|
+
}
|
|
7961
|
+
function safeUser() {
|
|
7962
|
+
try {
|
|
7963
|
+
return userInfo().username || undefined;
|
|
7964
|
+
} catch {
|
|
7965
|
+
return;
|
|
7966
|
+
}
|
|
7967
|
+
}
|
|
7739
7968
|
// ../daemon/package.json
|
|
7740
7969
|
var package_default = {
|
|
7741
7970
|
name: "@toyon/daemon",
|
|
7742
|
-
version: "0.
|
|
7971
|
+
version: "0.6.0",
|
|
7743
7972
|
private: true,
|
|
7744
7973
|
type: "module",
|
|
7745
7974
|
main: "src/index.ts",
|
|
@@ -12084,6 +12313,7 @@ function spawnAcp(app, launch, cwd, tag) {
|
|
|
12084
12313
|
}
|
|
12085
12314
|
|
|
12086
12315
|
// ../daemon/src/agent/attachments.ts
|
|
12316
|
+
import { createHash } from "crypto";
|
|
12087
12317
|
import { mkdir, writeFile } from "fs/promises";
|
|
12088
12318
|
import { join } from "path";
|
|
12089
12319
|
var EXT = {
|
|
@@ -12093,7 +12323,17 @@ var EXT = {
|
|
|
12093
12323
|
"image/webp": "webp"
|
|
12094
12324
|
};
|
|
12095
12325
|
var ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
12096
|
-
var FILE =
|
|
12326
|
+
var FILE = /^(\d{1,6}|[0-9a-f]{16})\.(png|jpg|gif|webp|txt)$/;
|
|
12327
|
+
function toolImage(data, mimeType) {
|
|
12328
|
+
const ext = EXT[mimeType];
|
|
12329
|
+
if (!ext)
|
|
12330
|
+
return null;
|
|
12331
|
+
const bytes = Buffer.from(data, "base64");
|
|
12332
|
+
if (bytes.length === 0)
|
|
12333
|
+
return null;
|
|
12334
|
+
const file = `${createHash("sha256").update(bytes).digest("hex").slice(0, 16)}.${ext}`;
|
|
12335
|
+
return { ref: { file, mimeType, bytes: bytes.length }, bytes };
|
|
12336
|
+
}
|
|
12097
12337
|
var isAttachmentFile = (file) => FILE.test(file);
|
|
12098
12338
|
function attachmentsDirFor(attachmentsDir, worktreeId) {
|
|
12099
12339
|
return join(attachmentsDir, worktreeId);
|
|
@@ -12101,9 +12341,21 @@ function attachmentsDirFor(attachmentsDir, worktreeId) {
|
|
|
12101
12341
|
|
|
12102
12342
|
class AttachmentStore {
|
|
12103
12343
|
dir;
|
|
12344
|
+
pending = new Map;
|
|
12104
12345
|
constructor(dir) {
|
|
12105
12346
|
this.dir = dir;
|
|
12106
12347
|
}
|
|
12348
|
+
async putToolImage(worktreeId, file, bytes) {
|
|
12349
|
+
if (!ID.test(worktreeId) || !FILE.test(file))
|
|
12350
|
+
throw new UserError("bad tool image");
|
|
12351
|
+
const path = join(attachmentsDirFor(this.dir, worktreeId), file);
|
|
12352
|
+
if (this.pending.has(path) || await Bun.file(path).exists())
|
|
12353
|
+
return;
|
|
12354
|
+
await this.write(worktreeId, file, bytes);
|
|
12355
|
+
}
|
|
12356
|
+
whenWritten(worktreeId, file) {
|
|
12357
|
+
return this.pending.get(join(attachmentsDirFor(this.dir, worktreeId), file)) ?? Promise.resolve();
|
|
12358
|
+
}
|
|
12107
12359
|
async put(worktreeId, n, input) {
|
|
12108
12360
|
if (!ID.test(worktreeId))
|
|
12109
12361
|
throw new UserError("bad worktree id");
|
|
@@ -12148,10 +12400,12 @@ class AttachmentStore {
|
|
|
12148
12400
|
ref: { kind: "paste", n, ...name ? { name } : {}, ...source ? { source } : {}, ...pasteSummary(text), file }
|
|
12149
12401
|
};
|
|
12150
12402
|
}
|
|
12151
|
-
|
|
12403
|
+
write(worktreeId, file, data) {
|
|
12152
12404
|
const wtDir = attachmentsDirFor(this.dir, worktreeId);
|
|
12153
|
-
|
|
12154
|
-
|
|
12405
|
+
const path = join(wtDir, file);
|
|
12406
|
+
const done = mkdir(wtDir, { recursive: true }).then(() => writeFile(path, data)).finally(() => this.pending.delete(path));
|
|
12407
|
+
this.pending.set(path, done);
|
|
12408
|
+
return done;
|
|
12155
12409
|
}
|
|
12156
12410
|
fileFor(worktreeId, file) {
|
|
12157
12411
|
if (!ID.test(worktreeId) || !FILE.test(file))
|
|
@@ -12313,7 +12567,7 @@ class OptionProbe {
|
|
|
12313
12567
|
}
|
|
12314
12568
|
|
|
12315
12569
|
// ../daemon/src/agent/registry.ts
|
|
12316
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as
|
|
12570
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
12317
12571
|
import { homedir as homedir3 } from "os";
|
|
12318
12572
|
import { dirname as dirname3, isAbsolute, join as join5, resolve as resolve3 } from "path";
|
|
12319
12573
|
|
|
@@ -12339,8 +12593,9 @@ var cloud = {
|
|
|
12339
12593
|
};
|
|
12340
12594
|
|
|
12341
12595
|
// ../daemon/src/git/exec.ts
|
|
12342
|
-
import {
|
|
12343
|
-
import {
|
|
12596
|
+
import { spawn as spawn2 } from "child_process";
|
|
12597
|
+
import { createHash as createHash2 } from "crypto";
|
|
12598
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
12344
12599
|
import { join as join2 } from "path";
|
|
12345
12600
|
|
|
12346
12601
|
// ../daemon/src/core/metrics.ts
|
|
@@ -12380,10 +12635,10 @@ var LOCKFILES = [
|
|
|
12380
12635
|
"Cargo.lock"
|
|
12381
12636
|
];
|
|
12382
12637
|
function lockfileHash(dir) {
|
|
12383
|
-
const h =
|
|
12638
|
+
const h = createHash2("sha1");
|
|
12384
12639
|
for (const f of LOCKFILES) {
|
|
12385
12640
|
try {
|
|
12386
|
-
h.update(
|
|
12641
|
+
h.update(readFileSync2(join2(dir, f)));
|
|
12387
12642
|
} catch {}
|
|
12388
12643
|
}
|
|
12389
12644
|
return h.digest("hex");
|
|
@@ -12494,6 +12749,80 @@ async function runLive(cmd, args, cwd, opts = {}) {
|
|
|
12494
12749
|
}
|
|
12495
12750
|
}
|
|
12496
12751
|
}
|
|
12752
|
+
function runWatched(cmd, args, cwd, opts = {}) {
|
|
12753
|
+
const started = Date.now();
|
|
12754
|
+
return new Promise((resolve) => {
|
|
12755
|
+
let timer;
|
|
12756
|
+
let timedOut = false;
|
|
12757
|
+
let stop;
|
|
12758
|
+
let settled = false;
|
|
12759
|
+
const settle = (r) => {
|
|
12760
|
+
if (settled)
|
|
12761
|
+
return;
|
|
12762
|
+
settled = true;
|
|
12763
|
+
if (timer)
|
|
12764
|
+
clearTimeout(timer);
|
|
12765
|
+
if (stop)
|
|
12766
|
+
opts.signal?.removeEventListener("abort", stop);
|
|
12767
|
+
if (cmd === GIT) {
|
|
12768
|
+
lastGit.cmd = `git ${args.slice(0, 3).join(" ")}`;
|
|
12769
|
+
lastGit.ms = Date.now() - started;
|
|
12770
|
+
lastGit.at = Date.now();
|
|
12771
|
+
}
|
|
12772
|
+
resolve(r);
|
|
12773
|
+
};
|
|
12774
|
+
const failed = (msg) => settle({ ok: false, out: "", err: msg, exit: msg, text: msg });
|
|
12775
|
+
let child;
|
|
12776
|
+
try {
|
|
12777
|
+
child = spawn2(cmd, args, {
|
|
12778
|
+
cwd,
|
|
12779
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12780
|
+
detached: true,
|
|
12781
|
+
env: { ...process.env, ...SPAWN_ENV, ...opts.env }
|
|
12782
|
+
});
|
|
12783
|
+
} catch (e) {
|
|
12784
|
+
failed(e instanceof Error ? e.message : String(e));
|
|
12785
|
+
return;
|
|
12786
|
+
}
|
|
12787
|
+
child.on("error", (e) => failed(e.message));
|
|
12788
|
+
const exited = new Promise((r) => child.once("exit", () => r()));
|
|
12789
|
+
const kill = () => {
|
|
12790
|
+
if (child.pid)
|
|
12791
|
+
fireAndForget("git", killGroup(child.pid, exited), `stopping ${cmd} ${args[0] ?? ""}`);
|
|
12792
|
+
};
|
|
12793
|
+
if (opts.timeoutMs) {
|
|
12794
|
+
timer = setTimeout(() => {
|
|
12795
|
+
timedOut = true;
|
|
12796
|
+
kill();
|
|
12797
|
+
}, opts.timeoutMs);
|
|
12798
|
+
}
|
|
12799
|
+
if (opts.signal) {
|
|
12800
|
+
stop = kill;
|
|
12801
|
+
if (opts.signal.aborted)
|
|
12802
|
+
kill();
|
|
12803
|
+
else
|
|
12804
|
+
opts.signal.addEventListener("abort", stop, { once: true });
|
|
12805
|
+
}
|
|
12806
|
+
let text = "";
|
|
12807
|
+
const out = [];
|
|
12808
|
+
const err = [];
|
|
12809
|
+
const read = (stream, into) => {
|
|
12810
|
+
const dec = new TextDecoder;
|
|
12811
|
+
stream?.on("data", (b) => {
|
|
12812
|
+
const chunk = dec.decode(b, { stream: true });
|
|
12813
|
+
into.push(chunk);
|
|
12814
|
+
text += chunk;
|
|
12815
|
+
opts.onText?.(chunk);
|
|
12816
|
+
});
|
|
12817
|
+
};
|
|
12818
|
+
read(child.stdout, out);
|
|
12819
|
+
read(child.stderr, err);
|
|
12820
|
+
child.on("close", (code, signal) => {
|
|
12821
|
+
const exit = timedOut ? "timeout" : signal ?? code;
|
|
12822
|
+
settle({ ok: code === 0 && !timedOut, out: out.join("").trim(), err: err.join("").trim(), exit, text });
|
|
12823
|
+
});
|
|
12824
|
+
});
|
|
12825
|
+
}
|
|
12497
12826
|
async function git(cwd, ...args) {
|
|
12498
12827
|
const { rawOut: _raw, ...r } = await run(GIT, args, cwd);
|
|
12499
12828
|
return r;
|
|
@@ -12526,7 +12855,7 @@ async function defaultBranch(path) {
|
|
|
12526
12855
|
}
|
|
12527
12856
|
|
|
12528
12857
|
// ../daemon/src/agent/confine.ts
|
|
12529
|
-
import { statSync } from "fs";
|
|
12858
|
+
import { statSync as statSync2 } from "fs";
|
|
12530
12859
|
var SANDBOX_EXEC = "/usr/bin/sandbox-exec";
|
|
12531
12860
|
var uniq = (xs) => [...new Set(xs)];
|
|
12532
12861
|
var literal2 = (p) => `"${p.replaceAll("\\", "\\\\").replaceAll('"', "\\\"")}"`;
|
|
@@ -12544,7 +12873,7 @@ function seatbeltProfile(b, extraWrite = []) {
|
|
|
12544
12873
|
}
|
|
12545
12874
|
function kindOf(p) {
|
|
12546
12875
|
try {
|
|
12547
|
-
return
|
|
12876
|
+
return statSync2(p).isDirectory() ? "dir" : "file";
|
|
12548
12877
|
} catch {
|
|
12549
12878
|
return null;
|
|
12550
12879
|
}
|
|
@@ -12584,7 +12913,7 @@ function confine(launch, b, extraWrite = [], platform = process.platform, which
|
|
|
12584
12913
|
}
|
|
12585
12914
|
|
|
12586
12915
|
// ../daemon/src/agent/native.ts
|
|
12587
|
-
import { existsSync as existsSync2, readdirSync, readFileSync as
|
|
12916
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
12588
12917
|
var OS = { darwin: "darwin", linux: "linux", win32: "windows" };
|
|
12589
12918
|
function nativePackage(pkg, host) {
|
|
12590
12919
|
const os = OS[host.platform];
|
|
@@ -12614,7 +12943,7 @@ function muslLoader() {
|
|
|
12614
12943
|
}
|
|
12615
12944
|
function cpuFlags() {
|
|
12616
12945
|
try {
|
|
12617
|
-
return
|
|
12946
|
+
return readFileSync3("/proc/cpuinfo", "utf8").match(/^flags\s*:(.*)$/m)?.[1]?.trim().split(/\s+/) ?? [];
|
|
12618
12947
|
} catch {
|
|
12619
12948
|
return [];
|
|
12620
12949
|
}
|
|
@@ -12677,7 +13006,7 @@ function opencodeEnv() {
|
|
|
12677
13006
|
}
|
|
12678
13007
|
|
|
12679
13008
|
// ../daemon/src/agent/sandbox.ts
|
|
12680
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as
|
|
13009
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
12681
13010
|
import { homedir as homedir2, tmpdir } from "os";
|
|
12682
13011
|
import { join as join4, resolve as resolve2 } from "path";
|
|
12683
13012
|
|
|
@@ -12717,7 +13046,7 @@ function ensureDirs(p) {
|
|
|
12717
13046
|
}
|
|
12718
13047
|
|
|
12719
13048
|
// ../daemon/src/git/exclude.ts
|
|
12720
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as
|
|
13049
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
12721
13050
|
import { dirname } from "path";
|
|
12722
13051
|
async function excludeFile(cwd) {
|
|
12723
13052
|
const r = await git(cwd, "rev-parse", "--path-format=absolute", "--git-path", "info/exclude");
|
|
@@ -12730,7 +13059,7 @@ async function excludeFromGit(cwd, pattern) {
|
|
|
12730
13059
|
const file = await excludeFile(cwd);
|
|
12731
13060
|
if (!file)
|
|
12732
13061
|
return;
|
|
12733
|
-
const current = existsSync3(file) ?
|
|
13062
|
+
const current = existsSync3(file) ? readFileSync4(file, "utf8") : "";
|
|
12734
13063
|
if (current.split(`
|
|
12735
13064
|
`).some((l) => l.trim() === pattern))
|
|
12736
13065
|
return;
|
|
@@ -12746,7 +13075,7 @@ async function excludeBlock(cwd, name, patterns) {
|
|
|
12746
13075
|
return;
|
|
12747
13076
|
const open = `# toyon ${name}`;
|
|
12748
13077
|
const close = `# toyon end ${name}`;
|
|
12749
|
-
const current = existsSync3(file) ?
|
|
13078
|
+
const current = existsSync3(file) ? readFileSync4(file, "utf8") : "";
|
|
12750
13079
|
const lines = current.split(`
|
|
12751
13080
|
`);
|
|
12752
13081
|
const from = lines.indexOf(open);
|
|
@@ -12767,7 +13096,7 @@ async function unexcludeFromGit(cwd, pattern) {
|
|
|
12767
13096
|
const file = await excludeFile(cwd);
|
|
12768
13097
|
if (!file || !existsSync3(file))
|
|
12769
13098
|
return;
|
|
12770
|
-
const lines =
|
|
13099
|
+
const lines = readFileSync4(file, "utf8").split(`
|
|
12771
13100
|
`);
|
|
12772
13101
|
const kept = lines.filter((l) => l.trim() !== pattern);
|
|
12773
13102
|
if (kept.length === lines.length)
|
|
@@ -12846,7 +13175,7 @@ async function writeClaudeLocalSettings(cwd, b) {
|
|
|
12846
13175
|
let existing = {};
|
|
12847
13176
|
if (existsSync5(file)) {
|
|
12848
13177
|
try {
|
|
12849
|
-
const parsed = JSON.parse(
|
|
13178
|
+
const parsed = JSON.parse(readFileSync5(file, "utf8"));
|
|
12850
13179
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
12851
13180
|
existing = parsed;
|
|
12852
13181
|
} catch (e) {
|
|
@@ -12870,6 +13199,7 @@ async function writeClaudeLocalSettings(cwd, b) {
|
|
|
12870
13199
|
|
|
12871
13200
|
// ../daemon/src/agent/registry.ts
|
|
12872
13201
|
var CONFINEMENTS = ["claude-settings", "adapter-sandbox", "toyon-sandbox", "none"];
|
|
13202
|
+
var DEFAULT_AGENT_ID = "claude";
|
|
12873
13203
|
var BUILTIN_AGENTS = [
|
|
12874
13204
|
{
|
|
12875
13205
|
id: "claude",
|
|
@@ -12878,7 +13208,9 @@ var BUILTIN_AGENTS = [
|
|
|
12878
13208
|
builtin: true,
|
|
12879
13209
|
run: { kind: "npm-bin", pkg: "@agentclientprotocol/claude-agent-acp", version: "0.75.1", bin: "claude-agent-acp" },
|
|
12880
13210
|
...cloud.enabled ? { env: { NO_BROWSER: "1" } } : {},
|
|
12881
|
-
terminalLogins: {
|
|
13211
|
+
terminalLogins: {
|
|
13212
|
+
"claude-login": { args: ["--cli", "auth", "login", "--claudeai"], env: { NO_BROWSER: "1" }, plan: true }
|
|
13213
|
+
},
|
|
12882
13214
|
confinement: "claude-settings",
|
|
12883
13215
|
setup: (cwd, bounds) => writeClaudeLocalSettings(cwd, bounds).then(() => {
|
|
12884
13216
|
return;
|
|
@@ -12952,6 +13284,13 @@ class AgentRegistry {
|
|
|
12952
13284
|
list() {
|
|
12953
13285
|
return [...this.specs.values()];
|
|
12954
13286
|
}
|
|
13287
|
+
defaultId(persisted) {
|
|
13288
|
+
if (persisted !== undefined && this.specs.has(persisted))
|
|
13289
|
+
return persisted;
|
|
13290
|
+
if (this.specs.has(DEFAULT_AGENT_ID))
|
|
13291
|
+
return DEFAULT_AGENT_ID;
|
|
13292
|
+
return this.list()[0]?.id ?? DEFAULT_AGENT_ID;
|
|
13293
|
+
}
|
|
12955
13294
|
get(id) {
|
|
12956
13295
|
return this.specs.get(id);
|
|
12957
13296
|
}
|
|
@@ -12984,7 +13323,7 @@ class AgentRegistry {
|
|
|
12984
13323
|
if (!existsSync6(pkgJson))
|
|
12985
13324
|
return null;
|
|
12986
13325
|
try {
|
|
12987
|
-
const meta = JSON.parse(
|
|
13326
|
+
const meta = JSON.parse(readFileSync6(pkgJson, "utf8"));
|
|
12988
13327
|
const rel = typeof meta.bin === "string" ? meta.bin : meta.bin?.[spec.run.bin];
|
|
12989
13328
|
if (!rel)
|
|
12990
13329
|
return null;
|
|
@@ -13071,7 +13410,7 @@ class AgentRegistry {
|
|
|
13071
13410
|
return null;
|
|
13072
13411
|
const pkgJson = join5(this.dir, spec.id, "node_modules", name, "package.json");
|
|
13073
13412
|
try {
|
|
13074
|
-
return JSON.parse(
|
|
13413
|
+
return JSON.parse(readFileSync6(pkgJson, "utf8")).version ?? null;
|
|
13075
13414
|
} catch {
|
|
13076
13415
|
return null;
|
|
13077
13416
|
}
|
|
@@ -13139,9 +13478,13 @@ function parseCustomAgents(raw) {
|
|
|
13139
13478
|
}
|
|
13140
13479
|
return out;
|
|
13141
13480
|
}
|
|
13142
|
-
function
|
|
13143
|
-
const
|
|
13144
|
-
return
|
|
13481
|
+
function managedSpecs(specs, managed) {
|
|
13482
|
+
const kept = managed.customAgents ? specs : specs.filter((s) => s.builtin);
|
|
13483
|
+
return managed.agents === null ? kept : kept.filter((s) => managed.agents?.includes(s.id));
|
|
13484
|
+
}
|
|
13485
|
+
function loadAgentRegistry(agentsFile, agentsDir, sandbox, managed = MANAGED_DEFAULTS) {
|
|
13486
|
+
const custom = managed.customAgents && existsSync6(agentsFile) ? parseCustomAgents(readFileSync6(agentsFile, "utf8")) : [];
|
|
13487
|
+
return new AgentRegistry(managedSpecs([...BUILTIN_AGENTS, ...custom], managed), agentsDir, undefined, undefined, sandbox);
|
|
13145
13488
|
}
|
|
13146
13489
|
|
|
13147
13490
|
// ../daemon/src/agent/acp/session.ts
|
|
@@ -13366,7 +13709,7 @@ ${a.text}
|
|
|
13366
13709
|
}
|
|
13367
13710
|
|
|
13368
13711
|
// ../daemon/src/agent/transcript.ts
|
|
13369
|
-
import { existsSync as existsSync7, readFileSync as
|
|
13712
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
13370
13713
|
import { appendFile } from "fs/promises";
|
|
13371
13714
|
import { join as join6 } from "path";
|
|
13372
13715
|
function transcriptPathFor(transcriptsDir, worktreeId) {
|
|
@@ -13377,8 +13720,16 @@ function coalesce(entries) {
|
|
|
13377
13720
|
for (const entry of entries) {
|
|
13378
13721
|
const last = out.at(-1);
|
|
13379
13722
|
const { event } = entry;
|
|
13380
|
-
if (
|
|
13381
|
-
|
|
13723
|
+
if (event.type === "text-delta" && last?.event.type === "text-delta") {
|
|
13724
|
+
const messageId = event.messageId ?? last.event.messageId;
|
|
13725
|
+
out[out.length - 1] = {
|
|
13726
|
+
seq: last.seq,
|
|
13727
|
+
event: { type: "text-delta", text: last.event.text + event.text, ...messageId ? { messageId } : {} }
|
|
13728
|
+
};
|
|
13729
|
+
} else if (event.type === "thinking-delta" && last?.event.type === "thinking-delta") {
|
|
13730
|
+
out[out.length - 1] = { seq: last.seq, event: { type: "thinking-delta", text: last.event.text + event.text } };
|
|
13731
|
+
} else if (event.type === "tool-delta" && last?.event.type === "tool-delta" && last.event.toolId === event.toolId) {
|
|
13732
|
+
out[out.length - 1] = { seq: last.seq, event: { ...event, text: last.event.text + event.text } };
|
|
13382
13733
|
} else {
|
|
13383
13734
|
out.push(entry);
|
|
13384
13735
|
}
|
|
@@ -13416,7 +13767,7 @@ class Transcript {
|
|
|
13416
13767
|
this.seq = (this.entries.at(-1)?.seq ?? -1) + 1;
|
|
13417
13768
|
}
|
|
13418
13769
|
read() {
|
|
13419
|
-
return existsSync7(this.path) ? parseTranscript(
|
|
13770
|
+
return existsSync7(this.path) ? parseTranscript(readFileSync7(this.path, "utf8"), this.tag) : [];
|
|
13420
13771
|
}
|
|
13421
13772
|
append(event) {
|
|
13422
13773
|
const entry = { seq: this.seq++, event };
|
|
@@ -13724,7 +14075,7 @@ function abandoned(memos, parent) {
|
|
|
13724
14075
|
}
|
|
13725
14076
|
return out;
|
|
13726
14077
|
}
|
|
13727
|
-
function mapUpdate(update, memos, tag) {
|
|
14078
|
+
function mapUpdate(update, memos, tag, sink) {
|
|
13728
14079
|
switch (update.sessionUpdate) {
|
|
13729
14080
|
case "agent_message_chunk": {
|
|
13730
14081
|
const { parentToolId } = spawnOf(update._meta);
|
|
@@ -13732,7 +14083,7 @@ function mapUpdate(update, memos, tag) {
|
|
|
13732
14083
|
const text = textOf(update.content, tag, "message");
|
|
13733
14084
|
if (text === null)
|
|
13734
14085
|
return out;
|
|
13735
|
-
out.push(parentToolId ? { type: "tool-delta", toolId: parentToolId, text } : { type: "text-delta", text });
|
|
14086
|
+
out.push(parentToolId ? { type: "tool-delta", toolId: parentToolId, text } : { type: "text-delta", text, ...update.messageId ? { messageId: update.messageId } : {} });
|
|
13736
14087
|
return out;
|
|
13737
14088
|
}
|
|
13738
14089
|
case "agent_thought_chunk": {
|
|
@@ -13768,7 +14119,7 @@ function mapUpdate(update, memos, tag) {
|
|
|
13768
14119
|
...spawn
|
|
13769
14120
|
});
|
|
13770
14121
|
if (update.status === "completed" || update.status === "failed")
|
|
13771
|
-
out.push(endOf(update.toolCallId, memo, update.status));
|
|
14122
|
+
out.push(endOf(update.toolCallId, memo, update.status, sink));
|
|
13772
14123
|
return out;
|
|
13773
14124
|
}
|
|
13774
14125
|
case "tool_call_update": {
|
|
@@ -13821,7 +14172,7 @@ function mapUpdate(update, memos, tag) {
|
|
|
13821
14172
|
if (update.rawOutput !== undefined)
|
|
13822
14173
|
memo.rawOutput = update.rawOutput;
|
|
13823
14174
|
if ((update.status === "completed" || update.status === "failed") && !memo.ended) {
|
|
13824
|
-
out.push(endOf(update.toolCallId, memo, update.status));
|
|
14175
|
+
out.push(endOf(update.toolCallId, memo, update.status, sink));
|
|
13825
14176
|
}
|
|
13826
14177
|
return out;
|
|
13827
14178
|
}
|
|
@@ -13852,25 +14203,43 @@ function textOf(content, tag, what) {
|
|
|
13852
14203
|
log.debug(tag, `acp: dropping non-text ${what} block (${content.type})`);
|
|
13853
14204
|
return null;
|
|
13854
14205
|
}
|
|
13855
|
-
function endOf(toolId, memo, status) {
|
|
14206
|
+
function endOf(toolId, memo, status, sink) {
|
|
13856
14207
|
memo.ended = true;
|
|
14208
|
+
const images = sink ? toolImages(memo.content, sink) : [];
|
|
13857
14209
|
return {
|
|
13858
14210
|
type: "tool-end",
|
|
13859
14211
|
toolId,
|
|
13860
14212
|
output: summarizeToolOutput(memo.content, memo.rawOutput),
|
|
13861
|
-
isError: status === "failed" || exitCodeOf(memo.rawOutput) > 0
|
|
14213
|
+
isError: status === "failed" || exitCodeOf(memo.rawOutput) > 0,
|
|
14214
|
+
...images.length ? { images } : {}
|
|
13862
14215
|
};
|
|
13863
14216
|
}
|
|
14217
|
+
function toolImages(content, sink) {
|
|
14218
|
+
const out = [];
|
|
14219
|
+
for (const c of content) {
|
|
14220
|
+
if (c.type !== "content" || c.content.type !== "image")
|
|
14221
|
+
continue;
|
|
14222
|
+
const img = toolImage(c.content.data, c.content.mimeType);
|
|
14223
|
+
if (!img)
|
|
14224
|
+
continue;
|
|
14225
|
+
sink(img.ref.file, img.bytes);
|
|
14226
|
+
out.push(img.ref);
|
|
14227
|
+
}
|
|
14228
|
+
return out;
|
|
14229
|
+
}
|
|
13864
14230
|
function exitCodeOf(rawOutput) {
|
|
13865
14231
|
const exit = rawOutput?.metadata?.exit;
|
|
13866
14232
|
return typeof exit === "number" ? exit : 0;
|
|
13867
14233
|
}
|
|
13868
14234
|
function summarizeToolOutput(content, rawOutput) {
|
|
13869
14235
|
const parts = [];
|
|
14236
|
+
let pictured = false;
|
|
13870
14237
|
for (const c of content) {
|
|
13871
14238
|
if (c.type === "content") {
|
|
13872
14239
|
if (c.content.type === "text")
|
|
13873
14240
|
parts.push(c.content.text);
|
|
14241
|
+
else if (c.content.type === "image")
|
|
14242
|
+
pictured = true;
|
|
13874
14243
|
} else if (c.type === "diff") {
|
|
13875
14244
|
const body = unifiedDiff(c.oldText ?? "", c.newText);
|
|
13876
14245
|
if (body)
|
|
@@ -13882,6 +14251,8 @@ ${body}
|
|
|
13882
14251
|
if (parts.length > 0)
|
|
13883
14252
|
return truncate(parts.join(`
|
|
13884
14253
|
`));
|
|
14254
|
+
if (pictured)
|
|
14255
|
+
return "";
|
|
13885
14256
|
if (typeof rawOutput === "string")
|
|
13886
14257
|
return truncate(rawOutput);
|
|
13887
14258
|
if (rawOutput && typeof rawOutput === "object") {
|
|
@@ -13943,6 +14314,7 @@ class AcpSession {
|
|
|
13943
14314
|
running = false;
|
|
13944
14315
|
interrupted = false;
|
|
13945
14316
|
stopped = false;
|
|
14317
|
+
held = false;
|
|
13946
14318
|
conn = null;
|
|
13947
14319
|
connecting = null;
|
|
13948
14320
|
live = null;
|
|
@@ -14056,7 +14428,7 @@ class AcpSession {
|
|
|
14056
14428
|
return log.warn(this.d.worktreeId, "send after close dropped");
|
|
14057
14429
|
const { context, attachments } = opts;
|
|
14058
14430
|
const item = { text, context, ...attachments?.length ? { attachments } : {} };
|
|
14059
|
-
if ((this.running || this.own) && !this.interrupted && this.live?.conn.steering) {
|
|
14431
|
+
if ((this.running || this.own) && !this.interrupted && !this.held && this.live?.conn.steering) {
|
|
14060
14432
|
return fireAndForget(this.d.worktreeId, this.steer(item), "agent steer");
|
|
14061
14433
|
}
|
|
14062
14434
|
this.enqueue(item);
|
|
@@ -14064,9 +14436,21 @@ class AcpSession {
|
|
|
14064
14436
|
enqueue(item) {
|
|
14065
14437
|
this.queue.push(item);
|
|
14066
14438
|
this.queueChanged();
|
|
14067
|
-
if (!this.running)
|
|
14439
|
+
if (!this.running && !this.held)
|
|
14068
14440
|
fireAndForget(this.d.worktreeId, this.drain(), "agent drain");
|
|
14069
14441
|
}
|
|
14442
|
+
hold() {
|
|
14443
|
+
this.held = true;
|
|
14444
|
+
let released = false;
|
|
14445
|
+
return () => {
|
|
14446
|
+
if (released)
|
|
14447
|
+
return;
|
|
14448
|
+
released = true;
|
|
14449
|
+
this.held = false;
|
|
14450
|
+
if (!this.running && this.queue.length > 0)
|
|
14451
|
+
fireAndForget(this.d.worktreeId, this.drain(), "agent drain");
|
|
14452
|
+
};
|
|
14453
|
+
}
|
|
14070
14454
|
async steer(item) {
|
|
14071
14455
|
item.recorded ??= await this.record(item);
|
|
14072
14456
|
const live = this.live;
|
|
@@ -14144,7 +14528,7 @@ class AcpSession {
|
|
|
14144
14528
|
this.setStatus("working");
|
|
14145
14529
|
let item = null;
|
|
14146
14530
|
try {
|
|
14147
|
-
while (this.queue.length > 0) {
|
|
14531
|
+
while (this.queue.length > 0 && !this.held) {
|
|
14148
14532
|
item = this.queue.shift();
|
|
14149
14533
|
this.queueChanged();
|
|
14150
14534
|
try {
|
|
@@ -14168,7 +14552,7 @@ class AcpSession {
|
|
|
14168
14552
|
type: "agent-auth-required",
|
|
14169
14553
|
agent: this.conn.spec.id,
|
|
14170
14554
|
agentName: this.conn.spec.name,
|
|
14171
|
-
|
|
14555
|
+
...this.offeredMethods(this.conn),
|
|
14172
14556
|
...rejected ? { rejected: true } : {},
|
|
14173
14557
|
ts: Date.now()
|
|
14174
14558
|
});
|
|
@@ -14208,9 +14592,6 @@ class AcpSession {
|
|
|
14208
14592
|
caps: { close: conn.closeSupported, delete: conn.deleteSupported },
|
|
14209
14593
|
tag: this.d.worktreeId
|
|
14210
14594
|
});
|
|
14211
|
-
} catch (e) {
|
|
14212
|
-
log.warn(this.d.worktreeId, "ask failed", e);
|
|
14213
|
-
return null;
|
|
14214
14595
|
} finally {
|
|
14215
14596
|
this.asking--;
|
|
14216
14597
|
this.maybeArmReaper();
|
|
@@ -14221,6 +14602,9 @@ class AcpSession {
|
|
|
14221
14602
|
const method = conn.authMethods.find((m) => m.id === methodId);
|
|
14222
14603
|
if (!method)
|
|
14223
14604
|
throw new UserError(`${conn.spec.name} offers no login method "${methodId}"`);
|
|
14605
|
+
if (this.planWithheld(conn, methodId)) {
|
|
14606
|
+
throw new UserError("Signing in with a Claude plan is turned off by your organization's policy");
|
|
14607
|
+
}
|
|
14224
14608
|
const login = terminalLogin(method);
|
|
14225
14609
|
if (login) {
|
|
14226
14610
|
const l = this.d.launch(conn.spec);
|
|
@@ -14256,6 +14640,14 @@ class AcpSession {
|
|
|
14256
14640
|
if (item)
|
|
14257
14641
|
this.send(item.text, item);
|
|
14258
14642
|
}
|
|
14643
|
+
planWithheld(conn, methodId) {
|
|
14644
|
+
return this.d.planSignIn?.() === false && conn.spec.terminalLogins?.[methodId]?.plan === true;
|
|
14645
|
+
}
|
|
14646
|
+
offeredMethods(conn) {
|
|
14647
|
+
const kept = conn.authMethods.filter((m) => !this.planWithheld(conn, m.id));
|
|
14648
|
+
const methods2 = kept.map(authMethodInfo);
|
|
14649
|
+
return kept.length === conn.authMethods.length ? { methods: methods2 } : { methods: methods2, withheld: true };
|
|
14650
|
+
}
|
|
14259
14651
|
rejectedCredential(e) {
|
|
14260
14652
|
if (!this.conn?.authMethods.length)
|
|
14261
14653
|
return false;
|
|
@@ -14517,7 +14909,7 @@ class AcpSession {
|
|
|
14517
14909
|
}
|
|
14518
14910
|
if (params.update.sessionUpdate === "config_option_update")
|
|
14519
14911
|
this.absorb(live, params.update.configOptions);
|
|
14520
|
-
const events = mapUpdate(params.update, live.tools, this.d.worktreeId);
|
|
14912
|
+
const events = mapUpdate(params.update, live.tools, this.d.worktreeId, (file, bytes) => fireAndForget(this.d.worktreeId, this.d.attachments.putToolImage(this.d.worktreeId, file, bytes), "tool image"));
|
|
14521
14913
|
if (!this.running)
|
|
14522
14914
|
this.trackOwn(events);
|
|
14523
14915
|
for (const ev of events) {
|
|
@@ -14775,7 +15167,7 @@ function sameCommands(a, b) {
|
|
|
14775
15167
|
}
|
|
14776
15168
|
|
|
14777
15169
|
// ../daemon/src/agent/planDoc.ts
|
|
14778
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as
|
|
15170
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
14779
15171
|
import { join as join7 } from "path";
|
|
14780
15172
|
var PLANS_DIR = join7(".toyon", "plans");
|
|
14781
15173
|
var PLAN_NAME = /^(\d+)\.md$/;
|
|
@@ -14813,7 +15205,7 @@ function planEdited(cwd, rel, proposed) {
|
|
|
14813
15205
|
if (!existsSync8(file))
|
|
14814
15206
|
return false;
|
|
14815
15207
|
try {
|
|
14816
|
-
return
|
|
15208
|
+
return readFileSync8(file, "utf8").trim() !== proposed.trim();
|
|
14817
15209
|
} catch (e) {
|
|
14818
15210
|
log.warn(cwd, `could not read ${rel}`, e);
|
|
14819
15211
|
return false;
|
|
@@ -15097,8 +15489,8 @@ function expandEnv(env, vars) {
|
|
|
15097
15489
|
|
|
15098
15490
|
// ../daemon/src/core/remote.ts
|
|
15099
15491
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
15100
|
-
import { existsSync as existsSync9, readFileSync as
|
|
15101
|
-
function loadRemote(file, env = process.env) {
|
|
15492
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
15493
|
+
function loadRemote(file, env = process.env, managed = MANAGED_DEFAULTS) {
|
|
15102
15494
|
if (env.TOYON_CLOUD === "1") {
|
|
15103
15495
|
const host = (env.TOYON_PUBLIC_HOST ?? "").toLowerCase();
|
|
15104
15496
|
if (!isRemoteHost(host)) {
|
|
@@ -15112,11 +15504,19 @@ function loadRemote(file, env = process.env) {
|
|
|
15112
15504
|
}
|
|
15113
15505
|
if (!existsSync9(file))
|
|
15114
15506
|
return null;
|
|
15115
|
-
|
|
15507
|
+
if (managed.remote === "off") {
|
|
15508
|
+
log.warn("remote", `${file} is ignored: remote access is turned off by your organization's policy`);
|
|
15509
|
+
return null;
|
|
15510
|
+
}
|
|
15511
|
+
const view = parseRemote(readFileSync9(file, "utf8"));
|
|
15116
15512
|
if (!view) {
|
|
15117
15513
|
log.warn("remote", `${file} names no valid host or previews; remote access is off`);
|
|
15118
15514
|
return null;
|
|
15119
15515
|
}
|
|
15516
|
+
if (managed.remote === "tailscale" && !view.host.endsWith(".ts.net")) {
|
|
15517
|
+
log.warn("remote", `${file} names ${view.host}; your organization's policy allows a tailnet name only, so remote access is off`);
|
|
15518
|
+
return null;
|
|
15519
|
+
}
|
|
15120
15520
|
return { ...view, front: "local" };
|
|
15121
15521
|
}
|
|
15122
15522
|
function sameSecret(given, want) {
|
|
@@ -15485,31 +15885,50 @@ class PtyStream {
|
|
|
15485
15885
|
// ../daemon/src/runtime/lines.ts
|
|
15486
15886
|
var MAX_PENDING = 64 * 1024;
|
|
15487
15887
|
var ANSI = /\u001b(?:\[[0-9;?]*[ -/]*[@-~]|\][^\u0007\u001b]*(?:\u0007|\u001b\\)|[@-Z\\-_])/g;
|
|
15888
|
+
var UP = /\u001b\[(\d*)[AF]/g;
|
|
15488
15889
|
function clean(raw) {
|
|
15489
15890
|
const parts = raw.replace(ANSI, "").split("\r");
|
|
15490
15891
|
while (parts.length > 1 && !parts[parts.length - 1]?.trim())
|
|
15491
15892
|
parts.pop();
|
|
15492
15893
|
return (parts[parts.length - 1] ?? "").trimEnd();
|
|
15493
15894
|
}
|
|
15895
|
+
function takeUps(raw) {
|
|
15896
|
+
let n = 0;
|
|
15897
|
+
const rest = raw.replace(UP, (_, count) => {
|
|
15898
|
+
n += count === "" ? 1 : Number(count);
|
|
15899
|
+
return "";
|
|
15900
|
+
});
|
|
15901
|
+
return [rest, n];
|
|
15902
|
+
}
|
|
15494
15903
|
|
|
15495
15904
|
class LineSplitter {
|
|
15496
15905
|
pending = "";
|
|
15497
15906
|
feed(chunk) {
|
|
15498
15907
|
this.pending += chunk;
|
|
15499
15908
|
const out = [];
|
|
15909
|
+
const emit = (raw) => {
|
|
15910
|
+
const [rest, retract] = takeUps(raw);
|
|
15911
|
+
const line = clean(rest);
|
|
15912
|
+
if (line || retract)
|
|
15913
|
+
out.push({ line, retract });
|
|
15914
|
+
};
|
|
15500
15915
|
for (let nl = this.pending.indexOf(`
|
|
15501
15916
|
`);nl !== -1; nl = this.pending.indexOf(`
|
|
15502
15917
|
`)) {
|
|
15503
|
-
const
|
|
15918
|
+
const raw = this.pending.slice(0, nl);
|
|
15504
15919
|
this.pending = this.pending.slice(nl + 1);
|
|
15505
|
-
|
|
15506
|
-
out.push(line);
|
|
15920
|
+
emit(raw);
|
|
15507
15921
|
}
|
|
15508
15922
|
if (this.pending.length > MAX_PENDING) {
|
|
15509
|
-
const
|
|
15923
|
+
const raw = this.pending;
|
|
15510
15924
|
this.pending = "";
|
|
15511
|
-
|
|
15512
|
-
|
|
15925
|
+
emit(raw);
|
|
15926
|
+
return out;
|
|
15927
|
+
}
|
|
15928
|
+
const [rest, retract] = takeUps(this.pending);
|
|
15929
|
+
if (retract) {
|
|
15930
|
+
this.pending = rest;
|
|
15931
|
+
out.push({ line: "", retract });
|
|
15513
15932
|
}
|
|
15514
15933
|
return out;
|
|
15515
15934
|
}
|
|
@@ -15655,8 +16074,8 @@ class WorktreeProcs {
|
|
|
15655
16074
|
ring: PROC_RING
|
|
15656
16075
|
}, (data) => {
|
|
15657
16076
|
this.onData(name, data);
|
|
15658
|
-
for (const
|
|
15659
|
-
this.pushLine(name, line);
|
|
16077
|
+
for (const ev of mp.lines.feed(data))
|
|
16078
|
+
this.pushLine(name, ev.line, ev.retract);
|
|
15660
16079
|
}, (code) => this.handleExit(mp, code));
|
|
15661
16080
|
} catch (e) {
|
|
15662
16081
|
mp.state.status = "crashed";
|
|
@@ -15822,11 +16241,11 @@ class WorktreeProcs {
|
|
|
15822
16241
|
states() {
|
|
15823
16242
|
return [...this.procs.values()].map((p) => ({ ...p.state }));
|
|
15824
16243
|
}
|
|
15825
|
-
pushLine(proc, line) {
|
|
15826
|
-
this.lines.
|
|
16244
|
+
pushLine(proc, line, retract = 0) {
|
|
16245
|
+
this.lines = applyLog(this.lines, proc, line, retract);
|
|
15827
16246
|
if (this.lines.length > LOG_RING_SIZE)
|
|
15828
16247
|
this.lines.shift();
|
|
15829
|
-
this.onLog(proc, line);
|
|
16248
|
+
this.onLog(proc, line, retract);
|
|
15830
16249
|
}
|
|
15831
16250
|
}
|
|
15832
16251
|
function execForm(command) {
|
|
@@ -15871,7 +16290,6 @@ function terminalEnv(base, own, urls) {
|
|
|
15871
16290
|
}
|
|
15872
16291
|
return { ...env, ...urls, TERM: "xterm-256color", COLORTERM: "truecolor", ...own };
|
|
15873
16292
|
}
|
|
15874
|
-
var DEFAULT_AGENT_ID = "claude";
|
|
15875
16293
|
var PREVIEW_WAKE_MS = 8000;
|
|
15876
16294
|
var PREVIEW_POLL_MS = 100;
|
|
15877
16295
|
function defaultAgent(wt, d, preview, onProcess) {
|
|
@@ -15883,7 +16301,7 @@ function defaultAgent(wt, d, preview, onProcess) {
|
|
|
15883
16301
|
spec: () => {
|
|
15884
16302
|
const w = d.state.requireWorktree(wt.id);
|
|
15885
16303
|
if (!w.agent) {
|
|
15886
|
-
w.agent = d.state.defaultAgent
|
|
16304
|
+
w.agent = d.agents.defaultId(d.state.defaultAgent);
|
|
15887
16305
|
d.state.save();
|
|
15888
16306
|
d.hub.emit("worktreesChanged");
|
|
15889
16307
|
}
|
|
@@ -15891,6 +16309,7 @@ function defaultAgent(wt, d, preview, onProcess) {
|
|
|
15891
16309
|
},
|
|
15892
16310
|
connect: (app, spec, prepared) => spawnAcp(app, d.agents.launch(spec, prepared), wt.path, wt.id),
|
|
15893
16311
|
launch: (spec) => d.agents.command(spec),
|
|
16312
|
+
planSignIn: () => d.managed?.planSignIn ?? true,
|
|
15894
16313
|
transcriptsDir: d.paths.transcriptsDir,
|
|
15895
16314
|
attachments: d.attachments ?? new AttachmentStore(d.paths.attachmentsDir),
|
|
15896
16315
|
getSessionId: () => d.state.session(wt.id),
|
|
@@ -16429,7 +16848,7 @@ class RuntimeRegistry {
|
|
|
16429
16848
|
for (const g of rt.procs?.groups() ?? [])
|
|
16430
16849
|
live.push(g);
|
|
16431
16850
|
if (rt.agent.pgid !== null) {
|
|
16432
|
-
const agentId = this.deps.state.worktree(id)?.agent ?? this.deps.state.defaultAgent
|
|
16851
|
+
const agentId = this.deps.state.worktree(id)?.agent ?? this.deps.agents.defaultId(this.deps.state.defaultAgent);
|
|
16433
16852
|
live.push({ name: `agent:${agentId}`, pgid: rt.agent.pgid });
|
|
16434
16853
|
}
|
|
16435
16854
|
if (rt.shell?.alive)
|
|
@@ -16894,7 +17313,7 @@ function makePlanner(agents) {
|
|
|
16894
17313
|
}
|
|
16895
17314
|
|
|
16896
17315
|
// ../daemon/src/core/assets.ts
|
|
16897
|
-
import { existsSync as existsSync11, readdirSync as readdirSync3, rmSync, statSync as
|
|
17316
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, rmSync, statSync as statSync3 } from "fs";
|
|
16898
17317
|
import { join as join9, resolve as resolve5 } from "path";
|
|
16899
17318
|
function locateAssets(here, env = process.env) {
|
|
16900
17319
|
const packaged = existsSync11(join9(here, "shell", "index.html"));
|
|
@@ -16918,7 +17337,7 @@ function pruneAssets(shellDist) {
|
|
|
16918
17337
|
for (const name of names) {
|
|
16919
17338
|
const path = join9(dir, name);
|
|
16920
17339
|
try {
|
|
16921
|
-
found.push({ path, mtime:
|
|
17340
|
+
found.push({ path, mtime: statSync3(path).mtimeMs });
|
|
16922
17341
|
} catch {}
|
|
16923
17342
|
}
|
|
16924
17343
|
if (found.length === 0)
|
|
@@ -17025,7 +17444,7 @@ class Hub {
|
|
|
17025
17444
|
}
|
|
17026
17445
|
|
|
17027
17446
|
// ../daemon/src/core/restart.ts
|
|
17028
|
-
import { spawn as
|
|
17447
|
+
import { spawn as spawn3 } from "child_process";
|
|
17029
17448
|
import { openSync } from "fs";
|
|
17030
17449
|
function restartable(env = process.env) {
|
|
17031
17450
|
if (env.TOYON_WORKTREE !== undefined) {
|
|
@@ -17035,7 +17454,7 @@ function restartable(env = process.env) {
|
|
|
17035
17454
|
}
|
|
17036
17455
|
function respawn(logFile) {
|
|
17037
17456
|
const logFd = openSync(logFile, "a");
|
|
17038
|
-
const child =
|
|
17457
|
+
const child = spawn3(process.execPath, process.argv.slice(1), {
|
|
17039
17458
|
detached: true,
|
|
17040
17459
|
stdio: ["ignore", logFd, logFd],
|
|
17041
17460
|
env: { ...process.env }
|
|
@@ -17177,7 +17596,7 @@ class SelfWatch {
|
|
|
17177
17596
|
|
|
17178
17597
|
// ../daemon/src/core/state.ts
|
|
17179
17598
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
17180
|
-
import { existsSync as existsSync13, readFileSync as
|
|
17599
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
17181
17600
|
var GROUPS_SAVE_MS = 250;
|
|
17182
17601
|
var SEEN_WORKTREES = 50;
|
|
17183
17602
|
var empty = { repos: [], worktrees: [], sessions: {} };
|
|
@@ -17188,7 +17607,7 @@ function loadState(paths) {
|
|
|
17188
17607
|
return structuredClone(empty);
|
|
17189
17608
|
let raw;
|
|
17190
17609
|
try {
|
|
17191
|
-
raw =
|
|
17610
|
+
raw = readFileSync10(STATE_FILE, "utf8");
|
|
17192
17611
|
} catch (e) {
|
|
17193
17612
|
log.error("state", `cannot read ${STATE_FILE}; starting empty`, e);
|
|
17194
17613
|
return structuredClone(empty);
|
|
@@ -17237,7 +17656,7 @@ function loadOrCreateToken(paths) {
|
|
|
17237
17656
|
ensureDirs(paths);
|
|
17238
17657
|
const TOKEN_FILE = paths.tokenFile;
|
|
17239
17658
|
if (existsSync13(TOKEN_FILE))
|
|
17240
|
-
return
|
|
17659
|
+
return readFileSync10(TOKEN_FILE, "utf8").trim();
|
|
17241
17660
|
const token = randomBytes2(32).toString("hex");
|
|
17242
17661
|
writeFileSync5(TOKEN_FILE, token, { mode: 384 });
|
|
17243
17662
|
return token;
|
|
@@ -17414,6 +17833,12 @@ class StateStore {
|
|
|
17414
17833
|
this.state.defaultAgent = id;
|
|
17415
17834
|
this.save();
|
|
17416
17835
|
}
|
|
17836
|
+
clearDefaultAgent() {
|
|
17837
|
+
if (this.state.defaultAgent === undefined)
|
|
17838
|
+
return;
|
|
17839
|
+
delete this.state.defaultAgent;
|
|
17840
|
+
this.save();
|
|
17841
|
+
}
|
|
17417
17842
|
get theme() {
|
|
17418
17843
|
return this.state.theme;
|
|
17419
17844
|
}
|
|
@@ -18018,7 +18443,7 @@ async function loadProjectTypescript(root) {
|
|
|
18018
18443
|
}
|
|
18019
18444
|
|
|
18020
18445
|
// ../daemon/src/drafts/store.ts
|
|
18021
|
-
import { existsSync as existsSync14, readFileSync as
|
|
18446
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, renameSync as renameSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
18022
18447
|
var SAVE_DELAY_MS = 1000;
|
|
18023
18448
|
|
|
18024
18449
|
class DraftStore {
|
|
@@ -18030,7 +18455,7 @@ class DraftStore {
|
|
|
18030
18455
|
if (!existsSync14(d.file))
|
|
18031
18456
|
return;
|
|
18032
18457
|
try {
|
|
18033
|
-
const raw = JSON.parse(
|
|
18458
|
+
const raw = JSON.parse(readFileSync11(d.file, "utf8"));
|
|
18034
18459
|
if (!raw || typeof raw !== "object")
|
|
18035
18460
|
return;
|
|
18036
18461
|
for (const [id, text] of Object.entries(raw)) {
|
|
@@ -18106,6 +18531,7 @@ var OUTPUT_CAP = 200000;
|
|
|
18106
18531
|
var TIMEOUT_MS = 10 * 60000;
|
|
18107
18532
|
var KILL_GRACE_MS2 = 3000;
|
|
18108
18533
|
var DRAIN_GRACE_MS = 500;
|
|
18534
|
+
var LIVE_AFTER_MS = 1500;
|
|
18109
18535
|
|
|
18110
18536
|
class ExecService {
|
|
18111
18537
|
deps;
|
|
@@ -18114,18 +18540,26 @@ class ExecService {
|
|
|
18114
18540
|
constructor(deps) {
|
|
18115
18541
|
this.deps = deps;
|
|
18116
18542
|
}
|
|
18543
|
+
agentFor(worktreeId) {
|
|
18544
|
+
const wt = this.deps.state.requireWorktree(worktreeId);
|
|
18545
|
+
if (wt.kind === "spare")
|
|
18546
|
+
throw new UserError("no shell for a spare worktree");
|
|
18547
|
+
const agent = this.deps.runtime.agentFor(worktreeId);
|
|
18548
|
+
if (!agent)
|
|
18549
|
+
throw new UserError("worktree still starting; try again in a moment");
|
|
18550
|
+
return agent;
|
|
18551
|
+
}
|
|
18117
18552
|
run(worktreeId, command) {
|
|
18553
|
+
const op = this.deps.shipping?.(worktreeId);
|
|
18554
|
+
if (op)
|
|
18555
|
+
throw new UserError(`${shipNoun(op.op)} is running here; run the command once it is done`);
|
|
18118
18556
|
fireAndForget(worktreeId, this.exec(worktreeId, command), `exec ${command}`);
|
|
18119
18557
|
}
|
|
18120
18558
|
exec(worktreeId, command, name = SHELL_TOOL, opts = {}) {
|
|
18121
18559
|
const wt = this.deps.state.requireWorktree(worktreeId);
|
|
18122
|
-
if (wt.kind === "spare")
|
|
18123
|
-
throw new UserError("no shell for a spare worktree");
|
|
18124
18560
|
if (wt.kind === "main")
|
|
18125
18561
|
throw new UserError("nothing runs on main: the plus starts a worktree for it");
|
|
18126
|
-
const agent = this.
|
|
18127
|
-
if (!agent)
|
|
18128
|
-
throw new UserError("worktree still starting; try again in a moment");
|
|
18562
|
+
const agent = this.agentFor(worktreeId);
|
|
18129
18563
|
const toolId = `${name}-${Date.now().toString(36)}-${++this.n}`;
|
|
18130
18564
|
const start = { type: "tool-start", toolId, name, input: { command }, kind: "execute" };
|
|
18131
18565
|
if (!opts.quiet)
|
|
@@ -18149,22 +18583,58 @@ class ExecService {
|
|
|
18149
18583
|
return Promise.resolve({ exit: reason, text: "" });
|
|
18150
18584
|
}
|
|
18151
18585
|
const timer = setTimeout(() => this.kill(worktreeId, toolId), TIMEOUT_MS);
|
|
18152
|
-
|
|
18586
|
+
const stop = () => {
|
|
18587
|
+
proc.kill("SIGTERM");
|
|
18588
|
+
setTimeout(() => {
|
|
18589
|
+
if (this.running.get(worktreeId)?.has(toolId))
|
|
18590
|
+
proc.kill("SIGKILL");
|
|
18591
|
+
}, KILL_GRACE_MS2);
|
|
18592
|
+
};
|
|
18593
|
+
this.track(worktreeId, toolId, { stop, timer });
|
|
18153
18594
|
this.deps.runtime.hold(worktreeId, `exec:${toolId}`);
|
|
18154
18595
|
return this.collect(worktreeId, toolId, proc, agent, opts.quiet ? start : undefined);
|
|
18155
18596
|
}
|
|
18156
|
-
|
|
18157
|
-
const
|
|
18158
|
-
if (wt.kind === "spare")
|
|
18159
|
-
throw new UserError("no shell for a spare worktree");
|
|
18160
|
-
const agent = this.deps.runtime.agentFor(worktreeId);
|
|
18161
|
-
if (!agent)
|
|
18162
|
-
throw new UserError("worktree still starting; try again in a moment");
|
|
18597
|
+
async watch(worktreeId, command, run) {
|
|
18598
|
+
const agent = this.agentFor(worktreeId);
|
|
18163
18599
|
const toolId = `${SHELL_TOOL}-${Date.now().toString(36)}-${++this.n}`;
|
|
18164
|
-
|
|
18165
|
-
|
|
18166
|
-
const
|
|
18167
|
-
|
|
18600
|
+
const ctl = new AbortController;
|
|
18601
|
+
this.track(worktreeId, toolId, { stop: () => ctl.abort() });
|
|
18602
|
+
const start = { type: "tool-start", toolId, name: SHELL_TOOL, input: { command }, kind: "execute" };
|
|
18603
|
+
let text = "";
|
|
18604
|
+
let truncated = false;
|
|
18605
|
+
let up = false;
|
|
18606
|
+
const raise = () => {
|
|
18607
|
+
if (up)
|
|
18608
|
+
return;
|
|
18609
|
+
up = true;
|
|
18610
|
+
agent.note(start);
|
|
18611
|
+
if (text)
|
|
18612
|
+
agent.note({ type: "tool-delta", toolId, text });
|
|
18613
|
+
};
|
|
18614
|
+
const timer = setTimeout(raise, this.deps.liveAfterMs ?? LIVE_AFTER_MS);
|
|
18615
|
+
const onText = (chunk) => {
|
|
18616
|
+
if (truncated)
|
|
18617
|
+
return;
|
|
18618
|
+
const take = chunk.slice(0, OUTPUT_CAP - text.length);
|
|
18619
|
+
text += take;
|
|
18620
|
+
if (take.length < chunk.length)
|
|
18621
|
+
truncated = true;
|
|
18622
|
+
if (up && take)
|
|
18623
|
+
agent.note({ type: "tool-delta", toolId, text: take });
|
|
18624
|
+
};
|
|
18625
|
+
let r;
|
|
18626
|
+
try {
|
|
18627
|
+
r = await run(onText, ctl.signal);
|
|
18628
|
+
} finally {
|
|
18629
|
+
clearTimeout(timer);
|
|
18630
|
+
this.untrack(worktreeId, toolId);
|
|
18631
|
+
}
|
|
18632
|
+
if (!up && r.exit === 0)
|
|
18633
|
+
return { ...r, shown: false };
|
|
18634
|
+
if (!up)
|
|
18635
|
+
agent.note(start);
|
|
18636
|
+
agent.note({ type: "tool-end", toolId, output: formatOutput(text, r.exit, truncated), isError: r.exit !== 0 });
|
|
18637
|
+
return { ...r, shown: true };
|
|
18168
18638
|
}
|
|
18169
18639
|
stop(worktreeId) {
|
|
18170
18640
|
for (const toolId of this.running.get(worktreeId)?.keys() ?? [])
|
|
@@ -18187,18 +18657,12 @@ class ExecService {
|
|
|
18187
18657
|
return r;
|
|
18188
18658
|
}
|
|
18189
18659
|
kill(worktreeId, toolId) {
|
|
18190
|
-
|
|
18191
|
-
if (!r)
|
|
18192
|
-
return;
|
|
18193
|
-
r.proc.kill("SIGTERM");
|
|
18194
|
-
setTimeout(() => {
|
|
18195
|
-
if (this.running.get(worktreeId)?.has(toolId))
|
|
18196
|
-
r.proc.kill("SIGKILL");
|
|
18197
|
-
}, KILL_GRACE_MS2);
|
|
18660
|
+
this.running.get(worktreeId)?.get(toolId)?.stop();
|
|
18198
18661
|
}
|
|
18199
18662
|
async collect(worktreeId, toolId, proc, agent, heldStart) {
|
|
18200
18663
|
let text = "";
|
|
18201
18664
|
let truncated = false;
|
|
18665
|
+
const live = !heldStart;
|
|
18202
18666
|
const drain = async (stream) => {
|
|
18203
18667
|
if (!stream || typeof stream === "number")
|
|
18204
18668
|
return;
|
|
@@ -18210,11 +18674,13 @@ class ExecService {
|
|
|
18210
18674
|
return;
|
|
18211
18675
|
if (truncated)
|
|
18212
18676
|
continue;
|
|
18213
|
-
|
|
18214
|
-
|
|
18215
|
-
|
|
18677
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
18678
|
+
const take = chunk.slice(0, OUTPUT_CAP - text.length);
|
|
18679
|
+
text += take;
|
|
18680
|
+
if (take.length < chunk.length)
|
|
18216
18681
|
truncated = true;
|
|
18217
|
-
|
|
18682
|
+
if (live && take)
|
|
18683
|
+
agent.note({ type: "tool-delta", toolId, text: take });
|
|
18218
18684
|
}
|
|
18219
18685
|
};
|
|
18220
18686
|
let exit = null;
|
|
@@ -18259,7 +18725,7 @@ ${body}
|
|
|
18259
18725
|
}
|
|
18260
18726
|
|
|
18261
18727
|
// ../daemon/src/files/service.ts
|
|
18262
|
-
import { spawn as
|
|
18728
|
+
import { spawn as spawn4 } from "child_process";
|
|
18263
18729
|
import { stat, unlink } from "fs/promises";
|
|
18264
18730
|
|
|
18265
18731
|
// ../daemon/src/git/lock.ts
|
|
@@ -18797,7 +19263,7 @@ class FileService {
|
|
|
18797
19263
|
}
|
|
18798
19264
|
async viewableFile(worktreeId, path) {
|
|
18799
19265
|
const r = this.readable(worktreeId);
|
|
18800
|
-
if (!r || viewerOf(path) === null)
|
|
19266
|
+
if (!r || viewerOf(path) === null && !isPageAsset(path))
|
|
18801
19267
|
return null;
|
|
18802
19268
|
try {
|
|
18803
19269
|
const target = resolveInside(r.path, path);
|
|
@@ -18920,7 +19386,7 @@ class FileService {
|
|
|
18920
19386
|
revealPath(target) {
|
|
18921
19387
|
if (process.platform !== "darwin")
|
|
18922
19388
|
throw new UserError("reveal is only available on macOS");
|
|
18923
|
-
const child =
|
|
19389
|
+
const child = spawn4("open", ["-R", target], { stdio: "ignore" });
|
|
18924
19390
|
child.on("error", () => {});
|
|
18925
19391
|
child.unref();
|
|
18926
19392
|
}
|
|
@@ -19030,11 +19496,11 @@ function runSetup(cmd, cwd, onLine, extra = {}) {
|
|
|
19030
19496
|
args: ["-c", cmd],
|
|
19031
19497
|
ring: SETUP_RING
|
|
19032
19498
|
}, (data) => {
|
|
19033
|
-
for (const
|
|
19034
|
-
onLine(line);
|
|
19499
|
+
for (const ev of lines.feed(data))
|
|
19500
|
+
onLine(ev.line, ev.retract);
|
|
19035
19501
|
}, resolve);
|
|
19036
19502
|
} catch (e) {
|
|
19037
|
-
onLine(`could not run: ${e instanceof Error ? e.message : String(e)}
|
|
19503
|
+
onLine(`could not run: ${e instanceof Error ? e.message : String(e)}`, 0);
|
|
19038
19504
|
resolve(1);
|
|
19039
19505
|
}
|
|
19040
19506
|
});
|
|
@@ -19084,9 +19550,10 @@ class AfterLand {
|
|
|
19084
19550
|
for (const command of commands) {
|
|
19085
19551
|
this.tellMain(repo, `afterLand: ${command}`);
|
|
19086
19552
|
let last = "";
|
|
19087
|
-
const code = await runSetup(command, repo.path, (line) => {
|
|
19088
|
-
|
|
19089
|
-
|
|
19553
|
+
const code = await runSetup(command, repo.path, (line, retract) => {
|
|
19554
|
+
if (line)
|
|
19555
|
+
last = line;
|
|
19556
|
+
this.tellMain(repo, line, retract);
|
|
19090
19557
|
});
|
|
19091
19558
|
if (code === 0)
|
|
19092
19559
|
continue;
|
|
@@ -19106,10 +19573,10 @@ class AfterLand {
|
|
|
19106
19573
|
this.d.hub.emit("failed", main.id, notice);
|
|
19107
19574
|
}
|
|
19108
19575
|
}
|
|
19109
|
-
tellMain(repo, line) {
|
|
19576
|
+
tellMain(repo, line, retract) {
|
|
19110
19577
|
const main = this.mainOf(repo);
|
|
19111
19578
|
if (main)
|
|
19112
|
-
this.d.hub.emit("log", main.id, "land", line);
|
|
19579
|
+
this.d.hub.emit("log", main.id, "land", line, retract);
|
|
19113
19580
|
}
|
|
19114
19581
|
mainOf(repo) {
|
|
19115
19582
|
return this.d.state.worktrees.find((w) => w.repoId === repo.id && w.kind === "main");
|
|
@@ -19122,7 +19589,7 @@ import { homedir as homedir7 } from "os";
|
|
|
19122
19589
|
import { basename as basename3, dirname as dirname6, join as join16 } from "path";
|
|
19123
19590
|
|
|
19124
19591
|
// ../daemon/src/repos/browse.ts
|
|
19125
|
-
import { existsSync as existsSync15, statSync as
|
|
19592
|
+
import { existsSync as existsSync15, statSync as statSync4 } from "fs";
|
|
19126
19593
|
import { readdir } from "fs/promises";
|
|
19127
19594
|
import { homedir as homedir5 } from "os";
|
|
19128
19595
|
import { dirname as dirname4, join as join12 } from "path";
|
|
@@ -19151,7 +19618,7 @@ var NOWHERE = { exists: false, isDir: false, isRepo: false, parentExists: false
|
|
|
19151
19618
|
function describe2(typed, parent) {
|
|
19152
19619
|
let isDir = false;
|
|
19153
19620
|
try {
|
|
19154
|
-
isDir =
|
|
19621
|
+
isDir = statSync4(typed).isDirectory();
|
|
19155
19622
|
} catch {}
|
|
19156
19623
|
return {
|
|
19157
19624
|
exists: existsSync15(typed),
|
|
@@ -19189,7 +19656,7 @@ async function browsePath(raw) {
|
|
|
19189
19656
|
}
|
|
19190
19657
|
|
|
19191
19658
|
// ../daemon/src/repos/config.ts
|
|
19192
|
-
import { closeSync, existsSync as existsSync16, openSync as openSync2, readdirSync as readdirSync4, readFileSync as
|
|
19659
|
+
import { closeSync, existsSync as existsSync16, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync12, readSync } from "fs";
|
|
19193
19660
|
import { join as join13 } from "path";
|
|
19194
19661
|
|
|
19195
19662
|
// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
@@ -20579,7 +21046,7 @@ function readConfigFile(repoPath) {
|
|
|
20579
21046
|
for (const rel of files) {
|
|
20580
21047
|
let raw;
|
|
20581
21048
|
try {
|
|
20582
|
-
raw = parseSettings(
|
|
21049
|
+
raw = parseSettings(readFileSync12(join13(repoPath, rel), "utf8"));
|
|
20583
21050
|
} catch (e) {
|
|
20584
21051
|
return { ok: false, reason: `${rel} is not valid JSON: ${e instanceof Error ? e.message : String(e)}` };
|
|
20585
21052
|
}
|
|
@@ -20641,7 +21108,7 @@ function configBody(repoPath, rel, config) {
|
|
|
20641
21108
|
return config;
|
|
20642
21109
|
let shared;
|
|
20643
21110
|
try {
|
|
20644
|
-
shared = parseSettings(
|
|
21111
|
+
shared = parseSettings(readFileSync12(abs, "utf8"));
|
|
20645
21112
|
} catch {
|
|
20646
21113
|
return config;
|
|
20647
21114
|
}
|
|
@@ -20654,7 +21121,7 @@ function configText(repoPath, rel, config) {
|
|
|
20654
21121
|
let text;
|
|
20655
21122
|
let was;
|
|
20656
21123
|
try {
|
|
20657
|
-
text =
|
|
21124
|
+
text = readFileSync12(join13(repoPath, rel), "utf8");
|
|
20658
21125
|
was = parseSettings(text);
|
|
20659
21126
|
} catch {
|
|
20660
21127
|
return fresh;
|
|
@@ -20686,7 +21153,7 @@ function detectConfig(repoPath) {
|
|
|
20686
21153
|
log.warn("config", `${repoPath}: ${explicit.reason}; detecting instead`);
|
|
20687
21154
|
const pkgPath = join13(repoPath, "package.json");
|
|
20688
21155
|
if (existsSync16(pkgPath)) {
|
|
20689
|
-
const pkg = JSON.parse(
|
|
21156
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
20690
21157
|
const scripts = pkg.scripts ?? {};
|
|
20691
21158
|
const runner = detectRunner(repoPath, pkg.packageManager);
|
|
20692
21159
|
const pages = detectPages(repoPath, scripts, runner);
|
|
@@ -20832,7 +21299,7 @@ function detectPages(repoPath, scripts, runner) {
|
|
|
20832
21299
|
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join13(repoPath, f)).find((f) => existsSync16(f));
|
|
20833
21300
|
if (!cfg)
|
|
20834
21301
|
return null;
|
|
20835
|
-
const out =
|
|
21302
|
+
const out = readFileSync12(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
|
|
20836
21303
|
if (!out)
|
|
20837
21304
|
return null;
|
|
20838
21305
|
return { web: `${runner} run build && npx wrangler pages dev ${out} --ip 127.0.0.1 --port $PORT` };
|
|
@@ -20848,7 +21315,7 @@ function detectRunner(repoPath, packageManager) {
|
|
|
20848
21315
|
}
|
|
20849
21316
|
|
|
20850
21317
|
// ../daemon/src/repos/create.ts
|
|
20851
|
-
import { existsSync as existsSync17, readdirSync as readdirSync5, realpathSync as realpathSync2, statSync as
|
|
21318
|
+
import { existsSync as existsSync17, readdirSync as readdirSync5, realpathSync as realpathSync2, statSync as statSync5 } from "fs";
|
|
20852
21319
|
import { appendFile as appendFile2, mkdir as mkdir2, rm } from "fs/promises";
|
|
20853
21320
|
import { homedir as homedir6 } from "os";
|
|
20854
21321
|
import { dirname as dirname5, join as join14, resolve as resolve7 } from "path";
|
|
@@ -20879,7 +21346,7 @@ function planInPlace(opts) {
|
|
|
20879
21346
|
throw new UserError(`${opts.name} is not a folder name`);
|
|
20880
21347
|
}
|
|
20881
21348
|
const typed = join14(resolveParent(opts.parent), name);
|
|
20882
|
-
if (!existsSync17(typed) || !
|
|
21349
|
+
if (!existsSync17(typed) || !statSync5(typed).isDirectory())
|
|
20883
21350
|
throw new UserError(`${typed} is not a folder`);
|
|
20884
21351
|
const dir = realpathSync2(typed);
|
|
20885
21352
|
if (existsSync17(join14(dir, ".git")))
|
|
@@ -20895,7 +21362,7 @@ function resolveParent(raw) {
|
|
|
20895
21362
|
throw new UserError(`${raw} is not a folder path`);
|
|
20896
21363
|
if (!existsSync17(typed))
|
|
20897
21364
|
throw new UserError(`${raw} does not exist; make it first`);
|
|
20898
|
-
if (!
|
|
21365
|
+
if (!statSync5(typed).isDirectory())
|
|
20899
21366
|
throw new UserError(`${raw} is not a folder`);
|
|
20900
21367
|
return realpathSync2(typed);
|
|
20901
21368
|
}
|
|
@@ -22824,7 +23291,7 @@ class RouteService {
|
|
|
22824
23291
|
}
|
|
22825
23292
|
|
|
22826
23293
|
// ../daemon/src/runtime/bridge-script.ts
|
|
22827
|
-
import { existsSync as existsSync20, readFileSync as
|
|
23294
|
+
import { existsSync as existsSync20, readFileSync as readFileSync13, statSync as statSync6 } from "fs";
|
|
22828
23295
|
var LEARNED_MAX = 8;
|
|
22829
23296
|
|
|
22830
23297
|
class BridgeScript {
|
|
@@ -22856,14 +23323,14 @@ class BridgeScript {
|
|
|
22856
23323
|
get() {
|
|
22857
23324
|
if (!existsSync20(this.path))
|
|
22858
23325
|
return "// toyon bridge not built";
|
|
22859
|
-
const s =
|
|
23326
|
+
const s = statSync6(this.path);
|
|
22860
23327
|
const stamp = `${s.mtimeMs}:${s.size}`;
|
|
22861
23328
|
if (this.cache && stamp === this.stamp)
|
|
22862
23329
|
return this.cache;
|
|
22863
23330
|
this.stamp = stamp;
|
|
22864
23331
|
const origins = [...this.origins, ...this.learned];
|
|
22865
23332
|
this.cache = `window.__toyonShellOrigins=${JSON.stringify(origins)};
|
|
22866
|
-
${
|
|
23333
|
+
${readFileSync13(this.path, "utf8")}`;
|
|
22867
23334
|
return this.cache;
|
|
22868
23335
|
}
|
|
22869
23336
|
}
|
|
@@ -23183,7 +23650,7 @@ class PairCodes {
|
|
|
23183
23650
|
}
|
|
23184
23651
|
|
|
23185
23652
|
// ../daemon/src/agent/config.ts
|
|
23186
|
-
import { existsSync as existsSync21, readFileSync as
|
|
23653
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14 } from "fs";
|
|
23187
23654
|
import { homedir as homedir8 } from "os";
|
|
23188
23655
|
import { join as join18 } from "path";
|
|
23189
23656
|
function agentConfigFiles(spec, repoPath, home = homedir8()) {
|
|
@@ -23226,7 +23693,7 @@ function readJson(path) {
|
|
|
23226
23693
|
if (!existsSync21(path))
|
|
23227
23694
|
return null;
|
|
23228
23695
|
try {
|
|
23229
|
-
const v = JSON.parse(
|
|
23696
|
+
const v = JSON.parse(readFileSync14(path, "utf8"));
|
|
23230
23697
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
23231
23698
|
} catch (e) {
|
|
23232
23699
|
log.warn("agent-config", `${path} is not valid JSON`, e);
|
|
@@ -23248,7 +23715,7 @@ function codexServers(path) {
|
|
|
23248
23715
|
return [];
|
|
23249
23716
|
const out = [];
|
|
23250
23717
|
let current = null;
|
|
23251
|
-
for (const raw of
|
|
23718
|
+
for (const raw of readFileSync14(path, "utf8").split(`
|
|
23252
23719
|
`)) {
|
|
23253
23720
|
const line = raw.trim();
|
|
23254
23721
|
const header = /^\[mcp_servers\.("?)([^\]"]+)\1\]$/.exec(line);
|
|
@@ -24259,6 +24726,7 @@ function createFetch(opts) {
|
|
|
24259
24726
|
pid: process.pid,
|
|
24260
24727
|
branded: opts.branded(),
|
|
24261
24728
|
remote: remote && { host: remote.host, previews: remote.previews },
|
|
24729
|
+
managed: opts.managed,
|
|
24262
24730
|
...opts.metrics()
|
|
24263
24731
|
});
|
|
24264
24732
|
return async function fetch2(req, srv) {
|
|
@@ -24371,6 +24839,8 @@ function createFetch(opts) {
|
|
|
24371
24839
|
return new Response("unauthorized", { status: 401 });
|
|
24372
24840
|
const [worktreeId, file, extra] = url.pathname.slice("/attachments/".length).split("/");
|
|
24373
24841
|
const places = worktreeId && file && !extra ? [opts.attachments.fileFor(worktreeId, file), opts.archivedAttachment(worktreeId, file)] : [];
|
|
24842
|
+
if (worktreeId && file)
|
|
24843
|
+
await opts.attachments.whenWritten(worktreeId, file);
|
|
24374
24844
|
const path = places.find((p) => !!p && existsSync22(p));
|
|
24375
24845
|
if (!path)
|
|
24376
24846
|
return new Response("not found", { status: 404 });
|
|
@@ -24523,7 +24993,7 @@ function startServer(opts) {
|
|
|
24523
24993
|
s.hub.on("reposChanged", () => broadcast({ t: "repos", repos: s.state.repos }));
|
|
24524
24994
|
s.hub.on("pendingChanged", () => broadcast({ t: "pending-repos", pending: s.repos.pending }));
|
|
24525
24995
|
s.hub.on("proc", (worktreeId, proc) => broadcast({ t: "proc", worktreeId, proc }));
|
|
24526
|
-
s.hub.on("log", (worktreeId, proc, line) => sendTo(worktreeId, { t: "log", worktreeId, proc, line }));
|
|
24996
|
+
s.hub.on("log", (worktreeId, proc, line, retract) => sendTo(worktreeId, retract ? { t: "log", worktreeId, proc, line, retract } : { t: "log", worktreeId, proc, line }));
|
|
24527
24997
|
s.hub.on("queue", (worktreeId, items) => sendTo(worktreeId, { t: "queue", worktreeId, items }));
|
|
24528
24998
|
s.hub.on("agentCommands", (worktreeId, commands) => sendTo(worktreeId, { t: "agent-commands", worktreeId, commands }));
|
|
24529
24999
|
s.hub.on("termData", (worktreeId, stream, data) => queueTerm(worktreeId, stream, data));
|
|
@@ -24603,7 +25073,7 @@ function startServer(opts) {
|
|
|
24603
25073
|
const agentsMsg = () => ({
|
|
24604
25074
|
t: "agents",
|
|
24605
25075
|
agents: agentInfos(),
|
|
24606
|
-
defaultAgent: s.state.defaultAgent
|
|
25076
|
+
defaultAgent: s.agents.defaultId(s.state.defaultAgent),
|
|
24607
25077
|
agentChosen: s.state.defaultAgent !== undefined
|
|
24608
25078
|
});
|
|
24609
25079
|
s.hub.on("agentsChanged", () => broadcast(agentsMsg()));
|
|
@@ -24626,7 +25096,7 @@ function startServer(opts) {
|
|
|
24626
25096
|
themes: s.themes.themes,
|
|
24627
25097
|
themePrefs: s.themes.prefs,
|
|
24628
25098
|
agents: agentInfos(),
|
|
24629
|
-
defaultAgent: s.state.defaultAgent
|
|
25099
|
+
defaultAgent: s.agents.defaultId(s.state.defaultAgent),
|
|
24630
25100
|
agentChosen: s.state.defaultAgent !== undefined,
|
|
24631
25101
|
home: homedir9(),
|
|
24632
25102
|
folderDialog: process.platform === "darwin" && !cloud.enabled,
|
|
@@ -24637,7 +25107,8 @@ function startServer(opts) {
|
|
|
24637
25107
|
visits: s.routes.historyAll(),
|
|
24638
25108
|
self: s.self.get(),
|
|
24639
25109
|
update: s.update.get(),
|
|
24640
|
-
drafts: s.drafts.all()
|
|
25110
|
+
drafts: s.drafts.all(),
|
|
25111
|
+
managed: managedView(opts.managed)
|
|
24641
25112
|
};
|
|
24642
25113
|
};
|
|
24643
25114
|
let branded = false;
|
|
@@ -24655,6 +25126,7 @@ function startServer(opts) {
|
|
|
24655
25126
|
metrics,
|
|
24656
25127
|
noteShellOrigin: opts.noteShellOrigin,
|
|
24657
25128
|
remote: opts.remote,
|
|
25129
|
+
managed: { source: opts.managed.source, hash: opts.managed.hash },
|
|
24658
25130
|
preview: (id) => s.runtime.get(id)?.proxy?.handler ?? null,
|
|
24659
25131
|
bootstrap: helloFrame,
|
|
24660
25132
|
restart: (now) => s.restarter.request({ now }),
|
|
@@ -24753,7 +25225,7 @@ function startServer(opts) {
|
|
|
24753
25225
|
};
|
|
24754
25226
|
const server = Bun.serve({ ...serverConfig, port: opts.port });
|
|
24755
25227
|
let brandedServer = null;
|
|
24756
|
-
if (opts.port !== 80 && !cloud.enabled) {
|
|
25228
|
+
if (opts.port !== 80 && !cloud.enabled && opts.managed.policy.brandedListener) {
|
|
24757
25229
|
try {
|
|
24758
25230
|
brandedServer = Bun.serve({ ...serverConfig, hostname: "0.0.0.0", port: 80 });
|
|
24759
25231
|
branded = true;
|
|
@@ -24770,7 +25242,7 @@ function startServer(opts) {
|
|
|
24770
25242
|
}
|
|
24771
25243
|
|
|
24772
25244
|
// ../daemon/src/themes/store.ts
|
|
24773
|
-
import { existsSync as existsSync23, readdirSync as readdirSync6, readFileSync as
|
|
25245
|
+
import { existsSync as existsSync23, readdirSync as readdirSync6, readFileSync as readFileSync15, statSync as statSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
24774
25246
|
import { homedir as homedir10 } from "os";
|
|
24775
25247
|
import { basename as basename4, dirname as dirname7, join as join20, resolve as resolve8 } from "path";
|
|
24776
25248
|
var home = homedir10();
|
|
@@ -24870,7 +25342,7 @@ class ThemeStore {
|
|
|
24870
25342
|
}
|
|
24871
25343
|
}
|
|
24872
25344
|
function readJsonc(file) {
|
|
24873
|
-
return parse6(
|
|
25345
|
+
return parse6(readFileSync15(file, "utf8"), [], { allowTrailingComma: true });
|
|
24874
25346
|
}
|
|
24875
25347
|
function readVscodeTheme(file, depth = 0) {
|
|
24876
25348
|
const json = readJsonc(file);
|
|
@@ -24927,9 +25399,9 @@ function discover(dir) {
|
|
|
24927
25399
|
const extDir = join20(dir, name);
|
|
24928
25400
|
const pkgFile = join20(extDir, "package.json");
|
|
24929
25401
|
try {
|
|
24930
|
-
if (!
|
|
25402
|
+
if (!statSync7(extDir).isDirectory() || !existsSync23(pkgFile))
|
|
24931
25403
|
continue;
|
|
24932
|
-
const pkg = JSON.parse(
|
|
25404
|
+
const pkg = JSON.parse(readFileSync15(pkgFile, "utf8"));
|
|
24933
25405
|
const themes = pkg.contributes?.themes;
|
|
24934
25406
|
if (!Array.isArray(themes))
|
|
24935
25407
|
continue;
|
|
@@ -24938,7 +25410,7 @@ function discover(dir) {
|
|
|
24938
25410
|
const nlsFile = join20(extDir, "package.nls.json");
|
|
24939
25411
|
if (existsSync23(nlsFile)) {
|
|
24940
25412
|
try {
|
|
24941
|
-
nls = JSON.parse(
|
|
25413
|
+
nls = JSON.parse(readFileSync15(nlsFile, "utf8"));
|
|
24942
25414
|
} catch {}
|
|
24943
25415
|
}
|
|
24944
25416
|
for (const c of themes) {
|
|
@@ -25052,7 +25524,7 @@ class UpdateService {
|
|
|
25052
25524
|
this.announce();
|
|
25053
25525
|
}
|
|
25054
25526
|
async check() {
|
|
25055
|
-
if (this.d.method === "none" || this.d.
|
|
25527
|
+
if (this.d.method === "none" || this.d.managedBy)
|
|
25056
25528
|
return;
|
|
25057
25529
|
const answer = await this.d.latest();
|
|
25058
25530
|
if (answer.version === null) {
|
|
@@ -25065,8 +25537,7 @@ class UpdateService {
|
|
|
25065
25537
|
await this.auto();
|
|
25066
25538
|
}
|
|
25067
25539
|
async updateNow() {
|
|
25068
|
-
|
|
25069
|
-
throw new UserError("Updates are turned off for this machine (TOYON_UPDATES=off)");
|
|
25540
|
+
this.refuseManaged();
|
|
25070
25541
|
const target = this.target();
|
|
25071
25542
|
if (target === null)
|
|
25072
25543
|
throw new UserError("Toyon is up to date");
|
|
@@ -25078,8 +25549,7 @@ class UpdateService {
|
|
|
25078
25549
|
await this.proceed();
|
|
25079
25550
|
}
|
|
25080
25551
|
async checkNow() {
|
|
25081
|
-
|
|
25082
|
-
throw new UserError("Updates are turned off for this machine (TOYON_UPDATES=off)");
|
|
25552
|
+
this.refuseManaged();
|
|
25083
25553
|
if (this.d.method === "none") {
|
|
25084
25554
|
throw new UserError(`Toyon ${this.d.running} runs from a checkout, which does not update itself`);
|
|
25085
25555
|
}
|
|
@@ -25108,10 +25578,16 @@ class UpdateService {
|
|
|
25108
25578
|
};
|
|
25109
25579
|
}
|
|
25110
25580
|
status() {
|
|
25111
|
-
return {
|
|
25581
|
+
return { managedBy: this.d.managedBy, unreachable: this.unreachable, latest: this.latest };
|
|
25582
|
+
}
|
|
25583
|
+
refuseManaged() {
|
|
25584
|
+
if (this.d.managedBy === "policy")
|
|
25585
|
+
throw new UserError("Updates are turned off by your organization's policy");
|
|
25586
|
+
if (this.d.managedBy === "env")
|
|
25587
|
+
throw new UserError("Updates are turned off for this machine (TOYON_UPDATES=off)");
|
|
25112
25588
|
}
|
|
25113
25589
|
async auto() {
|
|
25114
|
-
if (this.wanted || this.installing || this.d.
|
|
25590
|
+
if (this.wanted || this.installing || this.d.managedBy)
|
|
25115
25591
|
return;
|
|
25116
25592
|
const target = this.target();
|
|
25117
25593
|
if (target === null)
|
|
@@ -25313,6 +25789,7 @@ class LandingService {
|
|
|
25313
25789
|
d;
|
|
25314
25790
|
judging = new Map;
|
|
25315
25791
|
busy = new Set;
|
|
25792
|
+
stopping = false;
|
|
25316
25793
|
constructor(d) {
|
|
25317
25794
|
this.d = d;
|
|
25318
25795
|
d.hub.on("turnSettled", (id, turn) => fireAndForget(id, this.settle(id, turn), "landing verdict"));
|
|
@@ -25332,6 +25809,16 @@ class LandingService {
|
|
|
25332
25809
|
this.judging.delete(worktreeId);
|
|
25333
25810
|
this.d.worktrees.setLanding(worktreeId, undefined);
|
|
25334
25811
|
}
|
|
25812
|
+
boot() {
|
|
25813
|
+
for (const wt of this.d.state.worktrees) {
|
|
25814
|
+
if (wt.landing?.check !== "pending" || !canLand(wt))
|
|
25815
|
+
continue;
|
|
25816
|
+
fireAndForget(wt.id, this.run(wt, { at: wt.landing.at, ask: true, quiet: false }), "landing verdict resumed");
|
|
25817
|
+
}
|
|
25818
|
+
}
|
|
25819
|
+
stop() {
|
|
25820
|
+
this.stopping = true;
|
|
25821
|
+
}
|
|
25335
25822
|
async settle(worktreeId, turn) {
|
|
25336
25823
|
const wt = this.d.state.worktree(worktreeId);
|
|
25337
25824
|
if (!wt || !canLand(wt))
|
|
@@ -25409,7 +25896,7 @@ class LandingService {
|
|
|
25409
25896
|
this.judging.set(worktreeId, opts.at);
|
|
25410
25897
|
const started = Date.now();
|
|
25411
25898
|
this.d.worktrees.setLanding(worktreeId, { at: opts.at, check: "pending", ready: false, fingerprint: "", ...kept });
|
|
25412
|
-
const live = () => this.judging.get(worktreeId) === opts.at && !!this.d.state.worktree(worktreeId);
|
|
25899
|
+
const live = () => !this.stopping && this.judging.get(worktreeId) === opts.at && !!this.d.state.worktree(worktreeId);
|
|
25413
25900
|
let check = "none";
|
|
25414
25901
|
let checkTail;
|
|
25415
25902
|
const command = repo.config.check?.trim();
|
|
@@ -25434,7 +25921,10 @@ class LandingService {
|
|
|
25434
25921
|
try {
|
|
25435
25922
|
verdict = await this.d.judge(wt, prompt);
|
|
25436
25923
|
} catch (e) {
|
|
25437
|
-
|
|
25924
|
+
if (!live())
|
|
25925
|
+
return;
|
|
25926
|
+
log.warn(worktreeId, "landing verdict unanswered; no verdict written", e);
|
|
25927
|
+
return this.clear(worktreeId);
|
|
25438
25928
|
}
|
|
25439
25929
|
if (!live())
|
|
25440
25930
|
return;
|
|
@@ -25597,7 +26087,7 @@ async function mergedBranches(repoPath, defaultBr) {
|
|
|
25597
26087
|
}
|
|
25598
26088
|
|
|
25599
26089
|
// ../daemon/src/worktrees/discover.ts
|
|
25600
|
-
import { createHash as
|
|
26090
|
+
import { createHash as createHash3 } from "crypto";
|
|
25601
26091
|
import { basename as basename5 } from "path";
|
|
25602
26092
|
|
|
25603
26093
|
// ../daemon/src/git/worktrees.ts
|
|
@@ -25666,7 +26156,7 @@ async function listWorktrees(repoPath) {
|
|
|
25666
26156
|
|
|
25667
26157
|
// ../daemon/src/worktrees/discover.ts
|
|
25668
26158
|
function discoveredId(path) {
|
|
25669
|
-
return `disc-${
|
|
26159
|
+
return `disc-${createHash3("sha1").update(canonical(path)).digest("hex").slice(0, 12)}`;
|
|
25670
26160
|
}
|
|
25671
26161
|
function knownPaths(known) {
|
|
25672
26162
|
const paths = new Set;
|
|
@@ -25849,12 +26339,15 @@ function rankRefs({ refs, merged, prs, query, defaultBranch, rowIdForPath }) {
|
|
|
25849
26339
|
}
|
|
25850
26340
|
|
|
25851
26341
|
// ../daemon/src/worktrees/service.ts
|
|
25852
|
-
import { cpSync, existsSync as existsSync26, readFileSync as
|
|
26342
|
+
import { cpSync, existsSync as existsSync26, readFileSync as readFileSync17, rmSync as rmSync5 } from "fs";
|
|
26343
|
+
import { rm as rm2 } from "fs/promises";
|
|
25853
26344
|
import { dirname as dirname9, join as join23 } from "path";
|
|
25854
26345
|
|
|
25855
26346
|
// ../daemon/src/git/archive.ts
|
|
25856
26347
|
import { rmSync as rmSync3 } from "fs";
|
|
25857
26348
|
var archiveRef = (worktreeId) => `refs/toyon/archive/${worktreeId}`;
|
|
26349
|
+
var SNAPSHOT_SUBJECT = "toyon: uncommitted work";
|
|
26350
|
+
var SNAPSHOT_AUTHOR = "toyon";
|
|
25858
26351
|
async function keepState(repoPath, wtPath, ref, indexFile) {
|
|
25859
26352
|
const head = await git(wtPath, "rev-parse", "--verify", "HEAD");
|
|
25860
26353
|
if (!head.ok)
|
|
@@ -25882,7 +26375,7 @@ async function snapshotCommit(wtPath, head, indexFile) {
|
|
|
25882
26375
|
return null;
|
|
25883
26376
|
const commit = await run(GIT, [
|
|
25884
26377
|
"-c",
|
|
25885
|
-
|
|
26378
|
+
`user.name=${SNAPSHOT_AUTHOR}`,
|
|
25886
26379
|
"-c",
|
|
25887
26380
|
"user.email=toyon@localhost",
|
|
25888
26381
|
"commit-tree",
|
|
@@ -25890,13 +26383,29 @@ async function snapshotCommit(wtPath, head, indexFile) {
|
|
|
25890
26383
|
"-p",
|
|
25891
26384
|
head,
|
|
25892
26385
|
"-m",
|
|
25893
|
-
|
|
26386
|
+
SNAPSHOT_SUBJECT
|
|
25894
26387
|
], wtPath);
|
|
25895
26388
|
return commit.ok ? commit.out : null;
|
|
25896
26389
|
} finally {
|
|
25897
26390
|
rmSync3(indexFile, { force: true });
|
|
25898
26391
|
}
|
|
25899
26392
|
}
|
|
26393
|
+
async function keptAt(repoPath, ref) {
|
|
26394
|
+
const r = await git(repoPath, "log", "-1", "--format=%H%n%P%n%an%n%s", ref);
|
|
26395
|
+
if (!r.ok)
|
|
26396
|
+
return null;
|
|
26397
|
+
const [commit, parents, author, subject] = r.out.split(`
|
|
26398
|
+
`);
|
|
26399
|
+
if (!commit)
|
|
26400
|
+
return null;
|
|
26401
|
+
const snapshot = subject === SNAPSHOT_SUBJECT && author === SNAPSHOT_AUTHOR && parents && !parents.includes(" ");
|
|
26402
|
+
if (!snapshot)
|
|
26403
|
+
return { head: commit };
|
|
26404
|
+
const files = await git(repoPath, "diff", "--name-only", parents, commit);
|
|
26405
|
+
const dirty = files.ok ? files.out.split(`
|
|
26406
|
+
`).filter(Boolean).length : 0;
|
|
26407
|
+
return { head: parents, snapshot: commit, ...dirty ? { dirty } : {} };
|
|
26408
|
+
}
|
|
25900
26409
|
async function checkOutKept(repoPath, wtPath, kept, branch) {
|
|
25901
26410
|
await git(repoPath, "worktree", "prune");
|
|
25902
26411
|
if (branch.create)
|
|
@@ -25956,16 +26465,27 @@ async function dropLandRefs(repoPath, worktreeId) {
|
|
|
25956
26465
|
}
|
|
25957
26466
|
|
|
25958
26467
|
// ../daemon/src/git/land.ts
|
|
25959
|
-
|
|
26468
|
+
var STEP_TIMEOUT_MS = 10 * 60000;
|
|
26469
|
+
var stepRun = (cwd, args, onText, signal) => runWatched(GIT, args, cwd, { env: NO_PROMPT, onText, timeoutMs: STEP_TIMEOUT_MS, signal });
|
|
26470
|
+
var UNWATCHED = {
|
|
26471
|
+
step() {},
|
|
26472
|
+
git: (cwd, args) => stepRun(cwd, args)
|
|
26473
|
+
};
|
|
26474
|
+
function refused(what, r, n = 200) {
|
|
26475
|
+
if (r.exit === "timeout") {
|
|
26476
|
+
const minutes = Math.round(STEP_TIMEOUT_MS / 60000);
|
|
26477
|
+
return `${what} gave up after ${minutes} minutes${r.shown ? "; what it printed is on the chat" : ""}`;
|
|
26478
|
+
}
|
|
26479
|
+
return `${what}: ${r.shown ? "what git and its hooks printed is on the chat" : r.err.slice(0, n)}`;
|
|
26480
|
+
}
|
|
26481
|
+
async function commitWorktree(worktreePath, message, w = UNWATCHED) {
|
|
25960
26482
|
if ((await statusFiles(worktreePath)).length === 0)
|
|
25961
26483
|
return { ok: false, message: "nothing to commit" };
|
|
25962
26484
|
await git(worktreePath, "add", "-A");
|
|
25963
|
-
|
|
25964
|
-
|
|
25965
|
-
|
|
25966
|
-
|
|
25967
|
-
return { ok: false, message: `commit failed: ${c.err.slice(0, 200)}`, output, exit: c.exit };
|
|
25968
|
-
}
|
|
26485
|
+
w.step("committing");
|
|
26486
|
+
const c = await w.git(worktreePath, ["commit", "-m", message]);
|
|
26487
|
+
if (!c.ok)
|
|
26488
|
+
return { ok: false, message: refused("commit refused", c) };
|
|
25969
26489
|
return { ok: true, message: `committed: ${message}` };
|
|
25970
26490
|
}
|
|
25971
26491
|
async function requireClean(worktreePath) {
|
|
@@ -25984,7 +26504,7 @@ async function requireMainReady(repoPath, defaultBr) {
|
|
|
25984
26504
|
}
|
|
25985
26505
|
return null;
|
|
25986
26506
|
}
|
|
25987
|
-
async function takeMainIn(worktreePath, defaultBr, own) {
|
|
26507
|
+
async function takeMainIn(worktreePath, defaultBr, own, w = UNWATCHED) {
|
|
25988
26508
|
const cErr = await requireClean(worktreePath);
|
|
25989
26509
|
if (cErr)
|
|
25990
26510
|
return cErr;
|
|
@@ -25992,26 +26512,27 @@ async function takeMainIn(worktreePath, defaultBr, own) {
|
|
|
25992
26512
|
if (behind === 0)
|
|
25993
26513
|
return { ok: true, message: `already up to date with ${defaultBr}` };
|
|
25994
26514
|
if (own) {
|
|
25995
|
-
|
|
26515
|
+
w.step(`rebasing onto ${defaultBr}`);
|
|
26516
|
+
const r = await w.git(worktreePath, ["rebase", defaultBr]);
|
|
25996
26517
|
if (!r.ok) {
|
|
25997
26518
|
await git(worktreePath, "rebase", "--abort");
|
|
25998
|
-
const
|
|
25999
|
-
`);
|
|
26519
|
+
const ask = `rebase onto ${defaultBr} conflicts: ask the agent to bring ${defaultBr} in and resolve them`;
|
|
26000
26520
|
return {
|
|
26001
26521
|
ok: false,
|
|
26002
26522
|
conflict: true,
|
|
26003
|
-
message:
|
|
26523
|
+
message: r.shown ? `${ask}; the conflicts are on the chat` : `${ask} (${r.text.slice(0, 200)})`
|
|
26004
26524
|
};
|
|
26005
26525
|
}
|
|
26006
26526
|
return { ok: true, message: `rebased onto ${defaultBr}, ${behind} commit(s) behind before` };
|
|
26007
26527
|
}
|
|
26008
|
-
|
|
26528
|
+
w.step(`merging ${defaultBr} in`);
|
|
26529
|
+
const m = await w.git(worktreePath, ["merge", "--no-edit", defaultBr]);
|
|
26009
26530
|
if (!m.ok) {
|
|
26010
26531
|
return mergeFailure(worktreePath, m, `sync conflicts with ${defaultBr}: ask the agent to merge ${defaultBr} and resolve them`);
|
|
26011
26532
|
}
|
|
26012
26533
|
return { ok: true, message: `synced ${behind} commit(s) from ${defaultBr}` };
|
|
26013
26534
|
}
|
|
26014
|
-
async function landLocally(worktreePath, branch, repoPath, defaultBr, method, message) {
|
|
26535
|
+
async function landLocally(worktreePath, branch, repoPath, defaultBr, method, message, w = UNWATCHED) {
|
|
26015
26536
|
const cErr = await requireClean(worktreePath);
|
|
26016
26537
|
if (cErr)
|
|
26017
26538
|
return cErr;
|
|
@@ -26022,25 +26543,28 @@ async function landLocally(worktreePath, branch, repoPath, defaultBr, method, me
|
|
|
26022
26543
|
if (mErr)
|
|
26023
26544
|
return mErr;
|
|
26024
26545
|
if (method === "rebase") {
|
|
26025
|
-
|
|
26546
|
+
w.step(`moving ${defaultBr} onto the branch`);
|
|
26547
|
+
const ff = await w.git(repoPath, ["merge", "--ff-only", branch]);
|
|
26026
26548
|
if (!ff.ok)
|
|
26027
26549
|
return { ok: false, message: `${branch} is not a fast-forward of ${defaultBr}: take ${defaultBr} in first` };
|
|
26028
26550
|
return { ok: true, message: `${defaultBr} moved onto ${branch}` };
|
|
26029
26551
|
}
|
|
26030
26552
|
if (method === "squash") {
|
|
26031
|
-
|
|
26553
|
+
w.step(`squashing onto ${defaultBr}`);
|
|
26554
|
+
const s = await w.git(repoPath, ["merge", "--squash", branch]);
|
|
26032
26555
|
if (!s.ok) {
|
|
26033
26556
|
await git(repoPath, "reset", "--hard", "HEAD");
|
|
26034
26557
|
return mergeFailure(repoPath, s, `merge conflicts with ${defaultBr}: take ${defaultBr} in first`);
|
|
26035
26558
|
}
|
|
26036
|
-
const c = await git(repoPath, "commit", "-m", message);
|
|
26559
|
+
const c = await w.git(repoPath, ["commit", "-m", message]);
|
|
26037
26560
|
if (!c.ok) {
|
|
26038
26561
|
await git(repoPath, "reset", "--hard", "HEAD");
|
|
26039
|
-
return { ok: false, message:
|
|
26562
|
+
return { ok: false, message: refused("squash commit refused", c) };
|
|
26040
26563
|
}
|
|
26041
26564
|
return { ok: true, message: `squashed ${branch} onto ${defaultBr}` };
|
|
26042
26565
|
}
|
|
26043
|
-
|
|
26566
|
+
w.step(`merging into ${defaultBr}`);
|
|
26567
|
+
const m = await w.git(repoPath, ["merge", "--no-ff", "--no-edit", branch]);
|
|
26044
26568
|
if (!m.ok)
|
|
26045
26569
|
return mergeFailure(repoPath, m, `merge conflicts with ${defaultBr}: take ${defaultBr} in first`);
|
|
26046
26570
|
return { ok: true, message: `merged ${branch} into ${defaultBr}` };
|
|
@@ -26057,20 +26581,21 @@ async function squashMessage(worktreePath, defaultBr, suggested) {
|
|
|
26057
26581
|
const subjects = await git(worktreePath, "log", "--format=%s", `${defaultBr}..HEAD`);
|
|
26058
26582
|
return subjects.out.trim() || "land";
|
|
26059
26583
|
}
|
|
26060
|
-
async function pullMain(repoPath, defaultBr) {
|
|
26584
|
+
async function pullMain(repoPath, defaultBr, w = UNWATCHED) {
|
|
26061
26585
|
const cErr = await requireClean(repoPath);
|
|
26062
26586
|
if (cErr)
|
|
26063
26587
|
return cErr;
|
|
26064
|
-
return fastForwardMain(repoPath, defaultBr);
|
|
26588
|
+
return fastForwardMain(repoPath, defaultBr, w);
|
|
26065
26589
|
}
|
|
26066
|
-
async function fastForwardMain(repoPath, defaultBr) {
|
|
26590
|
+
async function fastForwardMain(repoPath, defaultBr, w = UNWATCHED) {
|
|
26067
26591
|
const current = await git(repoPath, "branch", "--show-current");
|
|
26068
26592
|
if (current.out !== defaultBr) {
|
|
26069
26593
|
return { ok: false, message: `main checkout is on '${current.out}', not ${defaultBr}; switch it first` };
|
|
26070
26594
|
}
|
|
26071
|
-
|
|
26595
|
+
w.step(`pulling ${defaultBr} from origin`);
|
|
26596
|
+
const f = await w.git(repoPath, ["fetch", "--quiet"]);
|
|
26072
26597
|
if (!f.ok)
|
|
26073
|
-
return { ok: false, message:
|
|
26598
|
+
return { ok: false, message: refused("fetch failed", f) };
|
|
26074
26599
|
return fastForwardFetched(repoPath, defaultBr);
|
|
26075
26600
|
}
|
|
26076
26601
|
async function fastForwardFetched(repoPath, defaultBr) {
|
|
@@ -26103,45 +26628,77 @@ ${m.err}`);
|
|
|
26103
26628
|
}
|
|
26104
26629
|
return { ok: true, moved: true, message: `pulled ${behind} commit(s) from origin` };
|
|
26105
26630
|
}
|
|
26106
|
-
async function pushMain(repoPath, defaultBr) {
|
|
26107
|
-
|
|
26631
|
+
async function pushMain(repoPath, defaultBr, w = UNWATCHED) {
|
|
26632
|
+
w.step(`pushing ${defaultBr}`);
|
|
26633
|
+
const p = await w.git(repoPath, ["push", "origin", defaultBr]);
|
|
26108
26634
|
if (p.ok)
|
|
26109
26635
|
return { ok: true, message: `pushed ${defaultBr} to origin` };
|
|
26110
|
-
const moved =
|
|
26636
|
+
const moved = /\[rejected\]|fetch first|non-fast-forward/.test(p.err);
|
|
26111
26637
|
return {
|
|
26112
26638
|
ok: false,
|
|
26113
|
-
message: moved ? `${defaultBr} on origin moved; pull ${defaultBr} and land again` :
|
|
26639
|
+
message: moved ? `${defaultBr} on origin moved; pull ${defaultBr} and land again` : refused("push failed", p, 300)
|
|
26114
26640
|
};
|
|
26115
26641
|
}
|
|
26116
26642
|
async function mergeFailure(cwd, m, conflictMessage) {
|
|
26117
|
-
const
|
|
26118
|
-
`);
|
|
26119
|
-
const conflict = /CONFLICT|Automatic merge failed/.test(said);
|
|
26643
|
+
const conflict = /CONFLICT|Automatic merge failed/.test(m.text);
|
|
26120
26644
|
if (conflict) {
|
|
26121
26645
|
await git(cwd, "merge", "--abort");
|
|
26122
|
-
return {
|
|
26646
|
+
return {
|
|
26647
|
+
ok: false,
|
|
26648
|
+
conflict: true,
|
|
26649
|
+
message: m.shown ? `${conflictMessage}; the conflicts are on the chat` : `${conflictMessage} (${m.text.slice(0, 200)})`
|
|
26650
|
+
};
|
|
26651
|
+
}
|
|
26652
|
+
return {
|
|
26653
|
+
ok: false,
|
|
26654
|
+
message: m.shown ? "merge failed: what git printed is on the chat" : `merge failed: ${m.text.slice(0, 300)}`
|
|
26655
|
+
};
|
|
26656
|
+
}
|
|
26657
|
+
async function fetchTrunk(worktreePath, defaultBr, w = UNWATCHED) {
|
|
26658
|
+
const [remote, up] = await Promise.all([
|
|
26659
|
+
git(worktreePath, "config", "--get", `branch.${defaultBr}.remote`),
|
|
26660
|
+
git(worktreePath, "rev-parse", "--abbrev-ref", `${defaultBr}@{upstream}`)
|
|
26661
|
+
]);
|
|
26662
|
+
if (!remote.ok || !remote.out || !up.ok || !up.out) {
|
|
26663
|
+
return { ok: true, base: defaultBr, message: `${defaultBr} has no upstream` };
|
|
26123
26664
|
}
|
|
26124
|
-
|
|
26665
|
+
w.step(`fetching ${up.out}`);
|
|
26666
|
+
const f = await w.git(worktreePath, ["fetch", "--quiet", remote.out, defaultBr]);
|
|
26667
|
+
if (!f.ok)
|
|
26668
|
+
return { ok: false, base: up.out, message: refused("fetch failed", f) };
|
|
26669
|
+
return { ok: true, base: up.out, message: `fetched ${up.out}` };
|
|
26125
26670
|
}
|
|
26126
|
-
async function pushBranch(worktreePath, branch) {
|
|
26127
|
-
const
|
|
26671
|
+
async function pushBranch(worktreePath, branch, w = UNWATCHED) {
|
|
26672
|
+
const args = ["push", "-u", "--force-with-lease", "origin", branch];
|
|
26673
|
+
w.step(`pushing ${branch}`);
|
|
26674
|
+
let push = await w.git(worktreePath, args);
|
|
26675
|
+
if (!push.ok && /stale info/.test(push.err) && await goneFromOrigin(worktreePath, branch)) {
|
|
26676
|
+
await git(worktreePath, "update-ref", "-d", `refs/remotes/origin/${branch}`);
|
|
26677
|
+
push = await w.git(worktreePath, args);
|
|
26678
|
+
}
|
|
26128
26679
|
if (!push.ok)
|
|
26129
|
-
return { ok: false, message:
|
|
26680
|
+
return { ok: false, message: refused("push failed", push, 300) };
|
|
26130
26681
|
return { ok: true, message: `pushed ${branch}` };
|
|
26131
26682
|
}
|
|
26132
|
-
async function
|
|
26683
|
+
async function goneFromOrigin(worktreePath, branch) {
|
|
26684
|
+
const r = await run(GIT, ["ls-remote", "--exit-code", "--heads", "origin", branch], worktreePath, NO_PROMPT);
|
|
26685
|
+
return !r.ok && r.exit === 2;
|
|
26686
|
+
}
|
|
26687
|
+
async function openPr(o, w = UNWATCHED) {
|
|
26133
26688
|
const cErr = await requireClean(o.worktreePath);
|
|
26134
26689
|
if (cErr)
|
|
26135
26690
|
return cErr;
|
|
26136
|
-
const
|
|
26691
|
+
const base = o.base ?? o.defaultBr;
|
|
26692
|
+
const { ahead } = await aheadBehind(o.worktreePath, base);
|
|
26137
26693
|
if (ahead === 0)
|
|
26138
|
-
return { ok: false, message: `nothing to ship: no commits ahead of ${
|
|
26694
|
+
return { ok: false, message: `nothing to ship: no commits ahead of ${base}` };
|
|
26139
26695
|
const remote = await git(o.worktreePath, "remote", "get-url", "origin");
|
|
26140
26696
|
if (!remote.ok)
|
|
26141
26697
|
return { ok: false, message: "no 'origin' remote: add one, or land by merging here" };
|
|
26142
|
-
const push = await pushBranch(o.worktreePath, o.branch);
|
|
26698
|
+
const push = await pushBranch(o.worktreePath, o.branch, w);
|
|
26143
26699
|
if (!push.ok)
|
|
26144
26700
|
return push;
|
|
26701
|
+
w.step("opening the PR");
|
|
26145
26702
|
const titled = o.subject ? ["--title", o.subject, "--body", o.body ?? ""] : ["--fill"];
|
|
26146
26703
|
const created = await gh(["pr", "create", "--head", o.branch, "--base", o.defaultBr, ...titled], o.worktreePath);
|
|
26147
26704
|
let url = null;
|
|
@@ -26174,7 +26731,8 @@ ${created.exit}`)) {
|
|
|
26174
26731
|
pr: { number, url, state: "open", ...o.automerge ? { automerge: true } : {} }
|
|
26175
26732
|
};
|
|
26176
26733
|
}
|
|
26177
|
-
async function mergePr(worktreePath, number, wanted) {
|
|
26734
|
+
async function mergePr(worktreePath, number, wanted, w = UNWATCHED) {
|
|
26735
|
+
w.step("merging the PR");
|
|
26178
26736
|
const method = await ghMethod(worktreePath, wanted);
|
|
26179
26737
|
if (!method)
|
|
26180
26738
|
return { ok: false, message: "the repo allows no merge method gh can use" };
|
|
@@ -26191,7 +26749,7 @@ function compareUrl(remoteUrl, base, branch) {
|
|
|
26191
26749
|
}
|
|
26192
26750
|
|
|
26193
26751
|
// ../daemon/src/worktrees/archive.ts
|
|
26194
|
-
import { existsSync as existsSync24, mkdirSync as mkdirSync7, readdirSync as readdirSync7, readFileSync as
|
|
26752
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync7, readdirSync as readdirSync7, readFileSync as readFileSync16, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
26195
26753
|
import { basename as basename6, dirname as dirname8, join as join21 } from "path";
|
|
26196
26754
|
class WorktreeArchive {
|
|
26197
26755
|
dir;
|
|
@@ -26204,7 +26762,7 @@ class WorktreeArchive {
|
|
|
26204
26762
|
if (!existsSync24(file))
|
|
26205
26763
|
continue;
|
|
26206
26764
|
try {
|
|
26207
|
-
this.records.set(id, JSON.parse(
|
|
26765
|
+
this.records.set(id, JSON.parse(readFileSync16(file, "utf8")));
|
|
26208
26766
|
} catch (e) {
|
|
26209
26767
|
log.warn("archive", `skipped an unreadable record: ${file}`, e);
|
|
26210
26768
|
}
|
|
@@ -26254,7 +26812,7 @@ class WorktreeArchive {
|
|
|
26254
26812
|
if (!existsSync24(file))
|
|
26255
26813
|
return null;
|
|
26256
26814
|
try {
|
|
26257
|
-
return
|
|
26815
|
+
return readFileSync16(file, "utf8");
|
|
26258
26816
|
} catch (e) {
|
|
26259
26817
|
log.warn(id, `could not read its archived plan ${rel}`, e);
|
|
26260
26818
|
return null;
|
|
@@ -26293,7 +26851,7 @@ function summarize(r, repoId, transcript) {
|
|
|
26293
26851
|
function lastUsage(transcript) {
|
|
26294
26852
|
if (!existsSync24(transcript))
|
|
26295
26853
|
return null;
|
|
26296
|
-
const lines =
|
|
26854
|
+
const lines = readFileSync16(transcript, "utf8").split(`
|
|
26297
26855
|
`);
|
|
26298
26856
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
26299
26857
|
if (!lines[i]?.includes('"usage"'))
|
|
@@ -26309,7 +26867,7 @@ function lastUsage(transcript) {
|
|
|
26309
26867
|
function firstPrompt(transcript) {
|
|
26310
26868
|
if (!existsSync24(transcript))
|
|
26311
26869
|
return;
|
|
26312
|
-
for (const line of
|
|
26870
|
+
for (const line of readFileSync16(transcript, "utf8").split(`
|
|
26313
26871
|
`)) {
|
|
26314
26872
|
if (!line.includes('"user-message"'))
|
|
26315
26873
|
continue;
|
|
@@ -26541,8 +27099,10 @@ class SparePool {
|
|
|
26541
27099
|
this.d.state.save();
|
|
26542
27100
|
for (const cmd of repo.config.setup ?? []) {
|
|
26543
27101
|
const tail = [];
|
|
26544
|
-
const code = await runSetup(cmd, wt.path, (line) => {
|
|
26545
|
-
tail.
|
|
27102
|
+
const code = await runSetup(cmd, wt.path, (line, retract) => {
|
|
27103
|
+
tail.splice(Math.max(0, tail.length - retract), retract);
|
|
27104
|
+
if (line)
|
|
27105
|
+
tail.push(line);
|
|
26546
27106
|
if (tail.length > 20)
|
|
26547
27107
|
tail.shift();
|
|
26548
27108
|
}, worktreeEnv(wt, repo));
|
|
@@ -26770,6 +27330,15 @@ function stopOf(prev, next) {
|
|
|
26770
27330
|
// ../daemon/src/worktrees/service.ts
|
|
26771
27331
|
var LOCAL_CONFIG_FILES = [".env", ".env.local", ".env.development", ".env.development.local", ".dev.vars"];
|
|
26772
27332
|
var BASE_IGNORE = "base .gitignore";
|
|
27333
|
+
async function deleteTree(path) {
|
|
27334
|
+
try {
|
|
27335
|
+
await rm2(path, { recursive: true, force: true });
|
|
27336
|
+
return true;
|
|
27337
|
+
} catch (e) {
|
|
27338
|
+
log.warn("rm", `could not delete ${path}`, e);
|
|
27339
|
+
return false;
|
|
27340
|
+
}
|
|
27341
|
+
}
|
|
26773
27342
|
function withoutAttachments(event) {
|
|
26774
27343
|
if (event.type !== "user-message")
|
|
26775
27344
|
return event;
|
|
@@ -26783,16 +27352,20 @@ function withCarry(context, c) {
|
|
|
26783
27352
|
const line = c.moved ? `This worktree started with ${files} the person moved here from ${c.branch} by hand. They are part of the task, not something to clean up.` : `${c.branch} has ${files} the person chose to leave there. This worktree does not have them.`;
|
|
26784
27353
|
return [...context ?? [], line];
|
|
26785
27354
|
}
|
|
27355
|
+
var commandLine = (args) => args.map((a) => /^[\w./:=@,+-]+$/.test(a) ? a : JSON.stringify(a.split(`
|
|
27356
|
+
`, 1)[0])).join(" ");
|
|
26786
27357
|
|
|
26787
27358
|
class WorktreeService {
|
|
26788
27359
|
d;
|
|
26789
27360
|
spare;
|
|
27361
|
+
ops = new Map;
|
|
26790
27362
|
trunk;
|
|
26791
27363
|
archive;
|
|
26792
27364
|
archiving = new Map;
|
|
26793
27365
|
going = new Set;
|
|
26794
27366
|
countsCache = new Map;
|
|
26795
27367
|
handChecked = new Map;
|
|
27368
|
+
naming = new Set;
|
|
26796
27369
|
discoverCache = new Map;
|
|
26797
27370
|
usage = new Map;
|
|
26798
27371
|
constructor(d) {
|
|
@@ -26842,6 +27415,12 @@ class WorktreeService {
|
|
|
26842
27415
|
});
|
|
26843
27416
|
d.hub.on("turnSettled", (worktreeId, turn) => {
|
|
26844
27417
|
this.countsCache.delete(worktreeId);
|
|
27418
|
+
if (turn.end === "done") {
|
|
27419
|
+
const wt = d.state.worktree(worktreeId);
|
|
27420
|
+
const prompt = wt?.unnamed ? firstAskOf(d.runtime.agentFor(worktreeId)?.transcript() ?? []) : undefined;
|
|
27421
|
+
if (wt && prompt)
|
|
27422
|
+
this.scheduleNaming(wt, prompt, wt.variant);
|
|
27423
|
+
}
|
|
26845
27424
|
if (turn.end !== "done" && turn.end !== "stopped")
|
|
26846
27425
|
return;
|
|
26847
27426
|
for (const p of d.runtime.get(worktreeId)?.procs?.states() ?? []) {
|
|
@@ -27139,16 +27718,13 @@ class WorktreeService {
|
|
|
27139
27718
|
scheduleNaming(wt, prompt, variant) {
|
|
27140
27719
|
if (!prompt.trim())
|
|
27141
27720
|
return;
|
|
27142
|
-
if (
|
|
27143
|
-
fireAndForget(wt.id, (this.d.namer ?? makeNamer(this.d.runtime))(prompt, wt).then((name) => {
|
|
27144
|
-
if (name)
|
|
27145
|
-
return this.rename(wt.id, name);
|
|
27146
|
-
}), "auto-naming");
|
|
27721
|
+
if (variant && variant.index !== 1)
|
|
27147
27722
|
return;
|
|
27148
|
-
|
|
27149
|
-
if (variant.index !== 1)
|
|
27723
|
+
if (this.naming.has(wt.id))
|
|
27150
27724
|
return;
|
|
27151
|
-
|
|
27725
|
+
this.naming.add(wt.id);
|
|
27726
|
+
const asked = (this.d.namer ?? makeNamer(this.d.runtime))(prompt, wt);
|
|
27727
|
+
const applied = variant ? asked.then(async (name) => {
|
|
27152
27728
|
if (!name)
|
|
27153
27729
|
return;
|
|
27154
27730
|
for (const sibling of [wt, ...siblingsOf(wt, this.d.state.worktrees)]) {
|
|
@@ -27156,7 +27732,11 @@ class WorktreeService {
|
|
|
27156
27732
|
log.warn(sibling.id, "variant rename failed", e);
|
|
27157
27733
|
});
|
|
27158
27734
|
}
|
|
27159
|
-
})
|
|
27735
|
+
}) : asked.then((name) => {
|
|
27736
|
+
if (name)
|
|
27737
|
+
return this.rename(wt.id, name);
|
|
27738
|
+
});
|
|
27739
|
+
fireAndForget(wt.id, applied.finally(() => this.naming.delete(wt.id)), "auto-naming");
|
|
27160
27740
|
}
|
|
27161
27741
|
archiveWorktree(worktreeId, opts = {}) {
|
|
27162
27742
|
const running = this.archiving.get(worktreeId);
|
|
@@ -27198,7 +27778,7 @@ class WorktreeService {
|
|
|
27198
27778
|
if (archive)
|
|
27199
27779
|
this.keepPlans(wt);
|
|
27200
27780
|
try {
|
|
27201
|
-
await
|
|
27781
|
+
await this.removeDirectory(repo, wt);
|
|
27202
27782
|
} catch (e) {
|
|
27203
27783
|
rmSync5(this.plansScratch(wt.id), { recursive: true, force: true });
|
|
27204
27784
|
throw e;
|
|
@@ -27226,13 +27806,41 @@ class WorktreeService {
|
|
|
27226
27806
|
}
|
|
27227
27807
|
async keep(repo, wt) {
|
|
27228
27808
|
const index = join23(this.d.paths.archiveDir, `${wt.id}.index`);
|
|
27229
|
-
const kept = await keepState(repo.path, wt.path, archiveRef(wt.id), index);
|
|
27809
|
+
const kept = await keepState(repo.path, wt.path, archiveRef(wt.id), index) ?? await this.keepUnlinked(repo, wt);
|
|
27230
27810
|
if (!kept)
|
|
27231
27811
|
log.warn(wt.id, "could not keep its git state");
|
|
27232
27812
|
else if (kept.lost)
|
|
27233
27813
|
log.warn(wt.id, "could not keep its uncommitted changes: only its commits are archived");
|
|
27234
27814
|
return kept;
|
|
27235
27815
|
}
|
|
27816
|
+
async keepUnlinked(repo, wt) {
|
|
27817
|
+
if (await this.listed(repo, wt))
|
|
27818
|
+
return null;
|
|
27819
|
+
const ref = archiveRef(wt.id);
|
|
27820
|
+
const already = await keptAt(repo.path, ref);
|
|
27821
|
+
if (already)
|
|
27822
|
+
return already;
|
|
27823
|
+
const head = await commitOf(repo.path, `refs/heads/${wt.branch}`);
|
|
27824
|
+
if (!head || !(await git(repo.path, "update-ref", ref, head)).ok)
|
|
27825
|
+
return null;
|
|
27826
|
+
return { head };
|
|
27827
|
+
}
|
|
27828
|
+
async listed(repo, wt) {
|
|
27829
|
+
const path = canonical(wt.path);
|
|
27830
|
+
return (await listWorktrees(repo.path)).some((g) => canonical(g.path) === path);
|
|
27831
|
+
}
|
|
27832
|
+
async removeDirectory(repo, wt) {
|
|
27833
|
+
const r = await git(repo.path, "worktree", "remove", "--force", wt.path);
|
|
27834
|
+
if (r.ok)
|
|
27835
|
+
return;
|
|
27836
|
+
if (await this.listed(repo, wt))
|
|
27837
|
+
throw new Error(`git worktree remove --force ${wt.path} failed (${r.exit}): ${r.err}`);
|
|
27838
|
+
if (await deleteTree(wt.path))
|
|
27839
|
+
return;
|
|
27840
|
+
await run("chmod", ["-R", "u+w", wt.path], dirname9(wt.path));
|
|
27841
|
+
if (!await deleteTree(wt.path))
|
|
27842
|
+
log.warn(wt.id, `its directory could not be deleted and stays at ${wt.path}`);
|
|
27843
|
+
}
|
|
27236
27844
|
plansScratch(worktreeId) {
|
|
27237
27845
|
return join23(this.d.paths.archiveDir, `${worktreeId}.plans`);
|
|
27238
27846
|
}
|
|
@@ -27536,7 +28144,7 @@ class WorktreeService {
|
|
|
27536
28144
|
const src = join23(repo.path, ".gitignore");
|
|
27537
28145
|
const tracked = existsSync26(src) && (await git(repo.path, "ls-files", "--", ".gitignore")).out !== "";
|
|
27538
28146
|
const mirror = existsSync26(src) && !tracked;
|
|
27539
|
-
const lines = mirror ?
|
|
28147
|
+
const lines = mirror ? readFileSync17(src, "utf8").split(`
|
|
27540
28148
|
`).filter((l) => l.trim() !== "") : [];
|
|
27541
28149
|
await excludeBlock(repo.path, BASE_IGNORE, lines);
|
|
27542
28150
|
}
|
|
@@ -27570,7 +28178,7 @@ class WorktreeService {
|
|
|
27570
28178
|
log.warn(wt.id, `could not copy ${f}`, r.err);
|
|
27571
28179
|
}
|
|
27572
28180
|
for (const cmd of setupCommands ? repo.config.setup ?? [] : []) {
|
|
27573
|
-
const code = await runSetup(cmd, wt.path, (line) => this.d.hub.emit("log", wt.id, "setup", line), worktreeEnv(wt, repo));
|
|
28181
|
+
const code = await runSetup(cmd, wt.path, (line, retract) => this.d.hub.emit("log", wt.id, "setup", line, retract), worktreeEnv(wt, repo));
|
|
27574
28182
|
if (code !== 0)
|
|
27575
28183
|
this.d.hub.emit("log", wt.id, "setup", `setup failed (exit ${code}): ${cmd}`);
|
|
27576
28184
|
}
|
|
@@ -27631,23 +28239,29 @@ ${wt.landing.body}` : s;
|
|
|
27631
28239
|
return this.commitRecorded(wt, this.commitMessage(wt, typed));
|
|
27632
28240
|
}
|
|
27633
28241
|
async commitRecorded(wt, message) {
|
|
27634
|
-
const result = await commitWorktree(wt.path, message);
|
|
28242
|
+
const result = await commitWorktree(wt.path, message, this.landWatch(wt));
|
|
27635
28243
|
if (result.ok) {
|
|
27636
28244
|
await this.verdictSurvives(wt, { dropMessage: true });
|
|
27637
28245
|
this.headMoved(wt.id);
|
|
27638
|
-
return result;
|
|
27639
|
-
}
|
|
27640
|
-
if (!result.output?.trim() || !this.d.record)
|
|
27641
|
-
return result;
|
|
27642
|
-
const subject = message.split(`
|
|
27643
|
-
`, 1)[0] ?? "";
|
|
27644
|
-
try {
|
|
27645
|
-
this.d.record(wt.id, `git commit -m ${JSON.stringify(subject)}`, result.output, result.exit ?? null);
|
|
27646
|
-
} catch (e) {
|
|
27647
|
-
log.warn(wt.id, `could not put the refused commit on the transcript: ${e instanceof Error ? e.message : e}`);
|
|
27648
|
-
return result;
|
|
27649
28246
|
}
|
|
27650
|
-
return
|
|
28247
|
+
return result;
|
|
28248
|
+
}
|
|
28249
|
+
landWatch(wt) {
|
|
28250
|
+
return {
|
|
28251
|
+
step: (name) => this.step(wt.id, name),
|
|
28252
|
+
git: async (cwd, args) => {
|
|
28253
|
+
const where = cwd === wt.path ? "git" : `git -C ${cwd}`;
|
|
28254
|
+
const run = (onText, signal) => stepRun(cwd, args, onText, signal);
|
|
28255
|
+
if (!this.d.watch)
|
|
28256
|
+
return run(() => {});
|
|
28257
|
+
try {
|
|
28258
|
+
return await this.d.watch(wt.id, `${where} ${commandLine(args)}`, run);
|
|
28259
|
+
} catch (e) {
|
|
28260
|
+
log.warn(wt.id, `landing step runs unwatched: ${e instanceof Error ? e.message : e}`);
|
|
28261
|
+
return run(() => {});
|
|
28262
|
+
}
|
|
28263
|
+
}
|
|
28264
|
+
};
|
|
27651
28265
|
}
|
|
27652
28266
|
async verdictSurvives(wt, opts) {
|
|
27653
28267
|
const l = wt.landing;
|
|
@@ -27667,25 +28281,62 @@ ${wt.landing.body}` : s;
|
|
|
27667
28281
|
}
|
|
27668
28282
|
async land(worktreeId, message) {
|
|
27669
28283
|
const { wt, repo } = this.landable(worktreeId, "land");
|
|
28284
|
+
return this.ship(wt.id, "land", () => this.landOp(wt, repo, message));
|
|
28285
|
+
}
|
|
28286
|
+
shippingOf(id) {
|
|
28287
|
+
return this.ops.get(id);
|
|
28288
|
+
}
|
|
28289
|
+
async ship(id, op, run) {
|
|
28290
|
+
const out = this.ops.get(id);
|
|
28291
|
+
if (out)
|
|
28292
|
+
throw new UserError(`${shipNoun(out.op)} is already running here`);
|
|
28293
|
+
const release = this.d.runtime.agentFor(id)?.hold();
|
|
28294
|
+
this.ops.set(id, { op });
|
|
28295
|
+
this.d.hub.emit("worktreesChanged");
|
|
28296
|
+
try {
|
|
28297
|
+
return await run();
|
|
28298
|
+
} finally {
|
|
28299
|
+
this.ops.delete(id);
|
|
28300
|
+
release?.();
|
|
28301
|
+
this.d.hub.emit("worktreesChanged");
|
|
28302
|
+
}
|
|
28303
|
+
}
|
|
28304
|
+
step(id, name) {
|
|
28305
|
+
const out = this.ops.get(id);
|
|
28306
|
+
if (!out || out.step === name)
|
|
28307
|
+
return;
|
|
28308
|
+
out.step = name;
|
|
28309
|
+
this.d.hub.emit("worktreesChanged");
|
|
28310
|
+
}
|
|
28311
|
+
async landOp(wt, repo, message) {
|
|
27670
28312
|
const policy = landPolicy(repo.config);
|
|
27671
28313
|
const own = hasOwnBranch(wt);
|
|
28314
|
+
const w = this.landWatch(wt);
|
|
27672
28315
|
const suggested = wt.landing?.subject ? wt.landing.body ? `${wt.landing.subject}
|
|
27673
28316
|
|
|
27674
28317
|
${wt.landing.body}` : wt.landing.subject : undefined;
|
|
27675
28318
|
if (policy.land === "pr") {
|
|
28319
|
+
if (wt.pr?.state === "merged" && !wt.landed) {
|
|
28320
|
+
const r = await this.prMergedOp(wt.id, undefined, w);
|
|
28321
|
+
return { result: r.ok ? r : { ...r, message: `PR #${wt.pr.number} merged, but ${r.message}` } };
|
|
28322
|
+
}
|
|
28323
|
+
const trunk = await fetchTrunk(wt.path, repo.defaultBranch, w);
|
|
28324
|
+
if (!trunk.ok)
|
|
28325
|
+
return { result: trunk };
|
|
28326
|
+
const base = trunk.base;
|
|
27676
28327
|
if (wt.pr?.state === "open") {
|
|
27677
28328
|
const dirty = (await statusFiles(wt.path)).length > 0;
|
|
27678
28329
|
const missing = dirty || (await aheadUpstream(wt.path) ?? 0) > 0;
|
|
27679
28330
|
if (!missing)
|
|
27680
|
-
return { result: await mergePr(wt.path, wt.pr.number, policy.merge) };
|
|
28331
|
+
return { result: await mergePr(wt.path, wt.pr.number, policy.merge, w) };
|
|
27681
28332
|
const committed = await this.commitIfDirty(wt, message);
|
|
27682
28333
|
if (committed && !committed.ok)
|
|
27683
28334
|
return { result: committed };
|
|
27684
|
-
const taken = await takeMainIn(wt.path,
|
|
28335
|
+
const taken = await takeMainIn(wt.path, base, own, w);
|
|
27685
28336
|
if (!taken.ok)
|
|
27686
28337
|
return { result: taken };
|
|
27687
28338
|
this.headMoved(wt.id);
|
|
27688
|
-
const pushed = await pushBranch(wt.path, wt.branch);
|
|
28339
|
+
const pushed = await pushBranch(wt.path, wt.branch, w);
|
|
27689
28340
|
if (!pushed.ok)
|
|
27690
28341
|
return { result: pushed };
|
|
27691
28342
|
this.headMoved(wt.id);
|
|
@@ -27696,19 +28347,26 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27696
28347
|
const committed = await this.commitIfDirty(wt, message);
|
|
27697
28348
|
if (committed && !committed.ok)
|
|
27698
28349
|
return { result: committed };
|
|
27699
|
-
const
|
|
28350
|
+
const mark = await landingMark(wt.path, repo.defaultBranch);
|
|
28351
|
+
const taken = await takeMainIn(wt.path, base, own, w);
|
|
27700
28352
|
if (!taken.ok)
|
|
27701
28353
|
return { result: taken };
|
|
27702
28354
|
this.headMoved(wt.id);
|
|
28355
|
+
if ((await aheadBehind(wt.path, base)).ahead === 0) {
|
|
28356
|
+
const r = await this.prMergedOp(wt.id, mark, w);
|
|
28357
|
+
const on = `origin's ${repo.defaultBranch} has this work already`;
|
|
28358
|
+
return { result: { ...r, message: r.ok ? `${on}; ${r.message}` : `${on}, but ${r.message}` } };
|
|
28359
|
+
}
|
|
27703
28360
|
const result = await openPr({
|
|
27704
28361
|
worktreePath: wt.path,
|
|
27705
28362
|
branch: wt.branch,
|
|
27706
28363
|
defaultBr: repo.defaultBranch,
|
|
28364
|
+
base,
|
|
27707
28365
|
subject: wt.landing?.subject,
|
|
27708
28366
|
body: wt.landing?.body,
|
|
27709
28367
|
automerge: policy.automerge,
|
|
27710
28368
|
method: policy.merge
|
|
27711
|
-
});
|
|
28369
|
+
}, w);
|
|
27712
28370
|
if (result.ok) {
|
|
27713
28371
|
this.setLanding(wt.id, undefined);
|
|
27714
28372
|
if (result.pr)
|
|
@@ -27724,24 +28382,24 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27724
28382
|
return committed;
|
|
27725
28383
|
committedHere = committed !== null;
|
|
27726
28384
|
if (policy.land === "push") {
|
|
27727
|
-
const pulled = await fastForwardMain(repo.path, repo.defaultBranch);
|
|
28385
|
+
const pulled = await fastForwardMain(repo.path, repo.defaultBranch, w);
|
|
27728
28386
|
if (!pulled.ok && !/no upstream/.test(pulled.message))
|
|
27729
28387
|
return pulled;
|
|
27730
28388
|
}
|
|
27731
|
-
const taken = await takeMainIn(wt.path, repo.defaultBranch, own);
|
|
28389
|
+
const taken = await takeMainIn(wt.path, repo.defaultBranch, own, w);
|
|
27732
28390
|
if (!taken.ok)
|
|
27733
28391
|
return taken;
|
|
27734
28392
|
const mark = await landingMark(wt.path, repo.defaultBranch);
|
|
27735
28393
|
const method = policy.merge ?? DEFAULT_MERGE_METHOD;
|
|
27736
28394
|
const squash = method === "squash" ? await squashMessage(wt.path, repo.defaultBranch, suggested) : "";
|
|
27737
|
-
const landed = await landLocally(wt.path, wt.branch, repo.path, repo.defaultBranch, method, squash);
|
|
28395
|
+
const landed = await landLocally(wt.path, wt.branch, repo.path, repo.defaultBranch, method, squash, w);
|
|
27738
28396
|
if (!landed.ok)
|
|
27739
28397
|
return landed;
|
|
27740
28398
|
mergedHere = true;
|
|
27741
28399
|
await this.noteLand(repo, wt, mark);
|
|
27742
28400
|
await this.restartFromMain(wt, repo.defaultBranch);
|
|
27743
28401
|
if (policy.land === "push") {
|
|
27744
|
-
const pushed = await pushMain(repo.path, repo.defaultBranch);
|
|
28402
|
+
const pushed = await pushMain(repo.path, repo.defaultBranch, w);
|
|
27745
28403
|
if (!pushed.ok)
|
|
27746
28404
|
return { ...pushed, message: `merged into ${repo.defaultBranch} here, but ${pushed.message}` };
|
|
27747
28405
|
}
|
|
@@ -27780,10 +28438,16 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27780
28438
|
this.headMoved(wt.id);
|
|
27781
28439
|
}
|
|
27782
28440
|
async prMerged(worktreeId) {
|
|
28441
|
+
const out = this.ops.get(worktreeId);
|
|
28442
|
+
if (out)
|
|
28443
|
+
return { ok: false, message: `${shipNoun(out.op)} is already running here` };
|
|
28444
|
+
return this.ship(worktreeId, "land", () => this.prMergedOp(worktreeId));
|
|
28445
|
+
}
|
|
28446
|
+
async prMergedOp(worktreeId, carried, w = UNWATCHED) {
|
|
27783
28447
|
const { wt, repo } = this.d.state.requireWorktreeWithRepo(worktreeId);
|
|
27784
28448
|
const result = await withRepoLock(repo.path, async () => {
|
|
27785
|
-
const mark = await landingMark(wt.path, repo.defaultBranch);
|
|
27786
|
-
const pulled = await fastForwardMain(repo.path, repo.defaultBranch);
|
|
28449
|
+
const mark = carried === undefined ? await landingMark(wt.path, repo.defaultBranch) : carried;
|
|
28450
|
+
const pulled = await fastForwardMain(repo.path, repo.defaultBranch, w);
|
|
27787
28451
|
if (!pulled.ok)
|
|
27788
28452
|
return pulled;
|
|
27789
28453
|
await this.noteLand(repo, wt, mark);
|
|
@@ -27811,12 +28475,16 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27811
28475
|
throw new UserError(`${r.name} is held by another tool`);
|
|
27812
28476
|
const repo = this.d.state.requireRepo(r.repoId);
|
|
27813
28477
|
const own = r.wt ? hasOwnBranch(r.wt) : false;
|
|
27814
|
-
const
|
|
27815
|
-
|
|
27816
|
-
|
|
27817
|
-
if (
|
|
27818
|
-
|
|
27819
|
-
|
|
28478
|
+
const w = r.wt ? this.landWatch(r.wt) : UNWATCHED;
|
|
28479
|
+
const result = await this.ship(worktreeId, "sync-main", async () => {
|
|
28480
|
+
const taken = await withRepoLock(repo.path, () => takeMainIn(r.path, repo.defaultBranch, own, w));
|
|
28481
|
+
if (taken.ok) {
|
|
28482
|
+
this.headMoved(worktreeId);
|
|
28483
|
+
if (r.wt)
|
|
28484
|
+
await this.verdictSurvives(r.wt, { dropMessage: false });
|
|
28485
|
+
}
|
|
28486
|
+
return taken;
|
|
28487
|
+
});
|
|
27820
28488
|
return { result, defaultBranch: repo.defaultBranch };
|
|
27821
28489
|
}
|
|
27822
28490
|
async pull(worktreeId) {
|
|
@@ -27824,19 +28492,22 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27824
28492
|
if (!isMain(wt))
|
|
27825
28493
|
throw new UserError("pull on main; a worktree syncs from main instead");
|
|
27826
28494
|
const repo = this.d.state.requireRepo(wt.repoId);
|
|
27827
|
-
const
|
|
27828
|
-
|
|
27829
|
-
|
|
27830
|
-
|
|
27831
|
-
|
|
27832
|
-
|
|
28495
|
+
const w = { step: (name) => this.step(worktreeId, name), git: UNWATCHED.git };
|
|
28496
|
+
return this.ship(worktreeId, "pull-main", async () => {
|
|
28497
|
+
const result = await withRepoLock(repo.path, () => pullMain(repo.path, repo.defaultBranch, w));
|
|
28498
|
+
if (result.ok) {
|
|
28499
|
+
this.invalidateCounts();
|
|
28500
|
+
this.headMoved(worktreeId);
|
|
28501
|
+
}
|
|
28502
|
+
return result;
|
|
28503
|
+
});
|
|
27833
28504
|
}
|
|
27834
28505
|
async commit(worktreeId, message) {
|
|
27835
28506
|
const wt = this.d.state.requireWorktree(worktreeId);
|
|
27836
28507
|
const m = message.trim();
|
|
27837
28508
|
if (!m)
|
|
27838
28509
|
throw new UserError("commit message required");
|
|
27839
|
-
return this.commitRecorded(wt, m);
|
|
28510
|
+
return this.ship(wt.id, "commit", () => this.commitRecorded(wt, m));
|
|
27840
28511
|
}
|
|
27841
28512
|
setEmpty(wt, empty) {
|
|
27842
28513
|
if (wt.empty === empty)
|
|
@@ -27848,8 +28519,14 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27848
28519
|
syncTrunk(repoId) {
|
|
27849
28520
|
return this.trunk.sync(repoId);
|
|
27850
28521
|
}
|
|
27851
|
-
trunks(opts = {}) {
|
|
27852
|
-
|
|
28522
|
+
async trunks(opts = {}) {
|
|
28523
|
+
const all = await this.trunk.all(opts);
|
|
28524
|
+
for (const t of Object.values(all)) {
|
|
28525
|
+
const shipping = this.ops.get(t.id);
|
|
28526
|
+
if (shipping)
|
|
28527
|
+
t.shipping = shipping;
|
|
28528
|
+
}
|
|
28529
|
+
return all;
|
|
27853
28530
|
}
|
|
27854
28531
|
usageFor(worktreeId) {
|
|
27855
28532
|
const known = this.usage.get(worktreeId);
|
|
@@ -28112,7 +28789,8 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
28112
28789
|
unseen: isUnseen(wt) || undefined,
|
|
28113
28790
|
usage: this.usageFor(wt.id),
|
|
28114
28791
|
transcript: transcriptPathFor(this.d.paths.transcriptsDir, wt.id),
|
|
28115
|
-
sessionId: this.d.state.session(wt.id)
|
|
28792
|
+
sessionId: this.d.state.session(wt.id),
|
|
28793
|
+
shipping: this.ops.get(wt.id)
|
|
28116
28794
|
};
|
|
28117
28795
|
}));
|
|
28118
28796
|
}
|
|
@@ -28121,7 +28799,16 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
28121
28799
|
return Promise.all(found.map(async (f) => {
|
|
28122
28800
|
const repo = this.d.state.repo(f.repoId);
|
|
28123
28801
|
const { ahead, behind, dirty } = !repo ? {} : quick ? this.countsQuick(f.id, quick) : await this.counts(f.id, f.path, repo.defaultBranch, !!f.branch);
|
|
28124
|
-
return {
|
|
28802
|
+
return {
|
|
28803
|
+
...f,
|
|
28804
|
+
procs: [],
|
|
28805
|
+
agent: "idle",
|
|
28806
|
+
login: false,
|
|
28807
|
+
ahead,
|
|
28808
|
+
behind,
|
|
28809
|
+
dirty,
|
|
28810
|
+
shipping: this.ops.get(f.id)
|
|
28811
|
+
};
|
|
28125
28812
|
}));
|
|
28126
28813
|
}
|
|
28127
28814
|
}
|
|
@@ -28227,11 +28914,12 @@ var {
|
|
|
28227
28914
|
var pruned = pruneAssets(SHELL_DIST);
|
|
28228
28915
|
if (pruned > 0)
|
|
28229
28916
|
log.debug("daemon", `pruned ${pruned} assets from builds this one has outlived`);
|
|
28917
|
+
var managed = await loadManaged();
|
|
28230
28918
|
var paths = makePaths();
|
|
28231
28919
|
ensureDirs(paths);
|
|
28232
28920
|
var token = loadOrCreateToken(paths);
|
|
28233
28921
|
var port = Number(process.env.TOYON_PORT ?? DAEMON_DEFAULT_PORT);
|
|
28234
|
-
var remote = loadRemote(paths.remoteFile);
|
|
28922
|
+
var remote = loadRemote(paths.remoteFile, process.env, managed.policy);
|
|
28235
28923
|
if (remote && addressedByPort(remote.previews))
|
|
28236
28924
|
pinProxyPorts(PREVIEW_PORTS);
|
|
28237
28925
|
var state = new StateStore(paths);
|
|
@@ -28239,7 +28927,9 @@ var hub = new Hub;
|
|
|
28239
28927
|
var bridge = new BridgeScript(BRIDGE_JS);
|
|
28240
28928
|
var linuxSandbox = new LinuxSandbox;
|
|
28241
28929
|
await linuxSandbox.refresh();
|
|
28242
|
-
var agents = loadAgentRegistry(paths.agentsFile, paths.agentsDir, linuxSandbox);
|
|
28930
|
+
var agents = loadAgentRegistry(paths.agentsFile, paths.agentsDir, linuxSandbox, managed.policy);
|
|
28931
|
+
if (state.defaultAgent !== undefined && !agents.get(state.defaultAgent))
|
|
28932
|
+
state.clearDefaultAgent();
|
|
28243
28933
|
var probe = new OptionProbe({
|
|
28244
28934
|
infos: () => agents.infos(),
|
|
28245
28935
|
require: (id) => agents.require(id),
|
|
@@ -28266,6 +28956,7 @@ var runtime = new RuntimeRegistry({
|
|
|
28266
28956
|
state,
|
|
28267
28957
|
paths,
|
|
28268
28958
|
agents,
|
|
28959
|
+
managed: managed.policy,
|
|
28269
28960
|
accounts,
|
|
28270
28961
|
attachments: attachments2,
|
|
28271
28962
|
bridgeScript: () => bridge.get(),
|
|
@@ -28276,7 +28967,7 @@ var runtime = new RuntimeRegistry({
|
|
|
28276
28967
|
mainLeads: (repoId) => worktrees.spare.current(repoId) === null
|
|
28277
28968
|
});
|
|
28278
28969
|
var drafts = new DraftStore({ file: paths.draftsFile, hub });
|
|
28279
|
-
var exec = new ExecService({ state, runtime });
|
|
28970
|
+
var exec = new ExecService({ state, runtime, shipping: (id) => worktrees.shippingOf(id) });
|
|
28280
28971
|
var worktrees = new WorktreeService({
|
|
28281
28972
|
state,
|
|
28282
28973
|
hub,
|
|
@@ -28284,7 +28975,7 @@ var worktrees = new WorktreeService({
|
|
|
28284
28975
|
paths,
|
|
28285
28976
|
agents,
|
|
28286
28977
|
drafts,
|
|
28287
|
-
|
|
28978
|
+
watch: (id, command, run) => exec.watch(id, command, run)
|
|
28288
28979
|
});
|
|
28289
28980
|
var turns = new TurnService({
|
|
28290
28981
|
state,
|
|
@@ -28359,7 +29050,7 @@ var update = new UpdateService({
|
|
|
28359
29050
|
command: (version) => installCommand(method, version),
|
|
28360
29051
|
install: runInstall,
|
|
28361
29052
|
busy: () => runtime.anyBusy(),
|
|
28362
|
-
managed: process.env.TOYON_UPDATES === "off"
|
|
29053
|
+
managedBy: !managed.policy.updates ? "policy" : process.env.TOYON_UPDATES === "off" ? "env" : null
|
|
28363
29054
|
});
|
|
28364
29055
|
var { branded, stop: stopServer } = startServer({
|
|
28365
29056
|
port,
|
|
@@ -28368,6 +29059,7 @@ var { branded, stop: stopServer } = startServer({
|
|
|
28368
29059
|
version: package_default.version,
|
|
28369
29060
|
noteShellOrigin: (origin) => bridge.learnShellOrigin(origin),
|
|
28370
29061
|
remote,
|
|
29062
|
+
managed,
|
|
28371
29063
|
services: {
|
|
28372
29064
|
state,
|
|
28373
29065
|
hub,
|
|
@@ -28415,6 +29107,7 @@ await runtime.reclaimOrphans();
|
|
|
28415
29107
|
await repos.boot();
|
|
28416
29108
|
drafts.prune((id) => !!state.worktree(id) || worktrees.hasArchived(id));
|
|
28417
29109
|
idle.boot();
|
|
29110
|
+
landing.boot();
|
|
28418
29111
|
fireAndForget("agents", agents.installMissing().then(() => probe.missing()), "agent adapter install");
|
|
28419
29112
|
var repoArg = process.argv[2];
|
|
28420
29113
|
if (repoArg) {
|
|
@@ -28438,6 +29131,12 @@ if (remote?.front === "edge") {
|
|
|
28438
29131
|
console.log(" the token grants a shell on this machine; keep the link to yourself");
|
|
28439
29132
|
}
|
|
28440
29133
|
}
|
|
29134
|
+
for (const src of managed.sources) {
|
|
29135
|
+
if (src.state === "absent")
|
|
29136
|
+
continue;
|
|
29137
|
+
const what = src.state === "applied" ? describeManaged(managed.policy).join(", ") || "nothing turned off" : `${src.problem}; everything it governs is off`;
|
|
29138
|
+
console.log(` policy: ${src.path}: ${what}`);
|
|
29139
|
+
}
|
|
28441
29140
|
var shuttingDown = false;
|
|
28442
29141
|
async function shutdown(signal, opts = {}) {
|
|
28443
29142
|
if (shuttingDown)
|
|
@@ -28445,6 +29144,7 @@ async function shutdown(signal, opts = {}) {
|
|
|
28445
29144
|
shuttingDown = true;
|
|
28446
29145
|
log.info("daemon", `${signal}: stopping dev servers`);
|
|
28447
29146
|
idle.shutdown();
|
|
29147
|
+
landing.stop();
|
|
28448
29148
|
repos.stopWatchers();
|
|
28449
29149
|
prs.stop();
|
|
28450
29150
|
sweep.stop();
|