toyon 0.2.0 → 0.3.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 +62 -39
- package/dist/bridge.js +1 -1
- package/dist/cli.js +233 -70
- package/dist/daemon.js +1067 -210
- package/dist/shell/assets/{Editor-D-PlbMSY.js → Editor-Lle8R8mY.js} +3 -3
- package/dist/shell/assets/{XTerm-CM_FJEwN.js → XTerm-DcWpV1FL.js} +1 -1
- package/dist/shell/assets/{cssMode-Bkqyr9Lr.js → cssMode-Ck0mAapo.js} +1 -1
- package/dist/shell/assets/{freemarker2-DVAK5zoJ.js → freemarker2-B5OkSZOW.js} +1 -1
- package/dist/shell/assets/{handlebars-Bdtj7uMl.js → handlebars-C6vrFwYj.js} +1 -1
- package/dist/shell/assets/{html-B1pqTvyA.js → html-CE92cnIr.js} +1 -1
- package/dist/shell/assets/{htmlMode-CBtvzewS.js → htmlMode-CfM52A9Q.js} +1 -1
- package/dist/shell/assets/index-DOf1M9v5.js +233 -0
- package/dist/shell/assets/index-b4LMTPcs.css +1 -0
- package/dist/shell/assets/{javascript-FNavUzsm.js → javascript-C7Q1bCJn.js} +1 -1
- package/dist/shell/assets/{jsonMode-HgW1GXQH.js → jsonMode-Cq43R9DW.js} +1 -1
- package/dist/shell/assets/{liquid-6wkVlojW.js → liquid-CnxQAGJm.js} +1 -1
- package/dist/shell/assets/{lspLanguageFeatures-CvNG6b_1.js → lspLanguageFeatures-BtjVqb14.js} +1 -1
- package/dist/shell/assets/{mdx-C-byKky3.js → mdx-ycMLgnto.js} +1 -1
- package/dist/shell/assets/{python-BEJMbP4i.js → python-se_0lOPN.js} +1 -1
- package/dist/shell/assets/{razor-DeXWW25R.js → razor-DjByizBq.js} +1 -1
- package/dist/shell/assets/{tsMode-DMwJnvD-.js → tsMode-D1mOZbLC.js} +1 -1
- package/dist/shell/assets/{typescript-Cj57ccSp.js → typescript-B-dZdvXG.js} +1 -1
- package/dist/shell/assets/{workers-BZi9jqBp.js → workers-DXwGNRtC.js} +1 -1
- package/dist/shell/assets/{xml-DUDeQI0I.js → xml-CoqNFco9.js} +1 -1
- package/dist/shell/assets/{yaml-BCD8Shg5.js → yaml-D5Qru8sI.js} +1 -1
- package/dist/shell/index.html +2 -2
- package/dist/shell/manifest.json +1 -1
- package/package.json +2 -2
- package/dist/shell/assets/index-GBQLHG7z.css +0 -1
- package/dist/shell/assets/index-jy_zeFGs.js +0 -230
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 writeFileSync9 } from "fs";
|
|
4
4
|
import { dirname as dirname9 } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
|
|
@@ -13,6 +13,111 @@ function isWriteTool(ev) {
|
|
|
13
13
|
return ev.kind ? WRITE_KINDS.has(ev.kind) : WRITE_TOOLS.has(ev.name);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// ../shared/src/model.ts
|
|
17
|
+
var DEFAULT_PERMISSION_MODE = "auto";
|
|
18
|
+
function hasOwnBranch(wt) {
|
|
19
|
+
return wt.branch.startsWith("toyon/");
|
|
20
|
+
}
|
|
21
|
+
function isUnseen(wt) {
|
|
22
|
+
return wt.unread === true || wt.lastTurn != null && (wt.seenAt == null || wt.seenAt < wt.lastTurn.at);
|
|
23
|
+
}
|
|
24
|
+
var SHELL_STREAM = "shell";
|
|
25
|
+
var LOGIN_STREAM = "login";
|
|
26
|
+
function streamKey(worktreeId, stream) {
|
|
27
|
+
return `${worktreeId}/${stream}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ../shared/src/worktree-caps.ts
|
|
31
|
+
function isMain(wt) {
|
|
32
|
+
return wt.kind === "main";
|
|
33
|
+
}
|
|
34
|
+
function canArchive(wt) {
|
|
35
|
+
return wt.kind === "worktree";
|
|
36
|
+
}
|
|
37
|
+
function canRename(wt) {
|
|
38
|
+
return wt.kind === "worktree" && hasOwnBranch(wt);
|
|
39
|
+
}
|
|
40
|
+
function canLand(wt) {
|
|
41
|
+
return wt.kind === "worktree" && wt.from?.kind !== "pr";
|
|
42
|
+
}
|
|
43
|
+
function canGraft(wt) {
|
|
44
|
+
return wt.kind === "worktree";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ../shared/src/railOrder.ts
|
|
48
|
+
var sentAt = (w) => w.promptedAt ?? w.lastTurn?.at ?? w.createdAt;
|
|
49
|
+
var tierOf = (w) => isMain(w) ? 0 : w.landed ? 2 : 1;
|
|
50
|
+
var railUnitOf = (w) => w.variant ? `${tierOf(w)}:${w.variant.group}` : `row:${w.id}`;
|
|
51
|
+
function railOrder(rows) {
|
|
52
|
+
const units = [];
|
|
53
|
+
const groups = new Map;
|
|
54
|
+
for (const row of rows) {
|
|
55
|
+
const w = row.worktree;
|
|
56
|
+
const key = railUnitOf(w);
|
|
57
|
+
const unit = groups.get(key);
|
|
58
|
+
if (unit) {
|
|
59
|
+
unit.rows.push(row);
|
|
60
|
+
unit.at = Math.max(unit.at, sentAt(w));
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const fresh = { tier: tierOf(w), at: sentAt(w), rows: [row] };
|
|
64
|
+
units.push(fresh);
|
|
65
|
+
groups.set(key, fresh);
|
|
66
|
+
}
|
|
67
|
+
units.sort((a, b) => a.tier - b.tier || b.at - a.at);
|
|
68
|
+
return units.flatMap((u) => u.rows.length > 1 ? u.rows.sort((a, b) => (a.worktree.variant?.index ?? 0) - (b.worktree.variant?.index ?? 0)) : u.rows);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ../shared/src/archiveRule.ts
|
|
72
|
+
var ARCHIVE_KEEP_RECENT = 5;
|
|
73
|
+
function archiveReason(wt, f) {
|
|
74
|
+
const group = wt.variant ? f.rows.filter((w) => w.variant?.group === wt.variant?.group) : [wt];
|
|
75
|
+
const decided = group.some((w) => !!w.lands?.length);
|
|
76
|
+
const lost = decided && !wt.lands?.length;
|
|
77
|
+
if (decided) {
|
|
78
|
+
if (!nothingToKeep(wt, f, lost))
|
|
79
|
+
return null;
|
|
80
|
+
} else if (!group.every((w) => nothingToKeep(w, f, false)))
|
|
81
|
+
return null;
|
|
82
|
+
if (unitsAhead(wt, f.rows) < ARCHIVE_KEEP_RECENT)
|
|
83
|
+
return null;
|
|
84
|
+
const what = lost ? "a sibling landed" : wt.landed ? "landed" : "no changes";
|
|
85
|
+
return `${what}, not opened in ${duration(f.afterMs)}`;
|
|
86
|
+
}
|
|
87
|
+
function nothingToKeep(w, f, lost) {
|
|
88
|
+
if (w.kind !== "worktree" || !hasOwnBranch(w))
|
|
89
|
+
return false;
|
|
90
|
+
if (w.lastTurn?.end !== "done" || isUnseen(w))
|
|
91
|
+
return false;
|
|
92
|
+
if (w.planned || w.pr?.state === "open")
|
|
93
|
+
return false;
|
|
94
|
+
if (f.viewed(w.id) || f.pending(w.id) || f.drafted(w.id))
|
|
95
|
+
return false;
|
|
96
|
+
if (f.now - (w.viewedAt ?? w.createdAt) < f.afterMs)
|
|
97
|
+
return false;
|
|
98
|
+
const c = f.counts(w.id);
|
|
99
|
+
return c.dirty === 0 && (lost ? c.ahead !== undefined : c.ahead === 0);
|
|
100
|
+
}
|
|
101
|
+
function unitsAhead(wt, rows) {
|
|
102
|
+
const tasks = railOrder(rows.filter((w) => w.kind === "worktree").map((worktree) => ({ worktree })));
|
|
103
|
+
const mine = railUnitOf(wt);
|
|
104
|
+
const seen = new Set;
|
|
105
|
+
for (const { worktree: w } of tasks) {
|
|
106
|
+
const unit = railUnitOf(w);
|
|
107
|
+
if (unit === mine)
|
|
108
|
+
return seen.size;
|
|
109
|
+
seen.add(unit);
|
|
110
|
+
}
|
|
111
|
+
return seen.size;
|
|
112
|
+
}
|
|
113
|
+
function duration(ms) {
|
|
114
|
+
if (ms >= 60 * 60000)
|
|
115
|
+
return `${Math.round(ms / (60 * 60000))}h`;
|
|
116
|
+
if (ms >= 60000)
|
|
117
|
+
return `${Math.round(ms / 60000)} min`;
|
|
118
|
+
return `${Math.round(ms / 1000)}s`;
|
|
119
|
+
}
|
|
120
|
+
|
|
16
121
|
// ../shared/src/attachment.ts
|
|
17
122
|
var ATTACHMENT_KINDS = ["image", "paste", "pick"];
|
|
18
123
|
var ATTACHMENT_LIMITS = { image: 6, paste: 4, pick: 8 };
|
|
@@ -50,6 +155,7 @@ var DAEMON_FILES = {
|
|
|
50
155
|
token: "token",
|
|
51
156
|
pid: "daemon.pid",
|
|
52
157
|
state: "state.json",
|
|
158
|
+
drafts: "drafts.json",
|
|
53
159
|
log: "daemon.log",
|
|
54
160
|
remote: "remote.json"
|
|
55
161
|
};
|
|
@@ -163,6 +269,11 @@ function middle(a, b) {
|
|
|
163
269
|
return ops;
|
|
164
270
|
}
|
|
165
271
|
|
|
272
|
+
// ../shared/src/drafts.ts
|
|
273
|
+
var DRAFT_PREFIX = "draft:";
|
|
274
|
+
var draftKey = (repoId) => DRAFT_PREFIX + repoId;
|
|
275
|
+
var draftRepoOf = (boxId) => boxId.startsWith(DRAFT_PREFIX) ? boxId.slice(DRAFT_PREFIX.length) : null;
|
|
276
|
+
|
|
166
277
|
// ../shared/src/land.ts
|
|
167
278
|
var DEFAULT_LAND_ROUTE = "merge";
|
|
168
279
|
var DEFAULT_MERGE_METHOD = "merge";
|
|
@@ -175,19 +286,8 @@ function landPolicy(config) {
|
|
|
175
286
|
};
|
|
176
287
|
}
|
|
177
288
|
|
|
178
|
-
// ../shared/src/model.ts
|
|
179
|
-
var DEFAULT_PERMISSION_MODE = "auto";
|
|
180
|
-
function hasOwnBranch(wt) {
|
|
181
|
-
return wt.branch.startsWith("toyon/");
|
|
182
|
-
}
|
|
183
|
-
var SHELL_STREAM = "shell";
|
|
184
|
-
var LOGIN_STREAM = "login";
|
|
185
|
-
function streamKey(worktreeId, stream) {
|
|
186
|
-
return `${worktreeId}/${stream}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
289
|
// ../shared/src/protocol/limits.ts
|
|
190
|
-
var PROTOCOL_VERSION =
|
|
290
|
+
var PROTOCOL_VERSION = 56;
|
|
191
291
|
var FILE_MAX_CHARS = 5000000;
|
|
192
292
|
var IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
193
293
|
var IMAGE_MAX_EDGE = 2576;
|
|
@@ -1130,7 +1230,7 @@ var nanoid = /^[a-zA-Z0-9_-]{21}$/;
|
|
|
1130
1230
|
function nanoidOfLength(length) {
|
|
1131
1231
|
return new RegExp(`^[a-zA-Z0-9_-]{${length}}$`);
|
|
1132
1232
|
}
|
|
1133
|
-
var
|
|
1233
|
+
var duration2 = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
|
|
1134
1234
|
var guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
|
|
1135
1235
|
var uuid = (version) => {
|
|
1136
1236
|
if (!version)
|
|
@@ -1923,7 +2023,7 @@ var $ZodISOTime = /* @__PURE__ */ $constructor("$ZodISOTime", (inst, def) => {
|
|
|
1923
2023
|
$ZodStringFormat.init(inst, def);
|
|
1924
2024
|
});
|
|
1925
2025
|
var $ZodISODuration = /* @__PURE__ */ $constructor("$ZodISODuration", (inst, def) => {
|
|
1926
|
-
def.pattern ?? (def.pattern =
|
|
2026
|
+
def.pattern ?? (def.pattern = duration2);
|
|
1927
2027
|
$ZodStringFormat.init(inst, def);
|
|
1928
2028
|
});
|
|
1929
2029
|
var $ZodIPv4 = /* @__PURE__ */ $constructor("$ZodIPv4", (inst, def) => {
|
|
@@ -6264,6 +6364,7 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
6264
6364
|
object({
|
|
6265
6365
|
t: literal("chat"),
|
|
6266
6366
|
worktreeId: id,
|
|
6367
|
+
clientId: string2().max(64).optional(),
|
|
6267
6368
|
text: prose,
|
|
6268
6369
|
context: prose.optional(),
|
|
6269
6370
|
attachments
|
|
@@ -6295,7 +6396,7 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
6295
6396
|
object({ t: literal("set-worktree-model"), worktreeId: id, model: string2().max(200) }),
|
|
6296
6397
|
object({ t: literal("set-worktree-effort"), worktreeId: id, effort: string2().max(100) }),
|
|
6297
6398
|
object({ t: literal("set-worktree-profile"), worktreeId: id, profile: string2().max(100) }),
|
|
6298
|
-
object({ t: literal("
|
|
6399
|
+
object({ t: literal("archive-worktree"), worktreeId: id }),
|
|
6299
6400
|
object({ t: literal("list-archived"), repoId: id }),
|
|
6300
6401
|
object({
|
|
6301
6402
|
t: literal("restore-worktree"),
|
|
@@ -6319,6 +6420,7 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
6319
6420
|
object({ t: literal("sync-main"), worktreeId: id }),
|
|
6320
6421
|
object({ t: literal("run-after-land"), repoId: id }),
|
|
6321
6422
|
object({ t: literal("restart-daemon") }),
|
|
6423
|
+
object({ t: literal("update-now") }),
|
|
6322
6424
|
object({ t: literal("pull-main"), worktreeId: id }),
|
|
6323
6425
|
object({
|
|
6324
6426
|
t: literal("write-file"),
|
|
@@ -6338,6 +6440,7 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
6338
6440
|
object({ t: literal("stop-agent"), worktreeId: id }),
|
|
6339
6441
|
object({ t: literal("seen"), worktreeId: id }),
|
|
6340
6442
|
object({ t: literal("mark-unread"), worktreeId: id }),
|
|
6443
|
+
object({ t: literal("set-draft"), boxId: id, text: prose, clientId: string2().max(64).optional() }),
|
|
6341
6444
|
object({ t: literal("refresh-git"), repoId: id }),
|
|
6342
6445
|
object({ t: literal("visit"), worktreeId: id, path: routePath, title: pageTitle.optional() }),
|
|
6343
6446
|
object({ t: literal("page-title"), worktreeId: id, path: routePath, title: pageTitle }),
|
|
@@ -7377,6 +7480,52 @@ function isDark(hex) {
|
|
|
7377
7480
|
return luminance(hex) < 0.4;
|
|
7378
7481
|
}
|
|
7379
7482
|
|
|
7483
|
+
// ../shared/src/update.ts
|
|
7484
|
+
function installMethod(packageJson) {
|
|
7485
|
+
if (!packageJson)
|
|
7486
|
+
return "none";
|
|
7487
|
+
const p = packageJson.replaceAll("\\", "/");
|
|
7488
|
+
if (p.includes("/_npx/"))
|
|
7489
|
+
return "npx";
|
|
7490
|
+
if (p.includes("/.bun/install/global/"))
|
|
7491
|
+
return "bun";
|
|
7492
|
+
if (p.includes("/node_modules/toyon/"))
|
|
7493
|
+
return "npm";
|
|
7494
|
+
return "none";
|
|
7495
|
+
}
|
|
7496
|
+
function installCommand(method, version) {
|
|
7497
|
+
if (method === "npm")
|
|
7498
|
+
return ["npm", "install", "-g", `toyon@${version}`];
|
|
7499
|
+
if (method === "bun")
|
|
7500
|
+
return ["bun", "add", "-g", `toyon@${version}`];
|
|
7501
|
+
return null;
|
|
7502
|
+
}
|
|
7503
|
+
function newer(a, b) {
|
|
7504
|
+
const pa = parseVersion(a);
|
|
7505
|
+
const pb = parseVersion(b);
|
|
7506
|
+
if (!pa || !pb)
|
|
7507
|
+
return false;
|
|
7508
|
+
for (let i = 0;i < 3; i++) {
|
|
7509
|
+
const x = pa.nums[i] ?? 0;
|
|
7510
|
+
const y = pb.nums[i] ?? 0;
|
|
7511
|
+
if (x !== y)
|
|
7512
|
+
return x > y;
|
|
7513
|
+
}
|
|
7514
|
+
if (pa.pre === pb.pre)
|
|
7515
|
+
return false;
|
|
7516
|
+
if (pa.pre === null)
|
|
7517
|
+
return true;
|
|
7518
|
+
if (pb.pre === null)
|
|
7519
|
+
return false;
|
|
7520
|
+
return pa.pre > pb.pre;
|
|
7521
|
+
}
|
|
7522
|
+
function parseVersion(v) {
|
|
7523
|
+
const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(v.trim());
|
|
7524
|
+
if (!m)
|
|
7525
|
+
return null;
|
|
7526
|
+
return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ?? null };
|
|
7527
|
+
}
|
|
7528
|
+
|
|
7380
7529
|
// ../shared/src/vscode-theme.ts
|
|
7381
7530
|
var workbenchKeys = {
|
|
7382
7531
|
surface0: ["editor.background"],
|
|
@@ -7553,27 +7702,10 @@ function pickSyntax(tokenColors) {
|
|
|
7553
7702
|
function slug(s) {
|
|
7554
7703
|
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "theme";
|
|
7555
7704
|
}
|
|
7556
|
-
|
|
7557
|
-
// ../shared/src/worktree-caps.ts
|
|
7558
|
-
function isMain(wt) {
|
|
7559
|
-
return wt.kind === "main";
|
|
7560
|
-
}
|
|
7561
|
-
function canRemove(wt) {
|
|
7562
|
-
return wt.kind === "worktree";
|
|
7563
|
-
}
|
|
7564
|
-
function canRename(wt) {
|
|
7565
|
-
return wt.kind === "worktree" && hasOwnBranch(wt);
|
|
7566
|
-
}
|
|
7567
|
-
function canLand(wt) {
|
|
7568
|
-
return wt.kind === "worktree" && wt.from?.kind !== "pr";
|
|
7569
|
-
}
|
|
7570
|
-
function canGraft(wt) {
|
|
7571
|
-
return wt.kind === "worktree";
|
|
7572
|
-
}
|
|
7573
7705
|
// ../daemon/package.json
|
|
7574
7706
|
var package_default = {
|
|
7575
7707
|
name: "@toyon/daemon",
|
|
7576
|
-
version: "0.
|
|
7708
|
+
version: "0.3.0",
|
|
7577
7709
|
private: true,
|
|
7578
7710
|
type: "module",
|
|
7579
7711
|
main: "src/index.ts",
|
|
@@ -12494,6 +12626,7 @@ function makePaths(home = process.env.TOYON_HOME ?? join3(homedir(), ".toyon"))
|
|
|
12494
12626
|
return {
|
|
12495
12627
|
home,
|
|
12496
12628
|
stateFile: join3(home, DAEMON_FILES.state),
|
|
12629
|
+
draftsFile: join3(home, DAEMON_FILES.drafts),
|
|
12497
12630
|
tokenFile: join3(home, DAEMON_FILES.token),
|
|
12498
12631
|
pidFile: join3(home, DAEMON_FILES.pid),
|
|
12499
12632
|
logFile: join3(home, DAEMON_FILES.log),
|
|
@@ -13708,6 +13841,9 @@ class AcpSession {
|
|
|
13708
13841
|
get queueLength() {
|
|
13709
13842
|
return this.queue.length;
|
|
13710
13843
|
}
|
|
13844
|
+
get unsettled() {
|
|
13845
|
+
return this.queue.length > 0 || this.steered.length > 0 || this.refused !== null || this.asks.size > 0;
|
|
13846
|
+
}
|
|
13711
13847
|
get queueItems() {
|
|
13712
13848
|
return this.waiting().map((q) => q.text);
|
|
13713
13849
|
}
|
|
@@ -14230,6 +14366,8 @@ class AcpSession {
|
|
|
14230
14366
|
}
|
|
14231
14367
|
askPermission(params) {
|
|
14232
14368
|
const plan = params.toolCall.kind === "switch_mode";
|
|
14369
|
+
if (plan)
|
|
14370
|
+
this.d.onPlan?.();
|
|
14233
14371
|
const offered = plan ? params.options : params.options.filter((o) => o.kind !== "allow_always");
|
|
14234
14372
|
const choices = offered.map((o) => ({ id: o.optionId, name: o.name, kind: o.kind }));
|
|
14235
14373
|
return this.openAsk((id) => ({
|
|
@@ -15319,6 +15457,13 @@ function defaultAgent(wt, d) {
|
|
|
15319
15457
|
d.state.save();
|
|
15320
15458
|
d.hub.emit("worktreesChanged");
|
|
15321
15459
|
},
|
|
15460
|
+
onPlan: () => {
|
|
15461
|
+
const w = d.state.worktree(wt.id);
|
|
15462
|
+
if (!w || w.planned)
|
|
15463
|
+
return;
|
|
15464
|
+
w.planned = true;
|
|
15465
|
+
d.state.save();
|
|
15466
|
+
},
|
|
15322
15467
|
option: (category) => d.state.requireWorktree(wt.id)[OPTION_FIELDS[category]],
|
|
15323
15468
|
onOptionsLearned: (category, choices) => {
|
|
15324
15469
|
const agentId = d.state.requireWorktree(wt.id).agent ?? "";
|
|
@@ -15388,6 +15533,21 @@ class RuntimeRegistry {
|
|
|
15388
15533
|
holdCount(id) {
|
|
15389
15534
|
return this.holds.get(id)?.size ?? 0;
|
|
15390
15535
|
}
|
|
15536
|
+
busy(id) {
|
|
15537
|
+
const agent = this.agentFor(id);
|
|
15538
|
+
if (agent && (agent.status !== "idle" || agent.unsettled))
|
|
15539
|
+
return true;
|
|
15540
|
+
return this.holdCount(id) > 0;
|
|
15541
|
+
}
|
|
15542
|
+
anyBusy() {
|
|
15543
|
+
for (const id of this.runtimes.keys())
|
|
15544
|
+
if (this.busy(id))
|
|
15545
|
+
return true;
|
|
15546
|
+
for (const id of this.holds.keys())
|
|
15547
|
+
if (this.busy(id))
|
|
15548
|
+
return true;
|
|
15549
|
+
return false;
|
|
15550
|
+
}
|
|
15391
15551
|
markSetup(id, on) {
|
|
15392
15552
|
if (on)
|
|
15393
15553
|
this.settingUp.add(id);
|
|
@@ -16127,7 +16287,8 @@ function locateAssets(here, env = process.env) {
|
|
|
16127
16287
|
return {
|
|
16128
16288
|
shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join7(here, "shell") : join7(here, "../../shell/dist")),
|
|
16129
16289
|
bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join7(here, "bridge.js") : join7(here, "../../bridge/dist/bridge.js")),
|
|
16130
|
-
sourceRoot: packaged ? null : resolve5(here, "../../..")
|
|
16290
|
+
sourceRoot: packaged ? null : resolve5(here, "../../.."),
|
|
16291
|
+
packageJson: packaged ? join7(here, "..", "package.json") : null
|
|
16131
16292
|
};
|
|
16132
16293
|
}
|
|
16133
16294
|
var KEEP_MS = 3 * 24 * 60 * 60 * 1000;
|
|
@@ -16265,9 +16426,57 @@ function respawn(logFile) {
|
|
|
16265
16426
|
stdio: ["ignore", logFd, logFd],
|
|
16266
16427
|
env: { ...process.env }
|
|
16267
16428
|
});
|
|
16429
|
+
child.on("error", (e) => log.warn("daemon", `the replacement did not start: ${e.message}`));
|
|
16268
16430
|
child.unref();
|
|
16269
16431
|
}
|
|
16270
16432
|
|
|
16433
|
+
// ../daemon/src/core/restarter.ts
|
|
16434
|
+
class Restarter {
|
|
16435
|
+
d;
|
|
16436
|
+
asked = false;
|
|
16437
|
+
gone = false;
|
|
16438
|
+
announced = null;
|
|
16439
|
+
constructor(d) {
|
|
16440
|
+
this.d = d;
|
|
16441
|
+
const settle = () => {
|
|
16442
|
+
if (this.asked)
|
|
16443
|
+
this.attempt();
|
|
16444
|
+
};
|
|
16445
|
+
d.hub.on("agentStatus", settle);
|
|
16446
|
+
d.hub.on("worktreesChanged", settle);
|
|
16447
|
+
}
|
|
16448
|
+
request() {
|
|
16449
|
+
const refused = this.d.refusal();
|
|
16450
|
+
if (refused)
|
|
16451
|
+
return refused;
|
|
16452
|
+
this.asked = true;
|
|
16453
|
+
this.attempt();
|
|
16454
|
+
return null;
|
|
16455
|
+
}
|
|
16456
|
+
waitingOn() {
|
|
16457
|
+
if (!this.asked)
|
|
16458
|
+
return null;
|
|
16459
|
+
return this.gone ? [] : this.working();
|
|
16460
|
+
}
|
|
16461
|
+
working() {
|
|
16462
|
+
return this.d.state.worktrees.filter((w) => this.d.runtime.agentFor(w.id)?.status === "working").map((w) => w.title);
|
|
16463
|
+
}
|
|
16464
|
+
attempt() {
|
|
16465
|
+
if (this.gone)
|
|
16466
|
+
return;
|
|
16467
|
+
const names = this.working();
|
|
16468
|
+
if (names.length === 0)
|
|
16469
|
+
this.gone = true;
|
|
16470
|
+
const key = JSON.stringify(this.waitingOn());
|
|
16471
|
+
if (key !== this.announced) {
|
|
16472
|
+
this.announced = key;
|
|
16473
|
+
this.d.hub.emit("updateChanged");
|
|
16474
|
+
}
|
|
16475
|
+
if (this.gone)
|
|
16476
|
+
this.d.go();
|
|
16477
|
+
}
|
|
16478
|
+
}
|
|
16479
|
+
|
|
16271
16480
|
// ../daemon/src/core/self.ts
|
|
16272
16481
|
function needsRestart(path) {
|
|
16273
16482
|
return path.startsWith("packages/daemon/") || path.startsWith("packages/shared/") || path === "package.json" || path === "bun.lock";
|
|
@@ -16535,6 +16744,13 @@ class StateStore {
|
|
|
16535
16744
|
this.state.theme = prefs;
|
|
16536
16745
|
this.save();
|
|
16537
16746
|
}
|
|
16747
|
+
get updateFailed() {
|
|
16748
|
+
return this.state.updateFailed;
|
|
16749
|
+
}
|
|
16750
|
+
setUpdateFailed(failed) {
|
|
16751
|
+
this.state.updateFailed = failed;
|
|
16752
|
+
this.save();
|
|
16753
|
+
}
|
|
16538
16754
|
save() {
|
|
16539
16755
|
saveState(this.paths, this.state);
|
|
16540
16756
|
}
|
|
@@ -17112,6 +17328,87 @@ async function loadProjectTypescript(root) {
|
|
|
17112
17328
|
}
|
|
17113
17329
|
}
|
|
17114
17330
|
|
|
17331
|
+
// ../daemon/src/drafts/store.ts
|
|
17332
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
17333
|
+
var SAVE_DELAY_MS = 1000;
|
|
17334
|
+
|
|
17335
|
+
class DraftStore {
|
|
17336
|
+
d;
|
|
17337
|
+
drafts = {};
|
|
17338
|
+
saveTimer = null;
|
|
17339
|
+
constructor(d) {
|
|
17340
|
+
this.d = d;
|
|
17341
|
+
if (!existsSync12(d.file))
|
|
17342
|
+
return;
|
|
17343
|
+
try {
|
|
17344
|
+
const raw = JSON.parse(readFileSync9(d.file, "utf8"));
|
|
17345
|
+
if (!raw || typeof raw !== "object")
|
|
17346
|
+
return;
|
|
17347
|
+
for (const [id, text] of Object.entries(raw)) {
|
|
17348
|
+
if (typeof text === "string" && text)
|
|
17349
|
+
this.drafts[id] = text;
|
|
17350
|
+
}
|
|
17351
|
+
} catch (e) {
|
|
17352
|
+
log.warn("drafts", `skipped an unreadable ${d.file}`, e);
|
|
17353
|
+
}
|
|
17354
|
+
}
|
|
17355
|
+
all() {
|
|
17356
|
+
return { ...this.drafts };
|
|
17357
|
+
}
|
|
17358
|
+
has(boxId) {
|
|
17359
|
+
return !!this.drafts[boxId]?.trim();
|
|
17360
|
+
}
|
|
17361
|
+
set(boxId, text, clientId) {
|
|
17362
|
+
if ((this.drafts[boxId] ?? "") === text)
|
|
17363
|
+
return;
|
|
17364
|
+
if (text)
|
|
17365
|
+
this.drafts[boxId] = text;
|
|
17366
|
+
else
|
|
17367
|
+
delete this.drafts[boxId];
|
|
17368
|
+
this.scheduleSave();
|
|
17369
|
+
this.d.hub.emit("draftChanged", boxId, text, clientId);
|
|
17370
|
+
}
|
|
17371
|
+
drop(boxId) {
|
|
17372
|
+
this.set(boxId, "");
|
|
17373
|
+
}
|
|
17374
|
+
prune(known) {
|
|
17375
|
+
let changed = false;
|
|
17376
|
+
for (const id of Object.keys(this.drafts)) {
|
|
17377
|
+
if (known(id))
|
|
17378
|
+
continue;
|
|
17379
|
+
delete this.drafts[id];
|
|
17380
|
+
changed = true;
|
|
17381
|
+
}
|
|
17382
|
+
if (changed)
|
|
17383
|
+
this.scheduleSave();
|
|
17384
|
+
}
|
|
17385
|
+
flush() {
|
|
17386
|
+
if (!this.saveTimer)
|
|
17387
|
+
return;
|
|
17388
|
+
clearTimeout(this.saveTimer);
|
|
17389
|
+
this.saveTimer = null;
|
|
17390
|
+
this.write();
|
|
17391
|
+
}
|
|
17392
|
+
scheduleSave() {
|
|
17393
|
+
if (this.saveTimer)
|
|
17394
|
+
return;
|
|
17395
|
+
this.saveTimer = setTimeout(() => {
|
|
17396
|
+
this.saveTimer = null;
|
|
17397
|
+
this.write();
|
|
17398
|
+
}, this.d.saveDelayMs ?? SAVE_DELAY_MS);
|
|
17399
|
+
this.saveTimer.unref?.();
|
|
17400
|
+
}
|
|
17401
|
+
write() {
|
|
17402
|
+
const tmp = `${this.d.file}.tmp`;
|
|
17403
|
+
try {
|
|
17404
|
+
writeFileSync5(tmp, JSON.stringify(this.drafts));
|
|
17405
|
+
renameSync3(tmp, this.d.file);
|
|
17406
|
+
} catch (e) {
|
|
17407
|
+
log.error("drafts", "could not write the drafts", e);
|
|
17408
|
+
}
|
|
17409
|
+
}
|
|
17410
|
+
}
|
|
17411
|
+
|
|
17115
17412
|
// ../daemon/src/exec/service.ts
|
|
17116
17413
|
var OUTPUT_CAP = 200000;
|
|
17117
17414
|
var TIMEOUT_MS = 10 * 60000;
|
|
@@ -17363,10 +17660,13 @@ async function committedFiles(worktreePath, defaultBr) {
|
|
|
17363
17660
|
const base = await git(worktreePath, "merge-base", "HEAD", defaultBr);
|
|
17364
17661
|
if (!base.ok || !base.out)
|
|
17365
17662
|
return [];
|
|
17366
|
-
|
|
17663
|
+
return filesBetween(worktreePath, base.out, "HEAD");
|
|
17664
|
+
}
|
|
17665
|
+
async function filesBetween(cwd, from, to) {
|
|
17666
|
+
const r = await git(cwd, "diff", "--name-status", from, to);
|
|
17367
17667
|
if (!r.ok || !r.out)
|
|
17368
17668
|
return [];
|
|
17369
|
-
const counts = await numstat(
|
|
17669
|
+
const counts = await numstat(cwd, from, to);
|
|
17370
17670
|
return r.out.split(`
|
|
17371
17671
|
`).filter(Boolean).map((line) => {
|
|
17372
17672
|
const [status, ...rest] = line.split("\t");
|
|
@@ -17419,10 +17719,15 @@ async function logCommits(worktreePath, defaultBr, limit = LOG_LIMIT) {
|
|
|
17419
17719
|
git(worktreePath, "log", `-n${limit}`, FORMAT),
|
|
17420
17720
|
aheadShas(worktreePath, defaultBr)
|
|
17421
17721
|
]);
|
|
17422
|
-
|
|
17423
|
-
|
|
17722
|
+
return r.ok && r.out ? parseLog(r.out, (sha) => ahead.has(sha)) : [];
|
|
17723
|
+
}
|
|
17724
|
+
async function logRange(cwd, from, to, limit = LOG_LIMIT) {
|
|
17725
|
+
const r = await git(cwd, "log", `-n${limit}`, FORMAT, `${from}..${to}`);
|
|
17726
|
+
return r.ok && r.out ? parseLog(r.out, () => true) : [];
|
|
17727
|
+
}
|
|
17728
|
+
function parseLog(out, ahead) {
|
|
17424
17729
|
const commits = [];
|
|
17425
|
-
for (const rec of
|
|
17730
|
+
for (const rec of out.split("\x1E")) {
|
|
17426
17731
|
const [sha, short, author, at, subject] = rec.trim().split("\x00");
|
|
17427
17732
|
if (!sha || !short || subject === undefined)
|
|
17428
17733
|
continue;
|
|
@@ -17432,7 +17737,7 @@ async function logCommits(worktreePath, defaultBr, limit = LOG_LIMIT) {
|
|
|
17432
17737
|
subject,
|
|
17433
17738
|
author: author ?? "",
|
|
17434
17739
|
at: Number(at) * 1000 || 0,
|
|
17435
|
-
ahead: ahead
|
|
17740
|
+
ahead: ahead(sha)
|
|
17436
17741
|
});
|
|
17437
17742
|
}
|
|
17438
17743
|
return commits;
|
|
@@ -17613,6 +17918,30 @@ function rankElementSources(el, rows) {
|
|
|
17613
17918
|
};
|
|
17614
17919
|
}
|
|
17615
17920
|
|
|
17921
|
+
// ../daemon/src/files/listing.ts
|
|
17922
|
+
var SUBMODULE_MODE = "160000";
|
|
17923
|
+
var entries = (out) => out.split("\x00").filter(Boolean);
|
|
17924
|
+
function parseListing(staged, untracked, deleted) {
|
|
17925
|
+
const gone = new Set(entries(deleted));
|
|
17926
|
+
const paths = new Set;
|
|
17927
|
+
const submodules = new Set;
|
|
17928
|
+
for (const line of entries(staged)) {
|
|
17929
|
+
const tab = line.indexOf("\t");
|
|
17930
|
+
if (tab === -1)
|
|
17931
|
+
continue;
|
|
17932
|
+
const path = line.slice(tab + 1);
|
|
17933
|
+
if (gone.has(path))
|
|
17934
|
+
continue;
|
|
17935
|
+
if (line.startsWith(SUBMODULE_MODE))
|
|
17936
|
+
submodules.add(path);
|
|
17937
|
+
else
|
|
17938
|
+
paths.add(path);
|
|
17939
|
+
}
|
|
17940
|
+
for (const path of entries(untracked))
|
|
17941
|
+
paths.add(path);
|
|
17942
|
+
return { paths: [...paths], submodules: [...submodules] };
|
|
17943
|
+
}
|
|
17944
|
+
|
|
17616
17945
|
// ../daemon/src/files/vite-offset.ts
|
|
17617
17946
|
async function viteLineOffset(worktreePath, path, target) {
|
|
17618
17947
|
if (!target)
|
|
@@ -17693,6 +18022,9 @@ function sides(before, after) {
|
|
|
17693
18022
|
}
|
|
17694
18023
|
return { before, after, binary: false, tooLarge: false };
|
|
17695
18024
|
}
|
|
18025
|
+
function keptRead(before, after) {
|
|
18026
|
+
return { ...sides(before, after), version: null, writable: false };
|
|
18027
|
+
}
|
|
17696
18028
|
|
|
17697
18029
|
class FileService {
|
|
17698
18030
|
state;
|
|
@@ -17771,9 +18103,13 @@ class FileService {
|
|
|
17771
18103
|
});
|
|
17772
18104
|
}
|
|
17773
18105
|
async list(worktreeId) {
|
|
17774
|
-
const
|
|
17775
|
-
|
|
17776
|
-
|
|
18106
|
+
const cwd = this.require(worktreeId).path;
|
|
18107
|
+
const [staged, untracked, deleted] = await Promise.all([
|
|
18108
|
+
git(cwd, "ls-files", "-z", "-s"),
|
|
18109
|
+
git(cwd, "ls-files", "-z", "-o", "--exclude-standard"),
|
|
18110
|
+
git(cwd, "ls-files", "-z", "-d")
|
|
18111
|
+
]);
|
|
18112
|
+
return parseListing(staged.out, untracked.out, deleted.out);
|
|
17777
18113
|
}
|
|
17778
18114
|
async search(worktreeId, query) {
|
|
17779
18115
|
const { path: cwd } = this.require(worktreeId);
|
|
@@ -18015,7 +18351,7 @@ class AfterLand {
|
|
|
18015
18351
|
}
|
|
18016
18352
|
|
|
18017
18353
|
// ../daemon/src/repos/registry.ts
|
|
18018
|
-
import { existsSync as
|
|
18354
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
18019
18355
|
import { homedir as homedir7 } from "os";
|
|
18020
18356
|
import { basename as basename3, dirname as dirname6, join as join14 } from "path";
|
|
18021
18357
|
|
|
@@ -18043,7 +18379,7 @@ function variantLens(index) {
|
|
|
18043
18379
|
}
|
|
18044
18380
|
|
|
18045
18381
|
// ../daemon/src/repos/browse.ts
|
|
18046
|
-
import { existsSync as
|
|
18382
|
+
import { existsSync as existsSync13, statSync as statSync3 } from "fs";
|
|
18047
18383
|
import { readdir } from "fs/promises";
|
|
18048
18384
|
import { homedir as homedir5 } from "os";
|
|
18049
18385
|
import { dirname as dirname4, join as join10 } from "path";
|
|
@@ -18060,7 +18396,7 @@ function holdsNothing(names) {
|
|
|
18060
18396
|
}
|
|
18061
18397
|
async function describeFolder(abs) {
|
|
18062
18398
|
const path = collapseTilde(abs);
|
|
18063
|
-
if (
|
|
18399
|
+
if (existsSync13(join10(abs, ".git")))
|
|
18064
18400
|
return { path, kind: "project" };
|
|
18065
18401
|
try {
|
|
18066
18402
|
return { path, kind: holdsNothing(await readdir(abs)) ? "empty" : "folder" };
|
|
@@ -18075,10 +18411,10 @@ function describe2(typed, parent) {
|
|
|
18075
18411
|
isDir = statSync3(typed).isDirectory();
|
|
18076
18412
|
} catch {}
|
|
18077
18413
|
return {
|
|
18078
|
-
exists:
|
|
18414
|
+
exists: existsSync13(typed),
|
|
18079
18415
|
isDir,
|
|
18080
|
-
isRepo: isDir &&
|
|
18081
|
-
parentExists:
|
|
18416
|
+
isRepo: isDir && existsSync13(join10(typed, ".git")),
|
|
18417
|
+
parentExists: existsSync13(parent)
|
|
18082
18418
|
};
|
|
18083
18419
|
}
|
|
18084
18420
|
async function browsePath(raw) {
|
|
@@ -18103,17 +18439,17 @@ async function browsePath(raw) {
|
|
|
18103
18439
|
if (!name.toLowerCase().startsWith(prefix))
|
|
18104
18440
|
continue;
|
|
18105
18441
|
const abs = join10(dir, name);
|
|
18106
|
-
entries.push({ path: collapseTilde(abs), name, isRepo:
|
|
18442
|
+
entries.push({ path: collapseTilde(abs), name, isRepo: existsSync13(join10(abs, ".git")) });
|
|
18107
18443
|
}
|
|
18108
18444
|
entries.sort((a, b) => a.isRepo !== b.isRepo ? a.isRepo ? -1 : 1 : a.name.localeCompare(b.name));
|
|
18109
18445
|
return { entries: entries.slice(0, MAX_ENTRIES), target };
|
|
18110
18446
|
}
|
|
18111
18447
|
|
|
18112
18448
|
// ../daemon/src/repos/config.ts
|
|
18113
|
-
import { closeSync, existsSync as
|
|
18449
|
+
import { closeSync, existsSync as existsSync14, openSync as openSync2, readdirSync as readdirSync3, readFileSync as readFileSync10, readSync } from "fs";
|
|
18114
18450
|
import { join as join11 } from "path";
|
|
18115
18451
|
var PLACES = [CONFIG_FILES.folder, CONFIG_FILES.root];
|
|
18116
|
-
var present = (repoPath, place) => [place.shared, place.local].filter((rel) =>
|
|
18452
|
+
var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync14(join11(repoPath, rel)));
|
|
18117
18453
|
var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
18118
18454
|
function readConfigFile(repoPath) {
|
|
18119
18455
|
const found = PLACES.map((p) => present(repoPath, p)).filter((files) => files.length > 0);
|
|
@@ -18128,7 +18464,7 @@ function readConfigFile(repoPath) {
|
|
|
18128
18464
|
for (const rel of files) {
|
|
18129
18465
|
let raw;
|
|
18130
18466
|
try {
|
|
18131
|
-
raw = JSON.parse(
|
|
18467
|
+
raw = JSON.parse(readFileSync10(join11(repoPath, rel), "utf8"));
|
|
18132
18468
|
} catch (e) {
|
|
18133
18469
|
return { ok: false, reason: `${rel} is not valid JSON: ${e instanceof Error ? e.message : String(e)}` };
|
|
18134
18470
|
}
|
|
@@ -18186,11 +18522,11 @@ function configTarget(repoPath, made) {
|
|
|
18186
18522
|
function configBody(repoPath, rel, config) {
|
|
18187
18523
|
const place = PLACES.find((p) => p.local === rel);
|
|
18188
18524
|
const abs = place && join11(repoPath, place.shared);
|
|
18189
|
-
if (!abs || !
|
|
18525
|
+
if (!abs || !existsSync14(abs))
|
|
18190
18526
|
return config;
|
|
18191
18527
|
let shared;
|
|
18192
18528
|
try {
|
|
18193
|
-
shared = JSON.parse(
|
|
18529
|
+
shared = JSON.parse(readFileSync10(abs, "utf8"));
|
|
18194
18530
|
} catch {
|
|
18195
18531
|
return config;
|
|
18196
18532
|
}
|
|
@@ -18203,8 +18539,8 @@ function detectConfig(repoPath) {
|
|
|
18203
18539
|
if (explicit)
|
|
18204
18540
|
log.warn("config", `${repoPath}: ${explicit.reason}; detecting instead`);
|
|
18205
18541
|
const pkgPath = join11(repoPath, "package.json");
|
|
18206
|
-
if (
|
|
18207
|
-
const pkg = JSON.parse(
|
|
18542
|
+
if (existsSync14(pkgPath)) {
|
|
18543
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
|
|
18208
18544
|
const scripts = pkg.scripts ?? {};
|
|
18209
18545
|
const runner = detectRunner(repoPath, pkg.packageManager);
|
|
18210
18546
|
const pages = detectPages(repoPath, scripts, runner);
|
|
@@ -18225,7 +18561,7 @@ function detectConfig(repoPath) {
|
|
|
18225
18561
|
};
|
|
18226
18562
|
}
|
|
18227
18563
|
}
|
|
18228
|
-
if (
|
|
18564
|
+
if (existsSync14(join11(repoPath, "start.sh"))) {
|
|
18229
18565
|
return { config: { run: { app: "./start.sh" } }, needsSetup: true, from: "start.sh" };
|
|
18230
18566
|
}
|
|
18231
18567
|
const assumed = assumeNothingToRun(repoPath);
|
|
@@ -18268,7 +18604,7 @@ function assumeNothingToRun(repoPath) {
|
|
|
18268
18604
|
return marker && !servesPage(repoPath) ? marker : undefined;
|
|
18269
18605
|
}
|
|
18270
18606
|
function markerIn(repoPath) {
|
|
18271
|
-
const named = NO_PAGE_MARKERS.find((f) =>
|
|
18607
|
+
const named = NO_PAGE_MARKERS.find((f) => existsSync14(join11(repoPath, f)));
|
|
18272
18608
|
if (named)
|
|
18273
18609
|
return named;
|
|
18274
18610
|
let entries;
|
|
@@ -18280,7 +18616,7 @@ function markerIn(repoPath) {
|
|
|
18280
18616
|
return entries.find((f) => NO_PAGE_EXTENSIONS.some((ext) => f.endsWith(ext)));
|
|
18281
18617
|
}
|
|
18282
18618
|
function servesPage(repoPath) {
|
|
18283
|
-
if (WEB_FILES.some((f) =>
|
|
18619
|
+
if (WEB_FILES.some((f) => existsSync14(join11(repoPath, f))))
|
|
18284
18620
|
return true;
|
|
18285
18621
|
if (/^web:/m.test(head(join11(repoPath, "Procfile"))))
|
|
18286
18622
|
return true;
|
|
@@ -18317,7 +18653,7 @@ function mentions(text, name) {
|
|
|
18317
18653
|
return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i").test(text);
|
|
18318
18654
|
}
|
|
18319
18655
|
function head(path) {
|
|
18320
|
-
if (!
|
|
18656
|
+
if (!existsSync14(path))
|
|
18321
18657
|
return "";
|
|
18322
18658
|
try {
|
|
18323
18659
|
const fd = openSync2(path, "r");
|
|
@@ -18345,28 +18681,28 @@ function procCommand(runner, script, body) {
|
|
|
18345
18681
|
return `${base}${runner === "npm" ? " --" : ""} ${flag}`;
|
|
18346
18682
|
}
|
|
18347
18683
|
function detectPages(repoPath, scripts, runner) {
|
|
18348
|
-
if (!scripts.build || !
|
|
18684
|
+
if (!scripts.build || !existsSync14(join11(repoPath, "functions")))
|
|
18349
18685
|
return null;
|
|
18350
|
-
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join11(repoPath, f)).find((f) =>
|
|
18686
|
+
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join11(repoPath, f)).find((f) => existsSync14(f));
|
|
18351
18687
|
if (!cfg)
|
|
18352
18688
|
return null;
|
|
18353
|
-
const out =
|
|
18689
|
+
const out = readFileSync10(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
|
|
18354
18690
|
if (!out)
|
|
18355
18691
|
return null;
|
|
18356
18692
|
return { web: `${runner} run build && npx wrangler pages dev ${out} --ip 127.0.0.1 --port $PORT` };
|
|
18357
18693
|
}
|
|
18358
18694
|
function detectRunner(repoPath, packageManager) {
|
|
18359
|
-
if (
|
|
18695
|
+
if (existsSync14(join11(repoPath, "bun.lock")) || existsSync14(join11(repoPath, "bun.lockb")))
|
|
18360
18696
|
return "bun";
|
|
18361
|
-
if (
|
|
18697
|
+
if (existsSync14(join11(repoPath, "pnpm-lock.yaml")))
|
|
18362
18698
|
return "pnpm";
|
|
18363
|
-
if (
|
|
18699
|
+
if (existsSync14(join11(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
|
|
18364
18700
|
return "yarn";
|
|
18365
18701
|
return "npm";
|
|
18366
18702
|
}
|
|
18367
18703
|
|
|
18368
18704
|
// ../daemon/src/repos/create.ts
|
|
18369
|
-
import { existsSync as
|
|
18705
|
+
import { existsSync as existsSync15, readdirSync as readdirSync4, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
18370
18706
|
import { appendFile as appendFile2, mkdir as mkdir2, rm } from "fs/promises";
|
|
18371
18707
|
import { homedir as homedir6 } from "os";
|
|
18372
18708
|
import { dirname as dirname5, join as join12, resolve as resolve7 } from "path";
|
|
@@ -18381,13 +18717,13 @@ function planProject(opts, home = homedir6()) {
|
|
|
18381
18717
|
const first = firstProjectsFolder(opts.parent, home);
|
|
18382
18718
|
const parent = first ?? resolveParent(opts.parent);
|
|
18383
18719
|
const dir = join12(parent, opts.name.trim());
|
|
18384
|
-
if (
|
|
18720
|
+
if (existsSync15(dir))
|
|
18385
18721
|
throw new UserError(`${dir} already exists`);
|
|
18386
18722
|
return { parent, dir, makeParent: first !== null };
|
|
18387
18723
|
}
|
|
18388
18724
|
function firstProjectsFolder(raw, home) {
|
|
18389
18725
|
const typed = expandTilde(raw.trim()).replace(/\/+$/, "");
|
|
18390
|
-
if (typed !== join12(home, PROJECTS_FOLDER) ||
|
|
18726
|
+
if (typed !== join12(home, PROJECTS_FOLDER) || existsSync15(typed) || !existsSync15(home))
|
|
18391
18727
|
return null;
|
|
18392
18728
|
return join12(realpathSync2(home), PROJECTS_FOLDER);
|
|
18393
18729
|
}
|
|
@@ -18397,10 +18733,10 @@ function planInPlace(opts) {
|
|
|
18397
18733
|
throw new UserError(`${opts.name} is not a folder name`);
|
|
18398
18734
|
}
|
|
18399
18735
|
const typed = join12(resolveParent(opts.parent), name);
|
|
18400
|
-
if (!
|
|
18736
|
+
if (!existsSync15(typed) || !statSync4(typed).isDirectory())
|
|
18401
18737
|
throw new UserError(`${typed} is not a folder`);
|
|
18402
18738
|
const dir = realpathSync2(typed);
|
|
18403
|
-
if (
|
|
18739
|
+
if (existsSync15(join12(dir, ".git")))
|
|
18404
18740
|
throw new UserError(`${dir} is already a project; open it instead`);
|
|
18405
18741
|
if (!holdsNothing(readdirSync4(dir))) {
|
|
18406
18742
|
throw new UserError(`${dir} is not empty; only an empty folder can become a project where it is`);
|
|
@@ -18411,7 +18747,7 @@ function resolveParent(raw) {
|
|
|
18411
18747
|
const typed = expandTilde(raw.trim());
|
|
18412
18748
|
if (!typed.startsWith("/"))
|
|
18413
18749
|
throw new UserError(`${raw} is not a folder path`);
|
|
18414
|
-
if (!
|
|
18750
|
+
if (!existsSync15(typed))
|
|
18415
18751
|
throw new UserError(`${raw} does not exist; make it first`);
|
|
18416
18752
|
if (!statSync4(typed).isDirectory())
|
|
18417
18753
|
throw new UserError(`${raw} is not a folder`);
|
|
@@ -18527,7 +18863,7 @@ async function ok(p, what) {
|
|
|
18527
18863
|
}
|
|
18528
18864
|
|
|
18529
18865
|
// ../daemon/src/repos/watcher.ts
|
|
18530
|
-
import { existsSync as
|
|
18866
|
+
import { existsSync as existsSync16, watch } from "fs";
|
|
18531
18867
|
import { basename as basename2, join as join13 } from "path";
|
|
18532
18868
|
function watchDefaultBranch(repoPath, branch, onMove) {
|
|
18533
18869
|
let last = null;
|
|
@@ -18639,7 +18975,7 @@ function watchConfigFile(repoPath, onChange) {
|
|
|
18639
18975
|
const armInner = () => {
|
|
18640
18976
|
inner?.close();
|
|
18641
18977
|
inner = null;
|
|
18642
|
-
if (!
|
|
18978
|
+
if (!existsSync16(dir))
|
|
18643
18979
|
return;
|
|
18644
18980
|
try {
|
|
18645
18981
|
inner = watch(dir, (_event, filename) => {
|
|
@@ -18693,7 +19029,7 @@ class RepoRegistry {
|
|
|
18693
19029
|
}
|
|
18694
19030
|
async boot() {
|
|
18695
19031
|
const { state } = this.d;
|
|
18696
|
-
const gone = state.worktrees.filter((wt) => !
|
|
19032
|
+
const gone = state.worktrees.filter((wt) => !existsSync17(wt.path) || !state.repos.some((r) => r.id === wt.repoId));
|
|
18697
19033
|
for (const wt of gone)
|
|
18698
19034
|
await this.d.worktrees.forgetGone(wt);
|
|
18699
19035
|
for (const repo of state.repos) {
|
|
@@ -18760,7 +19096,7 @@ class RepoRegistry {
|
|
|
18760
19096
|
return `${repo.name} is set up now`;
|
|
18761
19097
|
if (main.promptedAt || main.lastTurn)
|
|
18762
19098
|
return `${repo.name} has a chat now`;
|
|
18763
|
-
if (
|
|
19099
|
+
if (existsSync17(join14(repo.path, ".claude")))
|
|
18764
19100
|
return `an agent has already run in ${repo.name}`;
|
|
18765
19101
|
if ((await statusFiles(repo.path)).length > 0 || !await treeEmpty(repo.path)) {
|
|
18766
19102
|
return `${repo.name} has files in it now`;
|
|
@@ -18837,7 +19173,7 @@ class RepoRegistry {
|
|
|
18837
19173
|
}
|
|
18838
19174
|
async register(rawPath, made) {
|
|
18839
19175
|
const path = expandTilde(rawPath);
|
|
18840
|
-
if (!
|
|
19176
|
+
if (!existsSync17(path))
|
|
18841
19177
|
throw new UserError(`${path} does not exist`);
|
|
18842
19178
|
if (!await isGitRepo(path))
|
|
18843
19179
|
throw new UserError(`${path} is not a git repository`);
|
|
@@ -18887,17 +19223,18 @@ class RepoRegistry {
|
|
|
18887
19223
|
const mine = this.d.state.worktrees.filter((w) => w.repoId === repoId);
|
|
18888
19224
|
const tasks = mine.filter((w) => w.kind !== "main" && w.kind !== "spare");
|
|
18889
19225
|
if (tasks.length > 0) {
|
|
18890
|
-
throw new UserError(`${repo.name} still has ${tasks.length} worktree${tasks.length === 1 ? "" : "s"};
|
|
19226
|
+
throw new UserError(`${repo.name} still has ${tasks.length} worktree${tasks.length === 1 ? "" : "s"}; archive them first`);
|
|
18891
19227
|
}
|
|
18892
19228
|
this.stopWatcher(repoId);
|
|
18893
19229
|
for (const wt of mine.filter((w) => w.kind === "spare"))
|
|
18894
|
-
await this.d.worktrees.
|
|
19230
|
+
await this.d.worktrees.discardWorktree(wt.id);
|
|
18895
19231
|
for (const wt of mine.filter((w) => w.kind === "main")) {
|
|
18896
19232
|
await this.d.runtime.stop(wt.id);
|
|
18897
19233
|
this.d.state.removeWorktree(wt.id);
|
|
18898
19234
|
this.d.worktrees.deleteChat(wt.id);
|
|
18899
19235
|
releasePort(wt.proxyPort);
|
|
18900
19236
|
}
|
|
19237
|
+
this.d.drafts?.drop(draftKey(repoId));
|
|
18901
19238
|
this.d.state.removeRepo(repoId);
|
|
18902
19239
|
this.d.hub.emit("reposChanged");
|
|
18903
19240
|
this.d.hub.emit("worktreesChanged");
|
|
@@ -18909,7 +19246,7 @@ class RepoRegistry {
|
|
|
18909
19246
|
throw new UserError(current.reason);
|
|
18910
19247
|
const was = configTarget(repo.path, !!repo.made);
|
|
18911
19248
|
const rel = configSibling(was, kind);
|
|
18912
|
-
const moved = rel !== was &&
|
|
19249
|
+
const moved = rel !== was && existsSync17(join14(repo.path, was));
|
|
18913
19250
|
if (moved && !(kind === "local" && await isTracked(repo.path, was))) {
|
|
18914
19251
|
try {
|
|
18915
19252
|
rmSync2(join14(repo.path, was));
|
|
@@ -18924,7 +19261,7 @@ class RepoRegistry {
|
|
|
18924
19261
|
const file = join14(repo.path, rel);
|
|
18925
19262
|
try {
|
|
18926
19263
|
mkdirSync5(dirname6(file), { recursive: true });
|
|
18927
|
-
|
|
19264
|
+
writeFileSync6(file, `${JSON.stringify(configBody(repo.path, rel, config), null, 2)}
|
|
18928
19265
|
`);
|
|
18929
19266
|
} catch (e) {
|
|
18930
19267
|
log.warn(repoId, `could not write ${rel}`, e);
|
|
@@ -18999,7 +19336,7 @@ class RepoRegistry {
|
|
|
18999
19336
|
}
|
|
19000
19337
|
placeConfig(repo) {
|
|
19001
19338
|
const rel = configTarget(repo.path, !!repo.made);
|
|
19002
|
-
if (isLocalConfigFile(rel) &&
|
|
19339
|
+
if (isLocalConfigFile(rel) && existsSync17(join14(repo.path, rel))) {
|
|
19003
19340
|
fireAndForget(repo.id, excludeFromGit(repo.path, rel), "exclude local settings");
|
|
19004
19341
|
}
|
|
19005
19342
|
if (repo.configFile === rel)
|
|
@@ -19336,7 +19673,7 @@ function retitle(pages, key, title) {
|
|
|
19336
19673
|
function rank(pages, now, n = KEEP) {
|
|
19337
19674
|
return Object.keys(pages).sort(byStanding(pages, now)).slice(0, n);
|
|
19338
19675
|
}
|
|
19339
|
-
function
|
|
19676
|
+
function entries2(pages, now) {
|
|
19340
19677
|
return rank(pages, now).map((path) => {
|
|
19341
19678
|
const v = pages[path];
|
|
19342
19679
|
return { path, score: rounded(decayed(v, now)), last: v.last, ...v.title ? { title: v.title } : {} };
|
|
@@ -20031,7 +20368,7 @@ function unseenOf(candidates, seen, hashes) {
|
|
|
20031
20368
|
}
|
|
20032
20369
|
|
|
20033
20370
|
// ../daemon/src/routes/service.ts
|
|
20034
|
-
var
|
|
20371
|
+
var SAVE_DELAY_MS2 = 5000;
|
|
20035
20372
|
var MAX_MANIFESTS = 50;
|
|
20036
20373
|
var SCAN_CACHE = 20;
|
|
20037
20374
|
var MAX_HASHED = 64;
|
|
@@ -20106,7 +20443,7 @@ class RouteService {
|
|
|
20106
20443
|
}
|
|
20107
20444
|
history(repoId) {
|
|
20108
20445
|
const pages = this.d.state.visitsOf(repoId);
|
|
20109
|
-
return pages ?
|
|
20446
|
+
return pages ? entries2(pages, this.now()) : [];
|
|
20110
20447
|
}
|
|
20111
20448
|
historyAll() {
|
|
20112
20449
|
const out = {};
|
|
@@ -20331,7 +20668,7 @@ class RouteService {
|
|
|
20331
20668
|
} catch (e) {
|
|
20332
20669
|
log.error("routes", "could not write the page lists", e);
|
|
20333
20670
|
}
|
|
20334
|
-
}, this.d.saveDelayMs ??
|
|
20671
|
+
}, this.d.saveDelayMs ?? SAVE_DELAY_MS2);
|
|
20335
20672
|
this.saveTimer.unref();
|
|
20336
20673
|
}
|
|
20337
20674
|
repoOf(worktreeId) {
|
|
@@ -20343,7 +20680,7 @@ class RouteService {
|
|
|
20343
20680
|
}
|
|
20344
20681
|
|
|
20345
20682
|
// ../daemon/src/runtime/bridge-script.ts
|
|
20346
|
-
import { existsSync as
|
|
20683
|
+
import { existsSync as existsSync18, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
|
|
20347
20684
|
var LEARNED_MAX = 8;
|
|
20348
20685
|
|
|
20349
20686
|
class BridgeScript {
|
|
@@ -20373,7 +20710,7 @@ class BridgeScript {
|
|
|
20373
20710
|
this.cache = null;
|
|
20374
20711
|
}
|
|
20375
20712
|
get() {
|
|
20376
|
-
if (!
|
|
20713
|
+
if (!existsSync18(this.path))
|
|
20377
20714
|
return "// toyon bridge not built";
|
|
20378
20715
|
const s = statSync5(this.path);
|
|
20379
20716
|
const stamp = `${s.mtimeMs}:${s.size}`;
|
|
@@ -20382,7 +20719,7 @@ class BridgeScript {
|
|
|
20382
20719
|
this.stamp = stamp;
|
|
20383
20720
|
const origins = [...this.origins, ...this.learned];
|
|
20384
20721
|
this.cache = `window.__toyonShellOrigins=${JSON.stringify(origins)};
|
|
20385
|
-
${
|
|
20722
|
+
${readFileSync11(this.path, "utf8")}`;
|
|
20386
20723
|
return this.cache;
|
|
20387
20724
|
}
|
|
20388
20725
|
}
|
|
@@ -20400,7 +20737,7 @@ function servedLocally(origin) {
|
|
|
20400
20737
|
}
|
|
20401
20738
|
|
|
20402
20739
|
// ../daemon/src/runtime/memory.ts
|
|
20403
|
-
var
|
|
20740
|
+
var MAC_PRESSURE_CRITICAL = 4;
|
|
20404
20741
|
var AVAILABLE_FLOOR = 0.15;
|
|
20405
20742
|
var unsupportedLogged = false;
|
|
20406
20743
|
async function memoryTight() {
|
|
@@ -20418,10 +20755,12 @@ async function darwin() {
|
|
|
20418
20755
|
const [level, available] = r.out.split(/\s+/).map(Number);
|
|
20419
20756
|
if (level === undefined || available === undefined || Number.isNaN(level) || Number.isNaN(available))
|
|
20420
20757
|
return null;
|
|
20421
|
-
|
|
20758
|
+
return darwinSignal(level, available);
|
|
20759
|
+
}
|
|
20760
|
+
function darwinSignal(level, available) {
|
|
20761
|
+
const pressure = level === 1 ? "normal" : level === 2 ? "warn" : level === MAC_PRESSURE_CRITICAL ? "critical" : `level ${level}`;
|
|
20422
20762
|
return {
|
|
20423
|
-
tight: level
|
|
20424
|
-
backstop: level === MAC_PRESSURE_NORMAL && available < AVAILABLE_FLOOR * 100,
|
|
20763
|
+
tight: level === MAC_PRESSURE_CRITICAL || available < AVAILABLE_FLOOR * 100,
|
|
20425
20764
|
why: `memory pressure ${pressure} with ${available}% available`
|
|
20426
20765
|
};
|
|
20427
20766
|
}
|
|
@@ -20441,7 +20780,7 @@ async function linux() {
|
|
|
20441
20780
|
if (!total || available === null)
|
|
20442
20781
|
return null;
|
|
20443
20782
|
const percent = Math.round(available / total * 100);
|
|
20444
|
-
return { tight: available / total < AVAILABLE_FLOOR,
|
|
20783
|
+
return { tight: available / total < AVAILABLE_FLOOR, why: `${percent}% of memory available` };
|
|
20445
20784
|
}
|
|
20446
20785
|
async function sampleCosts(pgids) {
|
|
20447
20786
|
const costs = new Map;
|
|
@@ -20593,16 +20932,12 @@ class IdlePolicy {
|
|
|
20593
20932
|
}
|
|
20594
20933
|
async checkPressure() {
|
|
20595
20934
|
const signal = await (this.d.memory ?? memoryTight)();
|
|
20596
|
-
if (!signal
|
|
20935
|
+
if (!signal?.tight)
|
|
20597
20936
|
return;
|
|
20598
20937
|
const pick = this.d.state.worktrees.filter((w) => this.candidate(w.id)).sort((a, b) => this.entry(a.id).activeAt - this.entry(b.id).activeAt)[0];
|
|
20599
20938
|
if (!pick)
|
|
20600
20939
|
return;
|
|
20601
|
-
|
|
20602
|
-
if (signal.tight)
|
|
20603
|
-
this.sleep(pick.id, why);
|
|
20604
|
-
else
|
|
20605
|
-
log.info(pick.id, `would sleep on the backstop: ${why}`);
|
|
20940
|
+
this.sleep(pick.id, `${signal.why}; least recently used`);
|
|
20606
20941
|
}
|
|
20607
20942
|
async sampleCosts() {
|
|
20608
20943
|
this.recordViewed();
|
|
@@ -20653,10 +20988,7 @@ class IdlePolicy {
|
|
|
20653
20988
|
return true;
|
|
20654
20989
|
}
|
|
20655
20990
|
busy(id) {
|
|
20656
|
-
|
|
20657
|
-
if (agent && (agent.status !== "idle" || agent.queueItems.length > 0))
|
|
20658
|
-
return true;
|
|
20659
|
-
return this.d.runtime.holdCount(id) > 0;
|
|
20991
|
+
return this.d.runtime.busy(id);
|
|
20660
20992
|
}
|
|
20661
20993
|
reconsider(id) {
|
|
20662
20994
|
if (!this.candidate(id)) {
|
|
@@ -20740,7 +21072,7 @@ function humanMs(ms) {
|
|
|
20740
21072
|
import { homedir as homedir9 } from "os";
|
|
20741
21073
|
|
|
20742
21074
|
// ../daemon/src/agent/config.ts
|
|
20743
|
-
import { existsSync as
|
|
21075
|
+
import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
|
|
20744
21076
|
import { homedir as homedir8 } from "os";
|
|
20745
21077
|
import { join as join16 } from "path";
|
|
20746
21078
|
function agentConfigFiles(spec, repoPath, home = homedir8()) {
|
|
@@ -20748,7 +21080,7 @@ function agentConfigFiles(spec, repoPath, home = homedir8()) {
|
|
|
20748
21080
|
id,
|
|
20749
21081
|
label,
|
|
20750
21082
|
path,
|
|
20751
|
-
exists:
|
|
21083
|
+
exists: existsSync19(path)
|
|
20752
21084
|
});
|
|
20753
21085
|
if (spec.id === "claude") {
|
|
20754
21086
|
return [
|
|
@@ -20780,10 +21112,10 @@ function describeAgentConfig(spec, repoPath, home = homedir8()) {
|
|
|
20780
21112
|
return { agent: spec.id, files, servers };
|
|
20781
21113
|
}
|
|
20782
21114
|
function readJson(path) {
|
|
20783
|
-
if (!
|
|
21115
|
+
if (!existsSync19(path))
|
|
20784
21116
|
return null;
|
|
20785
21117
|
try {
|
|
20786
|
-
const v = JSON.parse(
|
|
21118
|
+
const v = JSON.parse(readFileSync12(path, "utf8"));
|
|
20787
21119
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
20788
21120
|
} catch (e) {
|
|
20789
21121
|
log.warn("agent-config", `${path} is not valid JSON`, e);
|
|
@@ -20801,11 +21133,11 @@ function serversOf(raw, scope) {
|
|
|
20801
21133
|
});
|
|
20802
21134
|
}
|
|
20803
21135
|
function codexServers(path) {
|
|
20804
|
-
if (!
|
|
21136
|
+
if (!existsSync19(path))
|
|
20805
21137
|
return [];
|
|
20806
21138
|
const out = [];
|
|
20807
21139
|
let current = null;
|
|
20808
|
-
for (const raw of
|
|
21140
|
+
for (const raw of readFileSync12(path, "utf8").split(`
|
|
20809
21141
|
`)) {
|
|
20810
21142
|
const line = raw.trim();
|
|
20811
21143
|
const header = /^\[mcp_servers\.("?)([^\]"]+)\1\]$/.exec(line);
|
|
@@ -21376,6 +21708,7 @@ var handlers = {
|
|
|
21376
21708
|
ctx.reply({ t: "backfill", worktreeId: msg.worktreeId, events: coalesce(kept), log: [] });
|
|
21377
21709
|
ctx.reply({ t: "queue", worktreeId: msg.worktreeId, items: [] });
|
|
21378
21710
|
ctx.reply({ t: "agent-commands", worktreeId: msg.worktreeId, commands: [] });
|
|
21711
|
+
await gitStatus(s, ctx, msg.worktreeId);
|
|
21379
21712
|
return;
|
|
21380
21713
|
}
|
|
21381
21714
|
if (!ctx.subscribe(msg.worktreeId))
|
|
@@ -21410,6 +21743,9 @@ var handlers = {
|
|
|
21410
21743
|
"mark-unread"(msg, _ctx, s) {
|
|
21411
21744
|
s.turns.markUnread(msg.worktreeId);
|
|
21412
21745
|
},
|
|
21746
|
+
"set-draft"(msg, _ctx, s) {
|
|
21747
|
+
s.drafts.set(msg.boxId, msg.text, msg.clientId);
|
|
21748
|
+
},
|
|
21413
21749
|
"refresh-git"(msg, _ctx, s) {
|
|
21414
21750
|
s.worktrees.recount(msg.repoId);
|
|
21415
21751
|
},
|
|
@@ -21422,7 +21758,12 @@ var handlers = {
|
|
|
21422
21758
|
"forget-visit"(msg, _ctx, s) {
|
|
21423
21759
|
s.routes.forget(msg.repoId, msg.path);
|
|
21424
21760
|
},
|
|
21425
|
-
chat(msg, _ctx, s) {
|
|
21761
|
+
async chat(msg, _ctx, s) {
|
|
21762
|
+
await s.worktrees.archivingNow(msg.worktreeId);
|
|
21763
|
+
if (!s.state.worktree(msg.worktreeId) && s.worktrees.hasArchived(msg.worktreeId)) {
|
|
21764
|
+
await s.worktrees.restore(msg.worktreeId, msg.clientId, { text: msg.text, attachments: msg.attachments });
|
|
21765
|
+
return;
|
|
21766
|
+
}
|
|
21426
21767
|
requireRun(s, msg.worktreeId);
|
|
21427
21768
|
const agent = s.runtime.agentFor(msg.worktreeId);
|
|
21428
21769
|
if (!agent)
|
|
@@ -21475,8 +21816,8 @@ var handlers = {
|
|
|
21475
21816
|
ctx.reply(shipped("", false, `batch: ${started} started, ${failed} failed (see daemon log)`));
|
|
21476
21817
|
})(), "batch");
|
|
21477
21818
|
},
|
|
21478
|
-
async "
|
|
21479
|
-
await s.worktrees.
|
|
21819
|
+
async "archive-worktree"(msg, _ctx, s) {
|
|
21820
|
+
await s.worktrees.archiveWorktree(msg.worktreeId);
|
|
21480
21821
|
},
|
|
21481
21822
|
"list-archived"(msg, ctx, s) {
|
|
21482
21823
|
ctx.reply({ t: "archived", repoId: msg.repoId, items: s.worktrees.archived(msg.repoId) });
|
|
@@ -21496,7 +21837,9 @@ var handlers = {
|
|
|
21496
21837
|
async "read-file"(msg, ctx, s) {
|
|
21497
21838
|
const head = { t: "file-read", worktreeId: msg.worktreeId, path: msg.path, ref: msg.ref, seq: msg.seq };
|
|
21498
21839
|
try {
|
|
21499
|
-
|
|
21840
|
+
const kept = s.worktrees.readable(msg.worktreeId) ? null : await s.worktrees.archivedFile(msg.worktreeId, msg.path, msg.ref);
|
|
21841
|
+
const read = kept ? keptRead(kept.before, kept.after) : await s.files.read(msg.worktreeId, msg.path, msg.ref);
|
|
21842
|
+
ctx.reply({ ...head, ...read });
|
|
21500
21843
|
} catch (e) {
|
|
21501
21844
|
if (!(e instanceof UserError))
|
|
21502
21845
|
log.error(msg.worktreeId, "read-file failed", e);
|
|
@@ -21513,7 +21856,7 @@ var handlers = {
|
|
|
21513
21856
|
ctx.reply({ t: "git-commit", worktreeId: msg.worktreeId, sha: msg.sha, files });
|
|
21514
21857
|
},
|
|
21515
21858
|
async land(msg, ctx, s) {
|
|
21516
|
-
const { result,
|
|
21859
|
+
const { result, archiveIds } = await s.worktrees.land(msg.worktreeId, msg.message);
|
|
21517
21860
|
const suggestion = !result.ok && result.conflict ? "Bring main into this branch and resolve the conflicts, then verify the app still works." : undefined;
|
|
21518
21861
|
if (result.ok && (result.pr || s.state.worktree(msg.worktreeId)?.pr)) {
|
|
21519
21862
|
fireAndForget(msg.worktreeId, s.prs.refresh(msg.worktreeId), "pr refresh");
|
|
@@ -21521,7 +21864,7 @@ var handlers = {
|
|
|
21521
21864
|
await notify(s, ctx, msg.worktreeId, shipped(msg.worktreeId, result.ok, result.message, {
|
|
21522
21865
|
merged: result.ok,
|
|
21523
21866
|
url: result.url,
|
|
21524
|
-
|
|
21867
|
+
archiveIds: archiveIds ?? [],
|
|
21525
21868
|
...suggestion ? { suggestion } : {}
|
|
21526
21869
|
}));
|
|
21527
21870
|
},
|
|
@@ -21547,15 +21890,13 @@ var handlers = {
|
|
|
21547
21890
|
ctx.reply({ t: "self", self: s.self.get() });
|
|
21548
21891
|
},
|
|
21549
21892
|
"restart-daemon"(_msg, _ctx, s) {
|
|
21550
|
-
const
|
|
21551
|
-
if (busy.length > 0) {
|
|
21552
|
-
const names = busy.map((w) => w.title).join(", ");
|
|
21553
|
-
throw new UserError(`still working on ${names}; restart once that settles`);
|
|
21554
|
-
}
|
|
21555
|
-
const refused = s.restart();
|
|
21893
|
+
const refused = s.restarter.request();
|
|
21556
21894
|
if (refused)
|
|
21557
21895
|
throw new UserError(refused);
|
|
21558
21896
|
},
|
|
21897
|
+
"update-now"(_msg, _ctx, s) {
|
|
21898
|
+
return s.update.updateNow();
|
|
21899
|
+
},
|
|
21559
21900
|
async commit(msg, ctx, s) {
|
|
21560
21901
|
const result = await s.worktrees.commit(msg.worktreeId, msg.message);
|
|
21561
21902
|
await notify(s, ctx, msg.worktreeId, shipped(msg.worktreeId, result.ok, result.message));
|
|
@@ -21574,7 +21915,7 @@ var handlers = {
|
|
|
21574
21915
|
fireAndForget(msg.worktreeId, agent.warmCommands(), "warm commands");
|
|
21575
21916
|
},
|
|
21576
21917
|
async "list-files"(msg, ctx, s) {
|
|
21577
|
-
ctx.reply({ t: "files", worktreeId: msg.worktreeId,
|
|
21918
|
+
ctx.reply({ t: "files", worktreeId: msg.worktreeId, ...await s.files.list(msg.worktreeId) });
|
|
21578
21919
|
},
|
|
21579
21920
|
async search(msg, ctx, s) {
|
|
21580
21921
|
const { hits, truncated } = await s.files.search(msg.worktreeId, msg.query);
|
|
@@ -21792,7 +22133,7 @@ async function dispatch(msg, ctx, s) {
|
|
|
21792
22133
|
}
|
|
21793
22134
|
|
|
21794
22135
|
// ../daemon/src/server/http.ts
|
|
21795
|
-
import { existsSync as
|
|
22136
|
+
import { existsSync as existsSync20 } from "fs";
|
|
21796
22137
|
import { join as join17 } from "path";
|
|
21797
22138
|
var IMMUTABLE = "private, max-age=31536000, immutable";
|
|
21798
22139
|
var NO_STORE = "no-store";
|
|
@@ -21863,6 +22204,12 @@ function createFetch(opts) {
|
|
|
21863
22204
|
headers["set-cookie"] = grantCookie(grant, remote.host);
|
|
21864
22205
|
return Response.json(await opts.bootstrap(), { headers });
|
|
21865
22206
|
}
|
|
22207
|
+
if (url.pathname === "/restart" && req.method === "POST") {
|
|
22208
|
+
if (!sameSecret(url.searchParams.get("token"), opts.token))
|
|
22209
|
+
return new Response("unauthorized", { status: 401 });
|
|
22210
|
+
const refused = opts.restart();
|
|
22211
|
+
return refused ? new Response(refused, { status: 409 }) : new Response(null, { status: 202 });
|
|
22212
|
+
}
|
|
21866
22213
|
if (url.pathname === "/register" && req.method === "POST") {
|
|
21867
22214
|
if (!sameSecret(req.headers.get("authorization"), `Bearer ${opts.token}`)) {
|
|
21868
22215
|
return new Response("unauthorized", { status: 401 });
|
|
@@ -21885,7 +22232,7 @@ function createFetch(opts) {
|
|
|
21885
22232
|
return new Response("unauthorized", { status: 401 });
|
|
21886
22233
|
const [worktreeId, file, extra] = url.pathname.slice("/attachments/".length).split("/");
|
|
21887
22234
|
const places = worktreeId && file && !extra ? [opts.attachments.fileFor(worktreeId, file), opts.archivedAttachment(worktreeId, file)] : [];
|
|
21888
|
-
const path = places.find((p) => !!p &&
|
|
22235
|
+
const path = places.find((p) => !!p && existsSync20(p));
|
|
21889
22236
|
if (!path)
|
|
21890
22237
|
return new Response("not found", { status: 404 });
|
|
21891
22238
|
return new Response(Bun.file(path), { headers: { "cache-control": IMMUTABLE } });
|
|
@@ -21893,13 +22240,13 @@ function createFetch(opts) {
|
|
|
21893
22240
|
const rel = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
21894
22241
|
const file = join17(opts.shellDist, rel.replaceAll("..", ""));
|
|
21895
22242
|
const hashed = rel.startsWith("/assets/");
|
|
21896
|
-
if (
|
|
22243
|
+
if (existsSync20(file) && Bun.file(file).size > 0) {
|
|
21897
22244
|
return new Response(Bun.file(file), { headers: { "cache-control": hashed ? IMMUTABLE : NO_STORE } });
|
|
21898
22245
|
}
|
|
21899
22246
|
if (hashed)
|
|
21900
22247
|
return new Response("not found", { status: 404 });
|
|
21901
22248
|
const index = join17(opts.shellDist, "index.html");
|
|
21902
|
-
if (
|
|
22249
|
+
if (existsSync20(index))
|
|
21903
22250
|
return new Response(Bun.file(index), { headers: { "cache-control": NO_STORE } });
|
|
21904
22251
|
return new Response("Toyon daemon running; shell not built (run: bun run build)", { status: 200 });
|
|
21905
22252
|
};
|
|
@@ -21997,6 +22344,7 @@ function startServer(opts) {
|
|
|
21997
22344
|
...s.runtime.tiers()
|
|
21998
22345
|
},
|
|
21999
22346
|
costs: s.idle.costs(),
|
|
22347
|
+
updates: s.update.status(),
|
|
22000
22348
|
sockets: [...sockets].map((ws) => ({ subs: [...ws.data.subs], sent: ws.data.sent, bytes: ws.data.bytes }))
|
|
22001
22349
|
});
|
|
22002
22350
|
let statusesInFlight = false;
|
|
@@ -22106,28 +22454,35 @@ function startServer(opts) {
|
|
|
22106
22454
|
});
|
|
22107
22455
|
s.hub.on("agentsChanged", () => broadcast(agentsMsg()));
|
|
22108
22456
|
s.hub.on("selfChanged", () => broadcast({ t: "self", self: s.self.get() }));
|
|
22457
|
+
s.hub.on("updateChanged", () => broadcast({ t: "update", update: s.update.get() }));
|
|
22109
22458
|
s.hub.on("visitsChanged", (repoId) => broadcast({ t: "visits", repoId, pages: s.routes.history(repoId) }));
|
|
22110
22459
|
s.hub.on("archiveChanged", (repoId) => broadcast({ t: "archived", repoId, items: s.worktrees.archived(repoId) }));
|
|
22111
|
-
|
|
22112
|
-
|
|
22113
|
-
|
|
22114
|
-
|
|
22115
|
-
|
|
22116
|
-
|
|
22117
|
-
|
|
22118
|
-
|
|
22119
|
-
|
|
22120
|
-
|
|
22121
|
-
|
|
22122
|
-
|
|
22123
|
-
|
|
22124
|
-
|
|
22125
|
-
|
|
22126
|
-
|
|
22127
|
-
|
|
22128
|
-
|
|
22129
|
-
|
|
22130
|
-
|
|
22460
|
+
s.hub.on("draftChanged", (boxId, text, clientId) => broadcast({ t: "draft", boxId, text, ...clientId ? { clientId } : {} }));
|
|
22461
|
+
const helloFrame = async () => {
|
|
22462
|
+
await s.update.refresh();
|
|
22463
|
+
return {
|
|
22464
|
+
t: "hello",
|
|
22465
|
+
version,
|
|
22466
|
+
protocol: PROTOCOL_VERSION,
|
|
22467
|
+
repos: s.state.repos,
|
|
22468
|
+
rows: await s.worktrees.rows({ quick: true }),
|
|
22469
|
+
spares: s.worktrees.spares(),
|
|
22470
|
+
themes: s.themes.themes,
|
|
22471
|
+
themePrefs: s.themes.prefs,
|
|
22472
|
+
agents: agentInfos(),
|
|
22473
|
+
defaultAgent: s.state.defaultAgent ?? DEFAULT_AGENT_ID,
|
|
22474
|
+
agentChosen: s.state.defaultAgent !== undefined,
|
|
22475
|
+
home: homedir9(),
|
|
22476
|
+
folderDialog: process.platform === "darwin" && !cloud.enabled,
|
|
22477
|
+
remote: opts.remote && { host: opts.remote.host, previews: opts.remote.previews },
|
|
22478
|
+
gitIdentity: await s.repos.gitIdentity(),
|
|
22479
|
+
pending: s.repos.pending,
|
|
22480
|
+
visits: s.routes.historyAll(),
|
|
22481
|
+
self: s.self.get(),
|
|
22482
|
+
update: s.update.get(),
|
|
22483
|
+
drafts: s.drafts.all()
|
|
22484
|
+
};
|
|
22485
|
+
};
|
|
22131
22486
|
let branded = false;
|
|
22132
22487
|
const serverConfig = {
|
|
22133
22488
|
hostname: cloud.bindHost,
|
|
@@ -22143,7 +22498,8 @@ function startServer(opts) {
|
|
|
22143
22498
|
noteShellOrigin: opts.noteShellOrigin,
|
|
22144
22499
|
remote: opts.remote,
|
|
22145
22500
|
preview: (id) => s.runtime.get(id)?.proxy?.handler ?? null,
|
|
22146
|
-
bootstrap: helloFrame
|
|
22501
|
+
bootstrap: helloFrame,
|
|
22502
|
+
restart: () => s.restarter.request()
|
|
22147
22503
|
}),
|
|
22148
22504
|
websocket: {
|
|
22149
22505
|
maxPayloadLength: 64 * 1024 * 1024,
|
|
@@ -22220,7 +22576,8 @@ function startServer(opts) {
|
|
|
22220
22576
|
} catch (e) {
|
|
22221
22577
|
if (!(e instanceof UserError || e instanceof ThemeImportError))
|
|
22222
22578
|
log.error("ws", `${parsed.msg.t} failed`, e);
|
|
22223
|
-
const
|
|
22579
|
+
const m = parsed.msg;
|
|
22580
|
+
const worktreeId = "worktreeId" in m ? m.worktreeId : ("archiveId" in m) ? m.archiveId : undefined;
|
|
22224
22581
|
send(ws, {
|
|
22225
22582
|
t: "error",
|
|
22226
22583
|
message: e instanceof Error ? e.message : String(e),
|
|
@@ -22249,7 +22606,7 @@ function startServer(opts) {
|
|
|
22249
22606
|
}
|
|
22250
22607
|
|
|
22251
22608
|
// ../daemon/src/themes/store.ts
|
|
22252
|
-
import { existsSync as
|
|
22609
|
+
import { existsSync as existsSync21, readdirSync as readdirSync5, readFileSync as readFileSync13, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
22253
22610
|
import { homedir as homedir10 } from "os";
|
|
22254
22611
|
import { basename as basename4, dirname as dirname7, join as join18, resolve as resolve8 } from "path";
|
|
22255
22612
|
|
|
@@ -23148,14 +23505,14 @@ class ThemeStore {
|
|
|
23148
23505
|
name: json?.name ?? base,
|
|
23149
23506
|
source: "file"
|
|
23150
23507
|
});
|
|
23151
|
-
|
|
23508
|
+
writeFileSync7(join18(this.themesDir, `${base}.json`), `${JSON.stringify(theme, null, 2)}
|
|
23152
23509
|
`);
|
|
23153
23510
|
this.load();
|
|
23154
23511
|
return theme;
|
|
23155
23512
|
}
|
|
23156
23513
|
}
|
|
23157
23514
|
function readJsonc(file) {
|
|
23158
|
-
return parse7(
|
|
23515
|
+
return parse7(readFileSync13(file, "utf8"), [], { allowTrailingComma: true });
|
|
23159
23516
|
}
|
|
23160
23517
|
function readVscodeTheme(file, depth = 0) {
|
|
23161
23518
|
const json = readJsonc(file);
|
|
@@ -23177,7 +23534,7 @@ function readVscodeTheme(file, depth = 0) {
|
|
|
23177
23534
|
return t;
|
|
23178
23535
|
}
|
|
23179
23536
|
function loadDir(dir) {
|
|
23180
|
-
if (!
|
|
23537
|
+
if (!existsSync21(dir))
|
|
23181
23538
|
return [];
|
|
23182
23539
|
const out = [];
|
|
23183
23540
|
for (const f of readdirSync5(dir)) {
|
|
@@ -23199,7 +23556,7 @@ function loadDir(dir) {
|
|
|
23199
23556
|
return out;
|
|
23200
23557
|
}
|
|
23201
23558
|
function discover(dir) {
|
|
23202
|
-
if (!
|
|
23559
|
+
if (!existsSync21(dir))
|
|
23203
23560
|
return [];
|
|
23204
23561
|
const out = [];
|
|
23205
23562
|
let entries;
|
|
@@ -23212,18 +23569,18 @@ function discover(dir) {
|
|
|
23212
23569
|
const extDir = join18(dir, name);
|
|
23213
23570
|
const pkgFile = join18(extDir, "package.json");
|
|
23214
23571
|
try {
|
|
23215
|
-
if (!statSync6(extDir).isDirectory() || !
|
|
23572
|
+
if (!statSync6(extDir).isDirectory() || !existsSync21(pkgFile))
|
|
23216
23573
|
continue;
|
|
23217
|
-
const pkg = JSON.parse(
|
|
23574
|
+
const pkg = JSON.parse(readFileSync13(pkgFile, "utf8"));
|
|
23218
23575
|
const themes = pkg.contributes?.themes;
|
|
23219
23576
|
if (!Array.isArray(themes))
|
|
23220
23577
|
continue;
|
|
23221
23578
|
const ext = `${pkg.publisher ?? "local"}.${pkg.name ?? name}`;
|
|
23222
23579
|
let nls = {};
|
|
23223
23580
|
const nlsFile = join18(extDir, "package.nls.json");
|
|
23224
|
-
if (
|
|
23581
|
+
if (existsSync21(nlsFile)) {
|
|
23225
23582
|
try {
|
|
23226
|
-
nls = JSON.parse(
|
|
23583
|
+
nls = JSON.parse(readFileSync13(nlsFile, "utf8"));
|
|
23227
23584
|
} catch {}
|
|
23228
23585
|
}
|
|
23229
23586
|
for (const c of themes) {
|
|
@@ -23247,6 +23604,211 @@ function discover(dir) {
|
|
|
23247
23604
|
return out;
|
|
23248
23605
|
}
|
|
23249
23606
|
|
|
23607
|
+
// ../daemon/src/update/infra.ts
|
|
23608
|
+
import { homedir as homedir11 } from "os";
|
|
23609
|
+
async function latestVersion() {
|
|
23610
|
+
const config = await run("npm", ["config", "get", "registry"], homedir11());
|
|
23611
|
+
const registry = config.ok && config.out !== "" ? config.out : "the npm registry";
|
|
23612
|
+
const r = await run("npm", ["view", "toyon", "version", "--json"], homedir11());
|
|
23613
|
+
if (!r.ok)
|
|
23614
|
+
return { version: null, registry };
|
|
23615
|
+
try {
|
|
23616
|
+
const v = JSON.parse(r.out);
|
|
23617
|
+
return { version: typeof v === "string" ? v : null, registry };
|
|
23618
|
+
} catch {
|
|
23619
|
+
return { version: null, registry };
|
|
23620
|
+
}
|
|
23621
|
+
}
|
|
23622
|
+
var LOG_POINTER = /complete log of this run/i;
|
|
23623
|
+
async function runInstall(command) {
|
|
23624
|
+
const [cmd, ...args] = command;
|
|
23625
|
+
if (!cmd)
|
|
23626
|
+
return { ok: false, line: "nothing to run" };
|
|
23627
|
+
const r = await runLive(cmd, args, homedir11());
|
|
23628
|
+
const lines = `${r.out}
|
|
23629
|
+
${r.err}`.split(`
|
|
23630
|
+
`).map((l) => l.trim()).filter((l) => l !== "" && !LOG_POINTER.test(l));
|
|
23631
|
+
return { ok: r.ok, line: lines.at(-1) ?? String(r.exit) };
|
|
23632
|
+
}
|
|
23633
|
+
|
|
23634
|
+
// ../daemon/src/update/installed.ts
|
|
23635
|
+
async function readVersion(path) {
|
|
23636
|
+
try {
|
|
23637
|
+
const json = await Bun.file(path).json();
|
|
23638
|
+
return typeof json.version === "string" ? json.version : null;
|
|
23639
|
+
} catch {
|
|
23640
|
+
return null;
|
|
23641
|
+
}
|
|
23642
|
+
}
|
|
23643
|
+
|
|
23644
|
+
// ../daemon/src/update/service.ts
|
|
23645
|
+
var REFRESH_EVERY_MS = 60000;
|
|
23646
|
+
var CHECK_EVERY_MS = 6 * 60 * 60000;
|
|
23647
|
+
var FIRST_CHECK_MS = 2 * 60000;
|
|
23648
|
+
var SETTLED_MS = 2 * 60000;
|
|
23649
|
+
var RETRY_AFTER_MS = 24 * 60 * 60000;
|
|
23650
|
+
|
|
23651
|
+
class UpdateService {
|
|
23652
|
+
d;
|
|
23653
|
+
installed = null;
|
|
23654
|
+
latest = null;
|
|
23655
|
+
unreachable = null;
|
|
23656
|
+
installing = false;
|
|
23657
|
+
failed = null;
|
|
23658
|
+
wanted = false;
|
|
23659
|
+
busyAt;
|
|
23660
|
+
announced;
|
|
23661
|
+
now;
|
|
23662
|
+
constructor(d) {
|
|
23663
|
+
this.d = d;
|
|
23664
|
+
this.now = d.now ?? Date.now;
|
|
23665
|
+
this.busyAt = this.now();
|
|
23666
|
+
this.announced = JSON.stringify(this.get());
|
|
23667
|
+
const every = d.setInterval ?? ((fn, ms) => {
|
|
23668
|
+
setInterval(fn, ms).unref?.();
|
|
23669
|
+
});
|
|
23670
|
+
const later = d.setTimeout ?? ((fn, ms) => {
|
|
23671
|
+
setTimeout(fn, ms).unref?.();
|
|
23672
|
+
});
|
|
23673
|
+
every(() => fireAndForget("update", this.tick(), "update tick"), REFRESH_EVERY_MS);
|
|
23674
|
+
every(() => fireAndForget("update", this.check(), "update check"), CHECK_EVERY_MS);
|
|
23675
|
+
later(() => fireAndForget("update", this.check(), "update check"), FIRST_CHECK_MS);
|
|
23676
|
+
d.hub.on("agentStatus", () => {
|
|
23677
|
+
this.busyAt = this.now();
|
|
23678
|
+
if (this.wanted)
|
|
23679
|
+
fireAndForget("update", this.proceed(), "update");
|
|
23680
|
+
});
|
|
23681
|
+
d.hub.on("holdsChanged", () => {
|
|
23682
|
+
this.busyAt = this.now();
|
|
23683
|
+
});
|
|
23684
|
+
}
|
|
23685
|
+
async tick() {
|
|
23686
|
+
await this.refresh();
|
|
23687
|
+
await this.auto();
|
|
23688
|
+
}
|
|
23689
|
+
async refresh() {
|
|
23690
|
+
const v = await this.d.installed();
|
|
23691
|
+
if (v === null)
|
|
23692
|
+
return;
|
|
23693
|
+
this.installed = v === this.d.running ? null : v;
|
|
23694
|
+
this.announce();
|
|
23695
|
+
}
|
|
23696
|
+
async check() {
|
|
23697
|
+
if (this.d.method === "none" || this.d.managed)
|
|
23698
|
+
return;
|
|
23699
|
+
const answer = await this.d.latest();
|
|
23700
|
+
if (answer.version === null) {
|
|
23701
|
+
this.unreachable = answer.registry;
|
|
23702
|
+
return;
|
|
23703
|
+
}
|
|
23704
|
+
this.unreachable = null;
|
|
23705
|
+
this.latest = newer(answer.version, this.d.running) ? answer.version : null;
|
|
23706
|
+
this.announce();
|
|
23707
|
+
await this.auto();
|
|
23708
|
+
}
|
|
23709
|
+
async updateNow() {
|
|
23710
|
+
if (this.d.managed)
|
|
23711
|
+
throw new UserError("Updates are turned off for this machine (TOYON_UPDATES=off)");
|
|
23712
|
+
const target = this.target();
|
|
23713
|
+
if (target === null)
|
|
23714
|
+
throw new UserError("Toyon is up to date");
|
|
23715
|
+
if (this.needsInstall() && this.d.command(target) === null) {
|
|
23716
|
+
throw new UserError(`Run npx toyon@${target} to use it`);
|
|
23717
|
+
}
|
|
23718
|
+
this.failed = null;
|
|
23719
|
+
this.wanted = true;
|
|
23720
|
+
await this.proceed();
|
|
23721
|
+
}
|
|
23722
|
+
get() {
|
|
23723
|
+
const restarting = this.d.restarter.waitingOn() ?? (this.wanted && !this.installing ? this.d.restarter.working() : null);
|
|
23724
|
+
if (this.installed === null && this.latest === null && !this.installing && this.failed === null && restarting === null) {
|
|
23725
|
+
return null;
|
|
23726
|
+
}
|
|
23727
|
+
return {
|
|
23728
|
+
running: this.d.running,
|
|
23729
|
+
latest: this.latest,
|
|
23730
|
+
installed: this.installed,
|
|
23731
|
+
method: this.d.method,
|
|
23732
|
+
installing: this.installing,
|
|
23733
|
+
failed: this.failed,
|
|
23734
|
+
restarting
|
|
23735
|
+
};
|
|
23736
|
+
}
|
|
23737
|
+
status() {
|
|
23738
|
+
return { managed: this.d.managed, unreachable: this.unreachable, latest: this.latest };
|
|
23739
|
+
}
|
|
23740
|
+
async auto() {
|
|
23741
|
+
if (this.wanted || this.installing || this.d.managed)
|
|
23742
|
+
return;
|
|
23743
|
+
const target = this.target();
|
|
23744
|
+
if (target === null)
|
|
23745
|
+
return;
|
|
23746
|
+
if (this.needsInstall() && this.d.command(target) === null)
|
|
23747
|
+
return;
|
|
23748
|
+
const failed = this.d.state.updateFailed;
|
|
23749
|
+
if (failed?.version === target && this.now() - failed.at < RETRY_AFTER_MS)
|
|
23750
|
+
return;
|
|
23751
|
+
if (this.d.busy()) {
|
|
23752
|
+
this.busyAt = this.now();
|
|
23753
|
+
return;
|
|
23754
|
+
}
|
|
23755
|
+
if (this.now() - this.busyAt < SETTLED_MS)
|
|
23756
|
+
return;
|
|
23757
|
+
log.info("update", `nothing is busy: updating to ${target}`);
|
|
23758
|
+
this.wanted = true;
|
|
23759
|
+
await this.proceed();
|
|
23760
|
+
}
|
|
23761
|
+
target() {
|
|
23762
|
+
if (this.latest && (this.installed === null || newer(this.latest, this.installed)))
|
|
23763
|
+
return this.latest;
|
|
23764
|
+
return this.installed;
|
|
23765
|
+
}
|
|
23766
|
+
needsInstall() {
|
|
23767
|
+
const target = this.target();
|
|
23768
|
+
return target !== null && target !== this.installed;
|
|
23769
|
+
}
|
|
23770
|
+
async proceed() {
|
|
23771
|
+
if (!this.wanted || this.installing)
|
|
23772
|
+
return;
|
|
23773
|
+
if (this.d.restarter.working().length > 0) {
|
|
23774
|
+
this.announce();
|
|
23775
|
+
return;
|
|
23776
|
+
}
|
|
23777
|
+
const target = this.target();
|
|
23778
|
+
const command = target === null ? null : this.d.command(target);
|
|
23779
|
+
if (target !== null && command !== null && this.needsInstall()) {
|
|
23780
|
+
this.installing = true;
|
|
23781
|
+
this.announce();
|
|
23782
|
+
log.info("update", `installing ${target}: ${command.join(" ")}`);
|
|
23783
|
+
const r = await this.d.install(command);
|
|
23784
|
+
this.installing = false;
|
|
23785
|
+
if (!r.ok) {
|
|
23786
|
+
this.wanted = false;
|
|
23787
|
+
this.failed = { version: target, line: r.line, command: command.join(" ") };
|
|
23788
|
+
this.d.state.setUpdateFailed({ version: target, at: this.now() });
|
|
23789
|
+
log.warn("update", `installing ${target} failed: ${r.line}`);
|
|
23790
|
+
this.announce();
|
|
23791
|
+
return;
|
|
23792
|
+
}
|
|
23793
|
+
await this.refresh();
|
|
23794
|
+
}
|
|
23795
|
+
this.wanted = false;
|
|
23796
|
+
const refused = this.d.restarter.request();
|
|
23797
|
+
if (refused) {
|
|
23798
|
+
this.failed = { version: target ?? this.d.running, line: refused, command: "toyon restart" };
|
|
23799
|
+
log.warn("update", `restart refused: ${refused}`);
|
|
23800
|
+
}
|
|
23801
|
+
this.announce();
|
|
23802
|
+
}
|
|
23803
|
+
announce() {
|
|
23804
|
+
const key = JSON.stringify(this.get());
|
|
23805
|
+
if (key === this.announced)
|
|
23806
|
+
return;
|
|
23807
|
+
this.announced = key;
|
|
23808
|
+
this.d.hub.emit("updateChanged");
|
|
23809
|
+
}
|
|
23810
|
+
}
|
|
23811
|
+
|
|
23250
23812
|
// ../daemon/src/worktrees/chats.ts
|
|
23251
23813
|
var CHAT_HITS_PER_WORKTREE = 20;
|
|
23252
23814
|
var CHAT_HITS_MAX = 200;
|
|
@@ -23842,7 +24404,7 @@ function rankRefs({ refs, merged, prs, query, defaultBranch, rowIdForPath }) {
|
|
|
23842
24404
|
}
|
|
23843
24405
|
|
|
23844
24406
|
// ../daemon/src/worktrees/service.ts
|
|
23845
|
-
import { existsSync as
|
|
24407
|
+
import { existsSync as existsSync23, lstatSync, readlinkSync, rmSync as rmSync5, symlinkSync, unlinkSync } from "fs";
|
|
23846
24408
|
import { basename as basename6, dirname as dirname8, join as join21 } from "path";
|
|
23847
24409
|
|
|
23848
24410
|
// ../daemon/src/git/archive.ts
|
|
@@ -23899,6 +24461,24 @@ async function commitOf(repoPath, ref) {
|
|
|
23899
24461
|
const r = await git(repoPath, "rev-parse", "--verify", "--quiet", `${ref}^{commit}`);
|
|
23900
24462
|
return r.ok ? r.out : null;
|
|
23901
24463
|
}
|
|
24464
|
+
var landRef = (worktreeId, n) => `refs/toyon/lands/${worktreeId}/${n}`;
|
|
24465
|
+
async function landingMark(worktreePath, defaultBr) {
|
|
24466
|
+
const [tip, base] = await Promise.all([
|
|
24467
|
+
git(worktreePath, "rev-parse", "HEAD"),
|
|
24468
|
+
git(worktreePath, "merge-base", "HEAD", defaultBr)
|
|
24469
|
+
]);
|
|
24470
|
+
if (!tip.ok || !base.ok || !tip.out || tip.out === base.out)
|
|
24471
|
+
return null;
|
|
24472
|
+
return { base: base.out, tip: tip.out };
|
|
24473
|
+
}
|
|
24474
|
+
async function dropLandRefs(repoPath, worktreeId) {
|
|
24475
|
+
const r = await git(repoPath, "for-each-ref", "--format=%(refname)", `refs/toyon/lands/${worktreeId}/`);
|
|
24476
|
+
if (!r.ok || !r.out)
|
|
24477
|
+
return;
|
|
24478
|
+
for (const ref of r.out.split(`
|
|
24479
|
+
`).filter(Boolean))
|
|
24480
|
+
await git(repoPath, "update-ref", "-d", ref);
|
|
24481
|
+
}
|
|
23902
24482
|
|
|
23903
24483
|
// ../daemon/src/git/land.ts
|
|
23904
24484
|
async function commitWorktree(worktreePath, message) {
|
|
@@ -24111,7 +24691,7 @@ function compareUrl(remoteUrl, base, branch) {
|
|
|
24111
24691
|
}
|
|
24112
24692
|
|
|
24113
24693
|
// ../daemon/src/worktrees/archive.ts
|
|
24114
|
-
import { existsSync as
|
|
24694
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync6, readdirSync as readdirSync6, readFileSync as readFileSync14, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
24115
24695
|
import { join as join19 } from "path";
|
|
24116
24696
|
class WorktreeArchive {
|
|
24117
24697
|
dir;
|
|
@@ -24121,10 +24701,10 @@ class WorktreeArchive {
|
|
|
24121
24701
|
mkdirSync6(dir, { recursive: true });
|
|
24122
24702
|
for (const id of readdirSync6(dir)) {
|
|
24123
24703
|
const file = this.filesOf(id).record;
|
|
24124
|
-
if (!
|
|
24704
|
+
if (!existsSync22(file))
|
|
24125
24705
|
continue;
|
|
24126
24706
|
try {
|
|
24127
|
-
this.records.set(id, JSON.parse(
|
|
24707
|
+
this.records.set(id, JSON.parse(readFileSync14(file, "utf8")));
|
|
24128
24708
|
} catch (e) {
|
|
24129
24709
|
log.warn("archive", `skipped an unreadable record: ${file}`, e);
|
|
24130
24710
|
}
|
|
@@ -24138,7 +24718,7 @@ class WorktreeArchive {
|
|
|
24138
24718
|
mkdirSync6(at.dir, { recursive: true });
|
|
24139
24719
|
move(from.transcript, at.transcript);
|
|
24140
24720
|
move(from.attachments, at.attachments);
|
|
24141
|
-
|
|
24721
|
+
writeFileSync8(at.record, JSON.stringify(rec, null, 2));
|
|
24142
24722
|
this.records.set(rec.worktree.id, rec);
|
|
24143
24723
|
}
|
|
24144
24724
|
take(id, to) {
|
|
@@ -24187,13 +24767,14 @@ function summarize(r, repoId) {
|
|
|
24187
24767
|
restorable: !!r.kept,
|
|
24188
24768
|
...r.kept?.snapshot ? { uncommitted: true } : {},
|
|
24189
24769
|
...r.worktree.landed ? { landed: true } : {},
|
|
24190
|
-
...r.cost !== undefined ? { cost: r.cost } : {}
|
|
24770
|
+
...r.cost !== undefined ? { cost: r.cost } : {},
|
|
24771
|
+
...r.auto ? { auto: r.auto } : {}
|
|
24191
24772
|
};
|
|
24192
24773
|
}
|
|
24193
24774
|
function lastUsage(transcript) {
|
|
24194
|
-
if (!
|
|
24775
|
+
if (!existsSync22(transcript))
|
|
24195
24776
|
return null;
|
|
24196
|
-
const lines =
|
|
24777
|
+
const lines = readFileSync14(transcript, "utf8").split(`
|
|
24197
24778
|
`);
|
|
24198
24779
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
24199
24780
|
if (!lines[i]?.includes('"usage"'))
|
|
@@ -24207,9 +24788,9 @@ function lastUsage(transcript) {
|
|
|
24207
24788
|
return null;
|
|
24208
24789
|
}
|
|
24209
24790
|
function firstPrompt(transcript) {
|
|
24210
|
-
if (!
|
|
24791
|
+
if (!existsSync22(transcript))
|
|
24211
24792
|
return;
|
|
24212
|
-
for (const line of
|
|
24793
|
+
for (const line of readFileSync14(transcript, "utf8").split(`
|
|
24213
24794
|
`)) {
|
|
24214
24795
|
if (!line.includes('"user-message"'))
|
|
24215
24796
|
continue;
|
|
@@ -24222,10 +24803,67 @@ function firstPrompt(transcript) {
|
|
|
24222
24803
|
return;
|
|
24223
24804
|
}
|
|
24224
24805
|
function move(from, to) {
|
|
24225
|
-
if (!
|
|
24806
|
+
if (!existsSync22(from))
|
|
24226
24807
|
return;
|
|
24227
24808
|
rmSync4(to, { recursive: true, force: true });
|
|
24228
|
-
|
|
24809
|
+
renameSync4(from, to);
|
|
24810
|
+
}
|
|
24811
|
+
|
|
24812
|
+
// ../daemon/src/worktrees/archivedGit.ts
|
|
24813
|
+
class ArchivedGit {
|
|
24814
|
+
repoPath;
|
|
24815
|
+
kept;
|
|
24816
|
+
lands;
|
|
24817
|
+
defaultBr;
|
|
24818
|
+
constructor(repoPath, kept, lands, defaultBr) {
|
|
24819
|
+
this.repoPath = repoPath;
|
|
24820
|
+
this.kept = kept;
|
|
24821
|
+
this.lands = lands;
|
|
24822
|
+
this.defaultBr = defaultBr;
|
|
24823
|
+
}
|
|
24824
|
+
async tailBase() {
|
|
24825
|
+
const r = await git(this.repoPath, "merge-base", this.kept.head, this.defaultBr);
|
|
24826
|
+
return r.ok && r.out ? r.out : this.kept.head;
|
|
24827
|
+
}
|
|
24828
|
+
async status() {
|
|
24829
|
+
const base = await this.tailBase();
|
|
24830
|
+
const { head, snapshot } = this.kept;
|
|
24831
|
+
const [files, committed] = await Promise.all([
|
|
24832
|
+
snapshot ? filesBetween(this.repoPath, head, snapshot) : [],
|
|
24833
|
+
base === head ? [] : filesBetween(this.repoPath, base, head)
|
|
24834
|
+
]);
|
|
24835
|
+
return { files, committed, head: snapshot ?? head };
|
|
24836
|
+
}
|
|
24837
|
+
async log() {
|
|
24838
|
+
const seen = new Set;
|
|
24839
|
+
const unseen = (list) => {
|
|
24840
|
+
const out = list.filter((c) => !seen.has(c.sha));
|
|
24841
|
+
for (const c of out)
|
|
24842
|
+
seen.add(c.sha);
|
|
24843
|
+
return out;
|
|
24844
|
+
};
|
|
24845
|
+
const landed = [];
|
|
24846
|
+
for (const land of this.lands) {
|
|
24847
|
+
const commits = unseen(await logRange(this.repoPath, land.base, land.tip));
|
|
24848
|
+
landed.push(commits.map((c) => ({ ...c, landedAt: land.at })));
|
|
24849
|
+
}
|
|
24850
|
+
const base = await this.tailBase();
|
|
24851
|
+
const tail = base === this.kept.head ? [] : unseen(await logRange(this.repoPath, base, this.kept.head));
|
|
24852
|
+
return [...tail, ...landed.reverse().flat()];
|
|
24853
|
+
}
|
|
24854
|
+
commitFiles(sha) {
|
|
24855
|
+
return commitFiles(this.repoPath, sha);
|
|
24856
|
+
}
|
|
24857
|
+
async file(path, ref) {
|
|
24858
|
+
if (ref)
|
|
24859
|
+
return fileAtCommit(this.repoPath, ref, path);
|
|
24860
|
+
const base = await this.tailBase();
|
|
24861
|
+
const [before, after] = await Promise.all([
|
|
24862
|
+
gitRaw(this.repoPath, "show", `${base}:${path}`),
|
|
24863
|
+
gitRaw(this.repoPath, "show", `${this.kept.snapshot ?? this.kept.head}:${path}`)
|
|
24864
|
+
]);
|
|
24865
|
+
return { before: before.ok ? before.out : "", after: after.ok ? after.out : "" };
|
|
24866
|
+
}
|
|
24229
24867
|
}
|
|
24230
24868
|
|
|
24231
24869
|
// ../daemon/src/worktrees/spare.ts
|
|
@@ -24243,7 +24881,7 @@ class SparePool {
|
|
|
24243
24881
|
adopt(repoId) {
|
|
24244
24882
|
const persisted = this.d.state.worktrees.filter((w) => w.repoId === repoId && w.kind === "spare");
|
|
24245
24883
|
for (const extra of persisted.slice(1))
|
|
24246
|
-
fireAndForget(extra.id, this.d.
|
|
24884
|
+
fireAndForget(extra.id, this.d.discard(extra.id), "stale spare removal");
|
|
24247
24885
|
const spare = persisted[0];
|
|
24248
24886
|
if (!spare)
|
|
24249
24887
|
return;
|
|
@@ -24300,7 +24938,7 @@ class SparePool {
|
|
|
24300
24938
|
log.warn(repoId, "spare warm-up failed; rolling back", e);
|
|
24301
24939
|
this.spares.delete(repoId);
|
|
24302
24940
|
if (entry.worktreeId && this.d.state.worktree(entry.worktreeId)) {
|
|
24303
|
-
await this.d.
|
|
24941
|
+
await this.d.discard(entry.worktreeId).catch((re) => log.warn(repoId, "spare rollback failed", re));
|
|
24304
24942
|
}
|
|
24305
24943
|
}
|
|
24306
24944
|
}
|
|
@@ -24506,9 +25144,6 @@ function stopOf(prev, next) {
|
|
|
24506
25144
|
return "failed";
|
|
24507
25145
|
return null;
|
|
24508
25146
|
}
|
|
24509
|
-
function isUnseen(wt) {
|
|
24510
|
-
return wt.unread === true || wt.lastTurn != null && (wt.seenAt == null || wt.seenAt < wt.lastTurn.at);
|
|
24511
|
-
}
|
|
24512
25147
|
|
|
24513
25148
|
// ../daemon/src/worktrees/service.ts
|
|
24514
25149
|
var LOCAL_CONFIG_FILES = [".env", ".env.local", ".env.development", ".env.development.local", ".dev.vars"];
|
|
@@ -24533,6 +25168,7 @@ class WorktreeService {
|
|
|
24533
25168
|
d;
|
|
24534
25169
|
spare;
|
|
24535
25170
|
archive;
|
|
25171
|
+
archiving = new Map;
|
|
24536
25172
|
countsCache = new Map;
|
|
24537
25173
|
discoverCache = new Map;
|
|
24538
25174
|
usage = new Map;
|
|
@@ -24546,8 +25182,8 @@ class WorktreeService {
|
|
|
24546
25182
|
runtime: d.runtime,
|
|
24547
25183
|
paths: d.paths,
|
|
24548
25184
|
setupAndStart: (wt, repo) => this.setupAndStart(wt, repo),
|
|
24549
|
-
|
|
24550
|
-
await this.
|
|
25185
|
+
discard: async (id) => {
|
|
25186
|
+
await this.discardWorktree(id);
|
|
24551
25187
|
}
|
|
24552
25188
|
});
|
|
24553
25189
|
for (const wt of d.state.worktrees)
|
|
@@ -24699,7 +25335,7 @@ class WorktreeService {
|
|
|
24699
25335
|
const branch = kind === "pr" ? `pr/${number}` : ref;
|
|
24700
25336
|
const title = kind === "pr" ? `pr-${number}` : cleanTitle2(ref) || "branch";
|
|
24701
25337
|
let slug = title;
|
|
24702
|
-
if (
|
|
25338
|
+
if (existsSync23(join21(this.d.paths.worktreesDir, repo.name, slug)))
|
|
24703
25339
|
slug = `${slug}-${shortId().slice(0, 4)}`;
|
|
24704
25340
|
const wtPath = join21(this.d.paths.worktreesDir, repo.name, slug);
|
|
24705
25341
|
await withRepoLock(repo.path, async () => {
|
|
@@ -24855,12 +25491,32 @@ class WorktreeService {
|
|
|
24855
25491
|
}
|
|
24856
25492
|
}), "auto-naming");
|
|
24857
25493
|
}
|
|
24858
|
-
|
|
25494
|
+
archiveWorktree(worktreeId, opts = {}) {
|
|
25495
|
+
const running = this.archiving.get(worktreeId);
|
|
25496
|
+
if (running)
|
|
25497
|
+
return running;
|
|
24859
25498
|
const wt = this.d.state.worktree(worktreeId);
|
|
24860
|
-
if (!wt || !(
|
|
24861
|
-
return null;
|
|
25499
|
+
if (!wt || !canArchive(wt))
|
|
25500
|
+
return Promise.resolve(null);
|
|
25501
|
+
if (opts.still && !opts.still())
|
|
25502
|
+
return Promise.resolve(null);
|
|
25503
|
+
const done = this.takeDown(wt, true, opts.reason).finally(() => this.archiving.delete(worktreeId));
|
|
25504
|
+
this.archiving.set(worktreeId, done);
|
|
25505
|
+
return done;
|
|
25506
|
+
}
|
|
25507
|
+
archivingNow(worktreeId) {
|
|
25508
|
+
return this.archiving.get(worktreeId);
|
|
25509
|
+
}
|
|
25510
|
+
async discardWorktree(worktreeId) {
|
|
25511
|
+
const wt = this.d.state.worktree(worktreeId);
|
|
25512
|
+
if (!wt || !(canArchive(wt) || wt.kind === "spare"))
|
|
25513
|
+
return;
|
|
25514
|
+
await this.takeDown(wt, false);
|
|
25515
|
+
this.d.drafts?.drop(worktreeId);
|
|
25516
|
+
}
|
|
25517
|
+
async takeDown(wt, archive, reason) {
|
|
25518
|
+
const worktreeId = wt.id;
|
|
24862
25519
|
const repo = this.d.state.requireRepo(wt.repoId);
|
|
24863
|
-
const archive = wt.kind !== "spare" && opts.archive !== false;
|
|
24864
25520
|
await this.d.runtime.stop(worktreeId);
|
|
24865
25521
|
const kept = await withRepoLock(repo.path, async () => {
|
|
24866
25522
|
const k = archive ? await this.keep(repo, wt) : null;
|
|
@@ -24878,9 +25534,12 @@ class WorktreeService {
|
|
|
24878
25534
|
releasePort(wt.proxyPort);
|
|
24879
25535
|
let archived = null;
|
|
24880
25536
|
if (archive)
|
|
24881
|
-
archived = this.archiveChat(wt, repo, kept, sessionId);
|
|
24882
|
-
else
|
|
25537
|
+
archived = this.archiveChat(wt, repo, kept, sessionId, reason);
|
|
25538
|
+
else {
|
|
24883
25539
|
this.deleteChat(worktreeId);
|
|
25540
|
+
if (wt.lands?.length)
|
|
25541
|
+
await dropLandRefs(repo.path, worktreeId);
|
|
25542
|
+
}
|
|
24884
25543
|
this.d.hub.emit("worktreesChanged");
|
|
24885
25544
|
return archived;
|
|
24886
25545
|
}
|
|
@@ -24893,7 +25552,7 @@ class WorktreeService {
|
|
|
24893
25552
|
log.warn(wt.id, "could not keep its uncommitted changes: only its commits are archived");
|
|
24894
25553
|
return kept;
|
|
24895
25554
|
}
|
|
24896
|
-
archiveChat(wt, repo, kept, sessionId) {
|
|
25555
|
+
archiveChat(wt, repo, kept, sessionId, reason) {
|
|
24897
25556
|
const files = this.chatFiles(wt.id);
|
|
24898
25557
|
const prompt = firstPrompt(files.transcript);
|
|
24899
25558
|
const cost = (this.usage.get(wt.id) ?? lastUsage(files.transcript))?.cost;
|
|
@@ -24904,7 +25563,8 @@ class WorktreeService {
|
|
|
24904
25563
|
...sessionId ? { sessionId } : {},
|
|
24905
25564
|
...prompt ? { prompt } : {},
|
|
24906
25565
|
...cost !== undefined ? { cost } : {},
|
|
24907
|
-
...kept ? { kept } : {}
|
|
25566
|
+
...kept ? { kept } : {},
|
|
25567
|
+
...reason ? { auto: reason } : {}
|
|
24908
25568
|
};
|
|
24909
25569
|
try {
|
|
24910
25570
|
this.archive.put(rec, files);
|
|
@@ -24950,6 +25610,9 @@ class WorktreeService {
|
|
|
24950
25610
|
});
|
|
24951
25611
|
this.archiveChat(wt, repo, kept, sessionId);
|
|
24952
25612
|
}
|
|
25613
|
+
hasArchived(archiveId) {
|
|
25614
|
+
return !!this.archive.get(archiveId);
|
|
25615
|
+
}
|
|
24953
25616
|
archived(repoId) {
|
|
24954
25617
|
const repo = this.d.state.repo(repoId);
|
|
24955
25618
|
return repo ? this.archive.list(repo) : [];
|
|
@@ -24971,6 +25634,28 @@ class WorktreeService {
|
|
|
24971
25634
|
const files = this.archive.chatFiles(archiveId);
|
|
24972
25635
|
return files && isAttachmentFile(file) ? join21(files.attachments, file) : null;
|
|
24973
25636
|
}
|
|
25637
|
+
archivedGit(archiveId) {
|
|
25638
|
+
const rec = this.archive.get(archiveId);
|
|
25639
|
+
const repo = rec && this.repoOf(rec);
|
|
25640
|
+
if (!rec?.kept || !repo)
|
|
25641
|
+
return null;
|
|
25642
|
+
return new ArchivedGit(repo.path, rec.kept, rec.worktree.lands ?? [], repo.defaultBranch);
|
|
25643
|
+
}
|
|
25644
|
+
async archivedStatus(archiveId) {
|
|
25645
|
+
const kept = this.archivedGit(archiveId);
|
|
25646
|
+
if (!kept)
|
|
25647
|
+
return null;
|
|
25648
|
+
try {
|
|
25649
|
+
return await kept.status();
|
|
25650
|
+
} catch (e) {
|
|
25651
|
+
log.warn(archiveId, "reading its kept changes failed", e);
|
|
25652
|
+
return null;
|
|
25653
|
+
}
|
|
25654
|
+
}
|
|
25655
|
+
async archivedFile(archiveId, path, ref) {
|
|
25656
|
+
const kept = this.archivedGit(archiveId);
|
|
25657
|
+
return kept ? kept.file(path, ref) : null;
|
|
25658
|
+
}
|
|
24974
25659
|
repoOf(rec) {
|
|
24975
25660
|
return this.d.state.repo(rec.worktree.repoId) ?? this.d.state.repos.find((r) => r.path === rec.repoPath);
|
|
24976
25661
|
}
|
|
@@ -24985,7 +25670,9 @@ class WorktreeService {
|
|
|
24985
25670
|
const kept = rec.kept;
|
|
24986
25671
|
if (!kept)
|
|
24987
25672
|
throw new UserError(`${old.title} was archived without its commits, so there is nothing to restore`);
|
|
24988
|
-
|
|
25673
|
+
if (rec.auto)
|
|
25674
|
+
log.info(old.id, `restoring a worktree that archived itself (${rec.auto})`);
|
|
25675
|
+
const path = existsSync23(old.path) ? join21(dirname8(old.path), `${basename6(old.path)}-${shortId().slice(0, 4)}`) : old.path;
|
|
24989
25676
|
const branch = await withRepoLock(repo.path, async () => {
|
|
24990
25677
|
if (!await commitOf(repo.path, archiveRef(old.id))) {
|
|
24991
25678
|
throw new UserError(`${old.title}'s kept commits are gone from git`);
|
|
@@ -25045,12 +25732,15 @@ class WorktreeService {
|
|
|
25045
25732
|
return;
|
|
25046
25733
|
const repo = this.repoOf(rec);
|
|
25047
25734
|
const repoPath = repo?.path ?? rec.repoPath;
|
|
25048
|
-
if (rec.kept &&
|
|
25735
|
+
if (rec.kept && existsSync23(repoPath)) {
|
|
25049
25736
|
const r = await git(repoPath, "update-ref", "-d", archiveRef(archiveId));
|
|
25050
25737
|
if (!r.ok)
|
|
25051
25738
|
log.warn(archiveId, `could not drop its archive ref: ${r.err}`);
|
|
25052
25739
|
}
|
|
25740
|
+
if (rec.worktree.lands?.length && existsSync23(repoPath))
|
|
25741
|
+
await dropLandRefs(repoPath, archiveId);
|
|
25053
25742
|
this.archive.delete(archiveId);
|
|
25743
|
+
this.d.drafts?.drop(archiveId);
|
|
25054
25744
|
if (repo)
|
|
25055
25745
|
this.d.hub.emit("archiveChanged", repo.id);
|
|
25056
25746
|
}
|
|
@@ -25120,7 +25810,7 @@ class WorktreeService {
|
|
|
25120
25810
|
const group = wt.variant.group;
|
|
25121
25811
|
const siblings = this.d.state.worktrees.filter((w) => w.variant?.group === group && w.id !== worktreeId);
|
|
25122
25812
|
for (const sibling of siblings) {
|
|
25123
|
-
await this.
|
|
25813
|
+
await this.archiveWorktree(sibling.id).catch((e) => log.warn(sibling.id, "could not archive variant sibling", e));
|
|
25124
25814
|
}
|
|
25125
25815
|
delete wt.variant;
|
|
25126
25816
|
this.d.state.save();
|
|
@@ -25165,7 +25855,7 @@ class WorktreeService {
|
|
|
25165
25855
|
agent.note(withoutAttachments(event));
|
|
25166
25856
|
}
|
|
25167
25857
|
for (const w of sources)
|
|
25168
|
-
await this.
|
|
25858
|
+
await this.discardWorktree(w.id);
|
|
25169
25859
|
this.setLanded(target.id, false);
|
|
25170
25860
|
this.countsCache.delete(target.id);
|
|
25171
25861
|
this.d.hub.emit("worktreesChanged");
|
|
@@ -25182,7 +25872,7 @@ class WorktreeService {
|
|
|
25182
25872
|
async setupThenStart(wt, repo, depsSource, { setupCommands = true }) {
|
|
25183
25873
|
const srcNm = join21(depsSource, "node_modules");
|
|
25184
25874
|
const dstNm = join21(wt.path, "node_modules");
|
|
25185
|
-
if (
|
|
25875
|
+
if (existsSync23(srcNm) && !existsSync23(dstNm)) {
|
|
25186
25876
|
const started = Date.now();
|
|
25187
25877
|
const attempts = [
|
|
25188
25878
|
["clonefile", ["-Rc", srcNm, dstNm]],
|
|
@@ -25199,7 +25889,7 @@ class WorktreeService {
|
|
|
25199
25889
|
}
|
|
25200
25890
|
for (const f of LOCAL_CONFIG_FILES) {
|
|
25201
25891
|
const src = join21(depsSource, f);
|
|
25202
|
-
if (!
|
|
25892
|
+
if (!existsSync23(src) || existsSync23(join21(wt.path, f)))
|
|
25203
25893
|
continue;
|
|
25204
25894
|
const r = await run("cp", [src, join21(wt.path, f)], wt.path);
|
|
25205
25895
|
if (r.ok)
|
|
@@ -25319,12 +26009,14 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
25319
26009
|
const taken = await takeMainIn(wt.path, repo.defaultBranch, own);
|
|
25320
26010
|
if (!taken.ok)
|
|
25321
26011
|
return taken;
|
|
26012
|
+
const mark = await landingMark(wt.path, repo.defaultBranch);
|
|
25322
26013
|
const method = policy.merge ?? DEFAULT_MERGE_METHOD;
|
|
25323
26014
|
const squash = method === "squash" ? await squashMessage(wt.path, repo.defaultBranch, suggested) : "";
|
|
25324
26015
|
const landed = await landLocally(wt.path, wt.branch, repo.path, repo.defaultBranch, method, squash);
|
|
25325
26016
|
if (!landed.ok)
|
|
25326
26017
|
return landed;
|
|
25327
26018
|
mergedHere = true;
|
|
26019
|
+
await this.noteLand(repo, wt, mark);
|
|
25328
26020
|
await this.restartFromMain(wt, repo.defaultBranch);
|
|
25329
26021
|
if (policy.land === "push") {
|
|
25330
26022
|
const pushed = await pushMain(repo.path, repo.defaultBranch);
|
|
@@ -25340,9 +26032,21 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
25340
26032
|
}
|
|
25341
26033
|
if (!result.ok)
|
|
25342
26034
|
return { result };
|
|
25343
|
-
const
|
|
26035
|
+
const archiveIds = wt.variant ? this.d.state.worktrees.filter((w) => w.variant?.group === wt.variant?.group && w.id !== wt.id).map((w) => w.id) : [];
|
|
25344
26036
|
const where = policy.land === "push" ? `${repo.defaultBranch}, pushed` : repo.defaultBranch;
|
|
25345
|
-
return { result: { ...result, message: `${wt.title} is on ${where}` },
|
|
26037
|
+
return { result: { ...result, message: `${wt.title} is on ${where}` }, archiveIds };
|
|
26038
|
+
}
|
|
26039
|
+
async noteLand(repo, wt, mark) {
|
|
26040
|
+
if (!mark)
|
|
26041
|
+
return;
|
|
26042
|
+
const n = wt.lands?.length ?? 0;
|
|
26043
|
+
const pinned = await git(repo.path, "update-ref", landRef(wt.id, n), mark.tip);
|
|
26044
|
+
if (!pinned.ok) {
|
|
26045
|
+
log.warn(wt.id, `could not keep the commits it landed: ${pinned.err}`);
|
|
26046
|
+
return;
|
|
26047
|
+
}
|
|
26048
|
+
wt.lands = [...wt.lands ?? [], { ...mark, at: Date.now() }];
|
|
26049
|
+
this.d.state.save();
|
|
25346
26050
|
}
|
|
25347
26051
|
async restartFromMain(wt, defaultBr) {
|
|
25348
26052
|
if (!hasOwnBranch(wt))
|
|
@@ -25355,9 +26059,11 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
25355
26059
|
async prMerged(worktreeId) {
|
|
25356
26060
|
const { wt, repo } = this.d.state.requireWorktreeWithRepo(worktreeId);
|
|
25357
26061
|
const result = await withRepoLock(repo.path, async () => {
|
|
26062
|
+
const mark = await landingMark(wt.path, repo.defaultBranch);
|
|
25358
26063
|
const pulled = await fastForwardMain(repo.path, repo.defaultBranch);
|
|
25359
26064
|
if (!pulled.ok)
|
|
25360
26065
|
return pulled;
|
|
26066
|
+
await this.noteLand(repo, wt, mark);
|
|
25361
26067
|
await this.restartFromMain(wt, repo.defaultBranch);
|
|
25362
26068
|
return pulled;
|
|
25363
26069
|
});
|
|
@@ -25505,6 +26211,14 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
25505
26211
|
return cached ?? {};
|
|
25506
26212
|
}
|
|
25507
26213
|
}
|
|
26214
|
+
async freshCounts(worktreeId) {
|
|
26215
|
+
const wt = this.d.state.worktree(worktreeId);
|
|
26216
|
+
const repo = wt && this.d.state.repo(wt.repoId);
|
|
26217
|
+
if (!wt || !repo)
|
|
26218
|
+
return {};
|
|
26219
|
+
this.countsCache.delete(worktreeId);
|
|
26220
|
+
return this.counts(wt.id, wt.path, repo.defaultBranch, true);
|
|
26221
|
+
}
|
|
25508
26222
|
readable(id) {
|
|
25509
26223
|
const wt = this.d.state.worktree(id);
|
|
25510
26224
|
if (wt) {
|
|
@@ -25530,7 +26244,7 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
25530
26244
|
async gitStatus(worktreeId) {
|
|
25531
26245
|
const r = this.readable(worktreeId);
|
|
25532
26246
|
if (!r)
|
|
25533
|
-
return
|
|
26247
|
+
return this.archivedStatus(worktreeId);
|
|
25534
26248
|
try {
|
|
25535
26249
|
const isMain = r.wt?.kind === "main";
|
|
25536
26250
|
const [files, counts, head] = await Promise.all([
|
|
@@ -25568,11 +26282,15 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
25568
26282
|
}
|
|
25569
26283
|
async gitLog(worktreeId) {
|
|
25570
26284
|
const r = this.readable(worktreeId);
|
|
25571
|
-
|
|
26285
|
+
if (r)
|
|
26286
|
+
return logCommits(r.path, r.defaultBranch);
|
|
26287
|
+
return await this.archivedGit(worktreeId)?.log() ?? [];
|
|
25572
26288
|
}
|
|
25573
26289
|
async commitFiles(worktreeId, sha) {
|
|
25574
26290
|
const r = this.readable(worktreeId);
|
|
25575
|
-
|
|
26291
|
+
if (r)
|
|
26292
|
+
return commitFiles(r.path, sha);
|
|
26293
|
+
return await this.archivedGit(worktreeId)?.commitFiles(sha) ?? [];
|
|
25576
26294
|
}
|
|
25577
26295
|
markPrompted(worktreeId) {
|
|
25578
26296
|
const wt = this.d.state.worktree(worktreeId);
|
|
@@ -25639,11 +26357,112 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
25639
26357
|
}
|
|
25640
26358
|
}
|
|
25641
26359
|
|
|
26360
|
+
// ../daemon/src/worktrees/sweep.ts
|
|
26361
|
+
var ARCHIVE_AFTER_MS = 2 * 60 * 60000;
|
|
26362
|
+
var SWEEP_EVERY_MS = 10 * 60000;
|
|
26363
|
+
function archiveAfterFrom(raw) {
|
|
26364
|
+
if (raw === "off")
|
|
26365
|
+
return null;
|
|
26366
|
+
const n = Number(raw);
|
|
26367
|
+
return raw && Number.isFinite(n) && n > 0 ? n : ARCHIVE_AFTER_MS;
|
|
26368
|
+
}
|
|
26369
|
+
|
|
26370
|
+
class ArchiveSweep {
|
|
26371
|
+
d;
|
|
26372
|
+
timer = null;
|
|
26373
|
+
running = false;
|
|
26374
|
+
again = false;
|
|
26375
|
+
afterMs;
|
|
26376
|
+
constructor(d) {
|
|
26377
|
+
this.d = d;
|
|
26378
|
+
this.afterMs = d.afterMs === undefined ? ARCHIVE_AFTER_MS : d.afterMs;
|
|
26379
|
+
if (this.afterMs === null)
|
|
26380
|
+
return;
|
|
26381
|
+
const every = d.setInterval ?? ((fn, ms) => {
|
|
26382
|
+
const t = setInterval(fn, ms);
|
|
26383
|
+
t.unref?.();
|
|
26384
|
+
return t;
|
|
26385
|
+
});
|
|
26386
|
+
this.timer = every(() => this.request(), Math.min(d.everyMs ?? SWEEP_EVERY_MS, this.afterMs));
|
|
26387
|
+
}
|
|
26388
|
+
stop() {
|
|
26389
|
+
if (this.timer && !this.d.setInterval)
|
|
26390
|
+
clearInterval(this.timer);
|
|
26391
|
+
this.timer = null;
|
|
26392
|
+
}
|
|
26393
|
+
request() {
|
|
26394
|
+
if (this.running) {
|
|
26395
|
+
this.again = true;
|
|
26396
|
+
return;
|
|
26397
|
+
}
|
|
26398
|
+
this.running = true;
|
|
26399
|
+
fireAndForget("sweep", this.sweep().finally(() => {
|
|
26400
|
+
this.running = false;
|
|
26401
|
+
if (!this.again)
|
|
26402
|
+
return;
|
|
26403
|
+
this.again = false;
|
|
26404
|
+
this.request();
|
|
26405
|
+
}), "archive sweep");
|
|
26406
|
+
}
|
|
26407
|
+
async sweep() {
|
|
26408
|
+
if (this.afterMs === null)
|
|
26409
|
+
return;
|
|
26410
|
+
for (const repo of this.d.state.repos) {
|
|
26411
|
+
const beforeGit = this.facts(repo.id, () => ({ dirty: 0, ahead: 0 }));
|
|
26412
|
+
const maybe = beforeGit.rows.filter((w) => archiveReason(w, beforeGit) !== null);
|
|
26413
|
+
if (maybe.length === 0)
|
|
26414
|
+
continue;
|
|
26415
|
+
const fresh = new Map;
|
|
26416
|
+
for (const w of beforeGit.rows) {
|
|
26417
|
+
if (!maybe.some((m) => m.id === w.id || !!m.variant && m.variant.group === w.variant?.group))
|
|
26418
|
+
continue;
|
|
26419
|
+
fresh.set(w.id, await this.d.worktrees.freshCounts(w.id));
|
|
26420
|
+
}
|
|
26421
|
+
const counts = (id) => fresh.get(id) ?? {};
|
|
26422
|
+
for (const w of maybe) {
|
|
26423
|
+
const reason = archiveReason(w, this.facts(repo.id, counts));
|
|
26424
|
+
if (!reason)
|
|
26425
|
+
continue;
|
|
26426
|
+
log.info(w.id, `archiving on its own: ${reason}`);
|
|
26427
|
+
try {
|
|
26428
|
+
await this.d.worktrees.archiveWorktree(w.id, {
|
|
26429
|
+
reason,
|
|
26430
|
+
still: () => this.stillGoes(repo.id, w.id, counts)
|
|
26431
|
+
});
|
|
26432
|
+
} catch (e) {
|
|
26433
|
+
log.warn(w.id, "could not archive it", e);
|
|
26434
|
+
}
|
|
26435
|
+
}
|
|
26436
|
+
}
|
|
26437
|
+
}
|
|
26438
|
+
facts(repoId, counts) {
|
|
26439
|
+
return {
|
|
26440
|
+
now: (this.d.now ?? Date.now)(),
|
|
26441
|
+
afterMs: this.afterMs ?? ARCHIVE_AFTER_MS,
|
|
26442
|
+
rows: this.d.state.worktrees.filter((w) => w.repoId === repoId && w.kind !== "spare"),
|
|
26443
|
+
viewed: this.d.viewed,
|
|
26444
|
+
pending: this.d.busy,
|
|
26445
|
+
drafted: (id) => this.d.drafts.has(id),
|
|
26446
|
+
counts
|
|
26447
|
+
};
|
|
26448
|
+
}
|
|
26449
|
+
stillGoes(repoId, id, counts) {
|
|
26450
|
+
const f = this.facts(repoId, counts);
|
|
26451
|
+
const wt = f.rows.find((w) => w.id === id);
|
|
26452
|
+
return !!wt && archiveReason(wt, f) !== null;
|
|
26453
|
+
}
|
|
26454
|
+
}
|
|
26455
|
+
|
|
25642
26456
|
// ../daemon/src/index.ts
|
|
25643
26457
|
process.on("unhandledRejection", (e) => log.error("daemon", "unhandled rejection", e));
|
|
25644
26458
|
process.on("uncaughtException", (e) => log.error("daemon", "uncaught exception", e));
|
|
25645
26459
|
var here = dirname9(fileURLToPath(import.meta.url));
|
|
25646
|
-
var {
|
|
26460
|
+
var {
|
|
26461
|
+
shellDist: SHELL_DIST,
|
|
26462
|
+
bridgeJs: BRIDGE_JS,
|
|
26463
|
+
sourceRoot: SOURCE_ROOT,
|
|
26464
|
+
packageJson: PACKAGE_JSON
|
|
26465
|
+
} = locateAssets(here);
|
|
25647
26466
|
var pruned = pruneAssets(SHELL_DIST);
|
|
25648
26467
|
if (pruned > 0)
|
|
25649
26468
|
log.debug("daemon", `pruned ${pruned} assets from builds this one has outlived`);
|
|
@@ -25692,7 +26511,8 @@ var runtime = new RuntimeRegistry({
|
|
|
25692
26511
|
remote,
|
|
25693
26512
|
grant: previewGrant(token)
|
|
25694
26513
|
});
|
|
25695
|
-
var
|
|
26514
|
+
var drafts = new DraftStore({ file: paths.draftsFile, hub });
|
|
26515
|
+
var worktrees = new WorktreeService({ state, hub, runtime, paths, agents, drafts });
|
|
25696
26516
|
var turns = new TurnService({
|
|
25697
26517
|
state,
|
|
25698
26518
|
hub,
|
|
@@ -25724,7 +26544,7 @@ var prs = new PrService({ state, hub, worktrees, view: viewPr });
|
|
|
25724
26544
|
var self = new SelfWatch(SOURCE_ROOT);
|
|
25725
26545
|
await self.start();
|
|
25726
26546
|
var afterLand = new AfterLand({ state, hub, self });
|
|
25727
|
-
var repos = new RepoRegistry({ state, hub, runtime, worktrees, afterLand, self });
|
|
26547
|
+
var repos = new RepoRegistry({ state, hub, runtime, worktrees, afterLand, self, drafts });
|
|
25728
26548
|
var idle = new IdlePolicy({
|
|
25729
26549
|
runtime,
|
|
25730
26550
|
state,
|
|
@@ -25732,8 +26552,43 @@ var idle = new IdlePolicy({
|
|
|
25732
26552
|
wake: (id) => repos.touch(id),
|
|
25733
26553
|
warmSpare: (repoId) => repos.warm(repoId)
|
|
25734
26554
|
});
|
|
26555
|
+
var sweep = new ArchiveSweep({
|
|
26556
|
+
state,
|
|
26557
|
+
viewed: (id) => idle.isViewed(id),
|
|
26558
|
+
busy: (id) => runtime.busy(id),
|
|
26559
|
+
drafts,
|
|
26560
|
+
worktrees,
|
|
26561
|
+
afterMs: archiveAfterFrom(process.env.TOYON_ARCHIVE_AFTER_MS)
|
|
26562
|
+
});
|
|
25735
26563
|
var themes = new ThemeStore({ get: () => state.theme, set: (p) => state.setTheme(p) }, paths.themesDir);
|
|
25736
26564
|
themes.load();
|
|
26565
|
+
var restarter = new Restarter({
|
|
26566
|
+
hub,
|
|
26567
|
+
state,
|
|
26568
|
+
runtime,
|
|
26569
|
+
refusal: () => {
|
|
26570
|
+
const can = restartable();
|
|
26571
|
+
return can.ok ? null : can.reason;
|
|
26572
|
+
},
|
|
26573
|
+
go: () => {
|
|
26574
|
+
setTimeout(() => fireAndForget("daemon", shutdown("restart", { respawn: true }), "restart"), 100);
|
|
26575
|
+
}
|
|
26576
|
+
});
|
|
26577
|
+
var installedPackage = cloud.enabled || process.env.TOYON_WORKTREE !== undefined ? null : PACKAGE_JSON;
|
|
26578
|
+
var method = installMethod(installedPackage);
|
|
26579
|
+
var update = new UpdateService({
|
|
26580
|
+
hub,
|
|
26581
|
+
state,
|
|
26582
|
+
running: package_default.version,
|
|
26583
|
+
method,
|
|
26584
|
+
restarter,
|
|
26585
|
+
installed: async () => installedPackage ? readVersion(installedPackage) : null,
|
|
26586
|
+
latest: latestVersion,
|
|
26587
|
+
command: (version) => installCommand(method, version),
|
|
26588
|
+
install: runInstall,
|
|
26589
|
+
busy: () => runtime.anyBusy(),
|
|
26590
|
+
managed: process.env.TOYON_UPDATES === "off"
|
|
26591
|
+
});
|
|
25737
26592
|
var { branded, stop: stopServer } = startServer({
|
|
25738
26593
|
port,
|
|
25739
26594
|
token,
|
|
@@ -25755,6 +26610,7 @@ var { branded, stop: stopServer } = startServer({
|
|
|
25755
26610
|
exec,
|
|
25756
26611
|
refs,
|
|
25757
26612
|
chats,
|
|
26613
|
+
drafts,
|
|
25758
26614
|
prs,
|
|
25759
26615
|
themes,
|
|
25760
26616
|
agents,
|
|
@@ -25762,13 +26618,8 @@ var { branded, stop: stopServer } = startServer({
|
|
|
25762
26618
|
attachments: attachments2,
|
|
25763
26619
|
self,
|
|
25764
26620
|
afterLand,
|
|
25765
|
-
|
|
25766
|
-
|
|
25767
|
-
if (!can.ok)
|
|
25768
|
-
return can.reason;
|
|
25769
|
-
fireAndForget("daemon", shutdown("restart", { respawn: true }), "restart");
|
|
25770
|
-
return null;
|
|
25771
|
-
},
|
|
26621
|
+
restarter,
|
|
26622
|
+
update,
|
|
25772
26623
|
planTasks: makePlanner(agents),
|
|
25773
26624
|
folderDialog: folderDialog()
|
|
25774
26625
|
}
|
|
@@ -25784,10 +26635,14 @@ bridge.setShellOrigins([
|
|
|
25784
26635
|
],
|
|
25785
26636
|
...remote ? [`https://${remote.host}`] : []
|
|
25786
26637
|
]);
|
|
25787
|
-
|
|
26638
|
+
writeFileSync9(paths.pidFile, `${process.pid}
|
|
25788
26639
|
`);
|
|
25789
26640
|
var stopLagSampler = startLagSampler();
|
|
25790
26641
|
await repos.boot();
|
|
26642
|
+
drafts.prune((id) => {
|
|
26643
|
+
const repoId = draftRepoOf(id);
|
|
26644
|
+
return repoId ? !!state.repo(repoId) : !!state.worktree(id) || worktrees.hasArchived(id);
|
|
26645
|
+
});
|
|
25791
26646
|
idle.boot();
|
|
25792
26647
|
fireAndForget("agents", agents.installMissing().then(() => probe.missing()), "agent adapter install");
|
|
25793
26648
|
var repoArg = process.argv[2];
|
|
@@ -25821,9 +26676,11 @@ async function shutdown(signal, opts = {}) {
|
|
|
25821
26676
|
idle.shutdown();
|
|
25822
26677
|
repos.stopWatchers();
|
|
25823
26678
|
prs.stop();
|
|
26679
|
+
sweep.stop();
|
|
25824
26680
|
stopLagSampler();
|
|
25825
26681
|
stopServer();
|
|
25826
26682
|
routes.flush();
|
|
26683
|
+
drafts.flush();
|
|
25827
26684
|
const deadline = new Promise((resolve) => setTimeout(resolve, 5000));
|
|
25828
26685
|
await Promise.race([runtime.shutdown(), deadline]);
|
|
25829
26686
|
rmSync6(paths.pidFile, { force: true });
|