toyon 0.4.0 → 0.4.1
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 +9 -12
- package/dist/cli.js +1 -1
- package/dist/daemon.js +296 -196
- package/dist/shell/assets/{Editor-CgKsVXHS.js → Editor-BV5mlMUd.js} +4 -4
- package/dist/shell/assets/{XTerm-CMRoVDAh.js → XTerm-vYHVHYnx.js} +1 -1
- package/dist/shell/assets/{cssMode-Dg15kh0f.js → cssMode-DmPPq0RM.js} +1 -1
- package/dist/shell/assets/{freemarker2-CU21n-BA.js → freemarker2-D0jw6qr7.js} +1 -1
- package/dist/shell/assets/{grammar-DGBFIuP2.js → grammar-SjzA6dB3.js} +1 -1
- package/dist/shell/assets/{handlebars-DVAbWe34.js → handlebars-DP51I8rr.js} +1 -1
- package/dist/shell/assets/{html-BkxU7kNX.js → html-lErft7TP.js} +1 -1
- package/dist/shell/assets/{htmlMode-Brl4kLPo.js → htmlMode-B8WYzHvO.js} +1 -1
- package/dist/shell/assets/index-2d0IGh4f.js +232 -0
- package/dist/shell/assets/index-DZ08PVmu.css +1 -0
- package/dist/shell/assets/{javascript-DoIo3Pg8.js → javascript-2o8dwNxB.js} +1 -1
- package/dist/shell/assets/{jsonMode-B9qq6lCy.js → jsonMode-BzyCh8f1.js} +1 -1
- package/dist/shell/assets/{liquid-Cn1aqWby.js → liquid-DwwOicEv.js} +1 -1
- package/dist/shell/assets/{lspLanguageFeatures-C5ez8E_9.js → lspLanguageFeatures-BSPiRlDO.js} +1 -1
- package/dist/shell/assets/{mdx-1MuVz0xd.js → mdx-Dx7gR42C.js} +1 -1
- package/dist/shell/assets/{python-DFhVdcGk.js → python-B09pxq08.js} +1 -1
- package/dist/shell/assets/{razor-BitqUXpg.js → razor-DO8UQZDP.js} +1 -1
- package/dist/shell/assets/{tsMode-loK_YDOL.js → tsMode-1xhdljsc.js} +1 -1
- package/dist/shell/assets/{xml-Cc4ljERX.js → xml-Be6HmYJA.js} +1 -1
- package/dist/shell/assets/{yaml-0hpVC8PU.js → yaml-D4xpEWFo.js} +1 -1
- package/dist/shell/index.html +2 -2
- package/package.json +1 -1
- package/dist/shell/assets/index-DUmxVabc.css +0 -1
- package/dist/shell/assets/index-WHwsRrlj.js +0 -232
package/dist/daemon.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../daemon/src/index.ts
|
|
3
|
-
import { rmSync as rmSync6, writeFileSync as
|
|
3
|
+
import { rmSync as rmSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
4
4
|
import { dirname as dirname9 } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
|
|
@@ -297,7 +297,7 @@ var PAIR_TTL_MS = 120000;
|
|
|
297
297
|
var pairLink = (host, code) => `https://${host}/#pair=${code}`;
|
|
298
298
|
|
|
299
299
|
// ../shared/src/protocol/limits.ts
|
|
300
|
-
var PROTOCOL_VERSION =
|
|
300
|
+
var PROTOCOL_VERSION = 60;
|
|
301
301
|
var FILE_MAX_CHARS = 5000000;
|
|
302
302
|
var IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
303
303
|
var IMAGE_MAX_EDGE = 2576;
|
|
@@ -7722,7 +7722,7 @@ function slug(s) {
|
|
|
7722
7722
|
// ../daemon/package.json
|
|
7723
7723
|
var package_default = {
|
|
7724
7724
|
name: "@toyon/daemon",
|
|
7725
|
-
version: "0.4.
|
|
7725
|
+
version: "0.4.1",
|
|
7726
7726
|
private: true,
|
|
7727
7727
|
type: "module",
|
|
7728
7728
|
main: "src/index.ts",
|
|
@@ -12694,6 +12694,29 @@ async function excludeFromGit(cwd, pattern) {
|
|
|
12694
12694
|
` : ""}${pattern}
|
|
12695
12695
|
`);
|
|
12696
12696
|
}
|
|
12697
|
+
async function excludeBlock(cwd, name, patterns) {
|
|
12698
|
+
const file = await excludeFile(cwd);
|
|
12699
|
+
if (!file)
|
|
12700
|
+
return;
|
|
12701
|
+
const open = `# toyon ${name}`;
|
|
12702
|
+
const close = `# toyon end ${name}`;
|
|
12703
|
+
const current = existsSync3(file) ? readFileSync3(file, "utf8") : "";
|
|
12704
|
+
const lines = current.split(`
|
|
12705
|
+
`);
|
|
12706
|
+
const from = lines.indexOf(open);
|
|
12707
|
+
const to = lines.indexOf(close);
|
|
12708
|
+
const kept = from >= 0 && to > from ? [...lines.slice(0, from), ...lines.slice(to + 1)] : lines;
|
|
12709
|
+
while (kept.length > 0 && kept[kept.length - 1] === "")
|
|
12710
|
+
kept.pop();
|
|
12711
|
+
const next = [...kept, ...patterns.length > 0 ? [open, ...patterns, close] : []];
|
|
12712
|
+
const text = next.length > 0 ? `${next.join(`
|
|
12713
|
+
`)}
|
|
12714
|
+
` : "";
|
|
12715
|
+
if (text === current)
|
|
12716
|
+
return;
|
|
12717
|
+
mkdirSync2(dirname(file), { recursive: true });
|
|
12718
|
+
writeFileSync(file, text);
|
|
12719
|
+
}
|
|
12697
12720
|
async function unexcludeFromGit(cwd, pattern) {
|
|
12698
12721
|
const file = await excludeFile(cwd);
|
|
12699
12722
|
if (!file || !existsSync3(file))
|
|
@@ -14417,17 +14440,18 @@ class AcpSession {
|
|
|
14417
14440
|
}
|
|
14418
14441
|
return pickOption(params.options, verdict);
|
|
14419
14442
|
}
|
|
14420
|
-
askPermission(params) {
|
|
14443
|
+
async askPermission(params) {
|
|
14421
14444
|
const plan = params.toolCall.kind === "switch_mode";
|
|
14422
|
-
|
|
14423
|
-
|
|
14445
|
+
const detail = permissionDetail(params);
|
|
14446
|
+
const planPath = plan ? await this.d.onPlan?.(detail) ?? null : null;
|
|
14424
14447
|
const offered = plan ? params.options : params.options.filter((o) => o.kind !== "allow_always");
|
|
14425
14448
|
const choices = offered.map((o) => ({ id: o.optionId, name: o.name, kind: o.kind }));
|
|
14426
14449
|
return this.openAsk((id) => ({
|
|
14427
14450
|
type: "agent-permission",
|
|
14428
14451
|
id,
|
|
14429
14452
|
title: params.toolCall.title ?? params.toolCall.name ?? "the agent needs a decision",
|
|
14430
|
-
...
|
|
14453
|
+
...detail ? { detail } : {},
|
|
14454
|
+
...planPath ? { plan: planPath } : {},
|
|
14431
14455
|
choices,
|
|
14432
14456
|
...params.toolCall.toolCallId ? { toolId: params.toolCall.toolCallId } : {},
|
|
14433
14457
|
ts: Date.now()
|
|
@@ -14438,10 +14462,17 @@ class AcpSession {
|
|
|
14438
14462
|
return { outcome: { outcome: "cancelled" } };
|
|
14439
14463
|
if (plan && (choice.kind === "allow_once" || choice.kind === "allow_always")) {
|
|
14440
14464
|
this.d.setMode?.(modeAfterPlan(choice.name));
|
|
14465
|
+
if (planPath)
|
|
14466
|
+
fireAndForget(this.d.worktreeId, this.sayPlanEdited(detail, planPath), "edited plan");
|
|
14441
14467
|
}
|
|
14442
14468
|
return { outcome: { outcome: "selected", optionId: choice.id } };
|
|
14443
14469
|
});
|
|
14444
14470
|
}
|
|
14471
|
+
async sayPlanEdited(proposed, path) {
|
|
14472
|
+
if (!await this.d.planEdited?.(proposed))
|
|
14473
|
+
return;
|
|
14474
|
+
this.send(`I edited the plan before approving it. Build what is in ${path}, not the plan you proposed.`);
|
|
14475
|
+
}
|
|
14445
14476
|
onElicit(params, signal) {
|
|
14446
14477
|
const live = this.live;
|
|
14447
14478
|
if (!live || !("sessionId" in params) || params.sessionId !== live.sessionId) {
|
|
@@ -14593,6 +14624,35 @@ function sameCommands(a, b) {
|
|
|
14593
14624
|
return a.length === b.length && a.every((c, i) => c.name === b[i]?.name && c.description === b[i]?.description && c.hint === b[i]?.hint);
|
|
14594
14625
|
}
|
|
14595
14626
|
|
|
14627
|
+
// ../daemon/src/agent/planDoc.ts
|
|
14628
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
14629
|
+
import { join as join7 } from "path";
|
|
14630
|
+
var PLAN_REL = join7(".toyon", "plan.md");
|
|
14631
|
+
async function writePlanDoc(cwd, markdown) {
|
|
14632
|
+
try {
|
|
14633
|
+
mkdirSync5(join7(cwd, ".toyon"), { recursive: true });
|
|
14634
|
+
writeFileSync4(join7(cwd, PLAN_REL), markdown.endsWith(`
|
|
14635
|
+
`) ? markdown : `${markdown}
|
|
14636
|
+
`);
|
|
14637
|
+
} catch (e) {
|
|
14638
|
+
log.warn(cwd, `could not write ${PLAN_REL}; the plan stays on the card`, e);
|
|
14639
|
+
return null;
|
|
14640
|
+
}
|
|
14641
|
+
await excludeFromGit(cwd, PLAN_REL);
|
|
14642
|
+
return PLAN_REL;
|
|
14643
|
+
}
|
|
14644
|
+
function planEdited(cwd, proposed) {
|
|
14645
|
+
const file = join7(cwd, PLAN_REL);
|
|
14646
|
+
if (!existsSync8(file))
|
|
14647
|
+
return false;
|
|
14648
|
+
try {
|
|
14649
|
+
return readFileSync7(file, "utf8").trim() !== proposed.trim();
|
|
14650
|
+
} catch (e) {
|
|
14651
|
+
log.warn(cwd, `could not read ${PLAN_REL}`, e);
|
|
14652
|
+
return false;
|
|
14653
|
+
}
|
|
14654
|
+
}
|
|
14655
|
+
|
|
14596
14656
|
// ../daemon/src/runtime/ports.ts
|
|
14597
14657
|
var allocated = new Set;
|
|
14598
14658
|
var range = cloud.proxyPorts;
|
|
@@ -14705,7 +14765,7 @@ function expandEnv(env, vars) {
|
|
|
14705
14765
|
|
|
14706
14766
|
// ../daemon/src/core/remote.ts
|
|
14707
14767
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
14708
|
-
import { existsSync as
|
|
14768
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
14709
14769
|
function loadRemote(file, env = process.env) {
|
|
14710
14770
|
if (env.TOYON_CLOUD === "1") {
|
|
14711
14771
|
const host = (env.TOYON_PUBLIC_HOST ?? "").toLowerCase();
|
|
@@ -14718,9 +14778,9 @@ function loadRemote(file, env = process.env) {
|
|
|
14718
14778
|
throw new Error(`TOYON_PREVIEWS: ${why}`);
|
|
14719
14779
|
return { host, previews, front: "edge" };
|
|
14720
14780
|
}
|
|
14721
|
-
if (!
|
|
14781
|
+
if (!existsSync9(file))
|
|
14722
14782
|
return null;
|
|
14723
|
-
const view = parseRemote(
|
|
14783
|
+
const view = parseRemote(readFileSync8(file, "utf8"));
|
|
14724
14784
|
if (!view) {
|
|
14725
14785
|
log.warn("remote", `${file} names no valid host or previews; remote access is off`);
|
|
14726
14786
|
return null;
|
|
@@ -15512,13 +15572,15 @@ function defaultAgent(wt, d, preview) {
|
|
|
15512
15572
|
d.state.save();
|
|
15513
15573
|
d.hub.emit("worktreesChanged");
|
|
15514
15574
|
},
|
|
15515
|
-
onPlan: () => {
|
|
15575
|
+
onPlan: async (markdown) => {
|
|
15516
15576
|
const w = d.state.worktree(wt.id);
|
|
15517
|
-
if (
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15577
|
+
if (w && !w.planned) {
|
|
15578
|
+
w.planned = true;
|
|
15579
|
+
d.state.save();
|
|
15580
|
+
}
|
|
15581
|
+
return markdown.trim() ? writePlanDoc(wt.path, markdown) : null;
|
|
15521
15582
|
},
|
|
15583
|
+
planEdited: async (proposed) => planEdited(wt.path, proposed),
|
|
15522
15584
|
option: (category) => d.state.requireWorktree(wt.id)[OPTION_FIELDS[category]],
|
|
15523
15585
|
onOptionsLearned: (category, choices) => {
|
|
15524
15586
|
const agentId = d.state.requireWorktree(wt.id).agent ?? "";
|
|
@@ -16389,20 +16451,20 @@ function makePlanner(agents) {
|
|
|
16389
16451
|
}
|
|
16390
16452
|
|
|
16391
16453
|
// ../daemon/src/core/assets.ts
|
|
16392
|
-
import { existsSync as
|
|
16393
|
-
import { join as
|
|
16454
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, rmSync, statSync as statSync2 } from "fs";
|
|
16455
|
+
import { join as join8, resolve as resolve5 } from "path";
|
|
16394
16456
|
function locateAssets(here, env = process.env) {
|
|
16395
|
-
const packaged =
|
|
16457
|
+
const packaged = existsSync10(join8(here, "shell", "index.html"));
|
|
16396
16458
|
return {
|
|
16397
|
-
shellDist: env.TOYON_SHELL_DIST ?? (packaged ?
|
|
16398
|
-
bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ?
|
|
16459
|
+
shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join8(here, "shell") : join8(here, "../../shell/dist")),
|
|
16460
|
+
bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join8(here, "bridge.js") : join8(here, "../../bridge/dist/bridge.js")),
|
|
16399
16461
|
sourceRoot: packaged ? null : resolve5(here, "../../.."),
|
|
16400
|
-
packageJson: packaged ?
|
|
16462
|
+
packageJson: packaged ? join8(here, "..", "package.json") : null
|
|
16401
16463
|
};
|
|
16402
16464
|
}
|
|
16403
16465
|
var KEEP_MS = 3 * 24 * 60 * 60 * 1000;
|
|
16404
16466
|
function pruneAssets(shellDist) {
|
|
16405
|
-
const dir =
|
|
16467
|
+
const dir = join8(shellDist, "assets");
|
|
16406
16468
|
let names;
|
|
16407
16469
|
try {
|
|
16408
16470
|
names = readdirSync2(dir);
|
|
@@ -16411,7 +16473,7 @@ function pruneAssets(shellDist) {
|
|
|
16411
16473
|
}
|
|
16412
16474
|
const found = [];
|
|
16413
16475
|
for (const name of names) {
|
|
16414
|
-
const path =
|
|
16476
|
+
const path = join8(dir, name);
|
|
16415
16477
|
try {
|
|
16416
16478
|
found.push({ path, mtime: statSync2(path).mtimeMs });
|
|
16417
16479
|
} catch {}
|
|
@@ -16432,7 +16494,7 @@ function pruneAssets(shellDist) {
|
|
|
16432
16494
|
}
|
|
16433
16495
|
|
|
16434
16496
|
// ../daemon/src/core/dialog.ts
|
|
16435
|
-
import { existsSync as
|
|
16497
|
+
import { existsSync as existsSync11 } from "fs";
|
|
16436
16498
|
import { homedir as homedir4 } from "os";
|
|
16437
16499
|
var PROMPTS = {
|
|
16438
16500
|
location: "Choose where the new project goes",
|
|
@@ -16450,7 +16512,7 @@ function folderDialog() {
|
|
|
16450
16512
|
let cancelled = false;
|
|
16451
16513
|
const ask = async (start, purpose) => {
|
|
16452
16514
|
const typed = start === "~" || start.startsWith("~/") ? `${homedir4()}${start.slice(1)}` : start;
|
|
16453
|
-
const from = typed.startsWith("/") &&
|
|
16515
|
+
const from = typed.startsWith("/") && existsSync11(typed) ? typed : homedir4();
|
|
16454
16516
|
cancelled = false;
|
|
16455
16517
|
const p = Bun.spawn(["osascript", ...SCRIPT.flatMap((line) => ["-e", line]), PROMPTS[purpose], from], {
|
|
16456
16518
|
stdout: "pipe",
|
|
@@ -16667,17 +16729,17 @@ class SelfWatch {
|
|
|
16667
16729
|
|
|
16668
16730
|
// ../daemon/src/core/state.ts
|
|
16669
16731
|
import { randomBytes } from "crypto";
|
|
16670
|
-
import { existsSync as
|
|
16732
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
16671
16733
|
var SEEN_WORKTREES = 50;
|
|
16672
16734
|
var empty = { repos: [], worktrees: [], sessions: {} };
|
|
16673
16735
|
function loadState(paths) {
|
|
16674
16736
|
ensureDirs(paths);
|
|
16675
16737
|
const STATE_FILE = paths.stateFile;
|
|
16676
|
-
if (!
|
|
16738
|
+
if (!existsSync12(STATE_FILE))
|
|
16677
16739
|
return structuredClone(empty);
|
|
16678
16740
|
let raw;
|
|
16679
16741
|
try {
|
|
16680
|
-
raw =
|
|
16742
|
+
raw = readFileSync9(STATE_FILE, "utf8");
|
|
16681
16743
|
} catch (e) {
|
|
16682
16744
|
log.error("state", `cannot read ${STATE_FILE}; starting empty`, e);
|
|
16683
16745
|
return structuredClone(empty);
|
|
@@ -16688,7 +16750,7 @@ function loadState(paths) {
|
|
|
16688
16750
|
} catch (e) {
|
|
16689
16751
|
const backup = `${STATE_FILE}.corrupt-${Date.now()}`;
|
|
16690
16752
|
try {
|
|
16691
|
-
|
|
16753
|
+
writeFileSync5(backup, raw);
|
|
16692
16754
|
} catch (be) {
|
|
16693
16755
|
log.warn("state", "could not write the corrupt-state backup", be);
|
|
16694
16756
|
}
|
|
@@ -16715,16 +16777,16 @@ function loadState(paths) {
|
|
|
16715
16777
|
}
|
|
16716
16778
|
function saveState(paths, state) {
|
|
16717
16779
|
const tmp = `${paths.stateFile}.tmp`;
|
|
16718
|
-
|
|
16780
|
+
writeFileSync5(tmp, JSON.stringify(state, null, 2));
|
|
16719
16781
|
renameSync2(tmp, paths.stateFile);
|
|
16720
16782
|
}
|
|
16721
16783
|
function loadOrCreateToken(paths) {
|
|
16722
16784
|
ensureDirs(paths);
|
|
16723
16785
|
const TOKEN_FILE = paths.tokenFile;
|
|
16724
|
-
if (
|
|
16725
|
-
return
|
|
16786
|
+
if (existsSync12(TOKEN_FILE))
|
|
16787
|
+
return readFileSync9(TOKEN_FILE, "utf8").trim();
|
|
16726
16788
|
const token = randomBytes(32).toString("hex");
|
|
16727
|
-
|
|
16789
|
+
writeFileSync5(TOKEN_FILE, token, { mode: 384 });
|
|
16728
16790
|
return token;
|
|
16729
16791
|
}
|
|
16730
16792
|
|
|
@@ -16878,7 +16940,7 @@ class StateStore {
|
|
|
16878
16940
|
}
|
|
16879
16941
|
|
|
16880
16942
|
// ../daemon/src/design/service.ts
|
|
16881
|
-
import { join as
|
|
16943
|
+
import { join as join9 } from "path";
|
|
16882
16944
|
|
|
16883
16945
|
// ../daemon/src/design/scan.ts
|
|
16884
16946
|
var COLOR = /^(#|rgba?\(|hsla?\(|oklch\(|oklab\(|lab\(|lch\(|color\(|color-mix\(|light-dark\()/i;
|
|
@@ -17328,7 +17390,7 @@ class DesignService {
|
|
|
17328
17390
|
async function readAll(root, paths) {
|
|
17329
17391
|
const out = [];
|
|
17330
17392
|
for (const path of paths) {
|
|
17331
|
-
const file = Bun.file(
|
|
17393
|
+
const file = Bun.file(join9(root, path));
|
|
17332
17394
|
if (file.size === 0 || file.size > MAX_FILE_BYTES)
|
|
17333
17395
|
continue;
|
|
17334
17396
|
try {
|
|
@@ -17443,14 +17505,14 @@ function typicalUse(classes) {
|
|
|
17443
17505
|
var normal = (s) => s.replace(/[-_]/g, "").toLowerCase();
|
|
17444
17506
|
async function loadProjectTypescript(root) {
|
|
17445
17507
|
try {
|
|
17446
|
-
return await import(
|
|
17508
|
+
return await import(join9(root, "node_modules", "typescript", "lib", "typescript.js"));
|
|
17447
17509
|
} catch {
|
|
17448
17510
|
return null;
|
|
17449
17511
|
}
|
|
17450
17512
|
}
|
|
17451
17513
|
|
|
17452
17514
|
// ../daemon/src/drafts/store.ts
|
|
17453
|
-
import { existsSync as
|
|
17515
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, renameSync as renameSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
17454
17516
|
var SAVE_DELAY_MS = 1000;
|
|
17455
17517
|
|
|
17456
17518
|
class DraftStore {
|
|
@@ -17459,10 +17521,10 @@ class DraftStore {
|
|
|
17459
17521
|
saveTimer = null;
|
|
17460
17522
|
constructor(d) {
|
|
17461
17523
|
this.d = d;
|
|
17462
|
-
if (!
|
|
17524
|
+
if (!existsSync13(d.file))
|
|
17463
17525
|
return;
|
|
17464
17526
|
try {
|
|
17465
|
-
const raw = JSON.parse(
|
|
17527
|
+
const raw = JSON.parse(readFileSync10(d.file, "utf8"));
|
|
17466
17528
|
if (!raw || typeof raw !== "object")
|
|
17467
17529
|
return;
|
|
17468
17530
|
for (const [id, text] of Object.entries(raw)) {
|
|
@@ -17522,7 +17584,7 @@ class DraftStore {
|
|
|
17522
17584
|
write() {
|
|
17523
17585
|
const tmp = `${this.d.file}.tmp`;
|
|
17524
17586
|
try {
|
|
17525
|
-
|
|
17587
|
+
writeFileSync6(tmp, JSON.stringify(this.drafts));
|
|
17526
17588
|
renameSync3(tmp, this.d.file);
|
|
17527
17589
|
} catch (e) {
|
|
17528
17590
|
log.error("drafts", "could not write the drafts", e);
|
|
@@ -17687,7 +17749,7 @@ function withRepoLock(repoPath, fn) {
|
|
|
17687
17749
|
var fileLockKey = (absPath) => `file:${absPath}`;
|
|
17688
17750
|
|
|
17689
17751
|
// ../daemon/src/git/status.ts
|
|
17690
|
-
import { join as
|
|
17752
|
+
import { join as join10 } from "path";
|
|
17691
17753
|
async function treeFingerprint(worktreePath) {
|
|
17692
17754
|
const [head, diff, untracked] = await Promise.all([
|
|
17693
17755
|
git(worktreePath, "rev-parse", "HEAD"),
|
|
@@ -17697,7 +17759,7 @@ async function treeFingerprint(worktreePath) {
|
|
|
17697
17759
|
const others = await Promise.all(untracked.out.split(`
|
|
17698
17760
|
`).filter(Boolean).map(async (p) => {
|
|
17699
17761
|
try {
|
|
17700
|
-
const f = Bun.file(
|
|
17762
|
+
const f = Bun.file(join10(worktreePath, p));
|
|
17701
17763
|
const body = f.size > 2000000 ? String(f.size) : Bun.hash(await f.arrayBuffer()).toString(36);
|
|
17702
17764
|
return `${p}:${body}`;
|
|
17703
17765
|
} catch {
|
|
@@ -17748,7 +17810,7 @@ async function numstat(worktreePath, ...range) {
|
|
|
17748
17810
|
}
|
|
17749
17811
|
async function untrackedLines(worktreePath, file) {
|
|
17750
17812
|
try {
|
|
17751
|
-
const f = Bun.file(
|
|
17813
|
+
const f = Bun.file(join10(worktreePath, file));
|
|
17752
17814
|
if (f.size > 2000000)
|
|
17753
17815
|
return {};
|
|
17754
17816
|
const buf = new Uint8Array(await f.arrayBuffer());
|
|
@@ -17767,14 +17829,16 @@ async function untrackedLines(worktreePath, file) {
|
|
|
17767
17829
|
return {};
|
|
17768
17830
|
}
|
|
17769
17831
|
}
|
|
17832
|
+
var MAX_UNTRACKED_COUNTED = 500;
|
|
17770
17833
|
async function statusFilesWithCounts(worktreePath) {
|
|
17771
17834
|
const files = await statusFiles(worktreePath);
|
|
17772
17835
|
if (files.length === 0)
|
|
17773
17836
|
return files;
|
|
17774
17837
|
const counts = files.some((f) => f.xy !== "??") ? await numstat(worktreePath, "HEAD") : new Map;
|
|
17838
|
+
const countUntracked = files.filter((f) => f.xy === "??").length <= MAX_UNTRACKED_COUNTED;
|
|
17775
17839
|
return Promise.all(files.map(async (f) => ({
|
|
17776
17840
|
...f,
|
|
17777
|
-
...f.xy === "??" ? await untrackedLines(worktreePath, f.path) : counts.get(f.path) ?? {}
|
|
17841
|
+
...f.xy === "??" ? countUntracked ? await untrackedLines(worktreePath, f.path) : {} : counts.get(f.path) ?? {}
|
|
17778
17842
|
})));
|
|
17779
17843
|
}
|
|
17780
17844
|
async function committedFiles(worktreePath, defaultBr) {
|
|
@@ -18504,9 +18568,9 @@ class AfterLand {
|
|
|
18504
18568
|
}
|
|
18505
18569
|
|
|
18506
18570
|
// ../daemon/src/repos/registry.ts
|
|
18507
|
-
import { existsSync as
|
|
18571
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync6, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
18508
18572
|
import { homedir as homedir7 } from "os";
|
|
18509
|
-
import { basename as basename3, dirname as dirname6, join as
|
|
18573
|
+
import { basename as basename3, dirname as dirname6, join as join15 } from "path";
|
|
18510
18574
|
|
|
18511
18575
|
// ../daemon/src/worktrees/naming.ts
|
|
18512
18576
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -18532,13 +18596,13 @@ function variantLens(index) {
|
|
|
18532
18596
|
}
|
|
18533
18597
|
|
|
18534
18598
|
// ../daemon/src/repos/browse.ts
|
|
18535
|
-
import { existsSync as
|
|
18599
|
+
import { existsSync as existsSync14, statSync as statSync3 } from "fs";
|
|
18536
18600
|
import { readdir } from "fs/promises";
|
|
18537
18601
|
import { homedir as homedir5 } from "os";
|
|
18538
|
-
import { dirname as dirname4, join as
|
|
18602
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
18539
18603
|
var MAX_ENTRIES = 40;
|
|
18540
18604
|
function expandTilde(raw) {
|
|
18541
|
-
return raw === "~" || raw.startsWith("~/") ?
|
|
18605
|
+
return raw === "~" || raw.startsWith("~/") ? join11(homedir5(), raw.slice(1)) : raw;
|
|
18542
18606
|
}
|
|
18543
18607
|
function collapseTilde(abs) {
|
|
18544
18608
|
const home = homedir5();
|
|
@@ -18549,7 +18613,7 @@ function holdsNothing(names) {
|
|
|
18549
18613
|
}
|
|
18550
18614
|
async function describeFolder(abs) {
|
|
18551
18615
|
const path = collapseTilde(abs);
|
|
18552
|
-
if (
|
|
18616
|
+
if (existsSync14(join11(abs, ".git")))
|
|
18553
18617
|
return { path, kind: "project" };
|
|
18554
18618
|
try {
|
|
18555
18619
|
return { path, kind: holdsNothing(await readdir(abs)) ? "empty" : "folder" };
|
|
@@ -18564,10 +18628,10 @@ function describe2(typed, parent) {
|
|
|
18564
18628
|
isDir = statSync3(typed).isDirectory();
|
|
18565
18629
|
} catch {}
|
|
18566
18630
|
return {
|
|
18567
|
-
exists:
|
|
18631
|
+
exists: existsSync14(typed),
|
|
18568
18632
|
isDir,
|
|
18569
|
-
isRepo: isDir &&
|
|
18570
|
-
parentExists:
|
|
18633
|
+
isRepo: isDir && existsSync14(join11(typed, ".git")),
|
|
18634
|
+
parentExists: existsSync14(parent)
|
|
18571
18635
|
};
|
|
18572
18636
|
}
|
|
18573
18637
|
async function browsePath(raw) {
|
|
@@ -18591,16 +18655,16 @@ async function browsePath(raw) {
|
|
|
18591
18655
|
continue;
|
|
18592
18656
|
if (!name.toLowerCase().startsWith(prefix))
|
|
18593
18657
|
continue;
|
|
18594
|
-
const abs =
|
|
18595
|
-
entries.push({ path: collapseTilde(abs), name, isRepo:
|
|
18658
|
+
const abs = join11(dir, name);
|
|
18659
|
+
entries.push({ path: collapseTilde(abs), name, isRepo: existsSync14(join11(abs, ".git")) });
|
|
18596
18660
|
}
|
|
18597
18661
|
entries.sort((a, b) => a.isRepo !== b.isRepo ? a.isRepo ? -1 : 1 : a.name.localeCompare(b.name));
|
|
18598
18662
|
return { entries: entries.slice(0, MAX_ENTRIES), target };
|
|
18599
18663
|
}
|
|
18600
18664
|
|
|
18601
18665
|
// ../daemon/src/repos/config.ts
|
|
18602
|
-
import { closeSync, existsSync as
|
|
18603
|
-
import { join as
|
|
18666
|
+
import { closeSync, existsSync as existsSync15, openSync as openSync2, readdirSync as readdirSync3, readFileSync as readFileSync11, readSync } from "fs";
|
|
18667
|
+
import { join as join12 } from "path";
|
|
18604
18668
|
|
|
18605
18669
|
// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
18606
18670
|
function createScanner(text, ignoreTrivia = false) {
|
|
@@ -19964,7 +20028,7 @@ function applyEdits(text, edits) {
|
|
|
19964
20028
|
|
|
19965
20029
|
// ../daemon/src/repos/config.ts
|
|
19966
20030
|
var PLACES = [CONFIG_FILES.folder, CONFIG_FILES.root];
|
|
19967
|
-
var present = (repoPath, place) => [place.shared, place.local].filter((rel) =>
|
|
20031
|
+
var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync15(join12(repoPath, rel)));
|
|
19968
20032
|
var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
19969
20033
|
function parseSettings(text) {
|
|
19970
20034
|
const errors = [];
|
|
@@ -19989,7 +20053,7 @@ function readConfigFile(repoPath) {
|
|
|
19989
20053
|
for (const rel of files) {
|
|
19990
20054
|
let raw;
|
|
19991
20055
|
try {
|
|
19992
|
-
raw = parseSettings(
|
|
20056
|
+
raw = parseSettings(readFileSync11(join12(repoPath, rel), "utf8"));
|
|
19993
20057
|
} catch (e) {
|
|
19994
20058
|
return { ok: false, reason: `${rel} is not valid JSON: ${e instanceof Error ? e.message : String(e)}` };
|
|
19995
20059
|
}
|
|
@@ -20046,12 +20110,12 @@ function configTarget(repoPath, made) {
|
|
|
20046
20110
|
}
|
|
20047
20111
|
function configBody(repoPath, rel, config) {
|
|
20048
20112
|
const place = PLACES.find((p) => p.local === rel);
|
|
20049
|
-
const abs = place &&
|
|
20050
|
-
if (!abs || !
|
|
20113
|
+
const abs = place && join12(repoPath, place.shared);
|
|
20114
|
+
if (!abs || !existsSync15(abs))
|
|
20051
20115
|
return config;
|
|
20052
20116
|
let shared;
|
|
20053
20117
|
try {
|
|
20054
|
-
shared = parseSettings(
|
|
20118
|
+
shared = parseSettings(readFileSync11(abs, "utf8"));
|
|
20055
20119
|
} catch {
|
|
20056
20120
|
return config;
|
|
20057
20121
|
}
|
|
@@ -20064,7 +20128,7 @@ function configText(repoPath, rel, config) {
|
|
|
20064
20128
|
let text;
|
|
20065
20129
|
let was;
|
|
20066
20130
|
try {
|
|
20067
|
-
text =
|
|
20131
|
+
text = readFileSync11(join12(repoPath, rel), "utf8");
|
|
20068
20132
|
was = parseSettings(text);
|
|
20069
20133
|
} catch {
|
|
20070
20134
|
return fresh;
|
|
@@ -20094,9 +20158,9 @@ function detectConfig(repoPath) {
|
|
|
20094
20158
|
return { config: explicit.config, needsSetup: false };
|
|
20095
20159
|
if (explicit)
|
|
20096
20160
|
log.warn("config", `${repoPath}: ${explicit.reason}; detecting instead`);
|
|
20097
|
-
const pkgPath =
|
|
20098
|
-
if (
|
|
20099
|
-
const pkg = JSON.parse(
|
|
20161
|
+
const pkgPath = join12(repoPath, "package.json");
|
|
20162
|
+
if (existsSync15(pkgPath)) {
|
|
20163
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
|
|
20100
20164
|
const scripts = pkg.scripts ?? {};
|
|
20101
20165
|
const runner = detectRunner(repoPath, pkg.packageManager);
|
|
20102
20166
|
const pages = detectPages(repoPath, scripts, runner);
|
|
@@ -20117,7 +20181,7 @@ function detectConfig(repoPath) {
|
|
|
20117
20181
|
};
|
|
20118
20182
|
}
|
|
20119
20183
|
}
|
|
20120
|
-
if (
|
|
20184
|
+
if (existsSync15(join12(repoPath, "start.sh"))) {
|
|
20121
20185
|
return { config: { run: { app: "./start.sh" } }, needsSetup: true, from: "start.sh" };
|
|
20122
20186
|
}
|
|
20123
20187
|
const assumed = assumeNothingToRun(repoPath);
|
|
@@ -20160,7 +20224,7 @@ function assumeNothingToRun(repoPath) {
|
|
|
20160
20224
|
return marker && !servesPage(repoPath) ? marker : undefined;
|
|
20161
20225
|
}
|
|
20162
20226
|
function markerIn(repoPath) {
|
|
20163
|
-
const named = NO_PAGE_MARKERS.find((f) =>
|
|
20227
|
+
const named = NO_PAGE_MARKERS.find((f) => existsSync15(join12(repoPath, f)));
|
|
20164
20228
|
if (named)
|
|
20165
20229
|
return named;
|
|
20166
20230
|
let entries;
|
|
@@ -20172,12 +20236,12 @@ function markerIn(repoPath) {
|
|
|
20172
20236
|
return entries.find((f) => NO_PAGE_EXTENSIONS.some((ext) => f.endsWith(ext)));
|
|
20173
20237
|
}
|
|
20174
20238
|
function servesPage(repoPath) {
|
|
20175
|
-
if (WEB_FILES.some((f) =>
|
|
20239
|
+
if (WEB_FILES.some((f) => existsSync15(join12(repoPath, f))))
|
|
20176
20240
|
return true;
|
|
20177
|
-
if (/^web:/m.test(head(
|
|
20241
|
+
if (/^web:/m.test(head(join12(repoPath, "Procfile"))))
|
|
20178
20242
|
return true;
|
|
20179
20243
|
for (const [files, names] of WEB_DEPENDENCIES) {
|
|
20180
|
-
const text = files.map((f) => head(
|
|
20244
|
+
const text = files.map((f) => head(join12(repoPath, f))).join(`
|
|
20181
20245
|
`);
|
|
20182
20246
|
if (text.trim() && names.some((n) => mentions(text, n)))
|
|
20183
20247
|
return true;
|
|
@@ -20194,7 +20258,7 @@ function frontEndBeside(repoPath) {
|
|
|
20194
20258
|
return dirs.some((d) => {
|
|
20195
20259
|
if (!d.isDirectory() || d.name.startsWith(".") || NOT_A_PACKAGE.has(d.name))
|
|
20196
20260
|
return false;
|
|
20197
|
-
const pkg = head(
|
|
20261
|
+
const pkg = head(join12(repoPath, d.name, "package.json"));
|
|
20198
20262
|
if (!pkg)
|
|
20199
20263
|
return false;
|
|
20200
20264
|
try {
|
|
@@ -20209,7 +20273,7 @@ function mentions(text, name) {
|
|
|
20209
20273
|
return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i").test(text);
|
|
20210
20274
|
}
|
|
20211
20275
|
function head(path) {
|
|
20212
|
-
if (!
|
|
20276
|
+
if (!existsSync15(path))
|
|
20213
20277
|
return "";
|
|
20214
20278
|
try {
|
|
20215
20279
|
const fd = openSync2(path, "r");
|
|
@@ -20237,31 +20301,31 @@ function procCommand(runner, script, body) {
|
|
|
20237
20301
|
return `${base}${runner === "npm" ? " --" : ""} ${flag}`;
|
|
20238
20302
|
}
|
|
20239
20303
|
function detectPages(repoPath, scripts, runner) {
|
|
20240
|
-
if (!scripts.build || !
|
|
20304
|
+
if (!scripts.build || !existsSync15(join12(repoPath, "functions")))
|
|
20241
20305
|
return null;
|
|
20242
|
-
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) =>
|
|
20306
|
+
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join12(repoPath, f)).find((f) => existsSync15(f));
|
|
20243
20307
|
if (!cfg)
|
|
20244
20308
|
return null;
|
|
20245
|
-
const out =
|
|
20309
|
+
const out = readFileSync11(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
|
|
20246
20310
|
if (!out)
|
|
20247
20311
|
return null;
|
|
20248
20312
|
return { web: `${runner} run build && npx wrangler pages dev ${out} --ip 127.0.0.1 --port $PORT` };
|
|
20249
20313
|
}
|
|
20250
20314
|
function detectRunner(repoPath, packageManager) {
|
|
20251
|
-
if (
|
|
20315
|
+
if (existsSync15(join12(repoPath, "bun.lock")) || existsSync15(join12(repoPath, "bun.lockb")))
|
|
20252
20316
|
return "bun";
|
|
20253
|
-
if (
|
|
20317
|
+
if (existsSync15(join12(repoPath, "pnpm-lock.yaml")))
|
|
20254
20318
|
return "pnpm";
|
|
20255
|
-
if (
|
|
20319
|
+
if (existsSync15(join12(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
|
|
20256
20320
|
return "yarn";
|
|
20257
20321
|
return "npm";
|
|
20258
20322
|
}
|
|
20259
20323
|
|
|
20260
20324
|
// ../daemon/src/repos/create.ts
|
|
20261
|
-
import { existsSync as
|
|
20325
|
+
import { existsSync as existsSync16, readdirSync as readdirSync4, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
20262
20326
|
import { appendFile as appendFile2, mkdir as mkdir2, rm } from "fs/promises";
|
|
20263
20327
|
import { homedir as homedir6 } from "os";
|
|
20264
|
-
import { dirname as dirname5, join as
|
|
20328
|
+
import { dirname as dirname5, join as join13, resolve as resolve7 } from "path";
|
|
20265
20329
|
function isInside(dir, parent) {
|
|
20266
20330
|
const p = parent.replace(/\/+$/, "");
|
|
20267
20331
|
return dir === p || dir.startsWith(`${p}/`);
|
|
@@ -20272,27 +20336,27 @@ function planProject(opts, home = homedir6()) {
|
|
|
20272
20336
|
throw new UserError(nameError);
|
|
20273
20337
|
const first = firstProjectsFolder(opts.parent, home);
|
|
20274
20338
|
const parent = first ?? resolveParent(opts.parent);
|
|
20275
|
-
const dir =
|
|
20276
|
-
if (
|
|
20339
|
+
const dir = join13(parent, opts.name.trim());
|
|
20340
|
+
if (existsSync16(dir))
|
|
20277
20341
|
throw new UserError(`${dir} already exists`);
|
|
20278
20342
|
return { parent, dir, makeParent: first !== null };
|
|
20279
20343
|
}
|
|
20280
20344
|
function firstProjectsFolder(raw, home) {
|
|
20281
20345
|
const typed = expandTilde(raw.trim()).replace(/\/+$/, "");
|
|
20282
|
-
if (typed !==
|
|
20346
|
+
if (typed !== join13(home, PROJECTS_FOLDER) || existsSync16(typed) || !existsSync16(home))
|
|
20283
20347
|
return null;
|
|
20284
|
-
return
|
|
20348
|
+
return join13(realpathSync2(home), PROJECTS_FOLDER);
|
|
20285
20349
|
}
|
|
20286
20350
|
function planInPlace(opts) {
|
|
20287
20351
|
const name = opts.name.trim();
|
|
20288
20352
|
if (!name || name === "." || name === ".." || name.includes("/")) {
|
|
20289
20353
|
throw new UserError(`${opts.name} is not a folder name`);
|
|
20290
20354
|
}
|
|
20291
|
-
const typed =
|
|
20292
|
-
if (!
|
|
20355
|
+
const typed = join13(resolveParent(opts.parent), name);
|
|
20356
|
+
if (!existsSync16(typed) || !statSync4(typed).isDirectory())
|
|
20293
20357
|
throw new UserError(`${typed} is not a folder`);
|
|
20294
20358
|
const dir = realpathSync2(typed);
|
|
20295
|
-
if (
|
|
20359
|
+
if (existsSync16(join13(dir, ".git")))
|
|
20296
20360
|
throw new UserError(`${dir} is already a project; open it instead`);
|
|
20297
20361
|
if (!holdsNothing(readdirSync4(dir))) {
|
|
20298
20362
|
throw new UserError(`${dir} is not empty; only an empty folder can become a project where it is`);
|
|
@@ -20303,7 +20367,7 @@ function resolveParent(raw) {
|
|
|
20303
20367
|
const typed = expandTilde(raw.trim());
|
|
20304
20368
|
if (!typed.startsWith("/"))
|
|
20305
20369
|
throw new UserError(`${raw} is not a folder path`);
|
|
20306
|
-
if (!
|
|
20370
|
+
if (!existsSync16(typed))
|
|
20307
20371
|
throw new UserError(`${raw} does not exist; make it first`);
|
|
20308
20372
|
if (!statSync4(typed).isDirectory())
|
|
20309
20373
|
throw new UserError(`${raw} is not a folder`);
|
|
@@ -20383,13 +20447,13 @@ async function undoCreate(dir) {
|
|
|
20383
20447
|
}
|
|
20384
20448
|
async function undoInit(dir) {
|
|
20385
20449
|
try {
|
|
20386
|
-
await rm(
|
|
20450
|
+
await rm(join13(dir, ".git"), { recursive: true, force: true });
|
|
20387
20451
|
} catch (e) {
|
|
20388
20452
|
log.warn(dir, "could not take back a half-made .git", e);
|
|
20389
20453
|
}
|
|
20390
20454
|
}
|
|
20391
20455
|
async function unmakeProject(dir, made) {
|
|
20392
|
-
const target = made === "folder" ? dir :
|
|
20456
|
+
const target = made === "folder" ? dir : join13(dir, ".git");
|
|
20393
20457
|
try {
|
|
20394
20458
|
await rm(target, { recursive: true, force: true });
|
|
20395
20459
|
} catch (e) {
|
|
@@ -20419,8 +20483,8 @@ async function ok(p, what) {
|
|
|
20419
20483
|
}
|
|
20420
20484
|
|
|
20421
20485
|
// ../daemon/src/repos/watcher.ts
|
|
20422
|
-
import { existsSync as
|
|
20423
|
-
import { basename as basename2, join as
|
|
20486
|
+
import { existsSync as existsSync17, watch } from "fs";
|
|
20487
|
+
import { basename as basename2, join as join14 } from "path";
|
|
20424
20488
|
function watchDefaultBranch(repoPath, branch, onMove) {
|
|
20425
20489
|
let last = null;
|
|
20426
20490
|
fireAndForget(repoPath, git(repoPath, "rev-parse", branch).then((r) => {
|
|
@@ -20450,8 +20514,8 @@ function watchDefaultBranch(repoPath, branch, onMove) {
|
|
|
20450
20514
|
log.debug(repoPath, `not watching ${p}`, e);
|
|
20451
20515
|
}
|
|
20452
20516
|
};
|
|
20453
|
-
tryWatch(
|
|
20454
|
-
tryWatch(
|
|
20517
|
+
tryWatch(join14(repoPath, ".git"));
|
|
20518
|
+
tryWatch(join14(repoPath, ".git", "refs", "heads"));
|
|
20455
20519
|
return () => {
|
|
20456
20520
|
for (const w of watchers)
|
|
20457
20521
|
w.close();
|
|
@@ -20473,7 +20537,7 @@ function watchWorktreeDir(repoPath, onChange) {
|
|
|
20473
20537
|
}, 300);
|
|
20474
20538
|
};
|
|
20475
20539
|
const arm = (commonDir) => {
|
|
20476
|
-
const dir =
|
|
20540
|
+
const dir = join14(commonDir, "worktrees");
|
|
20477
20541
|
const armInner = () => {
|
|
20478
20542
|
inner?.close();
|
|
20479
20543
|
inner = null;
|
|
@@ -20516,7 +20580,7 @@ function watchWorktreeDir(repoPath, onChange) {
|
|
|
20516
20580
|
function watchConfigFile(repoPath, onChange) {
|
|
20517
20581
|
const atRoot = new Set([CONFIG_FILES.root.shared, CONFIG_FILES.root.local, CONFIG_DIR]);
|
|
20518
20582
|
const inDir = new Set([CONFIG_FILES.folder.shared, CONFIG_FILES.folder.local].map((rel) => basename2(rel)));
|
|
20519
|
-
const dir =
|
|
20583
|
+
const dir = join14(repoPath, CONFIG_DIR);
|
|
20520
20584
|
let timer = null;
|
|
20521
20585
|
let outer = null;
|
|
20522
20586
|
let inner = null;
|
|
@@ -20531,7 +20595,7 @@ function watchConfigFile(repoPath, onChange) {
|
|
|
20531
20595
|
const armInner = () => {
|
|
20532
20596
|
inner?.close();
|
|
20533
20597
|
inner = null;
|
|
20534
|
-
if (!
|
|
20598
|
+
if (!existsSync17(dir))
|
|
20535
20599
|
return;
|
|
20536
20600
|
try {
|
|
20537
20601
|
inner = watch(dir, (_event, filename) => {
|
|
@@ -20585,7 +20649,7 @@ class RepoRegistry {
|
|
|
20585
20649
|
}
|
|
20586
20650
|
async boot() {
|
|
20587
20651
|
const { state } = this.d;
|
|
20588
|
-
const gone = state.worktrees.filter((wt) => !
|
|
20652
|
+
const gone = state.worktrees.filter((wt) => !existsSync18(wt.path) || !state.repos.some((r) => r.id === wt.repoId));
|
|
20589
20653
|
for (const wt of gone)
|
|
20590
20654
|
await this.d.worktrees.forgetGone(wt);
|
|
20591
20655
|
for (const repo of state.repos) {
|
|
@@ -20652,7 +20716,7 @@ class RepoRegistry {
|
|
|
20652
20716
|
return `${repo.name} is set up now`;
|
|
20653
20717
|
if (main.promptedAt || main.lastTurn)
|
|
20654
20718
|
return `${repo.name} has a chat now`;
|
|
20655
|
-
if (
|
|
20719
|
+
if (existsSync18(join15(repo.path, ".claude")))
|
|
20656
20720
|
return `an agent has already run in ${repo.name}`;
|
|
20657
20721
|
if ((await statusFiles(repo.path)).length > 0 || !await treeEmpty(repo.path)) {
|
|
20658
20722
|
return `${repo.name} has files in it now`;
|
|
@@ -20729,7 +20793,7 @@ class RepoRegistry {
|
|
|
20729
20793
|
}
|
|
20730
20794
|
async register(rawPath, made) {
|
|
20731
20795
|
const path = expandTilde(rawPath);
|
|
20732
|
-
if (!
|
|
20796
|
+
if (!existsSync18(path))
|
|
20733
20797
|
throw new UserError(`${path} does not exist`);
|
|
20734
20798
|
if (!await isGitRepo(path))
|
|
20735
20799
|
throw new UserError(`${path} is not a git repository`);
|
|
@@ -20802,10 +20866,10 @@ class RepoRegistry {
|
|
|
20802
20866
|
throw new UserError(current.reason);
|
|
20803
20867
|
const was = configTarget(repo.path, !!repo.made);
|
|
20804
20868
|
const rel = configSibling(was, kind);
|
|
20805
|
-
const moved = rel !== was &&
|
|
20869
|
+
const moved = rel !== was && existsSync18(join15(repo.path, was));
|
|
20806
20870
|
if (moved && !(kind === "local" && await isTracked(repo.path, was))) {
|
|
20807
20871
|
try {
|
|
20808
|
-
rmSync2(
|
|
20872
|
+
rmSync2(join15(repo.path, was));
|
|
20809
20873
|
} catch (e) {
|
|
20810
20874
|
log.warn(repoId, `could not remove ${was}`, e);
|
|
20811
20875
|
}
|
|
@@ -20814,10 +20878,10 @@ class RepoRegistry {
|
|
|
20814
20878
|
}
|
|
20815
20879
|
if (isLocalConfigFile(rel))
|
|
20816
20880
|
await excludeFromGit(repo.path, rel);
|
|
20817
|
-
const file =
|
|
20881
|
+
const file = join15(repo.path, rel);
|
|
20818
20882
|
try {
|
|
20819
|
-
|
|
20820
|
-
|
|
20883
|
+
mkdirSync6(dirname6(file), { recursive: true });
|
|
20884
|
+
writeFileSync7(file, configText(repo.path, rel, config));
|
|
20821
20885
|
} catch (e) {
|
|
20822
20886
|
log.warn(repoId, `could not write ${rel}`, e);
|
|
20823
20887
|
}
|
|
@@ -20891,7 +20955,7 @@ class RepoRegistry {
|
|
|
20891
20955
|
}
|
|
20892
20956
|
placeConfig(repo) {
|
|
20893
20957
|
const rel = configTarget(repo.path, !!repo.made);
|
|
20894
|
-
if (isLocalConfigFile(rel) &&
|
|
20958
|
+
if (isLocalConfigFile(rel) && existsSync18(join15(repo.path, rel))) {
|
|
20895
20959
|
fireAndForget(repo.id, excludeFromGit(repo.path, rel), "exclude local settings");
|
|
20896
20960
|
}
|
|
20897
20961
|
if (repo.configFile === rel)
|
|
@@ -20966,7 +21030,7 @@ class RepoRegistry {
|
|
|
20966
21030
|
}
|
|
20967
21031
|
|
|
20968
21032
|
// ../daemon/src/routes/service.ts
|
|
20969
|
-
import { join as
|
|
21033
|
+
import { join as join16 } from "path";
|
|
20970
21034
|
|
|
20971
21035
|
// ../daemon/src/routes/fileRouters.ts
|
|
20972
21036
|
var SKIP = /(^|\/)(node_modules|dist|build|out|coverage|\.next|\.nuxt|\.output|\.svelte-kit|\.vercel|\.netlify)\//;
|
|
@@ -22166,7 +22230,7 @@ class RouteService {
|
|
|
22166
22230
|
return mergeRoutes(fileRoutes(paths, roots), code);
|
|
22167
22231
|
}
|
|
22168
22232
|
async cachedRead(root, path, had, kept, decode) {
|
|
22169
|
-
const abs =
|
|
22233
|
+
const abs = join16(root, path);
|
|
22170
22234
|
const stat = await this.fs.stat(abs);
|
|
22171
22235
|
if (!stat)
|
|
22172
22236
|
return null;
|
|
@@ -22191,7 +22255,7 @@ class RouteService {
|
|
|
22191
22255
|
}
|
|
22192
22256
|
}
|
|
22193
22257
|
async hash(root, file) {
|
|
22194
|
-
const abs =
|
|
22258
|
+
const abs = join16(root, file);
|
|
22195
22259
|
const stat = await this.fs.stat(abs);
|
|
22196
22260
|
if (!stat)
|
|
22197
22261
|
return null;
|
|
@@ -22235,7 +22299,7 @@ class RouteService {
|
|
|
22235
22299
|
}
|
|
22236
22300
|
|
|
22237
22301
|
// ../daemon/src/runtime/bridge-script.ts
|
|
22238
|
-
import { existsSync as
|
|
22302
|
+
import { existsSync as existsSync19, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
|
|
22239
22303
|
var LEARNED_MAX = 8;
|
|
22240
22304
|
|
|
22241
22305
|
class BridgeScript {
|
|
@@ -22265,7 +22329,7 @@ class BridgeScript {
|
|
|
22265
22329
|
this.cache = null;
|
|
22266
22330
|
}
|
|
22267
22331
|
get() {
|
|
22268
|
-
if (!
|
|
22332
|
+
if (!existsSync19(this.path))
|
|
22269
22333
|
return "// toyon bridge not built";
|
|
22270
22334
|
const s = statSync5(this.path);
|
|
22271
22335
|
const stamp = `${s.mtimeMs}:${s.size}`;
|
|
@@ -22274,7 +22338,7 @@ class BridgeScript {
|
|
|
22274
22338
|
this.stamp = stamp;
|
|
22275
22339
|
const origins = [...this.origins, ...this.learned];
|
|
22276
22340
|
this.cache = `window.__toyonShellOrigins=${JSON.stringify(origins)};
|
|
22277
|
-
${
|
|
22341
|
+
${readFileSync12(this.path, "utf8")}`;
|
|
22278
22342
|
return this.cache;
|
|
22279
22343
|
}
|
|
22280
22344
|
}
|
|
@@ -22647,50 +22711,50 @@ class PairCodes {
|
|
|
22647
22711
|
}
|
|
22648
22712
|
|
|
22649
22713
|
// ../daemon/src/agent/config.ts
|
|
22650
|
-
import { existsSync as
|
|
22714
|
+
import { existsSync as existsSync20, readFileSync as readFileSync13 } from "fs";
|
|
22651
22715
|
import { homedir as homedir8 } from "os";
|
|
22652
|
-
import { join as
|
|
22716
|
+
import { join as join17 } from "path";
|
|
22653
22717
|
function agentConfigFiles(spec, repoPath, home = homedir8()) {
|
|
22654
22718
|
const file = (id, label, path) => ({
|
|
22655
22719
|
id,
|
|
22656
22720
|
label,
|
|
22657
22721
|
path,
|
|
22658
|
-
exists:
|
|
22722
|
+
exists: existsSync20(path)
|
|
22659
22723
|
});
|
|
22660
22724
|
if (spec.id === "claude") {
|
|
22661
22725
|
return [
|
|
22662
|
-
file("user-config", "user config (MCP servers, projects)",
|
|
22663
|
-
file("user-settings", "user settings (permissions, hooks)",
|
|
22664
|
-
...repoPath ? [file("project-mcp", "project MCP servers",
|
|
22665
|
-
...repoPath ? [file("project-settings", "project settings",
|
|
22726
|
+
file("user-config", "user config (MCP servers, projects)", join17(home, ".claude.json")),
|
|
22727
|
+
file("user-settings", "user settings (permissions, hooks)", join17(home, ".claude", "settings.json")),
|
|
22728
|
+
...repoPath ? [file("project-mcp", "project MCP servers", join17(repoPath, ".mcp.json"))] : [],
|
|
22729
|
+
...repoPath ? [file("project-settings", "project settings", join17(repoPath, ".claude", "settings.json"))] : []
|
|
22666
22730
|
];
|
|
22667
22731
|
}
|
|
22668
22732
|
if (spec.id === "codex")
|
|
22669
|
-
return [file("user-config", "config",
|
|
22733
|
+
return [file("user-config", "config", join17(home, ".codex", "config.toml"))];
|
|
22670
22734
|
return [];
|
|
22671
22735
|
}
|
|
22672
22736
|
function describeAgentConfig(spec, repoPath, home = homedir8()) {
|
|
22673
22737
|
const files = agentConfigFiles(spec, repoPath, home);
|
|
22674
22738
|
const servers = [];
|
|
22675
22739
|
if (spec.id === "claude") {
|
|
22676
|
-
const user = readJson(
|
|
22740
|
+
const user = readJson(join17(home, ".claude.json"));
|
|
22677
22741
|
servers.push(...serversOf(user?.mcpServers, "user"));
|
|
22678
22742
|
if (repoPath) {
|
|
22679
22743
|
const projects = user?.projects;
|
|
22680
22744
|
const mine = projects?.[repoPath];
|
|
22681
22745
|
servers.push(...serversOf(mine?.mcpServers, "local"));
|
|
22682
|
-
servers.push(...serversOf(readJson(
|
|
22746
|
+
servers.push(...serversOf(readJson(join17(repoPath, ".mcp.json"))?.mcpServers, "project"));
|
|
22683
22747
|
}
|
|
22684
22748
|
} else if (spec.id === "codex") {
|
|
22685
|
-
servers.push(...codexServers(
|
|
22749
|
+
servers.push(...codexServers(join17(home, ".codex", "config.toml")));
|
|
22686
22750
|
}
|
|
22687
22751
|
return { agent: spec.id, files, servers };
|
|
22688
22752
|
}
|
|
22689
22753
|
function readJson(path) {
|
|
22690
|
-
if (!
|
|
22754
|
+
if (!existsSync20(path))
|
|
22691
22755
|
return null;
|
|
22692
22756
|
try {
|
|
22693
|
-
const v = JSON.parse(
|
|
22757
|
+
const v = JSON.parse(readFileSync13(path, "utf8"));
|
|
22694
22758
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
22695
22759
|
} catch (e) {
|
|
22696
22760
|
log.warn("agent-config", `${path} is not valid JSON`, e);
|
|
@@ -22708,11 +22772,11 @@ function serversOf(raw, scope) {
|
|
|
22708
22772
|
});
|
|
22709
22773
|
}
|
|
22710
22774
|
function codexServers(path) {
|
|
22711
|
-
if (!
|
|
22775
|
+
if (!existsSync20(path))
|
|
22712
22776
|
return [];
|
|
22713
22777
|
const out = [];
|
|
22714
22778
|
let current = null;
|
|
22715
|
-
for (const raw of
|
|
22779
|
+
for (const raw of readFileSync13(path, "utf8").split(`
|
|
22716
22780
|
`)) {
|
|
22717
22781
|
const line = raw.trim();
|
|
22718
22782
|
const header = /^\[mcp_servers\.("?)([^\]"]+)\1\]$/.exec(line);
|
|
@@ -23698,8 +23762,8 @@ async function dispatch(msg, ctx, s) {
|
|
|
23698
23762
|
}
|
|
23699
23763
|
|
|
23700
23764
|
// ../daemon/src/server/http.ts
|
|
23701
|
-
import { existsSync as
|
|
23702
|
-
import { join as
|
|
23765
|
+
import { existsSync as existsSync21 } from "fs";
|
|
23766
|
+
import { join as join18 } from "path";
|
|
23703
23767
|
var IMMUTABLE = "private, max-age=31536000, immutable";
|
|
23704
23768
|
var NO_STORE = "no-store";
|
|
23705
23769
|
function createFetch(opts) {
|
|
@@ -23821,7 +23885,7 @@ function createFetch(opts) {
|
|
|
23821
23885
|
return new Response("unauthorized", { status: 401 });
|
|
23822
23886
|
const [worktreeId, file, extra] = url.pathname.slice("/attachments/".length).split("/");
|
|
23823
23887
|
const places = worktreeId && file && !extra ? [opts.attachments.fileFor(worktreeId, file), opts.archivedAttachment(worktreeId, file)] : [];
|
|
23824
|
-
const path = places.find((p) => !!p &&
|
|
23888
|
+
const path = places.find((p) => !!p && existsSync21(p));
|
|
23825
23889
|
if (!path)
|
|
23826
23890
|
return new Response("not found", { status: 404 });
|
|
23827
23891
|
return new Response(Bun.file(path), { headers: { "cache-control": IMMUTABLE } });
|
|
@@ -23842,15 +23906,15 @@ function createFetch(opts) {
|
|
|
23842
23906
|
});
|
|
23843
23907
|
}
|
|
23844
23908
|
const rel = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
23845
|
-
const file =
|
|
23909
|
+
const file = join18(opts.shellDist, rel.replaceAll("..", ""));
|
|
23846
23910
|
const hashed = rel.startsWith("/assets/");
|
|
23847
|
-
if (
|
|
23911
|
+
if (existsSync21(file) && Bun.file(file).size > 0) {
|
|
23848
23912
|
return new Response(Bun.file(file), { headers: { "cache-control": hashed ? IMMUTABLE : NO_STORE } });
|
|
23849
23913
|
}
|
|
23850
23914
|
if (hashed)
|
|
23851
23915
|
return new Response("not found", { status: 404 });
|
|
23852
|
-
const index =
|
|
23853
|
-
if (
|
|
23916
|
+
const index = join18(opts.shellDist, "index.html");
|
|
23917
|
+
if (existsSync21(index))
|
|
23854
23918
|
return new Response(Bun.file(index), { headers: { "cache-control": NO_STORE } });
|
|
23855
23919
|
return new Response("Toyon daemon running; shell not built (run: bun run build)", { status: 200 });
|
|
23856
23920
|
};
|
|
@@ -24218,16 +24282,16 @@ function startServer(opts) {
|
|
|
24218
24282
|
}
|
|
24219
24283
|
|
|
24220
24284
|
// ../daemon/src/themes/store.ts
|
|
24221
|
-
import { existsSync as
|
|
24285
|
+
import { existsSync as existsSync22, readdirSync as readdirSync5, readFileSync as readFileSync14, statSync as statSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
24222
24286
|
import { homedir as homedir10 } from "os";
|
|
24223
|
-
import { basename as basename4, dirname as dirname7, join as
|
|
24287
|
+
import { basename as basename4, dirname as dirname7, join as join19, resolve as resolve8 } from "path";
|
|
24224
24288
|
var home = homedir10();
|
|
24225
24289
|
var defaultExtensionDirs = [
|
|
24226
|
-
|
|
24227
|
-
|
|
24228
|
-
|
|
24229
|
-
|
|
24230
|
-
|
|
24290
|
+
join19(home, ".vscode", "extensions"),
|
|
24291
|
+
join19(home, ".vscode-insiders", "extensions"),
|
|
24292
|
+
join19(home, ".vscode-oss", "extensions"),
|
|
24293
|
+
join19(home, ".cursor", "extensions"),
|
|
24294
|
+
join19(home, ".windsurf", "extensions"),
|
|
24231
24295
|
"/Applications/Visual Studio Code.app/Contents/Resources/app/extensions",
|
|
24232
24296
|
"/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/extensions",
|
|
24233
24297
|
"/Applications/Cursor.app/Contents/Resources/app/extensions",
|
|
@@ -24311,14 +24375,14 @@ class ThemeStore {
|
|
|
24311
24375
|
name: json?.name ?? base,
|
|
24312
24376
|
source: "file"
|
|
24313
24377
|
});
|
|
24314
|
-
|
|
24378
|
+
writeFileSync8(join19(this.themesDir, `${base}.json`), `${JSON.stringify(theme, null, 2)}
|
|
24315
24379
|
`);
|
|
24316
24380
|
this.load();
|
|
24317
24381
|
return theme;
|
|
24318
24382
|
}
|
|
24319
24383
|
}
|
|
24320
24384
|
function readJsonc(file) {
|
|
24321
|
-
return parse6(
|
|
24385
|
+
return parse6(readFileSync14(file, "utf8"), [], { allowTrailingComma: true });
|
|
24322
24386
|
}
|
|
24323
24387
|
function readVscodeTheme(file, depth = 0) {
|
|
24324
24388
|
const json = readJsonc(file);
|
|
@@ -24340,13 +24404,13 @@ function readVscodeTheme(file, depth = 0) {
|
|
|
24340
24404
|
return t;
|
|
24341
24405
|
}
|
|
24342
24406
|
function loadDir(dir) {
|
|
24343
|
-
if (!
|
|
24407
|
+
if (!existsSync22(dir))
|
|
24344
24408
|
return [];
|
|
24345
24409
|
const out = [];
|
|
24346
24410
|
for (const f of readdirSync5(dir)) {
|
|
24347
24411
|
if (!/\.jsonc?$/i.test(f))
|
|
24348
24412
|
continue;
|
|
24349
|
-
const file =
|
|
24413
|
+
const file = join19(dir, f);
|
|
24350
24414
|
const id = `file:${slug(f.replace(/\.(json|jsonc)$/i, ""))}`;
|
|
24351
24415
|
try {
|
|
24352
24416
|
const json = readJsonc(file);
|
|
@@ -24362,7 +24426,7 @@ function loadDir(dir) {
|
|
|
24362
24426
|
return out;
|
|
24363
24427
|
}
|
|
24364
24428
|
function discover(dir) {
|
|
24365
|
-
if (!
|
|
24429
|
+
if (!existsSync22(dir))
|
|
24366
24430
|
return [];
|
|
24367
24431
|
const out = [];
|
|
24368
24432
|
let entries;
|
|
@@ -24372,21 +24436,21 @@ function discover(dir) {
|
|
|
24372
24436
|
return out;
|
|
24373
24437
|
}
|
|
24374
24438
|
for (const name of entries) {
|
|
24375
|
-
const extDir =
|
|
24376
|
-
const pkgFile =
|
|
24439
|
+
const extDir = join19(dir, name);
|
|
24440
|
+
const pkgFile = join19(extDir, "package.json");
|
|
24377
24441
|
try {
|
|
24378
|
-
if (!statSync6(extDir).isDirectory() || !
|
|
24442
|
+
if (!statSync6(extDir).isDirectory() || !existsSync22(pkgFile))
|
|
24379
24443
|
continue;
|
|
24380
|
-
const pkg = JSON.parse(
|
|
24444
|
+
const pkg = JSON.parse(readFileSync14(pkgFile, "utf8"));
|
|
24381
24445
|
const themes = pkg.contributes?.themes;
|
|
24382
24446
|
if (!Array.isArray(themes))
|
|
24383
24447
|
continue;
|
|
24384
24448
|
const ext = `${pkg.publisher ?? "local"}.${pkg.name ?? name}`;
|
|
24385
24449
|
let nls = {};
|
|
24386
|
-
const nlsFile =
|
|
24387
|
-
if (
|
|
24450
|
+
const nlsFile = join19(extDir, "package.nls.json");
|
|
24451
|
+
if (existsSync22(nlsFile)) {
|
|
24388
24452
|
try {
|
|
24389
|
-
nls = JSON.parse(
|
|
24453
|
+
nls = JSON.parse(readFileSync14(nlsFile, "utf8"));
|
|
24390
24454
|
} catch {}
|
|
24391
24455
|
}
|
|
24392
24456
|
for (const c of themes) {
|
|
@@ -25210,8 +25274,8 @@ function rankRefs({ refs, merged, prs, query, defaultBranch, rowIdForPath }) {
|
|
|
25210
25274
|
}
|
|
25211
25275
|
|
|
25212
25276
|
// ../daemon/src/worktrees/service.ts
|
|
25213
|
-
import { existsSync as
|
|
25214
|
-
import { basename as basename6, dirname as dirname8, join as
|
|
25277
|
+
import { existsSync as existsSync24, lstatSync, readFileSync as readFileSync16, readlinkSync, rmSync as rmSync5, symlinkSync, unlinkSync } from "fs";
|
|
25278
|
+
import { basename as basename6, dirname as dirname8, join as join22 } from "path";
|
|
25215
25279
|
|
|
25216
25280
|
// ../daemon/src/git/archive.ts
|
|
25217
25281
|
import { rmSync as rmSync3 } from "fs";
|
|
@@ -25220,12 +25284,16 @@ async function keepState(repoPath, wtPath, ref, indexFile) {
|
|
|
25220
25284
|
const head = await git(wtPath, "rev-parse", "--verify", "HEAD");
|
|
25221
25285
|
if (!head.ok)
|
|
25222
25286
|
return null;
|
|
25223
|
-
const status = await
|
|
25224
|
-
const
|
|
25287
|
+
const status = await gitRaw(wtPath, "status", "--porcelain", "-uall");
|
|
25288
|
+
const files = status.ok ? parsePorcelain(status.out).length : null;
|
|
25289
|
+
const dirty = files === null || files > 0;
|
|
25225
25290
|
const snapshot = dirty ? await snapshotCommit(wtPath, head.out, indexFile) : null;
|
|
25226
25291
|
if (!(await git(repoPath, "update-ref", ref, snapshot ?? head.out)).ok)
|
|
25227
25292
|
return null;
|
|
25228
|
-
return {
|
|
25293
|
+
return {
|
|
25294
|
+
head: head.out,
|
|
25295
|
+
...snapshot ? { snapshot, ...files ? { dirty: files } : {} } : dirty ? { lost: true } : {}
|
|
25296
|
+
};
|
|
25229
25297
|
}
|
|
25230
25298
|
async function snapshotCommit(wtPath, head, indexFile) {
|
|
25231
25299
|
const env = { GIT_INDEX_FILE: indexFile };
|
|
@@ -25497,20 +25565,20 @@ function compareUrl(remoteUrl, base, branch) {
|
|
|
25497
25565
|
}
|
|
25498
25566
|
|
|
25499
25567
|
// ../daemon/src/worktrees/archive.ts
|
|
25500
|
-
import { existsSync as
|
|
25501
|
-
import { join as
|
|
25568
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync7, readdirSync as readdirSync6, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
25569
|
+
import { join as join20 } from "path";
|
|
25502
25570
|
class WorktreeArchive {
|
|
25503
25571
|
dir;
|
|
25504
25572
|
records = new Map;
|
|
25505
25573
|
constructor(dir) {
|
|
25506
25574
|
this.dir = dir;
|
|
25507
|
-
|
|
25575
|
+
mkdirSync7(dir, { recursive: true });
|
|
25508
25576
|
for (const id of readdirSync6(dir)) {
|
|
25509
25577
|
const file = this.filesOf(id).record;
|
|
25510
|
-
if (!
|
|
25578
|
+
if (!existsSync23(file))
|
|
25511
25579
|
continue;
|
|
25512
25580
|
try {
|
|
25513
|
-
this.records.set(id, JSON.parse(
|
|
25581
|
+
this.records.set(id, JSON.parse(readFileSync15(file, "utf8")));
|
|
25514
25582
|
} catch (e) {
|
|
25515
25583
|
log.warn("archive", `skipped an unreadable record: ${file}`, e);
|
|
25516
25584
|
}
|
|
@@ -25521,10 +25589,10 @@ class WorktreeArchive {
|
|
|
25521
25589
|
}
|
|
25522
25590
|
put(rec, from) {
|
|
25523
25591
|
const at = this.filesOf(rec.worktree.id);
|
|
25524
|
-
|
|
25592
|
+
mkdirSync7(at.dir, { recursive: true });
|
|
25525
25593
|
move(from.transcript, at.transcript);
|
|
25526
25594
|
move(from.attachments, at.attachments);
|
|
25527
|
-
|
|
25595
|
+
writeFileSync9(at.record, JSON.stringify(rec, null, 2));
|
|
25528
25596
|
this.records.set(rec.worktree.id, rec);
|
|
25529
25597
|
}
|
|
25530
25598
|
take(id, to) {
|
|
@@ -25551,12 +25619,12 @@ class WorktreeArchive {
|
|
|
25551
25619
|
return { transcript: at.transcript, attachments: at.attachments };
|
|
25552
25620
|
}
|
|
25553
25621
|
filesOf(id) {
|
|
25554
|
-
const dir =
|
|
25622
|
+
const dir = join20(this.dir, id);
|
|
25555
25623
|
return {
|
|
25556
25624
|
dir,
|
|
25557
|
-
record:
|
|
25558
|
-
transcript:
|
|
25559
|
-
attachments:
|
|
25625
|
+
record: join20(dir, "record.json"),
|
|
25626
|
+
transcript: join20(dir, "transcript.jsonl"),
|
|
25627
|
+
attachments: join20(dir, "attachments")
|
|
25560
25628
|
};
|
|
25561
25629
|
}
|
|
25562
25630
|
}
|
|
@@ -25571,16 +25639,16 @@ function summarize(r, repoId) {
|
|
|
25571
25639
|
archivedAt: r.archivedAt,
|
|
25572
25640
|
...r.prompt ? { prompt: r.prompt } : {},
|
|
25573
25641
|
restorable: !!r.kept,
|
|
25574
|
-
...r.kept?.snapshot ? { uncommitted: true } : {},
|
|
25642
|
+
...r.kept?.snapshot ? { uncommitted: true, ...r.kept.dirty ? { dirty: r.kept.dirty } : {} } : {},
|
|
25575
25643
|
...r.worktree.landed ? { landed: true } : {},
|
|
25576
25644
|
...r.cost !== undefined ? { cost: r.cost } : {},
|
|
25577
25645
|
...r.auto ? { auto: r.auto } : {}
|
|
25578
25646
|
};
|
|
25579
25647
|
}
|
|
25580
25648
|
function lastUsage(transcript) {
|
|
25581
|
-
if (!
|
|
25649
|
+
if (!existsSync23(transcript))
|
|
25582
25650
|
return null;
|
|
25583
|
-
const lines =
|
|
25651
|
+
const lines = readFileSync15(transcript, "utf8").split(`
|
|
25584
25652
|
`);
|
|
25585
25653
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
25586
25654
|
if (!lines[i]?.includes('"usage"'))
|
|
@@ -25594,9 +25662,9 @@ function lastUsage(transcript) {
|
|
|
25594
25662
|
return null;
|
|
25595
25663
|
}
|
|
25596
25664
|
function firstPrompt(transcript) {
|
|
25597
|
-
if (!
|
|
25665
|
+
if (!existsSync23(transcript))
|
|
25598
25666
|
return;
|
|
25599
|
-
for (const line of
|
|
25667
|
+
for (const line of readFileSync15(transcript, "utf8").split(`
|
|
25600
25668
|
`)) {
|
|
25601
25669
|
if (!line.includes('"user-message"'))
|
|
25602
25670
|
continue;
|
|
@@ -25609,7 +25677,7 @@ function firstPrompt(transcript) {
|
|
|
25609
25677
|
return;
|
|
25610
25678
|
}
|
|
25611
25679
|
function move(from, to) {
|
|
25612
|
-
if (!
|
|
25680
|
+
if (!existsSync23(from))
|
|
25613
25681
|
return;
|
|
25614
25682
|
rmSync4(to, { recursive: true, force: true });
|
|
25615
25683
|
renameSync4(from, to);
|
|
@@ -25673,7 +25741,7 @@ class ArchivedGit {
|
|
|
25673
25741
|
}
|
|
25674
25742
|
|
|
25675
25743
|
// ../daemon/src/worktrees/spare.ts
|
|
25676
|
-
import { join as
|
|
25744
|
+
import { join as join21 } from "path";
|
|
25677
25745
|
class SparePool {
|
|
25678
25746
|
d;
|
|
25679
25747
|
spares = new Map;
|
|
@@ -25722,7 +25790,7 @@ class SparePool {
|
|
|
25722
25790
|
this.spares.set(repoId, entry);
|
|
25723
25791
|
try {
|
|
25724
25792
|
const slug = `wt-${shortId().slice(0, 4)}`;
|
|
25725
|
-
const wtPath =
|
|
25793
|
+
const wtPath = join21(this.d.paths.worktreesDir, repo.name, slug);
|
|
25726
25794
|
await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "--detach", wtPath, repo.defaultBranch));
|
|
25727
25795
|
const wt = {
|
|
25728
25796
|
id: shortId(),
|
|
@@ -25953,6 +26021,7 @@ function stopOf(prev, next) {
|
|
|
25953
26021
|
|
|
25954
26022
|
// ../daemon/src/worktrees/service.ts
|
|
25955
26023
|
var LOCAL_CONFIG_FILES = [".env", ".env.local", ".env.development", ".env.development.local", ".dev.vars"];
|
|
26024
|
+
var BASE_IGNORE = "base .gitignore";
|
|
25956
26025
|
function withoutAttachments(event) {
|
|
25957
26026
|
if (event.type !== "user-message")
|
|
25958
26027
|
return event;
|
|
@@ -25973,6 +26042,7 @@ class WorktreeService {
|
|
|
25973
26042
|
spare;
|
|
25974
26043
|
archive;
|
|
25975
26044
|
archiving = new Map;
|
|
26045
|
+
going = new Set;
|
|
25976
26046
|
countsCache = new Map;
|
|
25977
26047
|
discoverCache = new Map;
|
|
25978
26048
|
usage = new Map;
|
|
@@ -26055,7 +26125,7 @@ class WorktreeService {
|
|
|
26055
26125
|
throw new UserError(carried.unmoved);
|
|
26056
26126
|
return claimed;
|
|
26057
26127
|
}
|
|
26058
|
-
const wtPath =
|
|
26128
|
+
const wtPath = join22(this.d.paths.worktreesDir, repo.name, slug);
|
|
26059
26129
|
await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "-b", branch, wtPath, repo.defaultBranch));
|
|
26060
26130
|
const wt = {
|
|
26061
26131
|
id: shortId(),
|
|
@@ -26106,6 +26176,9 @@ class WorktreeService {
|
|
|
26106
26176
|
`).indexOf(sha);
|
|
26107
26177
|
return i < 0 ? null : `stash@{${i}}`;
|
|
26108
26178
|
};
|
|
26179
|
+
const carried = await git(repo.path, "ls-tree", "-r", "--name-only", `${sha}^3`);
|
|
26180
|
+
const restored = (carried.ok ? carried.out.split(`
|
|
26181
|
+
`).filter(Boolean) : []).filter((p) => !existsSync24(join22(wt.path, p)));
|
|
26109
26182
|
const applied = await git(wt.path, "stash", "apply", sha);
|
|
26110
26183
|
if (applied.ok) {
|
|
26111
26184
|
const ref = await entry();
|
|
@@ -26115,7 +26188,9 @@ class WorktreeService {
|
|
|
26115
26188
|
}
|
|
26116
26189
|
log.warn(wt.id, `moving ${main}'s changes failed`, applied.err);
|
|
26117
26190
|
await git(wt.path, "reset", "--hard");
|
|
26118
|
-
|
|
26191
|
+
for (let i = 0;i < restored.length; i += 100) {
|
|
26192
|
+
await git(wt.path, "--literal-pathspecs", "clean", "-fd", "--", ...restored.slice(i, i + 100));
|
|
26193
|
+
}
|
|
26119
26194
|
const ref = await entry();
|
|
26120
26195
|
const back = ref ? await git(repo.path, "stash", "pop", ref) : null;
|
|
26121
26196
|
return back?.ok ? `the changes on ${main} did not apply in ${wt.title}, so they stayed on ${main}` : `the changes on ${main} did not apply in ${wt.title}; they are kept in git stash as "toyon: into ${wt.title}"`;
|
|
@@ -26139,9 +26214,9 @@ class WorktreeService {
|
|
|
26139
26214
|
const branch = kind === "pr" ? `pr/${number}` : ref;
|
|
26140
26215
|
const title = kind === "pr" ? `pr-${number}` : cleanTitle2(ref) || "branch";
|
|
26141
26216
|
let slug = title;
|
|
26142
|
-
if (
|
|
26217
|
+
if (existsSync24(join22(this.d.paths.worktreesDir, repo.name, slug)))
|
|
26143
26218
|
slug = `${slug}-${shortId().slice(0, 4)}`;
|
|
26144
|
-
const wtPath =
|
|
26219
|
+
const wtPath = join22(this.d.paths.worktreesDir, repo.name, slug);
|
|
26145
26220
|
await withRepoLock(repo.path, async () => {
|
|
26146
26221
|
const out = (await listWorktrees(repo.path)).find((w) => w.branch === branch);
|
|
26147
26222
|
if (out)
|
|
@@ -26317,6 +26392,14 @@ class WorktreeService {
|
|
|
26317
26392
|
this.d.drafts?.drop(worktreeId);
|
|
26318
26393
|
}
|
|
26319
26394
|
async takeDown(wt, archive, reason) {
|
|
26395
|
+
this.going.add(wt.id);
|
|
26396
|
+
try {
|
|
26397
|
+
return await this.removeAndRecord(wt, archive, reason);
|
|
26398
|
+
} finally {
|
|
26399
|
+
this.going.delete(wt.id);
|
|
26400
|
+
}
|
|
26401
|
+
}
|
|
26402
|
+
async removeAndRecord(wt, archive, reason) {
|
|
26320
26403
|
const worktreeId = wt.id;
|
|
26321
26404
|
const repo = this.d.state.requireRepo(wt.repoId);
|
|
26322
26405
|
await this.d.runtime.stop(worktreeId);
|
|
@@ -26348,7 +26431,7 @@ class WorktreeService {
|
|
|
26348
26431
|
return archived;
|
|
26349
26432
|
}
|
|
26350
26433
|
async keep(repo, wt) {
|
|
26351
|
-
const index =
|
|
26434
|
+
const index = join22(this.d.paths.archiveDir, `${wt.id}.index`);
|
|
26352
26435
|
const kept = await keepState(repo.path, wt.path, archiveRef(wt.id), index);
|
|
26353
26436
|
if (!kept)
|
|
26354
26437
|
log.warn(wt.id, "could not keep its git state");
|
|
@@ -26436,7 +26519,7 @@ class WorktreeService {
|
|
|
26436
26519
|
}
|
|
26437
26520
|
archivedAttachment(archiveId, file) {
|
|
26438
26521
|
const files = this.archive.chatFiles(archiveId);
|
|
26439
|
-
return files && isAttachmentFile(file) ?
|
|
26522
|
+
return files && isAttachmentFile(file) ? join22(files.attachments, file) : null;
|
|
26440
26523
|
}
|
|
26441
26524
|
archivedGit(archiveId) {
|
|
26442
26525
|
const rec = this.archive.get(archiveId);
|
|
@@ -26476,7 +26559,7 @@ class WorktreeService {
|
|
|
26476
26559
|
throw new UserError(`${old.title} was archived without its commits, so there is nothing to restore`);
|
|
26477
26560
|
if (rec.auto)
|
|
26478
26561
|
log.info(old.id, `restoring a worktree that archived itself (${rec.auto})`);
|
|
26479
|
-
const path =
|
|
26562
|
+
const path = existsSync24(old.path) ? join22(dirname8(old.path), `${basename6(old.path)}-${shortId().slice(0, 4)}`) : old.path;
|
|
26480
26563
|
const branch = await withRepoLock(repo.path, async () => {
|
|
26481
26564
|
if (!await commitOf(repo.path, archiveRef(old.id))) {
|
|
26482
26565
|
throw new UserError(`${old.title}'s kept commits are gone from git`);
|
|
@@ -26536,12 +26619,12 @@ class WorktreeService {
|
|
|
26536
26619
|
return;
|
|
26537
26620
|
const repo = this.repoOf(rec);
|
|
26538
26621
|
const repoPath = repo?.path ?? rec.repoPath;
|
|
26539
|
-
if (rec.kept &&
|
|
26622
|
+
if (rec.kept && existsSync24(repoPath)) {
|
|
26540
26623
|
const r = await git(repoPath, "update-ref", "-d", archiveRef(archiveId));
|
|
26541
26624
|
if (!r.ok)
|
|
26542
26625
|
log.warn(archiveId, `could not drop its archive ref: ${r.err}`);
|
|
26543
26626
|
}
|
|
26544
|
-
if (rec.worktree.lands?.length &&
|
|
26627
|
+
if (rec.worktree.lands?.length && existsSync24(repoPath))
|
|
26545
26628
|
await dropLandRefs(repoPath, archiveId);
|
|
26546
26629
|
this.archive.delete(archiveId);
|
|
26547
26630
|
this.d.drafts?.drop(archiveId);
|
|
@@ -26579,7 +26662,7 @@ class WorktreeService {
|
|
|
26579
26662
|
if (!hasOwnBranch(wt))
|
|
26580
26663
|
return;
|
|
26581
26664
|
const name = wt.branch.replace(/^toyon\//, "");
|
|
26582
|
-
const desired = wt.kind === "worktree" ?
|
|
26665
|
+
const desired = wt.kind === "worktree" ? join22(dirname8(wt.path), name) : wt.path;
|
|
26583
26666
|
if (wt.linkPath && wt.linkPath !== desired)
|
|
26584
26667
|
this.dropLink(wt);
|
|
26585
26668
|
if (desired === wt.path)
|
|
@@ -26671,10 +26754,18 @@ class WorktreeService {
|
|
|
26671
26754
|
this.d.runtime.markSetup(wt.id, false);
|
|
26672
26755
|
}
|
|
26673
26756
|
}
|
|
26757
|
+
async mirrorBaseIgnore(wt, repo, depsSource) {
|
|
26758
|
+
const src = join22(depsSource, ".gitignore");
|
|
26759
|
+
const mirror = existsSync24(src) && !existsSync24(join22(wt.path, ".gitignore"));
|
|
26760
|
+
const lines = mirror ? readFileSync16(src, "utf8").split(`
|
|
26761
|
+
`).filter((l) => l.trim() !== "") : [];
|
|
26762
|
+
await excludeBlock(repo.path, BASE_IGNORE, lines);
|
|
26763
|
+
}
|
|
26674
26764
|
async setupThenStart(wt, repo, depsSource, { setupCommands = true }) {
|
|
26675
|
-
|
|
26676
|
-
const
|
|
26677
|
-
|
|
26765
|
+
await this.mirrorBaseIgnore(wt, repo, depsSource);
|
|
26766
|
+
const srcNm = join22(depsSource, "node_modules");
|
|
26767
|
+
const dstNm = join22(wt.path, "node_modules");
|
|
26768
|
+
if (existsSync24(srcNm) && !existsSync24(dstNm)) {
|
|
26678
26769
|
const started = Date.now();
|
|
26679
26770
|
const attempts = [
|
|
26680
26771
|
["clonefile", ["-Rc", srcNm, dstNm]],
|
|
@@ -26690,10 +26781,10 @@ class WorktreeService {
|
|
|
26690
26781
|
}
|
|
26691
26782
|
}
|
|
26692
26783
|
for (const f of LOCAL_CONFIG_FILES) {
|
|
26693
|
-
const src =
|
|
26694
|
-
if (!
|
|
26784
|
+
const src = join22(depsSource, f);
|
|
26785
|
+
if (!existsSync24(src) || existsSync24(join22(wt.path, f)))
|
|
26695
26786
|
continue;
|
|
26696
|
-
const r = await run("cp", [src,
|
|
26787
|
+
const r = await run("cp", [src, join22(wt.path, f)], wt.path);
|
|
26697
26788
|
if (r.ok)
|
|
26698
26789
|
this.d.hub.emit("log", wt.id, "setup", `copied ${f}`);
|
|
26699
26790
|
else
|
|
@@ -27004,9 +27095,14 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27004
27095
|
const cached = this.countsCache.get(id);
|
|
27005
27096
|
if (cached && Date.now() - cached.at < 1e4)
|
|
27006
27097
|
return cached;
|
|
27098
|
+
if (this.going.has(id))
|
|
27099
|
+
return cached ?? {};
|
|
27007
27100
|
try {
|
|
27008
27101
|
const ab = countable ? await aheadBehind(path, defaultBranch) : await this.mainCounts(path);
|
|
27009
|
-
const
|
|
27102
|
+
const dirty = (await statusFiles(path)).length;
|
|
27103
|
+
if (this.going.has(id))
|
|
27104
|
+
return cached ?? {};
|
|
27105
|
+
const fresh = { ...ab, dirty, at: Date.now() };
|
|
27010
27106
|
this.countsCache.set(id, fresh);
|
|
27011
27107
|
return fresh;
|
|
27012
27108
|
} catch {
|
|
@@ -27044,6 +27140,8 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27044
27140
|
};
|
|
27045
27141
|
}
|
|
27046
27142
|
async gitStatus(worktreeId) {
|
|
27143
|
+
if (this.going.has(worktreeId))
|
|
27144
|
+
return null;
|
|
27047
27145
|
const r = this.readable(worktreeId);
|
|
27048
27146
|
if (!r)
|
|
27049
27147
|
return this.archivedStatus(worktreeId);
|
|
@@ -27065,6 +27163,8 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
27065
27163
|
this.setLanding(r.wt.id, undefined);
|
|
27066
27164
|
if (r.wt && isMain)
|
|
27067
27165
|
this.setEmpty(r.wt, files.length === 0 ? await treeEmpty(r.path) : false);
|
|
27166
|
+
if (this.going.has(worktreeId))
|
|
27167
|
+
return null;
|
|
27068
27168
|
this.noteCounts(worktreeId, isMain, files.length, counts);
|
|
27069
27169
|
return { files, committed, head: head.ok ? head.out : undefined, ...counts };
|
|
27070
27170
|
} catch (e) {
|
|
@@ -27449,7 +27549,7 @@ bridge.setShellOrigins([
|
|
|
27449
27549
|
],
|
|
27450
27550
|
...remote ? [`https://${remote.host}`] : []
|
|
27451
27551
|
]);
|
|
27452
|
-
|
|
27552
|
+
writeFileSync10(paths.pidFile, `${process.pid}
|
|
27453
27553
|
`);
|
|
27454
27554
|
var stopLagSampler = startLagSampler();
|
|
27455
27555
|
await repos.boot();
|