toyon 0.7.0 → 0.8.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 +1 -1
- package/dist/bridge.js +1 -1
- package/dist/cli.js +4 -1
- package/dist/daemon.js +677 -386
- package/dist/shell/assets/{Editor-Duhn2F88.js → Editor-CFH2QbSg.js} +3 -3
- package/dist/shell/assets/XTerm-C3aMkE_w.js +58 -0
- package/dist/shell/assets/{cssMode-CaBWxaDj.js → cssMode-dc6AdHsZ.js} +1 -1
- package/dist/shell/assets/{freemarker2-Qj5oMLk2.js → freemarker2-CxtmNsLB.js} +1 -1
- package/dist/shell/assets/{grammar-YjPoOd1d.js → grammar-B-X0FPRk.js} +1 -1
- package/dist/shell/assets/{handlebars-N_jFNLBH.js → handlebars-DVzmQ0KM.js} +1 -1
- package/dist/shell/assets/{html-CuTufuXa.js → html-CqqXL9ke.js} +1 -1
- package/dist/shell/assets/{htmlMode-DzHlP1s-.js → htmlMode-Bg-Em0rL.js} +1 -1
- package/dist/shell/assets/index-BskWflhx.css +1 -0
- package/dist/shell/assets/index-C8QP4sgP.js +255 -0
- package/dist/shell/assets/{javascript-DBXFKiXL.js → javascript-W-C4G7BB.js} +1 -1
- package/dist/shell/assets/{jsonMode-DVynVZZ-.js → jsonMode-De4ajMpV.js} +1 -1
- package/dist/shell/assets/{liquid-CPflmua8.js → liquid-BSUg_7LQ.js} +1 -1
- package/dist/shell/assets/{lspLanguageFeatures-Bw23HdhQ.js → lspLanguageFeatures-BImXYajo.js} +1 -1
- package/dist/shell/assets/{mdx-BIo7iUIS.js → mdx-B9A7RorP.js} +1 -1
- package/dist/shell/assets/{python-w7xWVunv.js → python-BD9sq3DF.js} +1 -1
- package/dist/shell/assets/{razor-C_h6ql0l.js → razor-DGQVRh5-.js} +1 -1
- package/dist/shell/assets/{tsMode-BD-oIhVs.js → tsMode-DudQlgN_.js} +1 -1
- package/dist/shell/assets/{xml-BDJjSxEs.js → xml-BI6-GyBn.js} +1 -1
- package/dist/shell/assets/{yaml-BXfVW0R6.js → yaml-BEUvyYtP.js} +1 -1
- package/dist/shell/index.html +2 -2
- package/package.json +1 -1
- package/dist/shell/assets/XTerm-CF8bkkwi.js +0 -40
- package/dist/shell/assets/index-BWSk_4yc.js +0 -250
- package/dist/shell/assets/index-C01XmhdL.css +0 -1
package/dist/daemon.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../daemon/src/index.ts
|
|
3
3
|
import { rmSync as rmSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
4
|
-
import { dirname as
|
|
4
|
+
import { dirname as dirname12 } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
|
|
7
7
|
// ../shared/src/agent-tools.ts
|
|
@@ -6469,7 +6469,7 @@ var PAIR_TTL_MS = 120000;
|
|
|
6469
6469
|
var pairLink = (host, code) => `https://${host}/#pair=${code}`;
|
|
6470
6470
|
|
|
6471
6471
|
// ../shared/src/protocol/limits.ts
|
|
6472
|
-
var PROTOCOL_VERSION =
|
|
6472
|
+
var PROTOCOL_VERSION = 82;
|
|
6473
6473
|
var FILE_MAX_CHARS = 5000000;
|
|
6474
6474
|
var IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
6475
6475
|
var IMAGE_MAX_EDGE = 2576;
|
|
@@ -6542,6 +6542,7 @@ var elementTraitsSchema = object({
|
|
|
6542
6542
|
// ../shared/src/protocol/events.ts
|
|
6543
6543
|
var SHELL_TOOL = "shell";
|
|
6544
6544
|
var CHECK_TOOL = "check";
|
|
6545
|
+
var TOOL_SEARCH = "ToolSearch";
|
|
6545
6546
|
|
|
6546
6547
|
// ../shared/src/protocol/ws.ts
|
|
6547
6548
|
var id = string2().min(1).max(200);
|
|
@@ -6793,7 +6794,7 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
6793
6794
|
object({ t: literal("search"), worktreeId: id, query: string2().max(500) }),
|
|
6794
6795
|
object({ t: literal("find-element"), worktreeId: id, seq, element: elementTraitsSchema }),
|
|
6795
6796
|
object({ t: literal("design-scan"), worktreeId: id }),
|
|
6796
|
-
object({ t: literal("discard-
|
|
6797
|
+
object({ t: literal("discard-files"), worktreeId: id, paths: array(relPath).min(1).max(500) }),
|
|
6797
6798
|
object({ t: literal("judge"), worktreeId: id, note: string2().max(500).optional() }),
|
|
6798
6799
|
object({ t: literal("reveal"), worktreeId: id, path: relPath.optional() }),
|
|
6799
6800
|
object({ t: literal("stop-agent"), worktreeId: id }),
|
|
@@ -6905,6 +6906,18 @@ function parseClientMsg(raw) {
|
|
|
6905
6906
|
return { ok: false, reason: issueReason(r.error, "invalid message") };
|
|
6906
6907
|
}
|
|
6907
6908
|
|
|
6909
|
+
// ../shared/src/query.ts
|
|
6910
|
+
var TERM = /["\u201C\u201D]([^"\u201C\u201D]*)["\u201C\u201D]?|(\S+)/g;
|
|
6911
|
+
function queryTerms(query) {
|
|
6912
|
+
const out = new Set;
|
|
6913
|
+
for (const m of query.matchAll(TERM)) {
|
|
6914
|
+
const term = (m[1] ?? m[2] ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
6915
|
+
if (term)
|
|
6916
|
+
out.add(term);
|
|
6917
|
+
}
|
|
6918
|
+
return [...out];
|
|
6919
|
+
}
|
|
6920
|
+
|
|
6908
6921
|
// ../shared/src/restart.ts
|
|
6909
6922
|
var RESTART_NOW = "now";
|
|
6910
6923
|
|
|
@@ -7869,6 +7882,15 @@ function installCommand(method, version) {
|
|
|
7869
7882
|
return ["bun", "add", "-g", `toyon@${version}`];
|
|
7870
7883
|
return null;
|
|
7871
7884
|
}
|
|
7885
|
+
function registryHost(registry) {
|
|
7886
|
+
if (registry === null || registry === "")
|
|
7887
|
+
return "the npm registry";
|
|
7888
|
+
try {
|
|
7889
|
+
return new URL(registry).host;
|
|
7890
|
+
} catch {
|
|
7891
|
+
return registry;
|
|
7892
|
+
}
|
|
7893
|
+
}
|
|
7872
7894
|
function newer(a, b) {
|
|
7873
7895
|
const pa = parseVersion(a);
|
|
7874
7896
|
const pb = parseVersion(b);
|
|
@@ -8141,7 +8163,7 @@ function safeUser() {
|
|
|
8141
8163
|
// ../daemon/package.json
|
|
8142
8164
|
var package_default = {
|
|
8143
8165
|
name: "@toyon/daemon",
|
|
8144
|
-
version: "0.
|
|
8166
|
+
version: "0.8.0",
|
|
8145
8167
|
private: true,
|
|
8146
8168
|
type: "module",
|
|
8147
8169
|
main: "src/index.ts",
|
|
@@ -13424,7 +13446,7 @@ var BUILTIN_AGENTS = [
|
|
|
13424
13446
|
name: "Claude Code",
|
|
13425
13447
|
short: "Claude",
|
|
13426
13448
|
builtin: true,
|
|
13427
|
-
run: { kind: "npm-bin", pkg: "@agentclientprotocol/claude-agent-acp", version: "0.
|
|
13449
|
+
run: { kind: "npm-bin", pkg: "@agentclientprotocol/claude-agent-acp", version: "0.84.0", bin: "claude-agent-acp" },
|
|
13428
13450
|
...cloud.enabled ? { env: { NO_BROWSER: "1" } } : {},
|
|
13429
13451
|
terminalLogins: {
|
|
13430
13452
|
"claude-login": { args: ["--cli", "auth", "login", "--claudeai"], env: { NO_BROWSER: "1" }, plan: true }
|
|
@@ -13443,7 +13465,7 @@ var BUILTIN_AGENTS = [
|
|
|
13443
13465
|
id: "codex",
|
|
13444
13466
|
name: "Codex",
|
|
13445
13467
|
builtin: true,
|
|
13446
|
-
run: { kind: "npm-bin", pkg: "@agentclientprotocol/codex-acp", version: "
|
|
13468
|
+
run: { kind: "npm-bin", pkg: "@agentclientprotocol/codex-acp", version: "2.0.1", bin: "codex-acp" },
|
|
13447
13469
|
env: {
|
|
13448
13470
|
INITIAL_AGENT_MODE: "agent",
|
|
13449
13471
|
CODEX_CONFIG: JSON.stringify({ features: { default_mode_request_user_input: true } }),
|
|
@@ -13460,7 +13482,7 @@ var BUILTIN_AGENTS = [
|
|
|
13460
13482
|
id: "opencode",
|
|
13461
13483
|
name: "OpenCode",
|
|
13462
13484
|
builtin: true,
|
|
13463
|
-
run: { kind: "npm-native", pkg: "opencode", version: "1.18.
|
|
13485
|
+
run: { kind: "npm-native", pkg: "opencode", version: "1.18.33", bin: "bin/opencode", args: ["acp"] },
|
|
13464
13486
|
onDemand: true,
|
|
13465
13487
|
confinement: "toyon-sandbox",
|
|
13466
13488
|
stateDirs: [".local/share/opencode", ".local/state/opencode", ".cache/opencode"],
|
|
@@ -13725,6 +13747,29 @@ function modeAfterPlan(optionName) {
|
|
|
13725
13747
|
return /manual/i.test(optionName) ? "ask" : "auto";
|
|
13726
13748
|
}
|
|
13727
13749
|
|
|
13750
|
+
// ../daemon/src/agent/output.ts
|
|
13751
|
+
var OUTPUT_CAP = 200000;
|
|
13752
|
+
function formatOutput(text, exit, truncated, ceiling) {
|
|
13753
|
+
const body = text.replace(/\n+$/, "");
|
|
13754
|
+
const notes = [];
|
|
13755
|
+
if (truncated)
|
|
13756
|
+
notes.push(`output cut at ${Math.round(OUTPUT_CAP / 1000)} KB`);
|
|
13757
|
+
if (exit === "timeout")
|
|
13758
|
+
notes.push(ceiling ? `gave up after ${describeDuration(ceiling)}` : "gave up at the ceiling");
|
|
13759
|
+
else if (typeof exit === "string")
|
|
13760
|
+
notes.push(`killed (${exit})`);
|
|
13761
|
+
else if (exit !== 0)
|
|
13762
|
+
notes.push(`exit ${exit}`);
|
|
13763
|
+
const parts = [];
|
|
13764
|
+
if (body.trim())
|
|
13765
|
+
parts.push(`\`\`\`
|
|
13766
|
+
${body}
|
|
13767
|
+
\`\`\``);
|
|
13768
|
+
parts.push(...notes);
|
|
13769
|
+
return parts.join(`
|
|
13770
|
+
`);
|
|
13771
|
+
}
|
|
13772
|
+
|
|
13728
13773
|
// ../daemon/src/agent/policy.ts
|
|
13729
13774
|
import { isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
|
|
13730
13775
|
var NON_WRITE_KINDS = new Set(["execute", "read", "search", "fetch", "think"]);
|
|
@@ -14087,9 +14132,168 @@ async function cleanUp(ctx, a, sessionId) {
|
|
|
14087
14132
|
await ctx.request(methods.agent.session.delete, { sessionId }).catch(quietly("delete"));
|
|
14088
14133
|
}
|
|
14089
14134
|
|
|
14135
|
+
// ../daemon/src/agent/acp/background.ts
|
|
14136
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
14137
|
+
import { open as open2 } from "fs/promises";
|
|
14138
|
+
import { homedir as homedir4 } from "os";
|
|
14139
|
+
import { basename as basename2, dirname as dirname4, join as join7 } from "path";
|
|
14140
|
+
var STARTED = /Command running in background with ID: (\S+)\. Output is being written to: (\S+)\./;
|
|
14141
|
+
function backgroundStart(input, output) {
|
|
14142
|
+
const flag = input && typeof input === "object" ? input.run_in_background : undefined;
|
|
14143
|
+
if (flag !== true)
|
|
14144
|
+
return null;
|
|
14145
|
+
const m = STARTED.exec(output);
|
|
14146
|
+
return m ? { taskId: m[1], file: m[2] } : null;
|
|
14147
|
+
}
|
|
14148
|
+
function claudeConfigDir() {
|
|
14149
|
+
return process.env.CLAUDE_CONFIG_DIR || join7(homedir4(), ".claude");
|
|
14150
|
+
}
|
|
14151
|
+
function sessionLogFor(file, configDir = claudeConfigDir()) {
|
|
14152
|
+
const tasks = dirname4(file);
|
|
14153
|
+
if (basename2(tasks) !== "tasks")
|
|
14154
|
+
return null;
|
|
14155
|
+
const session = dirname4(tasks);
|
|
14156
|
+
const path = join7(configDir, "projects", basename2(dirname4(session)), `${basename2(session)}.jsonl`);
|
|
14157
|
+
return existsSync8(path) ? path : null;
|
|
14158
|
+
}
|
|
14159
|
+
function taskEnd(text, toolId) {
|
|
14160
|
+
const at = text.indexOf(`<tool-use-id>${toolId}</tool-use-id>`);
|
|
14161
|
+
if (at === -1)
|
|
14162
|
+
return null;
|
|
14163
|
+
const tail = text.slice(at, at + 2000);
|
|
14164
|
+
const status = /<status>(\w+)<\/status>/.exec(tail)?.[1] ?? "completed";
|
|
14165
|
+
const exit = /exit code (\d+)/.exec(tail)?.[1];
|
|
14166
|
+
return { status, exit: exit === undefined ? null : Number(exit) };
|
|
14167
|
+
}
|
|
14168
|
+
var POLL_MS = 500;
|
|
14169
|
+
var DEFAULT_BASH_TIMEOUT_MS = 120000;
|
|
14170
|
+
var GRACE_MS = 60000;
|
|
14171
|
+
var READ_CHUNK = 1 << 20;
|
|
14172
|
+
var CARRY = 4000;
|
|
14173
|
+
|
|
14174
|
+
class BackgroundTasks {
|
|
14175
|
+
deps;
|
|
14176
|
+
watched = new Map;
|
|
14177
|
+
constructor(deps) {
|
|
14178
|
+
this.deps = deps;
|
|
14179
|
+
}
|
|
14180
|
+
get size() {
|
|
14181
|
+
return this.watched.size;
|
|
14182
|
+
}
|
|
14183
|
+
start(toolId, start, input) {
|
|
14184
|
+
const logPath = sessionLogFor(start.file, this.deps.configDir);
|
|
14185
|
+
if (!logPath)
|
|
14186
|
+
return false;
|
|
14187
|
+
const timeout = input?.timeout;
|
|
14188
|
+
const ceiling = (typeof timeout === "number" ? timeout : DEFAULT_BASH_TIMEOUT_MS) + (this.deps.graceMs ?? GRACE_MS);
|
|
14189
|
+
const w = {
|
|
14190
|
+
toolId,
|
|
14191
|
+
file: start.file,
|
|
14192
|
+
log: logPath,
|
|
14193
|
+
read: 0,
|
|
14194
|
+
logRead: statSync3(logPath).size,
|
|
14195
|
+
carry: "",
|
|
14196
|
+
decoder: new TextDecoder,
|
|
14197
|
+
text: "",
|
|
14198
|
+
truncated: false,
|
|
14199
|
+
ceiling: Date.now() + ceiling,
|
|
14200
|
+
timer: null
|
|
14201
|
+
};
|
|
14202
|
+
this.watched.set(toolId, w);
|
|
14203
|
+
this.schedule(w);
|
|
14204
|
+
return true;
|
|
14205
|
+
}
|
|
14206
|
+
endAll(reason) {
|
|
14207
|
+
for (const w of [...this.watched.values()]) {
|
|
14208
|
+
fireAndForget("background", this.finish(w, null, reason, true), `ending ${w.toolId}`);
|
|
14209
|
+
}
|
|
14210
|
+
}
|
|
14211
|
+
schedule(w) {
|
|
14212
|
+
w.timer = setTimeout(() => this.poll(w), this.deps.pollMs ?? POLL_MS);
|
|
14213
|
+
w.timer.unref?.();
|
|
14214
|
+
}
|
|
14215
|
+
async poll(w) {
|
|
14216
|
+
w.timer = null;
|
|
14217
|
+
try {
|
|
14218
|
+
await this.drain(w);
|
|
14219
|
+
const fresh = await readSince(w.log, w.logRead);
|
|
14220
|
+
w.logRead += fresh.length;
|
|
14221
|
+
const text = w.carry + fresh.toString("utf8");
|
|
14222
|
+
w.carry = text.slice(-CARRY);
|
|
14223
|
+
const end = fresh.length > 0 ? taskEnd(text, w.toolId) : null;
|
|
14224
|
+
if (end)
|
|
14225
|
+
return this.finish(w, end);
|
|
14226
|
+
if (Date.now() >= w.ceiling)
|
|
14227
|
+
return this.finish(w, null, "no word of its end; the row stops watching");
|
|
14228
|
+
} catch (e) {
|
|
14229
|
+
log.warn("background", `watching ${w.toolId} failed`, e);
|
|
14230
|
+
}
|
|
14231
|
+
if (this.watched.has(w.toolId))
|
|
14232
|
+
this.schedule(w);
|
|
14233
|
+
}
|
|
14234
|
+
async drain(w) {
|
|
14235
|
+
const fresh = await readSince(w.file, w.read);
|
|
14236
|
+
w.read += fresh.length;
|
|
14237
|
+
if (fresh.length === 0 || w.truncated)
|
|
14238
|
+
return;
|
|
14239
|
+
const chunk = w.decoder.decode(fresh, { stream: true });
|
|
14240
|
+
const take = chunk.slice(0, OUTPUT_CAP - w.text.length);
|
|
14241
|
+
w.text += take;
|
|
14242
|
+
if (take.length < chunk.length)
|
|
14243
|
+
w.truncated = true;
|
|
14244
|
+
if (take)
|
|
14245
|
+
this.deps.note({ type: "tool-delta", toolId: w.toolId, text: take });
|
|
14246
|
+
}
|
|
14247
|
+
async finish(w, end, note, killed = false) {
|
|
14248
|
+
if (!this.watched.delete(w.toolId))
|
|
14249
|
+
return;
|
|
14250
|
+
if (w.timer)
|
|
14251
|
+
clearTimeout(w.timer);
|
|
14252
|
+
try {
|
|
14253
|
+
await this.drain(w);
|
|
14254
|
+
} catch (e) {
|
|
14255
|
+
log.warn("background", `reading the last of ${w.toolId} failed`, e);
|
|
14256
|
+
}
|
|
14257
|
+
const exit = end ? end.exit ?? (end.status === "completed" ? 0 : null) : null;
|
|
14258
|
+
const lines = [formatOutput(w.text, exit ?? 0, w.truncated)];
|
|
14259
|
+
if (end && exit === null)
|
|
14260
|
+
lines.push(end.status);
|
|
14261
|
+
if (note)
|
|
14262
|
+
lines.push(note);
|
|
14263
|
+
this.deps.note({
|
|
14264
|
+
type: "tool-end",
|
|
14265
|
+
toolId: w.toolId,
|
|
14266
|
+
output: lines.filter(Boolean).join(`
|
|
14267
|
+
`),
|
|
14268
|
+
isError: killed || end !== null && (exit !== 0 || end.status !== "completed")
|
|
14269
|
+
});
|
|
14270
|
+
this.deps.onEnd?.();
|
|
14271
|
+
}
|
|
14272
|
+
}
|
|
14273
|
+
async function readSince(path, offset) {
|
|
14274
|
+
let fh;
|
|
14275
|
+
try {
|
|
14276
|
+
fh = await open2(path, "r");
|
|
14277
|
+
} catch (e) {
|
|
14278
|
+
if (e.code === "ENOENT")
|
|
14279
|
+
return Buffer.alloc(0);
|
|
14280
|
+
throw e;
|
|
14281
|
+
}
|
|
14282
|
+
try {
|
|
14283
|
+
const { size } = await fh.stat();
|
|
14284
|
+
if (size <= offset)
|
|
14285
|
+
return Buffer.alloc(0);
|
|
14286
|
+
const buf = Buffer.alloc(Math.min(size - offset, READ_CHUNK));
|
|
14287
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, offset);
|
|
14288
|
+
return buf.subarray(0, bytesRead);
|
|
14289
|
+
} finally {
|
|
14290
|
+
await fh.close();
|
|
14291
|
+
}
|
|
14292
|
+
}
|
|
14293
|
+
|
|
14090
14294
|
// ../daemon/src/agent/acp/elicit.ts
|
|
14091
|
-
var CUSTOM_ANSWER_META = "_askUserQuestionCustomAnswer";
|
|
14092
14295
|
var CODEX_META = "codex";
|
|
14296
|
+
var CLAUDE_CUSTOM_KEY = /^(question_\d+)_custom$/;
|
|
14093
14297
|
var OPTION_PREVIEW_META = "_claude/askUserQuestionOption";
|
|
14094
14298
|
function field(value, key) {
|
|
14095
14299
|
return value && typeof value === "object" ? value[key] : undefined;
|
|
@@ -14100,12 +14304,12 @@ function meta2(value, key) {
|
|
|
14100
14304
|
function text(value) {
|
|
14101
14305
|
return typeof value === "string" && value ? value : undefined;
|
|
14102
14306
|
}
|
|
14103
|
-
function customAnswerFor(prop) {
|
|
14104
|
-
const claude = text(field(meta2(prop, CUSTOM_ANSWER_META), "questionId"));
|
|
14105
|
-
if (claude)
|
|
14106
|
-
return claude;
|
|
14307
|
+
function customAnswerFor(key, prop, keys) {
|
|
14107
14308
|
const codex = meta2(prop, CODEX_META);
|
|
14108
|
-
|
|
14309
|
+
if (field(codex, "role") === "user_note")
|
|
14310
|
+
return text(field(codex, "questionId")) ?? null;
|
|
14311
|
+
const owner = CLAUDE_CUSTOM_KEY.exec(key)?.[1];
|
|
14312
|
+
return owner && keys.has(owner) ? owner : null;
|
|
14109
14313
|
}
|
|
14110
14314
|
function optionsOf(titled) {
|
|
14111
14315
|
return titled.map((o) => {
|
|
@@ -14148,26 +14352,28 @@ function parseForm(req) {
|
|
|
14148
14352
|
if (!props)
|
|
14149
14353
|
return null;
|
|
14150
14354
|
const entries = Object.entries(props);
|
|
14355
|
+
const keys = new Set(Object.keys(props));
|
|
14151
14356
|
const required = new Set(req.requestedSchema.required ?? []);
|
|
14152
14357
|
const notes = new Map;
|
|
14153
14358
|
for (const [key, prop] of entries) {
|
|
14154
|
-
const owner = customAnswerFor(prop);
|
|
14359
|
+
const owner = customAnswerFor(key, prop, keys);
|
|
14155
14360
|
if (owner)
|
|
14156
14361
|
notes.set(owner, { key, label: text(field(prop, "title")) ?? "Other" });
|
|
14157
14362
|
}
|
|
14158
14363
|
const questions = [];
|
|
14159
14364
|
const fields = [];
|
|
14160
14365
|
for (const [key, prop] of entries) {
|
|
14161
|
-
if (customAnswerFor(prop))
|
|
14366
|
+
if (customAnswerFor(key, prop, keys))
|
|
14162
14367
|
continue;
|
|
14163
14368
|
const select = selectOf(prop);
|
|
14164
14369
|
if (!select)
|
|
14165
14370
|
return null;
|
|
14166
14371
|
const note = notes.get(key);
|
|
14167
|
-
const
|
|
14372
|
+
const swapped = meta2(prop, CODEX_META) !== undefined;
|
|
14373
|
+
const header = text(field(prop, swapped ? "description" : "title"));
|
|
14168
14374
|
questions.push({
|
|
14169
14375
|
id: key,
|
|
14170
|
-
text: text(field(prop, "description")) ?? "",
|
|
14376
|
+
text: text(field(prop, swapped ? "title" : "description")) ?? "",
|
|
14171
14377
|
...header ? { header } : {},
|
|
14172
14378
|
options: select.options,
|
|
14173
14379
|
...select.multi ? { multi: true } : {},
|
|
@@ -14193,8 +14399,7 @@ function toContent(form, answers) {
|
|
|
14193
14399
|
const note = answer.note?.trim();
|
|
14194
14400
|
if (!note || !field.note)
|
|
14195
14401
|
return;
|
|
14196
|
-
|
|
14197
|
-
out[field.note] = labels.length > 0 ? `${labels.join(", ")}: ${note}` : note;
|
|
14402
|
+
out[field.note] = note;
|
|
14198
14403
|
});
|
|
14199
14404
|
return out;
|
|
14200
14405
|
}
|
|
@@ -14271,7 +14476,6 @@ var NETWORK_ASK = "SandboxNetworkAccess";
|
|
|
14271
14476
|
function isNetworkAsk(update) {
|
|
14272
14477
|
return update.name === NETWORK_ASK || asRecord(asRecord(update._meta).claudeCode).toolName === NETWORK_ASK;
|
|
14273
14478
|
}
|
|
14274
|
-
var TOOL_SEARCH = "ToolSearch";
|
|
14275
14479
|
function isToolSearch(update) {
|
|
14276
14480
|
return update.name === TOOL_SEARCH || asRecord(asRecord(update._meta).claudeCode).toolName === TOOL_SEARCH;
|
|
14277
14481
|
}
|
|
@@ -14319,7 +14523,7 @@ function abandoned(memos, parent) {
|
|
|
14319
14523
|
}
|
|
14320
14524
|
return out;
|
|
14321
14525
|
}
|
|
14322
|
-
function mapUpdate(update, memos, tag, sink) {
|
|
14526
|
+
function mapUpdate(update, memos, tag, sink, background) {
|
|
14323
14527
|
switch (update.sessionUpdate) {
|
|
14324
14528
|
case "agent_message_chunk": {
|
|
14325
14529
|
const { parentToolId } = spawnOf(update._meta);
|
|
@@ -14347,6 +14551,7 @@ function mapUpdate(update, memos, tag, sink) {
|
|
|
14347
14551
|
const head = heading(update);
|
|
14348
14552
|
const memo = {
|
|
14349
14553
|
...head,
|
|
14554
|
+
input,
|
|
14350
14555
|
content: update.content ?? [],
|
|
14351
14556
|
rawOutput: update.rawOutput,
|
|
14352
14557
|
ended: false,
|
|
@@ -14364,7 +14569,7 @@ function mapUpdate(update, memos, tag, sink) {
|
|
|
14364
14569
|
...spawn
|
|
14365
14570
|
});
|
|
14366
14571
|
if (update.status === "completed" || update.status === "failed")
|
|
14367
|
-
out.push(endOf(update.toolCallId, memo, update.status, sink));
|
|
14572
|
+
out.push(endOf(update.toolCallId, memo, update.status, sink, background));
|
|
14368
14573
|
return out;
|
|
14369
14574
|
}
|
|
14370
14575
|
case "tool_call_update": {
|
|
@@ -14377,6 +14582,7 @@ function mapUpdate(update, memos, tag, sink) {
|
|
|
14377
14582
|
name: update.name ?? "",
|
|
14378
14583
|
title: update.title ?? update.name ?? "tool",
|
|
14379
14584
|
...update.kind ? { kind: update.kind } : {},
|
|
14585
|
+
input,
|
|
14380
14586
|
content: [],
|
|
14381
14587
|
ended: false,
|
|
14382
14588
|
writing: isWrittenKind(update.kind ?? undefined) && emptyInput(input),
|
|
@@ -14407,7 +14613,7 @@ function mapUpdate(update, memos, tag, sink) {
|
|
|
14407
14613
|
if (update.kind && update.kind !== memo.kind)
|
|
14408
14614
|
refined.kind = memo.kind = update.kind;
|
|
14409
14615
|
if (update.rawInput !== undefined) {
|
|
14410
|
-
refined.input = update.rawInput;
|
|
14616
|
+
refined.input = memo.input = update.rawInput;
|
|
14411
14617
|
if (!emptyInput(update.rawInput))
|
|
14412
14618
|
memo.writing = false;
|
|
14413
14619
|
}
|
|
@@ -14418,7 +14624,7 @@ function mapUpdate(update, memos, tag, sink) {
|
|
|
14418
14624
|
if (update.rawOutput !== undefined)
|
|
14419
14625
|
memo.rawOutput = update.rawOutput;
|
|
14420
14626
|
if ((update.status === "completed" || update.status === "failed") && !memo.ended) {
|
|
14421
|
-
out.push(endOf(update.toolCallId, memo, update.status, sink));
|
|
14627
|
+
out.push(endOf(update.toolCallId, memo, update.status, sink, background));
|
|
14422
14628
|
}
|
|
14423
14629
|
return out;
|
|
14424
14630
|
}
|
|
@@ -14449,13 +14655,19 @@ function textOf(content, tag, what) {
|
|
|
14449
14655
|
log.debug(tag, `acp: dropping non-text ${what} block (${content.type})`);
|
|
14450
14656
|
return null;
|
|
14451
14657
|
}
|
|
14452
|
-
function endOf(toolId, memo, status, sink) {
|
|
14658
|
+
function endOf(toolId, memo, status, sink, background) {
|
|
14453
14659
|
memo.ended = true;
|
|
14660
|
+
const output = summarizeToolOutput(memo.content, memo.rawOutput);
|
|
14661
|
+
if (status === "completed" && memo.kind === "execute" && background) {
|
|
14662
|
+
const start = backgroundStart(memo.input, output);
|
|
14663
|
+
if (start && background(toolId, start, memo.input))
|
|
14664
|
+
return { type: "tool-update", toolId, background: true };
|
|
14665
|
+
}
|
|
14454
14666
|
const images = sink ? toolImages(memo.content, sink) : [];
|
|
14455
14667
|
return {
|
|
14456
14668
|
type: "tool-end",
|
|
14457
14669
|
toolId,
|
|
14458
|
-
output
|
|
14670
|
+
output,
|
|
14459
14671
|
isError: status === "failed" || exitCodeOf(memo.rawOutput) > 0,
|
|
14460
14672
|
...images.length ? { images } : {}
|
|
14461
14673
|
};
|
|
@@ -14566,6 +14778,7 @@ class AcpSession {
|
|
|
14566
14778
|
live = null;
|
|
14567
14779
|
reaper = null;
|
|
14568
14780
|
own = null;
|
|
14781
|
+
tasks;
|
|
14569
14782
|
asking = 0;
|
|
14570
14783
|
asks = new Map;
|
|
14571
14784
|
log;
|
|
@@ -14575,6 +14788,12 @@ class AcpSession {
|
|
|
14575
14788
|
this.d = d;
|
|
14576
14789
|
this.commandList = d.seedCommands?.() ?? [];
|
|
14577
14790
|
this.log = new Transcript(transcriptPathFor(d.transcriptsDir, d.worktreeId), d.worktreeId);
|
|
14791
|
+
this.tasks = new BackgroundTasks({
|
|
14792
|
+
note: (e) => this.emit(e),
|
|
14793
|
+
onEnd: () => this.maybeArmReaper(),
|
|
14794
|
+
...d.taskPollMs !== undefined ? { pollMs: d.taskPollMs } : {},
|
|
14795
|
+
...d.claudeConfigDir !== undefined ? { configDir: d.claudeConfigDir } : {}
|
|
14796
|
+
});
|
|
14578
14797
|
this.seq = nextNumbers(this.log.entries.map(({ event }) => event.type === "user-message" ? event.attachments : undefined));
|
|
14579
14798
|
const open = new Set;
|
|
14580
14799
|
for (const { event } of this.log.entries) {
|
|
@@ -14585,6 +14804,20 @@ class AcpSession {
|
|
|
14585
14804
|
}
|
|
14586
14805
|
for (const id of open)
|
|
14587
14806
|
this.emit({ type: "agent-ask-end", id, outcome: "expired", ts: Date.now() });
|
|
14807
|
+
const held = new Map;
|
|
14808
|
+
for (const { event } of this.log.entries) {
|
|
14809
|
+
if (event.type === "tool-update" && event.background)
|
|
14810
|
+
held.set(event.toolId, "");
|
|
14811
|
+
else if (event.type === "tool-delta" && held.has(event.toolId))
|
|
14812
|
+
held.set(event.toolId, held.get(event.toolId) + event.text);
|
|
14813
|
+
else if (event.type === "tool-end")
|
|
14814
|
+
held.delete(event.toolId);
|
|
14815
|
+
}
|
|
14816
|
+
for (const [toolId, text] of held) {
|
|
14817
|
+
const output = [formatOutput(text, 0, false), "ended with the daemon that was watching it"].filter(Boolean);
|
|
14818
|
+
this.emit({ type: "tool-end", toolId, output: output.join(`
|
|
14819
|
+
`), isError: true });
|
|
14820
|
+
}
|
|
14588
14821
|
}
|
|
14589
14822
|
get commands() {
|
|
14590
14823
|
return this.commandList;
|
|
@@ -14983,6 +15216,7 @@ class AcpSession {
|
|
|
14983
15216
|
this.conn = null;
|
|
14984
15217
|
this.live = null;
|
|
14985
15218
|
this.cancelAsks();
|
|
15219
|
+
this.tasks.endAll("the agent stopped, and the command with it");
|
|
14986
15220
|
this.endOwn("interrupted");
|
|
14987
15221
|
}
|
|
14988
15222
|
});
|
|
@@ -15155,7 +15389,7 @@ class AcpSession {
|
|
|
15155
15389
|
}
|
|
15156
15390
|
if (params.update.sessionUpdate === "config_option_update")
|
|
15157
15391
|
this.absorb(live, params.update.configOptions);
|
|
15158
|
-
const events = mapUpdate(params.update, live.tools, this.d.worktreeId, (file, bytes) => fireAndForget(this.d.worktreeId, this.d.attachments.putToolImage(this.d.worktreeId, file, bytes), "tool image"));
|
|
15392
|
+
const events = mapUpdate(params.update, live.tools, this.d.worktreeId, (file, bytes) => fireAndForget(this.d.worktreeId, this.d.attachments.putToolImage(this.d.worktreeId, file, bytes), "tool image"), (toolId, start, input) => this.tasks.start(toolId, start, input));
|
|
15159
15393
|
if (!this.running)
|
|
15160
15394
|
this.trackOwn(events);
|
|
15161
15395
|
for (const ev of events) {
|
|
@@ -15296,7 +15530,7 @@ class AcpSession {
|
|
|
15296
15530
|
for (const e of events) {
|
|
15297
15531
|
if (e.type === "tool-start")
|
|
15298
15532
|
this.own.open.add(e.toolId);
|
|
15299
|
-
else if (e.type === "tool-end")
|
|
15533
|
+
else if (e.type === "tool-end" || e.type === "tool-update" && e.background)
|
|
15300
15534
|
this.own.open.delete(e.toolId);
|
|
15301
15535
|
}
|
|
15302
15536
|
if (this.own.settle)
|
|
@@ -15318,8 +15552,11 @@ class AcpSession {
|
|
|
15318
15552
|
this.syncStatus();
|
|
15319
15553
|
this.maybeArmReaper();
|
|
15320
15554
|
}
|
|
15555
|
+
quiet() {
|
|
15556
|
+
return !this.running && !this.own && !this.asking && this.asks.size === 0 && this.tasks.size === 0;
|
|
15557
|
+
}
|
|
15321
15558
|
maybeArmReaper() {
|
|
15322
|
-
if (
|
|
15559
|
+
if (this.quiet() && this.conn && !this.stopped)
|
|
15323
15560
|
this.armReaper();
|
|
15324
15561
|
}
|
|
15325
15562
|
armReaper() {
|
|
@@ -15327,7 +15564,7 @@ class AcpSession {
|
|
|
15327
15564
|
const idleMs = this.d.idleMs ?? DEFAULT_IDLE_MS;
|
|
15328
15565
|
const t = setTimeout(() => {
|
|
15329
15566
|
this.reaper = null;
|
|
15330
|
-
if (this.
|
|
15567
|
+
if (!this.quiet() || !this.conn)
|
|
15331
15568
|
return;
|
|
15332
15569
|
fireAndForget(this.d.worktreeId, this.dropConn(`idle for ${Math.round(idleMs / 60000)} min`), "reap agent");
|
|
15333
15570
|
}, idleMs);
|
|
@@ -15341,6 +15578,7 @@ class AcpSession {
|
|
|
15341
15578
|
}
|
|
15342
15579
|
async dropConn(why) {
|
|
15343
15580
|
this.cancelAsks();
|
|
15581
|
+
this.tasks.endAll("the agent stopped, and the command with it");
|
|
15344
15582
|
const conn = this.conn;
|
|
15345
15583
|
this.conn = null;
|
|
15346
15584
|
this.live = null;
|
|
@@ -15413,15 +15651,15 @@ function sameCommands(a, b) {
|
|
|
15413
15651
|
}
|
|
15414
15652
|
|
|
15415
15653
|
// ../daemon/src/agent/planDoc.ts
|
|
15416
|
-
import { existsSync as
|
|
15417
|
-
import { join as
|
|
15418
|
-
var PLANS_DIR =
|
|
15654
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
15655
|
+
import { join as join8 } from "path";
|
|
15656
|
+
var PLANS_DIR = join8(".toyon", "plans");
|
|
15419
15657
|
var PLAN_NAME = /^(\d+)\.md$/;
|
|
15420
15658
|
function isPlanPath(rel) {
|
|
15421
15659
|
return rel.startsWith(`${PLANS_DIR}/`) && PLAN_NAME.test(rel.slice(PLANS_DIR.length + 1));
|
|
15422
15660
|
}
|
|
15423
15661
|
function nextNumber(dir) {
|
|
15424
|
-
if (!
|
|
15662
|
+
if (!existsSync9(dir))
|
|
15425
15663
|
return 1;
|
|
15426
15664
|
let max = 0;
|
|
15427
15665
|
for (const name of readdirSync2(dir)) {
|
|
@@ -15432,11 +15670,11 @@ function nextNumber(dir) {
|
|
|
15432
15670
|
return max + 1;
|
|
15433
15671
|
}
|
|
15434
15672
|
async function writePlanDoc(cwd, markdown) {
|
|
15435
|
-
const dir =
|
|
15436
|
-
const rel =
|
|
15673
|
+
const dir = join8(cwd, PLANS_DIR);
|
|
15674
|
+
const rel = join8(PLANS_DIR, `${nextNumber(dir)}.md`);
|
|
15437
15675
|
try {
|
|
15438
15676
|
mkdirSync5(dir, { recursive: true });
|
|
15439
|
-
writeFileSync4(
|
|
15677
|
+
writeFileSync4(join8(cwd, rel), markdown.endsWith(`
|
|
15440
15678
|
`) ? markdown : `${markdown}
|
|
15441
15679
|
`);
|
|
15442
15680
|
} catch (e) {
|
|
@@ -15447,8 +15685,8 @@ async function writePlanDoc(cwd, markdown) {
|
|
|
15447
15685
|
return rel;
|
|
15448
15686
|
}
|
|
15449
15687
|
function planEdited(cwd, rel, proposed) {
|
|
15450
|
-
const file =
|
|
15451
|
-
if (!
|
|
15688
|
+
const file = join8(cwd, rel);
|
|
15689
|
+
if (!existsSync9(file))
|
|
15452
15690
|
return false;
|
|
15453
15691
|
try {
|
|
15454
15692
|
return readFileSync8(file, "utf8").trim() !== proposed.trim();
|
|
@@ -15459,7 +15697,7 @@ function planEdited(cwd, rel, proposed) {
|
|
|
15459
15697
|
}
|
|
15460
15698
|
|
|
15461
15699
|
// ../daemon/src/runtime/forward.ts
|
|
15462
|
-
var
|
|
15700
|
+
var POLL_MS2 = 100;
|
|
15463
15701
|
var HOLD_MS = 60000;
|
|
15464
15702
|
var PENDING_CAP = 1 << 20;
|
|
15465
15703
|
function keep(chunk) {
|
|
@@ -15547,7 +15785,7 @@ function startForward(opts) {
|
|
|
15547
15785
|
return connectUp(conn, t);
|
|
15548
15786
|
if (!opts.coming() || Date.now() >= deadline)
|
|
15549
15787
|
return closePair(conn);
|
|
15550
|
-
await Bun.sleep(
|
|
15788
|
+
await Bun.sleep(POLL_MS2);
|
|
15551
15789
|
}
|
|
15552
15790
|
};
|
|
15553
15791
|
const listener = Bun.listen({
|
|
@@ -15906,7 +16144,7 @@ function expandEnv(env, vars) {
|
|
|
15906
16144
|
|
|
15907
16145
|
// ../daemon/src/core/remote.ts
|
|
15908
16146
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
15909
|
-
import { existsSync as
|
|
16147
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
15910
16148
|
function loadRemote(file, env = process.env, managed = MANAGED_DEFAULTS) {
|
|
15911
16149
|
if (env.TOYON_CLOUD === "1") {
|
|
15912
16150
|
const host = (env.TOYON_PUBLIC_HOST ?? "").toLowerCase();
|
|
@@ -15919,7 +16157,7 @@ function loadRemote(file, env = process.env, managed = MANAGED_DEFAULTS) {
|
|
|
15919
16157
|
throw new Error(`TOYON_PREVIEWS: ${why}`);
|
|
15920
16158
|
return { host, previews, front: "edge" };
|
|
15921
16159
|
}
|
|
15922
|
-
if (!
|
|
16160
|
+
if (!existsSync10(file))
|
|
15923
16161
|
return null;
|
|
15924
16162
|
if (managed.remote === "off") {
|
|
15925
16163
|
log.warn("remote", `${file} is ignored: remote access is turned off by your organization's policy`);
|
|
@@ -17526,8 +17764,8 @@ class RuntimeRegistry {
|
|
|
17526
17764
|
|
|
17527
17765
|
// ../daemon/src/worktrees/naming.ts
|
|
17528
17766
|
import { randomBytes } from "crypto";
|
|
17529
|
-
import { existsSync as
|
|
17530
|
-
import { join as
|
|
17767
|
+
import { existsSync as existsSync11 } from "fs";
|
|
17768
|
+
import { join as join9 } from "path";
|
|
17531
17769
|
function shortId() {
|
|
17532
17770
|
return randomBytes(5).toString("hex");
|
|
17533
17771
|
}
|
|
@@ -17535,7 +17773,7 @@ function freeSlot(parent) {
|
|
|
17535
17773
|
for (;; ) {
|
|
17536
17774
|
const id = shortId();
|
|
17537
17775
|
const dir = `wt-${id.slice(0, 4)}`;
|
|
17538
|
-
if (!
|
|
17776
|
+
if (!existsSync11(join9(parent, dir)))
|
|
17539
17777
|
return { id, dir };
|
|
17540
17778
|
}
|
|
17541
17779
|
}
|
|
@@ -17949,20 +18187,20 @@ function makePlanner(agents) {
|
|
|
17949
18187
|
}
|
|
17950
18188
|
|
|
17951
18189
|
// ../daemon/src/core/assets.ts
|
|
17952
|
-
import { existsSync as
|
|
17953
|
-
import { join as
|
|
18190
|
+
import { existsSync as existsSync12, readdirSync as readdirSync3, rmSync, statSync as statSync4 } from "fs";
|
|
18191
|
+
import { join as join10, resolve as resolve5 } from "path";
|
|
17954
18192
|
function locateAssets(here, env = process.env) {
|
|
17955
|
-
const packaged =
|
|
18193
|
+
const packaged = existsSync12(join10(here, "shell", "index.html"));
|
|
17956
18194
|
return {
|
|
17957
|
-
shellDist: env.TOYON_SHELL_DIST ?? (packaged ?
|
|
17958
|
-
bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ?
|
|
18195
|
+
shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join10(here, "shell") : join10(here, "../../shell/dist")),
|
|
18196
|
+
bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join10(here, "bridge.js") : join10(here, "../../bridge/dist/bridge.js")),
|
|
17959
18197
|
sourceRoot: packaged ? null : resolve5(here, "../../.."),
|
|
17960
|
-
packageJson: packaged ?
|
|
18198
|
+
packageJson: packaged ? join10(here, "..", "package.json") : null
|
|
17961
18199
|
};
|
|
17962
18200
|
}
|
|
17963
18201
|
var KEEP_MS = 3 * 24 * 60 * 60 * 1000;
|
|
17964
18202
|
function pruneAssets(shellDist) {
|
|
17965
|
-
const dir =
|
|
18203
|
+
const dir = join10(shellDist, "assets");
|
|
17966
18204
|
let names;
|
|
17967
18205
|
try {
|
|
17968
18206
|
names = readdirSync3(dir);
|
|
@@ -17971,9 +18209,9 @@ function pruneAssets(shellDist) {
|
|
|
17971
18209
|
}
|
|
17972
18210
|
const found = [];
|
|
17973
18211
|
for (const name of names) {
|
|
17974
|
-
const path =
|
|
18212
|
+
const path = join10(dir, name);
|
|
17975
18213
|
try {
|
|
17976
|
-
found.push({ path, mtime:
|
|
18214
|
+
found.push({ path, mtime: statSync4(path).mtimeMs });
|
|
17977
18215
|
} catch {}
|
|
17978
18216
|
}
|
|
17979
18217
|
if (found.length === 0)
|
|
@@ -17992,8 +18230,8 @@ function pruneAssets(shellDist) {
|
|
|
17992
18230
|
}
|
|
17993
18231
|
|
|
17994
18232
|
// ../daemon/src/core/dialog.ts
|
|
17995
|
-
import { existsSync as
|
|
17996
|
-
import { homedir as
|
|
18233
|
+
import { existsSync as existsSync13 } from "fs";
|
|
18234
|
+
import { homedir as homedir5 } from "os";
|
|
17997
18235
|
var PROMPTS = {
|
|
17998
18236
|
location: "Choose where the new project goes",
|
|
17999
18237
|
open: "Choose a project folder to open"
|
|
@@ -18009,8 +18247,8 @@ function folderDialog() {
|
|
|
18009
18247
|
let child = null;
|
|
18010
18248
|
let cancelled = false;
|
|
18011
18249
|
const ask = async (start, purpose) => {
|
|
18012
|
-
const typed = start === "~" || start.startsWith("~/") ? `${
|
|
18013
|
-
const from = typed.startsWith("/") &&
|
|
18250
|
+
const typed = start === "~" || start.startsWith("~/") ? `${homedir5()}${start.slice(1)}` : start;
|
|
18251
|
+
const from = typed.startsWith("/") && existsSync13(typed) ? typed : homedir5();
|
|
18014
18252
|
cancelled = false;
|
|
18015
18253
|
const p = Bun.spawn(["osascript", ...SCRIPT.flatMap((line) => ["-e", line]), PROMPTS[purpose], from], {
|
|
18016
18254
|
stdout: "pipe",
|
|
@@ -18476,12 +18714,23 @@ class IdleExit {
|
|
|
18476
18714
|
// ../daemon/src/core/restart.ts
|
|
18477
18715
|
import { spawn as spawn3 } from "child_process";
|
|
18478
18716
|
import { openSync } from "fs";
|
|
18717
|
+
import { homedir as homedir6 } from "os";
|
|
18479
18718
|
function restartable(env = process.env) {
|
|
18480
18719
|
if (env.TOYON_WORKTREE !== undefined) {
|
|
18481
18720
|
return { ok: false, reason: "this daemon runs as a toyon process; restart it from its terminal tab" };
|
|
18482
18721
|
}
|
|
18483
18722
|
return { ok: true };
|
|
18484
18723
|
}
|
|
18724
|
+
async function replacementRuns() {
|
|
18725
|
+
const r = await run(process.execPath, ["--version"], homedir6());
|
|
18726
|
+
if (r.ok && /^\d+\.\d+\.\d+/.test(r.out))
|
|
18727
|
+
return { ok: true };
|
|
18728
|
+
const why = r.err !== "" ? r.err : `exit ${r.exit}`;
|
|
18729
|
+
return {
|
|
18730
|
+
ok: false,
|
|
18731
|
+
reason: `the bun Toyon runs on did not start (${process.execPath}: ${why}); an install may still be writing it`
|
|
18732
|
+
};
|
|
18733
|
+
}
|
|
18485
18734
|
function respawn(logFile) {
|
|
18486
18735
|
const logFd = openSync(logFile, "a");
|
|
18487
18736
|
const child = spawn3(process.execPath, process.argv.slice(1), {
|
|
@@ -18508,8 +18757,8 @@ class Restarter {
|
|
|
18508
18757
|
d.hub.on("agentStatus", settle);
|
|
18509
18758
|
d.hub.on("worktreesChanged", settle);
|
|
18510
18759
|
}
|
|
18511
|
-
request(opts = {}) {
|
|
18512
|
-
const refused = this.d.refusal();
|
|
18760
|
+
async request(opts = {}) {
|
|
18761
|
+
const refused = await this.d.refusal();
|
|
18513
18762
|
if (refused)
|
|
18514
18763
|
return refused;
|
|
18515
18764
|
this.asked = true;
|
|
@@ -18626,14 +18875,14 @@ class SelfWatch {
|
|
|
18626
18875
|
|
|
18627
18876
|
// ../daemon/src/core/state.ts
|
|
18628
18877
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
18629
|
-
import { existsSync as
|
|
18878
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
18630
18879
|
var GROUPS_SAVE_MS = 250;
|
|
18631
18880
|
var SEEN_WORKTREES = 50;
|
|
18632
18881
|
var empty = { repos: [], worktrees: [], sessions: {} };
|
|
18633
18882
|
function loadState(paths) {
|
|
18634
18883
|
ensureDirs(paths);
|
|
18635
18884
|
const STATE_FILE = paths.stateFile;
|
|
18636
|
-
if (!
|
|
18885
|
+
if (!existsSync14(STATE_FILE))
|
|
18637
18886
|
return structuredClone(empty);
|
|
18638
18887
|
let raw;
|
|
18639
18888
|
try {
|
|
@@ -18685,7 +18934,7 @@ function saveState(paths, state) {
|
|
|
18685
18934
|
function loadOrCreateToken(paths) {
|
|
18686
18935
|
ensureDirs(paths);
|
|
18687
18936
|
const TOKEN_FILE = paths.tokenFile;
|
|
18688
|
-
if (
|
|
18937
|
+
if (existsSync14(TOKEN_FILE))
|
|
18689
18938
|
return readFileSync10(TOKEN_FILE, "utf8").trim();
|
|
18690
18939
|
const token = randomBytes2(32).toString("hex");
|
|
18691
18940
|
writeFileSync5(TOKEN_FILE, token, { mode: 384 });
|
|
@@ -18901,7 +19150,7 @@ class StateStore {
|
|
|
18901
19150
|
}
|
|
18902
19151
|
|
|
18903
19152
|
// ../daemon/src/design/service.ts
|
|
18904
|
-
import { join as
|
|
19153
|
+
import { join as join11 } from "path";
|
|
18905
19154
|
|
|
18906
19155
|
// ../daemon/src/design/scan.ts
|
|
18907
19156
|
var COLOR = /^(#|rgba?\(|hsla?\(|oklch\(|oklab\(|lab\(|lch\(|color\(|color-mix\(|light-dark\()/i;
|
|
@@ -19351,7 +19600,7 @@ class DesignService {
|
|
|
19351
19600
|
async function readAll(root, paths) {
|
|
19352
19601
|
const out = [];
|
|
19353
19602
|
for (const path of paths) {
|
|
19354
|
-
const file = Bun.file(
|
|
19603
|
+
const file = Bun.file(join11(root, path));
|
|
19355
19604
|
if (file.size === 0 || file.size > MAX_FILE_BYTES)
|
|
19356
19605
|
continue;
|
|
19357
19606
|
try {
|
|
@@ -19466,14 +19715,14 @@ function typicalUse(classes) {
|
|
|
19466
19715
|
var normal = (s) => s.replace(/[-_]/g, "").toLowerCase();
|
|
19467
19716
|
async function loadProjectTypescript(root) {
|
|
19468
19717
|
try {
|
|
19469
|
-
return await import(
|
|
19718
|
+
return await import(join11(root, "node_modules", "typescript", "lib", "typescript.js"));
|
|
19470
19719
|
} catch {
|
|
19471
19720
|
return null;
|
|
19472
19721
|
}
|
|
19473
19722
|
}
|
|
19474
19723
|
|
|
19475
19724
|
// ../daemon/src/drafts/store.ts
|
|
19476
|
-
import { existsSync as
|
|
19725
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11, renameSync as renameSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
19477
19726
|
var SAVE_DELAY_MS = 1000;
|
|
19478
19727
|
|
|
19479
19728
|
class DraftStore {
|
|
@@ -19482,7 +19731,7 @@ class DraftStore {
|
|
|
19482
19731
|
saveTimer = null;
|
|
19483
19732
|
constructor(d) {
|
|
19484
19733
|
this.d = d;
|
|
19485
|
-
if (!
|
|
19734
|
+
if (!existsSync15(d.file))
|
|
19486
19735
|
return;
|
|
19487
19736
|
try {
|
|
19488
19737
|
const raw = JSON.parse(readFileSync11(d.file, "utf8"));
|
|
@@ -19557,7 +19806,6 @@ class DraftStore {
|
|
|
19557
19806
|
}
|
|
19558
19807
|
|
|
19559
19808
|
// ../daemon/src/exec/service.ts
|
|
19560
|
-
var OUTPUT_CAP = 200000;
|
|
19561
19809
|
var TIMEOUT_MS = 10 * 60000;
|
|
19562
19810
|
var DRAIN_GRACE_MS = 500;
|
|
19563
19811
|
var BACKGROUND_POLL_MS = 250;
|
|
@@ -19780,42 +20028,22 @@ class ExecService {
|
|
|
19780
20028
|
await Bun.sleep(BACKGROUND_POLL_MS);
|
|
19781
20029
|
}
|
|
19782
20030
|
}
|
|
19783
|
-
function formatOutput(text, exit, truncated, ceiling) {
|
|
19784
|
-
const body = text.replace(/\n+$/, "");
|
|
19785
|
-
const notes = [];
|
|
19786
|
-
if (truncated)
|
|
19787
|
-
notes.push(`output cut at ${Math.round(OUTPUT_CAP / 1000)} KB`);
|
|
19788
|
-
if (exit === "timeout")
|
|
19789
|
-
notes.push(ceiling ? `gave up after ${describeDuration(ceiling)}` : "gave up at the ceiling");
|
|
19790
|
-
else if (typeof exit === "string")
|
|
19791
|
-
notes.push(`killed (${exit})`);
|
|
19792
|
-
else if (exit !== 0)
|
|
19793
|
-
notes.push(`exit ${exit}`);
|
|
19794
|
-
const parts = [];
|
|
19795
|
-
if (body.trim())
|
|
19796
|
-
parts.push(`\`\`\`
|
|
19797
|
-
${body}
|
|
19798
|
-
\`\`\``);
|
|
19799
|
-
parts.push(...notes);
|
|
19800
|
-
return parts.join(`
|
|
19801
|
-
`);
|
|
19802
|
-
}
|
|
19803
20031
|
|
|
19804
20032
|
// ../daemon/src/files/open.ts
|
|
19805
20033
|
import { stat as stat2 } from "fs/promises";
|
|
19806
|
-
import { basename as
|
|
20034
|
+
import { basename as basename3, resolve as resolve7 } from "path";
|
|
19807
20035
|
|
|
19808
20036
|
// ../daemon/src/repos/browse.ts
|
|
19809
|
-
import { existsSync as
|
|
20037
|
+
import { existsSync as existsSync16, statSync as statSync5 } from "fs";
|
|
19810
20038
|
import { readdir } from "fs/promises";
|
|
19811
|
-
import { homedir as
|
|
19812
|
-
import { dirname as
|
|
20039
|
+
import { homedir as homedir7 } from "os";
|
|
20040
|
+
import { dirname as dirname5, join as join12 } from "path";
|
|
19813
20041
|
var MAX_ENTRIES = 40;
|
|
19814
20042
|
function expandTilde(raw) {
|
|
19815
|
-
return raw === "~" || raw.startsWith("~/") ?
|
|
20043
|
+
return raw === "~" || raw.startsWith("~/") ? join12(homedir7(), raw.slice(1)) : raw;
|
|
19816
20044
|
}
|
|
19817
20045
|
function collapseTilde(abs) {
|
|
19818
|
-
const home =
|
|
20046
|
+
const home = homedir7();
|
|
19819
20047
|
return abs === home || abs.startsWith(`${home}/`) ? `~${abs.slice(home.length)}` : abs;
|
|
19820
20048
|
}
|
|
19821
20049
|
function holdsNothing(names) {
|
|
@@ -19823,7 +20051,7 @@ function holdsNothing(names) {
|
|
|
19823
20051
|
}
|
|
19824
20052
|
async function describeFolder(abs) {
|
|
19825
20053
|
const path = collapseTilde(abs);
|
|
19826
|
-
if (
|
|
20054
|
+
if (existsSync16(join12(abs, ".git")))
|
|
19827
20055
|
return { path, kind: "project" };
|
|
19828
20056
|
try {
|
|
19829
20057
|
return { path, kind: holdsNothing(await readdir(abs)) ? "empty" : "folder" };
|
|
@@ -19835,13 +20063,13 @@ var NOWHERE = { exists: false, isDir: false, isRepo: false, parentExists: false
|
|
|
19835
20063
|
function describe2(typed, parent) {
|
|
19836
20064
|
let isDir = false;
|
|
19837
20065
|
try {
|
|
19838
|
-
isDir =
|
|
20066
|
+
isDir = statSync5(typed).isDirectory();
|
|
19839
20067
|
} catch {}
|
|
19840
20068
|
return {
|
|
19841
|
-
exists:
|
|
20069
|
+
exists: existsSync16(typed),
|
|
19842
20070
|
isDir,
|
|
19843
|
-
isRepo: isDir &&
|
|
19844
|
-
parentExists:
|
|
20071
|
+
isRepo: isDir && existsSync16(join12(typed, ".git")),
|
|
20072
|
+
parentExists: existsSync16(parent)
|
|
19845
20073
|
};
|
|
19846
20074
|
}
|
|
19847
20075
|
async function browsePath(raw) {
|
|
@@ -19849,7 +20077,7 @@ async function browsePath(raw) {
|
|
|
19849
20077
|
if (!typed.startsWith("/"))
|
|
19850
20078
|
return { entries: [], target: NOWHERE };
|
|
19851
20079
|
const listing = typed.endsWith("/");
|
|
19852
|
-
const dir = listing ? typed :
|
|
20080
|
+
const dir = listing ? typed : dirname5(typed);
|
|
19853
20081
|
const prefix = listing ? "" : typed.slice(dir.length).replace(/^\//, "").toLowerCase();
|
|
19854
20082
|
const target = describe2(listing ? typed.replace(/\/+$/, "") : typed, dir);
|
|
19855
20083
|
let names;
|
|
@@ -19865,8 +20093,8 @@ async function browsePath(raw) {
|
|
|
19865
20093
|
continue;
|
|
19866
20094
|
if (!name.toLowerCase().startsWith(prefix))
|
|
19867
20095
|
continue;
|
|
19868
|
-
const abs =
|
|
19869
|
-
entries.push({ path: collapseTilde(abs), name, isRepo:
|
|
20096
|
+
const abs = join12(dir, name);
|
|
20097
|
+
entries.push({ path: collapseTilde(abs), name, isRepo: existsSync16(join12(abs, ".git")) });
|
|
19870
20098
|
}
|
|
19871
20099
|
entries.sort((a, b) => a.isRepo !== b.isRepo ? a.isRepo ? -1 : 1 : a.name.localeCompare(b.name));
|
|
19872
20100
|
return { entries: entries.slice(0, MAX_ENTRIES), target };
|
|
@@ -19977,7 +20205,7 @@ function withRepoLock(repoPath, fn) {
|
|
|
19977
20205
|
var fileLockKey = (absPath) => `file:${absPath}`;
|
|
19978
20206
|
|
|
19979
20207
|
// ../daemon/src/git/status.ts
|
|
19980
|
-
import { join as
|
|
20208
|
+
import { join as join13 } from "path";
|
|
19981
20209
|
async function treeFingerprint(worktreePath) {
|
|
19982
20210
|
const [head, diff, untracked] = await Promise.all([
|
|
19983
20211
|
git(worktreePath, "rev-parse", "HEAD"),
|
|
@@ -19987,7 +20215,7 @@ async function treeFingerprint(worktreePath) {
|
|
|
19987
20215
|
const others = await Promise.all(untracked.out.split(`
|
|
19988
20216
|
`).filter(Boolean).map(async (p) => {
|
|
19989
20217
|
try {
|
|
19990
|
-
const f = Bun.file(
|
|
20218
|
+
const f = Bun.file(join13(worktreePath, p));
|
|
19991
20219
|
const body = f.size > 2000000 ? String(f.size) : Bun.hash(await f.arrayBuffer()).toString(36);
|
|
19992
20220
|
return `${p}:${body}`;
|
|
19993
20221
|
} catch {
|
|
@@ -20038,7 +20266,7 @@ async function numstat(worktreePath, ...range) {
|
|
|
20038
20266
|
}
|
|
20039
20267
|
async function untrackedLines(worktreePath, file) {
|
|
20040
20268
|
try {
|
|
20041
|
-
const f = Bun.file(
|
|
20269
|
+
const f = Bun.file(join13(worktreePath, file));
|
|
20042
20270
|
if (f.size > 2000000)
|
|
20043
20271
|
return {};
|
|
20044
20272
|
const buf = new Uint8Array(await f.arrayBuffer());
|
|
@@ -20662,7 +20890,7 @@ class OpenService {
|
|
|
20662
20890
|
return hit ? { kind: "file", worktreeId: hit.id, path: path.slice(hit.root.length + 1) } : null;
|
|
20663
20891
|
}
|
|
20664
20892
|
async grant(path, size, mtimeMs) {
|
|
20665
|
-
const name =
|
|
20893
|
+
const name = basename3(path);
|
|
20666
20894
|
if (this.d.refused.some((r) => within2(path, canonical(resolve7(r))))) {
|
|
20667
20895
|
throw new UserError(`${name} is inside Toyon's own folder, which is not for editing`);
|
|
20668
20896
|
}
|
|
@@ -20867,17 +21095,28 @@ class AfterLand {
|
|
|
20867
21095
|
return;
|
|
20868
21096
|
}
|
|
20869
21097
|
this.running.add(repoId);
|
|
20870
|
-
|
|
20871
|
-
this.
|
|
20872
|
-
|
|
20873
|
-
this.run(repoId);
|
|
20874
|
-
});
|
|
21098
|
+
if (this.d.self.building(true))
|
|
21099
|
+
this.d.hub.emit("selfChanged");
|
|
21100
|
+
const done = this.runs(repo, commands).finally(() => this.running.delete(repoId));
|
|
20875
21101
|
fireAndForget(repo.id, done, "afterLand");
|
|
20876
21102
|
}
|
|
20877
|
-
async
|
|
20878
|
-
|
|
20879
|
-
|
|
21103
|
+
async runs(repo, commands) {
|
|
21104
|
+
let failure;
|
|
21105
|
+
let notice;
|
|
21106
|
+
do {
|
|
21107
|
+
await this.d.settled?.();
|
|
21108
|
+
({ failure, notice } = await this.exec(repo, commands));
|
|
21109
|
+
} while (this.again.delete(repo.id));
|
|
21110
|
+
const onNotice = this.d.self.reports(repo);
|
|
21111
|
+
if (this.d.self.building(false, failure))
|
|
20880
21112
|
this.d.hub.emit("selfChanged");
|
|
21113
|
+
if (notice !== undefined && !onNotice) {
|
|
21114
|
+
const main = this.mainOf(repo);
|
|
21115
|
+
if (main)
|
|
21116
|
+
this.d.hub.emit("failed", main.id, notice);
|
|
21117
|
+
}
|
|
21118
|
+
}
|
|
21119
|
+
async exec(repo, commands) {
|
|
20881
21120
|
let failure;
|
|
20882
21121
|
let notice;
|
|
20883
21122
|
for (const command of commands) {
|
|
@@ -20897,14 +21136,7 @@ class AfterLand {
|
|
|
20897
21136
|
log.warn(repo.id, `afterLand stopped: ${command} exited ${code}`);
|
|
20898
21137
|
break;
|
|
20899
21138
|
}
|
|
20900
|
-
|
|
20901
|
-
if (this.d.self.building(false, failure))
|
|
20902
|
-
this.d.hub.emit("selfChanged");
|
|
20903
|
-
if (notice !== undefined && !onNotice) {
|
|
20904
|
-
const main = this.mainOf(repo);
|
|
20905
|
-
if (main)
|
|
20906
|
-
this.d.hub.emit("failed", main.id, notice);
|
|
20907
|
-
}
|
|
21139
|
+
return { failure, notice };
|
|
20908
21140
|
}
|
|
20909
21141
|
tellMain(repo, line, retract) {
|
|
20910
21142
|
const main = this.mainOf(repo);
|
|
@@ -20917,9 +21149,9 @@ class AfterLand {
|
|
|
20917
21149
|
}
|
|
20918
21150
|
|
|
20919
21151
|
// ../daemon/src/repos/registry.ts
|
|
20920
|
-
import { existsSync as
|
|
20921
|
-
import { homedir as
|
|
20922
|
-
import { basename as
|
|
21152
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync6, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
21153
|
+
import { homedir as homedir9 } from "os";
|
|
21154
|
+
import { basename as basename5, dirname as dirname7, join as join17 } from "path";
|
|
20923
21155
|
|
|
20924
21156
|
// ../daemon/src/git/base.ts
|
|
20925
21157
|
async function resolveBase(repoPath, defaultBranch, route) {
|
|
@@ -20930,8 +21162,8 @@ async function resolveBase(repoPath, defaultBranch, route) {
|
|
|
20930
21162
|
}
|
|
20931
21163
|
|
|
20932
21164
|
// ../daemon/src/repos/config.ts
|
|
20933
|
-
import { closeSync, existsSync as
|
|
20934
|
-
import { join as
|
|
21165
|
+
import { closeSync, existsSync as existsSync17, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync12, readSync, statSync as statSync6 } from "fs";
|
|
21166
|
+
import { join as join14 } from "path";
|
|
20935
21167
|
|
|
20936
21168
|
// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
20937
21169
|
function createScanner(text, ignoreTrivia = false) {
|
|
@@ -22295,7 +22527,7 @@ function applyEdits(text, edits) {
|
|
|
22295
22527
|
|
|
22296
22528
|
// ../daemon/src/repos/config.ts
|
|
22297
22529
|
var PLACES = [CONFIG_FILES.folder, CONFIG_FILES.root];
|
|
22298
|
-
var present = (repoPath, place) => [place.shared, place.local].filter((rel) =>
|
|
22530
|
+
var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync17(join14(repoPath, rel)));
|
|
22299
22531
|
var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
22300
22532
|
function parseSettings(text) {
|
|
22301
22533
|
const errors = [];
|
|
@@ -22320,7 +22552,7 @@ function readConfigFile(repoPath) {
|
|
|
22320
22552
|
for (const rel of files) {
|
|
22321
22553
|
let raw;
|
|
22322
22554
|
try {
|
|
22323
|
-
raw = parseSettings(readFileSync12(
|
|
22555
|
+
raw = parseSettings(readFileSync12(join14(repoPath, rel), "utf8"));
|
|
22324
22556
|
} catch (e) {
|
|
22325
22557
|
return { ok: false, reason: `${rel} is not valid JSON: ${e instanceof Error ? e.message : String(e)}` };
|
|
22326
22558
|
}
|
|
@@ -22379,8 +22611,8 @@ function configTarget(repoPath, made) {
|
|
|
22379
22611
|
}
|
|
22380
22612
|
function configBody(repoPath, rel, config) {
|
|
22381
22613
|
const place = PLACES.find((p) => p.local === rel);
|
|
22382
|
-
const abs = place &&
|
|
22383
|
-
if (!abs || !
|
|
22614
|
+
const abs = place && join14(repoPath, place.shared);
|
|
22615
|
+
if (!abs || !existsSync17(abs))
|
|
22384
22616
|
return config;
|
|
22385
22617
|
let shared;
|
|
22386
22618
|
try {
|
|
@@ -22397,7 +22629,7 @@ function configText(repoPath, rel, config) {
|
|
|
22397
22629
|
let text;
|
|
22398
22630
|
let was;
|
|
22399
22631
|
try {
|
|
22400
|
-
text = readFileSync12(
|
|
22632
|
+
text = readFileSync12(join14(repoPath, rel), "utf8");
|
|
22401
22633
|
was = parseSettings(text);
|
|
22402
22634
|
} catch {
|
|
22403
22635
|
return fresh;
|
|
@@ -22427,8 +22659,8 @@ function detectConfig(repoPath) {
|
|
|
22427
22659
|
return { config: explicit.config, needsSetup: false };
|
|
22428
22660
|
if (explicit)
|
|
22429
22661
|
log.warn("config", `${repoPath}: ${explicit.reason}; detecting instead`);
|
|
22430
|
-
const pkgPath =
|
|
22431
|
-
if (
|
|
22662
|
+
const pkgPath = join14(repoPath, "package.json");
|
|
22663
|
+
if (existsSync17(pkgPath)) {
|
|
22432
22664
|
const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
22433
22665
|
const scripts = pkg.scripts ?? {};
|
|
22434
22666
|
const runner = detectRunner(repoPath, pkg.packageManager);
|
|
@@ -22450,7 +22682,7 @@ function detectConfig(repoPath) {
|
|
|
22450
22682
|
};
|
|
22451
22683
|
}
|
|
22452
22684
|
}
|
|
22453
|
-
if (
|
|
22685
|
+
if (existsSync17(join14(repoPath, "start.sh"))) {
|
|
22454
22686
|
return { config: { run: { app: "./start.sh" } }, needsSetup: true, from: "start.sh" };
|
|
22455
22687
|
}
|
|
22456
22688
|
const assumed = assumeNothingToRun(repoPath);
|
|
@@ -22493,7 +22725,7 @@ function assumeNothingToRun(repoPath) {
|
|
|
22493
22725
|
return marker && !servesPage(repoPath) ? marker : undefined;
|
|
22494
22726
|
}
|
|
22495
22727
|
function markerIn(repoPath) {
|
|
22496
|
-
const named = NO_PAGE_MARKERS.find((f) =>
|
|
22728
|
+
const named = NO_PAGE_MARKERS.find((f) => existsSync17(join14(repoPath, f)));
|
|
22497
22729
|
if (named)
|
|
22498
22730
|
return named;
|
|
22499
22731
|
let entries;
|
|
@@ -22505,12 +22737,12 @@ function markerIn(repoPath) {
|
|
|
22505
22737
|
return entries.find((f) => NO_PAGE_EXTENSIONS.some((ext) => f.endsWith(ext)));
|
|
22506
22738
|
}
|
|
22507
22739
|
function servesPage(repoPath) {
|
|
22508
|
-
if (WEB_FILES.some((f) =>
|
|
22740
|
+
if (WEB_FILES.some((f) => existsSync17(join14(repoPath, f))))
|
|
22509
22741
|
return true;
|
|
22510
|
-
if (/^web:/m.test(head(
|
|
22742
|
+
if (/^web:/m.test(head(join14(repoPath, "Procfile"))))
|
|
22511
22743
|
return true;
|
|
22512
22744
|
for (const [files, names] of WEB_DEPENDENCIES) {
|
|
22513
|
-
const text = files.map((f) => head(
|
|
22745
|
+
const text = files.map((f) => head(join14(repoPath, f))).join(`
|
|
22514
22746
|
`);
|
|
22515
22747
|
if (text.trim() && names.some((n) => mentions(text, n)))
|
|
22516
22748
|
return true;
|
|
@@ -22527,7 +22759,7 @@ function frontEndBeside(repoPath) {
|
|
|
22527
22759
|
return dirs.some((d) => {
|
|
22528
22760
|
if (!d.isDirectory() || d.name.startsWith(".") || NOT_A_PACKAGE.has(d.name))
|
|
22529
22761
|
return false;
|
|
22530
|
-
const pkg = head(
|
|
22762
|
+
const pkg = head(join14(repoPath, d.name, "package.json"));
|
|
22531
22763
|
if (!pkg)
|
|
22532
22764
|
return false;
|
|
22533
22765
|
try {
|
|
@@ -22542,7 +22774,7 @@ function mentions(text, name) {
|
|
|
22542
22774
|
return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i").test(text);
|
|
22543
22775
|
}
|
|
22544
22776
|
function head(path) {
|
|
22545
|
-
if (!
|
|
22777
|
+
if (!existsSync17(path))
|
|
22546
22778
|
return "";
|
|
22547
22779
|
try {
|
|
22548
22780
|
const fd = openSync2(path, "r");
|
|
@@ -22583,11 +22815,11 @@ function isLocalDatabase(url) {
|
|
|
22583
22815
|
}
|
|
22584
22816
|
function detectServices(repoPath) {
|
|
22585
22817
|
const out = {};
|
|
22586
|
-
const compose = COMPOSE_FILES.find((f) =>
|
|
22818
|
+
const compose = COMPOSE_FILES.find((f) => existsSync17(join14(repoPath, f)));
|
|
22587
22819
|
if (compose)
|
|
22588
22820
|
out.compose = compose;
|
|
22589
22821
|
for (const file of ENV_FILES) {
|
|
22590
|
-
const text = head(
|
|
22822
|
+
const text = head(join14(repoPath, file));
|
|
22591
22823
|
if (!text)
|
|
22592
22824
|
continue;
|
|
22593
22825
|
for (const name of DB_URL_NAMES) {
|
|
@@ -22611,7 +22843,7 @@ function inferPaths(cmd, repoPath, depth = 1) {
|
|
|
22611
22843
|
return;
|
|
22612
22844
|
let dir = false;
|
|
22613
22845
|
try {
|
|
22614
|
-
dir =
|
|
22846
|
+
dir = statSync6(join14(repoPath, clean)).isDirectory();
|
|
22615
22847
|
} catch {
|
|
22616
22848
|
return;
|
|
22617
22849
|
}
|
|
@@ -22622,25 +22854,25 @@ function inferPaths(cmd, repoPath, depth = 1) {
|
|
|
22622
22854
|
const t = tokens[i];
|
|
22623
22855
|
if (t === "cd") {
|
|
22624
22856
|
const dir = tokens[i + 1];
|
|
22625
|
-
if (dir && !dir.startsWith("-") &&
|
|
22857
|
+
if (dir && !dir.startsWith("-") && existsSync17(join14(repoPath, dir)))
|
|
22626
22858
|
add(dir);
|
|
22627
22859
|
continue;
|
|
22628
22860
|
}
|
|
22629
22861
|
if (t.startsWith("-") || t.startsWith("$"))
|
|
22630
22862
|
continue;
|
|
22631
22863
|
const mod = /^([A-Za-z_]\w*)(?:\.\w+)*:\w+$/.exec(t);
|
|
22632
|
-
if (mod?.[1] &&
|
|
22864
|
+
if (mod?.[1] && existsSync17(join14(repoPath, mod[1]))) {
|
|
22633
22865
|
add(mod[1]);
|
|
22634
22866
|
continue;
|
|
22635
22867
|
}
|
|
22636
22868
|
const rel = t.replace(/^\.\//, "");
|
|
22637
22869
|
if (rel.startsWith("/") || rel.startsWith("..") || rel.includes("="))
|
|
22638
22870
|
continue;
|
|
22639
|
-
if (!
|
|
22871
|
+
if (!existsSync17(join14(repoPath, rel)))
|
|
22640
22872
|
continue;
|
|
22641
22873
|
add(rel);
|
|
22642
22874
|
if (depth > 0 && rel.endsWith(".sh")) {
|
|
22643
|
-
for (const p of inferPaths(head(
|
|
22875
|
+
for (const p of inferPaths(head(join14(repoPath, rel)), repoPath, depth - 1))
|
|
22644
22876
|
out.add(p);
|
|
22645
22877
|
}
|
|
22646
22878
|
}
|
|
@@ -22663,9 +22895,9 @@ function procCommand(runner, script, body) {
|
|
|
22663
22895
|
return `${base}${runner === "npm" ? " --" : ""} ${flag}`;
|
|
22664
22896
|
}
|
|
22665
22897
|
function detectPages(repoPath, scripts, runner) {
|
|
22666
|
-
if (!scripts.build || !
|
|
22898
|
+
if (!scripts.build || !existsSync17(join14(repoPath, "functions")))
|
|
22667
22899
|
return null;
|
|
22668
|
-
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) =>
|
|
22900
|
+
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join14(repoPath, f)).find((f) => existsSync17(f));
|
|
22669
22901
|
if (!cfg)
|
|
22670
22902
|
return null;
|
|
22671
22903
|
const out = readFileSync12(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
|
|
@@ -22674,68 +22906,68 @@ function detectPages(repoPath, scripts, runner) {
|
|
|
22674
22906
|
return { web: `${runner} run build && npx wrangler pages dev ${out} --ip 127.0.0.1 --port $PORT` };
|
|
22675
22907
|
}
|
|
22676
22908
|
function detectRunner(repoPath, packageManager) {
|
|
22677
|
-
if (
|
|
22909
|
+
if (existsSync17(join14(repoPath, "bun.lock")) || existsSync17(join14(repoPath, "bun.lockb")))
|
|
22678
22910
|
return "bun";
|
|
22679
|
-
if (
|
|
22911
|
+
if (existsSync17(join14(repoPath, "pnpm-lock.yaml")))
|
|
22680
22912
|
return "pnpm";
|
|
22681
|
-
if (
|
|
22913
|
+
if (existsSync17(join14(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
|
|
22682
22914
|
return "yarn";
|
|
22683
22915
|
return "npm";
|
|
22684
22916
|
}
|
|
22685
22917
|
|
|
22686
22918
|
// ../daemon/src/repos/create.ts
|
|
22687
|
-
import { existsSync as
|
|
22919
|
+
import { existsSync as existsSync18, readdirSync as readdirSync5, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
|
|
22688
22920
|
import { appendFile as appendFile2, mkdir as mkdir2, rm } from "fs/promises";
|
|
22689
|
-
import { homedir as
|
|
22690
|
-
import { dirname as
|
|
22921
|
+
import { homedir as homedir8 } from "os";
|
|
22922
|
+
import { dirname as dirname6, join as join15, resolve as resolve8 } from "path";
|
|
22691
22923
|
function isInside(dir, parent) {
|
|
22692
22924
|
const p = parent.replace(/\/+$/, "");
|
|
22693
22925
|
return dir === p || dir.startsWith(`${p}/`);
|
|
22694
22926
|
}
|
|
22695
|
-
function planProject(opts, home =
|
|
22927
|
+
function planProject(opts, home = homedir8()) {
|
|
22696
22928
|
const nameError = projectNameError(opts.name);
|
|
22697
22929
|
if (nameError)
|
|
22698
22930
|
throw new UserError(nameError);
|
|
22699
22931
|
const first = firstProjectsFolder(opts.parent, home);
|
|
22700
22932
|
const parent = first ?? resolveParent(opts.parent);
|
|
22701
|
-
const dir =
|
|
22702
|
-
if (
|
|
22933
|
+
const dir = join15(parent, opts.name.trim());
|
|
22934
|
+
if (existsSync18(dir))
|
|
22703
22935
|
throw new UserError(`${dir} already exists`);
|
|
22704
22936
|
return { parent, dir, makeParent: first !== null };
|
|
22705
22937
|
}
|
|
22706
22938
|
function firstProjectsFolder(raw, home) {
|
|
22707
22939
|
const typed = expandTilde(raw.trim()).replace(/\/+$/, "");
|
|
22708
|
-
if (typed !==
|
|
22940
|
+
if (typed !== join15(home, PROJECTS_FOLDER) || existsSync18(typed) || !existsSync18(home))
|
|
22709
22941
|
return null;
|
|
22710
|
-
return
|
|
22942
|
+
return join15(realpathSync2(home), PROJECTS_FOLDER);
|
|
22711
22943
|
}
|
|
22712
22944
|
function planInPlace(opts) {
|
|
22713
22945
|
const name = opts.name.trim();
|
|
22714
22946
|
if (!name || name === "." || name === ".." || name.includes("/")) {
|
|
22715
22947
|
throw new UserError(`${opts.name} is not a folder name`);
|
|
22716
22948
|
}
|
|
22717
|
-
const typed =
|
|
22718
|
-
if (!
|
|
22949
|
+
const typed = join15(resolveParent(opts.parent), name);
|
|
22950
|
+
if (!existsSync18(typed) || !statSync7(typed).isDirectory())
|
|
22719
22951
|
throw new UserError(`${typed} is not a folder`);
|
|
22720
22952
|
const dir = realpathSync2(typed);
|
|
22721
|
-
if (
|
|
22953
|
+
if (existsSync18(join15(dir, ".git")))
|
|
22722
22954
|
throw new UserError(`${dir} is already a project; open it instead`);
|
|
22723
22955
|
if (!holdsNothing(readdirSync5(dir))) {
|
|
22724
22956
|
throw new UserError(`${dir} is not empty; only an empty folder can become a project where it is`);
|
|
22725
22957
|
}
|
|
22726
|
-
return { parent:
|
|
22958
|
+
return { parent: dirname6(dir), dir, makeParent: false };
|
|
22727
22959
|
}
|
|
22728
22960
|
function resolveParent(raw) {
|
|
22729
22961
|
const typed = expandTilde(raw.trim());
|
|
22730
22962
|
if (!typed.startsWith("/"))
|
|
22731
22963
|
throw new UserError(`${raw} is not a folder path`);
|
|
22732
|
-
if (!
|
|
22964
|
+
if (!existsSync18(typed))
|
|
22733
22965
|
throw new UserError(`${raw} does not exist; make it first`);
|
|
22734
|
-
if (!
|
|
22966
|
+
if (!statSync7(typed).isDirectory())
|
|
22735
22967
|
throw new UserError(`${raw} is not a folder`);
|
|
22736
22968
|
return realpathSync2(typed);
|
|
22737
22969
|
}
|
|
22738
|
-
async function createRepoDir(opts, home =
|
|
22970
|
+
async function createRepoDir(opts, home = homedir8()) {
|
|
22739
22971
|
const { parent, dir, makeParent } = planProject(opts, home);
|
|
22740
22972
|
await requireGitIdentity(makeParent ? home : parent);
|
|
22741
22973
|
if (makeParent)
|
|
@@ -22770,7 +23002,7 @@ async function initWithEmptyCommit(dir) {
|
|
|
22770
23002
|
if (!exclude.ok)
|
|
22771
23003
|
throw new UserError(`git rev-parse failed: ${exclude.err}`);
|
|
22772
23004
|
const excludePath = resolve8(dir, exclude.out);
|
|
22773
|
-
await mkdir2(
|
|
23005
|
+
await mkdir2(dirname6(excludePath), { recursive: true });
|
|
22774
23006
|
await appendFile2(excludePath, `
|
|
22775
23007
|
${LOCAL_EXCLUDES.join(`
|
|
22776
23008
|
`)}
|
|
@@ -22809,13 +23041,13 @@ async function undoCreate(dir) {
|
|
|
22809
23041
|
}
|
|
22810
23042
|
async function undoInit(dir) {
|
|
22811
23043
|
try {
|
|
22812
|
-
await rm(
|
|
23044
|
+
await rm(join15(dir, ".git"), { recursive: true, force: true });
|
|
22813
23045
|
} catch (e) {
|
|
22814
23046
|
log.warn(dir, "could not take back a half-made .git", e);
|
|
22815
23047
|
}
|
|
22816
23048
|
}
|
|
22817
23049
|
async function unmakeProject(dir, made) {
|
|
22818
|
-
const target = made === "folder" ? dir :
|
|
23050
|
+
const target = made === "folder" ? dir : join15(dir, ".git");
|
|
22819
23051
|
try {
|
|
22820
23052
|
await rm(target, { recursive: true, force: true });
|
|
22821
23053
|
} catch (e) {
|
|
@@ -22830,8 +23062,8 @@ async function hasGitIdentity(cwd) {
|
|
|
22830
23062
|
return !!email.out && !!name.out;
|
|
22831
23063
|
}
|
|
22832
23064
|
async function setGitIdentity(identity) {
|
|
22833
|
-
await ok(git(
|
|
22834
|
-
await ok(git(
|
|
23065
|
+
await ok(git(homedir8(), "config", "--global", "user.name", identity.name.trim()), "git config");
|
|
23066
|
+
await ok(git(homedir8(), "config", "--global", "user.email", identity.email.trim()), "git config");
|
|
22835
23067
|
}
|
|
22836
23068
|
async function requireGitIdentity(cwd) {
|
|
22837
23069
|
if (!await hasGitIdentity(cwd)) {
|
|
@@ -22845,8 +23077,8 @@ async function ok(p, what) {
|
|
|
22845
23077
|
}
|
|
22846
23078
|
|
|
22847
23079
|
// ../daemon/src/repos/watcher.ts
|
|
22848
|
-
import { existsSync as
|
|
22849
|
-
import { basename as
|
|
23080
|
+
import { existsSync as existsSync19, watch } from "fs";
|
|
23081
|
+
import { basename as basename4, join as join16 } from "path";
|
|
22850
23082
|
function watchDefaultBranch(repoPath, branch, onMove) {
|
|
22851
23083
|
const last = { local: null, upstream: null };
|
|
22852
23084
|
const read = async () => {
|
|
@@ -22899,29 +23131,29 @@ function watchDefaultBranch(repoPath, branch, onMove) {
|
|
|
22899
23131
|
]);
|
|
22900
23132
|
if (stopped)
|
|
22901
23133
|
return;
|
|
22902
|
-
const gitDir = common.ok && common.out ? common.out :
|
|
23134
|
+
const gitDir = common.ok && common.out ? common.out : join16(repoPath, ".git");
|
|
22903
23135
|
tryWatch(gitDir);
|
|
22904
|
-
tryWatch(
|
|
23136
|
+
tryWatch(join16(gitDir, "refs", "heads"));
|
|
22905
23137
|
if (!remote.ok || !remote.out)
|
|
22906
23138
|
return;
|
|
22907
|
-
const remotes =
|
|
22908
|
-
const mine =
|
|
23139
|
+
const remotes = join16(gitDir, "refs", "remotes");
|
|
23140
|
+
const mine = join16(remotes, remote.out);
|
|
22909
23141
|
let inner = null;
|
|
22910
23142
|
let mid = null;
|
|
22911
23143
|
const armInner = () => {
|
|
22912
23144
|
inner?.close();
|
|
22913
|
-
inner =
|
|
23145
|
+
inner = existsSync19(mine) ? tryWatch(mine) : null;
|
|
22914
23146
|
};
|
|
22915
23147
|
const armMid = () => {
|
|
22916
23148
|
mid?.close();
|
|
22917
|
-
mid =
|
|
23149
|
+
mid = existsSync19(remotes) ? tryWatch(remotes, (_event, filename) => {
|
|
22918
23150
|
if (!filename || filename === remote.out)
|
|
22919
23151
|
armInner();
|
|
22920
23152
|
schedule();
|
|
22921
23153
|
}) : null;
|
|
22922
23154
|
armInner();
|
|
22923
23155
|
};
|
|
22924
|
-
tryWatch(
|
|
23156
|
+
tryWatch(join16(gitDir, "refs"), (_event, filename) => {
|
|
22925
23157
|
if (!filename || filename === "remotes")
|
|
22926
23158
|
armMid();
|
|
22927
23159
|
schedule();
|
|
@@ -22950,7 +23182,7 @@ function watchWorktreeDir(repoPath, onChange) {
|
|
|
22950
23182
|
}, 300);
|
|
22951
23183
|
};
|
|
22952
23184
|
const arm = (commonDir) => {
|
|
22953
|
-
const dir =
|
|
23185
|
+
const dir = join16(commonDir, "worktrees");
|
|
22954
23186
|
const armInner = () => {
|
|
22955
23187
|
inner?.close();
|
|
22956
23188
|
inner = null;
|
|
@@ -22992,8 +23224,8 @@ function watchWorktreeDir(repoPath, onChange) {
|
|
|
22992
23224
|
}
|
|
22993
23225
|
function watchConfigFile(repoPath, onChange) {
|
|
22994
23226
|
const atRoot = new Set([CONFIG_FILES.root.shared, CONFIG_FILES.root.local, CONFIG_DIR]);
|
|
22995
|
-
const inDir = new Set([CONFIG_FILES.folder.shared, CONFIG_FILES.folder.local].map((rel) =>
|
|
22996
|
-
const dir =
|
|
23227
|
+
const inDir = new Set([CONFIG_FILES.folder.shared, CONFIG_FILES.folder.local].map((rel) => basename4(rel)));
|
|
23228
|
+
const dir = join16(repoPath, CONFIG_DIR);
|
|
22997
23229
|
let timer = null;
|
|
22998
23230
|
let outer = null;
|
|
22999
23231
|
let inner = null;
|
|
@@ -23008,7 +23240,7 @@ function watchConfigFile(repoPath, onChange) {
|
|
|
23008
23240
|
const armInner = () => {
|
|
23009
23241
|
inner?.close();
|
|
23010
23242
|
inner = null;
|
|
23011
|
-
if (!
|
|
23243
|
+
if (!existsSync19(dir))
|
|
23012
23244
|
return;
|
|
23013
23245
|
try {
|
|
23014
23246
|
inner = watch(dir, (_event, filename) => {
|
|
@@ -23062,7 +23294,7 @@ class RepoRegistry {
|
|
|
23062
23294
|
}
|
|
23063
23295
|
async boot() {
|
|
23064
23296
|
const { state } = this.d;
|
|
23065
|
-
const gone = state.worktrees.filter((wt) => !
|
|
23297
|
+
const gone = state.worktrees.filter((wt) => !existsSync20(wt.path) || !state.repos.some((r) => r.id === wt.repoId));
|
|
23066
23298
|
for (const wt of gone)
|
|
23067
23299
|
await this.d.worktrees.forgetGone(wt);
|
|
23068
23300
|
for (const repo of state.repos) {
|
|
@@ -23107,7 +23339,7 @@ class RepoRegistry {
|
|
|
23107
23339
|
}
|
|
23108
23340
|
}
|
|
23109
23341
|
gitIdentity() {
|
|
23110
|
-
this.identity ??= hasGitIdentity(
|
|
23342
|
+
this.identity ??= hasGitIdentity(homedir9());
|
|
23111
23343
|
return this.identity;
|
|
23112
23344
|
}
|
|
23113
23345
|
async unmake(repoId) {
|
|
@@ -23130,7 +23362,7 @@ class RepoRegistry {
|
|
|
23130
23362
|
return `${repo.name} is set up now`;
|
|
23131
23363
|
if (main.promptedAt || main.lastTurn)
|
|
23132
23364
|
return `${repo.name} has a chat now`;
|
|
23133
|
-
if (
|
|
23365
|
+
if (existsSync20(join17(repo.path, ".claude")))
|
|
23134
23366
|
return `an agent has already run in ${repo.name}`;
|
|
23135
23367
|
if ((await statusFiles(repo.path)).length > 0 || !await treeEmpty(repo.path)) {
|
|
23136
23368
|
return `${repo.name} has files in it now`;
|
|
@@ -23207,7 +23439,7 @@ class RepoRegistry {
|
|
|
23207
23439
|
}
|
|
23208
23440
|
async register(rawPath, made) {
|
|
23209
23441
|
const path = expandTilde(rawPath);
|
|
23210
|
-
if (!
|
|
23442
|
+
if (!existsSync20(path))
|
|
23211
23443
|
throw new UserError(`${path} does not exist`);
|
|
23212
23444
|
if (!await isGitRepo(path))
|
|
23213
23445
|
throw new UserError(`${path} is not a git repository`);
|
|
@@ -23221,7 +23453,7 @@ class RepoRegistry {
|
|
|
23221
23453
|
const repo = {
|
|
23222
23454
|
id: shortId(),
|
|
23223
23455
|
path: root,
|
|
23224
|
-
name:
|
|
23456
|
+
name: basename5(root),
|
|
23225
23457
|
defaultBranch: await defaultBranch(root),
|
|
23226
23458
|
config: detected.config,
|
|
23227
23459
|
configFile: configTarget(root, !!made),
|
|
@@ -23281,10 +23513,10 @@ class RepoRegistry {
|
|
|
23281
23513
|
throw new UserError(current.reason);
|
|
23282
23514
|
const was = configTarget(repo.path, !!repo.made);
|
|
23283
23515
|
const rel = configSibling(was, kind);
|
|
23284
|
-
const moved = rel !== was &&
|
|
23516
|
+
const moved = rel !== was && existsSync20(join17(repo.path, was));
|
|
23285
23517
|
if (moved && !(kind === "local" && await isTracked(repo.path, was))) {
|
|
23286
23518
|
try {
|
|
23287
|
-
rmSync2(
|
|
23519
|
+
rmSync2(join17(repo.path, was));
|
|
23288
23520
|
} catch (e) {
|
|
23289
23521
|
log.warn(repoId, `could not remove ${was}`, e);
|
|
23290
23522
|
}
|
|
@@ -23293,9 +23525,9 @@ class RepoRegistry {
|
|
|
23293
23525
|
}
|
|
23294
23526
|
if (isLocalConfigFile(rel))
|
|
23295
23527
|
await excludeFromGit(repo.path, rel);
|
|
23296
|
-
const file =
|
|
23528
|
+
const file = join17(repo.path, rel);
|
|
23297
23529
|
try {
|
|
23298
|
-
mkdirSync6(
|
|
23530
|
+
mkdirSync6(dirname7(file), { recursive: true });
|
|
23299
23531
|
writeFileSync7(file, configText(repo.path, rel, config));
|
|
23300
23532
|
} catch (e) {
|
|
23301
23533
|
log.warn(repoId, `could not write ${rel}`, e);
|
|
@@ -23397,7 +23629,7 @@ class RepoRegistry {
|
|
|
23397
23629
|
}
|
|
23398
23630
|
placeConfig(repo) {
|
|
23399
23631
|
const rel = configTarget(repo.path, !!repo.made);
|
|
23400
|
-
if (isLocalConfigFile(rel) &&
|
|
23632
|
+
if (isLocalConfigFile(rel) && existsSync20(join17(repo.path, rel))) {
|
|
23401
23633
|
fireAndForget(repo.id, excludeFromGit(repo.path, rel), "exclude local settings");
|
|
23402
23634
|
}
|
|
23403
23635
|
if (repo.configFile === rel)
|
|
@@ -23489,7 +23721,7 @@ class RepoRegistry {
|
|
|
23489
23721
|
}
|
|
23490
23722
|
|
|
23491
23723
|
// ../daemon/src/routes/service.ts
|
|
23492
|
-
import { join as
|
|
23724
|
+
import { join as join18 } from "path";
|
|
23493
23725
|
|
|
23494
23726
|
// ../daemon/src/routes/fileRouters.ts
|
|
23495
23727
|
var SKIP = /(^|\/)(node_modules|dist|build|out|coverage|\.next|\.nuxt|\.output|\.svelte-kit|\.vercel|\.netlify)\//;
|
|
@@ -24689,7 +24921,7 @@ class RouteService {
|
|
|
24689
24921
|
return mergeRoutes(fileRoutes(paths, roots), code);
|
|
24690
24922
|
}
|
|
24691
24923
|
async cachedRead(root, path, had, kept, decode) {
|
|
24692
|
-
const abs =
|
|
24924
|
+
const abs = join18(root, path);
|
|
24693
24925
|
const stat = await this.fs.stat(abs);
|
|
24694
24926
|
if (!stat)
|
|
24695
24927
|
return null;
|
|
@@ -24714,7 +24946,7 @@ class RouteService {
|
|
|
24714
24946
|
}
|
|
24715
24947
|
}
|
|
24716
24948
|
async hash(root, file) {
|
|
24717
|
-
const abs =
|
|
24949
|
+
const abs = join18(root, file);
|
|
24718
24950
|
const stat = await this.fs.stat(abs);
|
|
24719
24951
|
if (!stat)
|
|
24720
24952
|
return null;
|
|
@@ -24976,7 +25208,7 @@ function isAlive(pid) {
|
|
|
24976
25208
|
}
|
|
24977
25209
|
|
|
24978
25210
|
// ../daemon/src/runtime/bridge-script.ts
|
|
24979
|
-
import { existsSync as
|
|
25211
|
+
import { existsSync as existsSync21, readFileSync as readFileSync13, statSync as statSync8 } from "fs";
|
|
24980
25212
|
var LEARNED_MAX = 8;
|
|
24981
25213
|
|
|
24982
25214
|
class BridgeScript {
|
|
@@ -25006,9 +25238,9 @@ class BridgeScript {
|
|
|
25006
25238
|
this.cache = null;
|
|
25007
25239
|
}
|
|
25008
25240
|
get() {
|
|
25009
|
-
if (!
|
|
25241
|
+
if (!existsSync21(this.path))
|
|
25010
25242
|
return "// toyon bridge not built";
|
|
25011
|
-
const s =
|
|
25243
|
+
const s = statSync8(this.path);
|
|
25012
25244
|
const stamp = `${s.mtimeMs}:${s.size}`;
|
|
25013
25245
|
if (this.cache && stamp === this.stamp)
|
|
25014
25246
|
return this.cache;
|
|
@@ -25033,7 +25265,7 @@ function servedLocally(origin) {
|
|
|
25033
25265
|
}
|
|
25034
25266
|
|
|
25035
25267
|
// ../daemon/src/server/ws.ts
|
|
25036
|
-
import { homedir as
|
|
25268
|
+
import { homedir as homedir11 } from "os";
|
|
25037
25269
|
|
|
25038
25270
|
// ../daemon/src/core/pair.ts
|
|
25039
25271
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
@@ -25056,47 +25288,47 @@ class PairCodes {
|
|
|
25056
25288
|
}
|
|
25057
25289
|
|
|
25058
25290
|
// ../daemon/src/agent/config.ts
|
|
25059
|
-
import { existsSync as
|
|
25060
|
-
import { homedir as
|
|
25061
|
-
import { join as
|
|
25062
|
-
function agentConfigFiles(spec, repoPath, home =
|
|
25291
|
+
import { existsSync as existsSync22, readFileSync as readFileSync14 } from "fs";
|
|
25292
|
+
import { homedir as homedir10 } from "os";
|
|
25293
|
+
import { join as join19 } from "path";
|
|
25294
|
+
function agentConfigFiles(spec, repoPath, home = homedir10()) {
|
|
25063
25295
|
const file = (id, label, path) => ({
|
|
25064
25296
|
id,
|
|
25065
25297
|
label,
|
|
25066
25298
|
path,
|
|
25067
|
-
exists:
|
|
25299
|
+
exists: existsSync22(path)
|
|
25068
25300
|
});
|
|
25069
25301
|
if (spec.id === "claude") {
|
|
25070
25302
|
return [
|
|
25071
|
-
file("user-config", "user config (MCP servers, projects)",
|
|
25072
|
-
file("user-settings", "user settings (permissions, hooks)",
|
|
25073
|
-
...repoPath ? [file("project-mcp", "project MCP servers",
|
|
25074
|
-
...repoPath ? [file("project-settings", "project settings",
|
|
25303
|
+
file("user-config", "user config (MCP servers, projects)", join19(home, ".claude.json")),
|
|
25304
|
+
file("user-settings", "user settings (permissions, hooks)", join19(home, ".claude", "settings.json")),
|
|
25305
|
+
...repoPath ? [file("project-mcp", "project MCP servers", join19(repoPath, ".mcp.json"))] : [],
|
|
25306
|
+
...repoPath ? [file("project-settings", "project settings", join19(repoPath, ".claude", "settings.json"))] : []
|
|
25075
25307
|
];
|
|
25076
25308
|
}
|
|
25077
25309
|
if (spec.id === "codex")
|
|
25078
|
-
return [file("user-config", "config",
|
|
25310
|
+
return [file("user-config", "config", join19(home, ".codex", "config.toml"))];
|
|
25079
25311
|
return [];
|
|
25080
25312
|
}
|
|
25081
|
-
function describeAgentConfig(spec, repoPath, home =
|
|
25313
|
+
function describeAgentConfig(spec, repoPath, home = homedir10()) {
|
|
25082
25314
|
const files = agentConfigFiles(spec, repoPath, home);
|
|
25083
25315
|
const servers = [];
|
|
25084
25316
|
if (spec.id === "claude") {
|
|
25085
|
-
const user = readJson(
|
|
25317
|
+
const user = readJson(join19(home, ".claude.json"));
|
|
25086
25318
|
servers.push(...serversOf(user?.mcpServers, "user"));
|
|
25087
25319
|
if (repoPath) {
|
|
25088
25320
|
const projects = user?.projects;
|
|
25089
25321
|
const mine = projects?.[repoPath];
|
|
25090
25322
|
servers.push(...serversOf(mine?.mcpServers, "local"));
|
|
25091
|
-
servers.push(...serversOf(readJson(
|
|
25323
|
+
servers.push(...serversOf(readJson(join19(repoPath, ".mcp.json"))?.mcpServers, "project"));
|
|
25092
25324
|
}
|
|
25093
25325
|
} else if (spec.id === "codex") {
|
|
25094
|
-
servers.push(...codexServers(
|
|
25326
|
+
servers.push(...codexServers(join19(home, ".codex", "config.toml")));
|
|
25095
25327
|
}
|
|
25096
25328
|
return { agent: spec.id, files, servers };
|
|
25097
25329
|
}
|
|
25098
25330
|
function readJson(path) {
|
|
25099
|
-
if (!
|
|
25331
|
+
if (!existsSync22(path))
|
|
25100
25332
|
return null;
|
|
25101
25333
|
try {
|
|
25102
25334
|
const v = JSON.parse(readFileSync14(path, "utf8"));
|
|
@@ -25117,7 +25349,7 @@ function serversOf(raw, scope) {
|
|
|
25117
25349
|
});
|
|
25118
25350
|
}
|
|
25119
25351
|
function codexServers(path) {
|
|
25120
|
-
if (!
|
|
25352
|
+
if (!existsSync22(path))
|
|
25121
25353
|
return [];
|
|
25122
25354
|
const out = [];
|
|
25123
25355
|
let current = null;
|
|
@@ -25883,8 +26115,8 @@ var handlers = {
|
|
|
25883
26115
|
s.afterLand.run(msg.repoId);
|
|
25884
26116
|
ctx.reply({ t: "self", self: s.self.get() });
|
|
25885
26117
|
},
|
|
25886
|
-
"restart-daemon"(_msg, _ctx, s) {
|
|
25887
|
-
const refused = s.restarter.request();
|
|
26118
|
+
async "restart-daemon"(_msg, _ctx, s) {
|
|
26119
|
+
const refused = await s.restarter.request();
|
|
25888
26120
|
if (refused)
|
|
25889
26121
|
throw new UserError(refused);
|
|
25890
26122
|
},
|
|
@@ -25945,10 +26177,14 @@ var handlers = {
|
|
|
25945
26177
|
reveal(msg, _ctx, s) {
|
|
25946
26178
|
s.files.reveal(msg.worktreeId, msg.path);
|
|
25947
26179
|
},
|
|
25948
|
-
async "discard-
|
|
25949
|
-
|
|
25950
|
-
|
|
25951
|
-
|
|
26180
|
+
async "discard-files"(msg, _ctx, s) {
|
|
26181
|
+
try {
|
|
26182
|
+
for (const path of msg.paths)
|
|
26183
|
+
await s.files.discard(msg.worktreeId, path);
|
|
26184
|
+
} finally {
|
|
26185
|
+
s.hub.emit("filesChanged", msg.worktreeId);
|
|
26186
|
+
s.landing.recheck(msg.worktreeId);
|
|
26187
|
+
}
|
|
25952
26188
|
},
|
|
25953
26189
|
async judge(msg, _ctx, s) {
|
|
25954
26190
|
await s.landing.judge(msg.worktreeId, msg.note);
|
|
@@ -26168,8 +26404,8 @@ async function dispatch(msg, ctx, s) {
|
|
|
26168
26404
|
}
|
|
26169
26405
|
|
|
26170
26406
|
// ../daemon/src/server/http.ts
|
|
26171
|
-
import { existsSync as
|
|
26172
|
-
import { join as
|
|
26407
|
+
import { existsSync as existsSync23 } from "fs";
|
|
26408
|
+
import { join as join20 } from "path";
|
|
26173
26409
|
var IMMUTABLE = "private, max-age=31536000, immutable";
|
|
26174
26410
|
var NO_STORE = "no-store";
|
|
26175
26411
|
function createFetch(opts) {
|
|
@@ -26245,7 +26481,7 @@ function createFetch(opts) {
|
|
|
26245
26481
|
return new Response("unauthorized", { status: 401 });
|
|
26246
26482
|
if (req.method === "GET")
|
|
26247
26483
|
return Response.json(opts.restartWait(), { headers: { "cache-control": NO_STORE } });
|
|
26248
|
-
const refused = opts.restart(url.searchParams.has(RESTART_NOW));
|
|
26484
|
+
const refused = await opts.restart(url.searchParams.has(RESTART_NOW));
|
|
26249
26485
|
return refused ? new Response(refused, { status: 409 }) : new Response(null, { status: 202 });
|
|
26250
26486
|
}
|
|
26251
26487
|
if (url.pathname === "/pair" && req.method === "POST") {
|
|
@@ -26312,7 +26548,7 @@ function createFetch(opts) {
|
|
|
26312
26548
|
const places = worktreeId && file && !extra ? [opts.attachments.fileFor(worktreeId, file), opts.archivedAttachment(worktreeId, file)] : [];
|
|
26313
26549
|
if (worktreeId && file)
|
|
26314
26550
|
await opts.attachments.whenWritten(worktreeId, file);
|
|
26315
|
-
const path = places.find((p) => !!p &&
|
|
26551
|
+
const path = places.find((p) => !!p && existsSync23(p));
|
|
26316
26552
|
if (!path)
|
|
26317
26553
|
return new Response("not found", { status: 404 });
|
|
26318
26554
|
return new Response(Bun.file(path), { headers: { "cache-control": IMMUTABLE } });
|
|
@@ -26343,15 +26579,15 @@ function createFetch(opts) {
|
|
|
26343
26579
|
});
|
|
26344
26580
|
}
|
|
26345
26581
|
const rel = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
26346
|
-
const file =
|
|
26582
|
+
const file = join20(opts.shellDist, rel.replaceAll("..", ""));
|
|
26347
26583
|
const hashed = rel.startsWith("/assets/");
|
|
26348
|
-
if (
|
|
26584
|
+
if (existsSync23(file) && Bun.file(file).size > 0) {
|
|
26349
26585
|
return new Response(Bun.file(file), { headers: { "cache-control": hashed ? IMMUTABLE : NO_STORE } });
|
|
26350
26586
|
}
|
|
26351
26587
|
if (hashed)
|
|
26352
26588
|
return new Response("not found", { status: 404 });
|
|
26353
|
-
const index =
|
|
26354
|
-
if (
|
|
26589
|
+
const index = join20(opts.shellDist, "index.html");
|
|
26590
|
+
if (existsSync23(index))
|
|
26355
26591
|
return new Response(Bun.file(index), { headers: { "cache-control": NO_STORE } });
|
|
26356
26592
|
return new Response("Toyon daemon running; shell not built (run: bun run build)", { status: 200 });
|
|
26357
26593
|
};
|
|
@@ -26592,6 +26828,7 @@ function startServer(opts) {
|
|
|
26592
26828
|
t: "hello",
|
|
26593
26829
|
version,
|
|
26594
26830
|
install: s.update.install(),
|
|
26831
|
+
registry: await s.update.registry(),
|
|
26595
26832
|
protocol: PROTOCOL_VERSION,
|
|
26596
26833
|
repos: s.state.repos,
|
|
26597
26834
|
rows: await s.worktrees.rows({ quick: true }),
|
|
@@ -26601,7 +26838,7 @@ function startServer(opts) {
|
|
|
26601
26838
|
agents: agentInfos(),
|
|
26602
26839
|
defaultAgent: s.agents.defaultId(s.state.defaultAgent),
|
|
26603
26840
|
agentChosen: s.state.defaultAgent !== undefined,
|
|
26604
|
-
home:
|
|
26841
|
+
home: homedir11(),
|
|
26605
26842
|
folderDialog: process.platform === "darwin" && !cloud.enabled,
|
|
26606
26843
|
remote: opts.remote && { host: opts.remote.host, previews: opts.remote.previews },
|
|
26607
26844
|
paired: s.state.paired,
|
|
@@ -26752,16 +26989,16 @@ function startServer(opts) {
|
|
|
26752
26989
|
}
|
|
26753
26990
|
|
|
26754
26991
|
// ../daemon/src/themes/store.ts
|
|
26755
|
-
import { existsSync as
|
|
26756
|
-
import { homedir as
|
|
26757
|
-
import { basename as
|
|
26758
|
-
var home =
|
|
26992
|
+
import { existsSync as existsSync24, readdirSync as readdirSync6, readFileSync as readFileSync15, statSync as statSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
26993
|
+
import { homedir as homedir12 } from "os";
|
|
26994
|
+
import { basename as basename6, dirname as dirname8, join as join21, resolve as resolve9 } from "path";
|
|
26995
|
+
var home = homedir12();
|
|
26759
26996
|
var defaultExtensionDirs = [
|
|
26760
|
-
|
|
26761
|
-
|
|
26762
|
-
|
|
26763
|
-
|
|
26764
|
-
|
|
26997
|
+
join21(home, ".vscode", "extensions"),
|
|
26998
|
+
join21(home, ".vscode-insiders", "extensions"),
|
|
26999
|
+
join21(home, ".vscode-oss", "extensions"),
|
|
27000
|
+
join21(home, ".cursor", "extensions"),
|
|
27001
|
+
join21(home, ".windsurf", "extensions"),
|
|
26765
27002
|
"/Applications/Visual Studio Code.app/Contents/Resources/app/extensions",
|
|
26766
27003
|
"/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/extensions",
|
|
26767
27004
|
"/Applications/Cursor.app/Contents/Resources/app/extensions",
|
|
@@ -26839,13 +27076,13 @@ class ThemeStore {
|
|
|
26839
27076
|
if (json && typeof json === "object" && typeof json.include === "string") {
|
|
26840
27077
|
throw new ThemeImportError("this theme file uses `include`; drop the whole extension folder's theme into ~/.toyon/themes or install it in VS Code and rescan");
|
|
26841
27078
|
}
|
|
26842
|
-
const base = slug(
|
|
27079
|
+
const base = slug(basename6(name).replace(/\.(json|jsonc)$/i, ""));
|
|
26843
27080
|
const theme = vscodeToTheme(json, {
|
|
26844
27081
|
id: `file:${base}`,
|
|
26845
27082
|
name: json?.name ?? base,
|
|
26846
27083
|
source: "file"
|
|
26847
27084
|
});
|
|
26848
|
-
writeFileSync8(
|
|
27085
|
+
writeFileSync8(join21(this.themesDir, `${base}.json`), `${JSON.stringify(theme, null, 2)}
|
|
26849
27086
|
`);
|
|
26850
27087
|
this.load();
|
|
26851
27088
|
return theme;
|
|
@@ -26860,7 +27097,7 @@ function readVscodeTheme(file, depth = 0) {
|
|
|
26860
27097
|
throw new ThemeImportError(`${file}: not a JSON object`);
|
|
26861
27098
|
const t = json;
|
|
26862
27099
|
if (typeof t.include === "string" && depth < 5) {
|
|
26863
|
-
const parent = readVscodeTheme(resolve9(
|
|
27100
|
+
const parent = readVscodeTheme(resolve9(dirname8(file), t.include), depth + 1);
|
|
26864
27101
|
return {
|
|
26865
27102
|
...parent,
|
|
26866
27103
|
...t,
|
|
@@ -26874,13 +27111,13 @@ function readVscodeTheme(file, depth = 0) {
|
|
|
26874
27111
|
return t;
|
|
26875
27112
|
}
|
|
26876
27113
|
function loadDir(dir) {
|
|
26877
|
-
if (!
|
|
27114
|
+
if (!existsSync24(dir))
|
|
26878
27115
|
return [];
|
|
26879
27116
|
const out = [];
|
|
26880
27117
|
for (const f of readdirSync6(dir)) {
|
|
26881
27118
|
if (!/\.jsonc?$/i.test(f))
|
|
26882
27119
|
continue;
|
|
26883
|
-
const file =
|
|
27120
|
+
const file = join21(dir, f);
|
|
26884
27121
|
const id = `file:${slug(f.replace(/\.(json|jsonc)$/i, ""))}`;
|
|
26885
27122
|
try {
|
|
26886
27123
|
const json = readJsonc(file);
|
|
@@ -26896,7 +27133,7 @@ function loadDir(dir) {
|
|
|
26896
27133
|
return out;
|
|
26897
27134
|
}
|
|
26898
27135
|
function discover(dir) {
|
|
26899
|
-
if (!
|
|
27136
|
+
if (!existsSync24(dir))
|
|
26900
27137
|
return [];
|
|
26901
27138
|
const out = [];
|
|
26902
27139
|
let entries;
|
|
@@ -26906,10 +27143,10 @@ function discover(dir) {
|
|
|
26906
27143
|
return out;
|
|
26907
27144
|
}
|
|
26908
27145
|
for (const name of entries) {
|
|
26909
|
-
const extDir =
|
|
26910
|
-
const pkgFile =
|
|
27146
|
+
const extDir = join21(dir, name);
|
|
27147
|
+
const pkgFile = join21(extDir, "package.json");
|
|
26911
27148
|
try {
|
|
26912
|
-
if (!
|
|
27149
|
+
if (!statSync9(extDir).isDirectory() || !existsSync24(pkgFile))
|
|
26913
27150
|
continue;
|
|
26914
27151
|
const pkg = JSON.parse(readFileSync15(pkgFile, "utf8"));
|
|
26915
27152
|
const themes = pkg.contributes?.themes;
|
|
@@ -26917,8 +27154,8 @@ function discover(dir) {
|
|
|
26917
27154
|
continue;
|
|
26918
27155
|
const ext = `${pkg.publisher ?? "local"}.${pkg.name ?? name}`;
|
|
26919
27156
|
let nls = {};
|
|
26920
|
-
const nlsFile =
|
|
26921
|
-
if (
|
|
27157
|
+
const nlsFile = join21(extDir, "package.nls.json");
|
|
27158
|
+
if (existsSync24(nlsFile)) {
|
|
26922
27159
|
try {
|
|
26923
27160
|
nls = JSON.parse(readFileSync15(nlsFile, "utf8"));
|
|
26924
27161
|
} catch {}
|
|
@@ -26945,11 +27182,10 @@ function discover(dir) {
|
|
|
26945
27182
|
}
|
|
26946
27183
|
|
|
26947
27184
|
// ../daemon/src/update/infra.ts
|
|
26948
|
-
import { homedir as
|
|
27185
|
+
import { homedir as homedir13 } from "os";
|
|
26949
27186
|
async function latestVersion() {
|
|
26950
|
-
const
|
|
26951
|
-
const
|
|
26952
|
-
const r = await run("npm", ["view", "toyon", "version", "--json"], homedir11());
|
|
27187
|
+
const registry = await npmRegistry();
|
|
27188
|
+
const r = await run("npm", ["view", "toyon", "version", "--json"], homedir13());
|
|
26953
27189
|
if (!r.ok)
|
|
26954
27190
|
return { version: null, registry };
|
|
26955
27191
|
try {
|
|
@@ -26959,12 +27195,16 @@ async function latestVersion() {
|
|
|
26959
27195
|
return { version: null, registry };
|
|
26960
27196
|
}
|
|
26961
27197
|
}
|
|
27198
|
+
async function npmRegistry() {
|
|
27199
|
+
const config = await run("npm", ["config", "get", "registry"], homedir13());
|
|
27200
|
+
return config.ok && config.out !== "" ? config.out : "the npm registry";
|
|
27201
|
+
}
|
|
26962
27202
|
var LOG_POINTER = /complete log of this run/i;
|
|
26963
27203
|
async function runInstall(command) {
|
|
26964
27204
|
const [cmd, ...args] = command;
|
|
26965
27205
|
if (!cmd)
|
|
26966
27206
|
return { ok: false, line: "nothing to run" };
|
|
26967
|
-
const r = await runLive(cmd, args,
|
|
27207
|
+
const r = await runLive(cmd, args, homedir13());
|
|
26968
27208
|
const lines = `${r.out}
|
|
26969
27209
|
${r.err}`.split(`
|
|
26970
27210
|
`).map((l) => l.trim()).filter((l) => l !== "" && !LOG_POINTER.test(l));
|
|
@@ -26987,12 +27227,16 @@ var CHECK_EVERY_MS = 6 * 60 * 60000;
|
|
|
26987
27227
|
var FIRST_CHECK_MS = 2 * 60000;
|
|
26988
27228
|
var SETTLED_MS = 2 * 60000;
|
|
26989
27229
|
var RETRY_AFTER_MS = 24 * 60 * 60000;
|
|
27230
|
+
var INSTALL_SETTLE_MS = 60000;
|
|
26990
27231
|
|
|
26991
27232
|
class UpdateService {
|
|
26992
27233
|
d;
|
|
26993
27234
|
installed = null;
|
|
26994
27235
|
latest = null;
|
|
26995
27236
|
unreachable = null;
|
|
27237
|
+
registryName = null;
|
|
27238
|
+
seen = null;
|
|
27239
|
+
seenAt = 0;
|
|
26996
27240
|
installing = false;
|
|
26997
27241
|
failed = null;
|
|
26998
27242
|
wanted = false;
|
|
@@ -27026,17 +27270,33 @@ class UpdateService {
|
|
|
27026
27270
|
await this.refresh();
|
|
27027
27271
|
await this.auto();
|
|
27028
27272
|
}
|
|
27029
|
-
async refresh() {
|
|
27273
|
+
async refresh(settled = false) {
|
|
27030
27274
|
const v = await this.d.installed();
|
|
27031
27275
|
if (v === null)
|
|
27032
27276
|
return;
|
|
27277
|
+
if (!settled && v !== this.d.running) {
|
|
27278
|
+
if (v !== this.seen) {
|
|
27279
|
+
this.seen = v;
|
|
27280
|
+
this.seenAt = this.now();
|
|
27281
|
+
return;
|
|
27282
|
+
}
|
|
27283
|
+
if (this.now() - this.seenAt < INSTALL_SETTLE_MS)
|
|
27284
|
+
return;
|
|
27285
|
+
}
|
|
27033
27286
|
this.installed = v === this.d.running ? null : v;
|
|
27034
27287
|
this.announce();
|
|
27035
27288
|
}
|
|
27289
|
+
async registry() {
|
|
27290
|
+
if (this.d.method === "none" || this.d.managedBy)
|
|
27291
|
+
return null;
|
|
27292
|
+
this.registryName ??= await this.d.registry();
|
|
27293
|
+
return this.registryName;
|
|
27294
|
+
}
|
|
27036
27295
|
async check() {
|
|
27037
27296
|
if (this.d.method === "none" || this.d.managedBy)
|
|
27038
27297
|
return;
|
|
27039
27298
|
const answer = await this.d.latest();
|
|
27299
|
+
this.registryName = answer.registry;
|
|
27040
27300
|
if (answer.version === null) {
|
|
27041
27301
|
this.unreachable = answer.registry;
|
|
27042
27302
|
return;
|
|
@@ -27066,8 +27326,9 @@ class UpdateService {
|
|
|
27066
27326
|
await this.check();
|
|
27067
27327
|
if (this.unreachable)
|
|
27068
27328
|
throw new UserError(`Could not reach ${this.unreachable}`);
|
|
27069
|
-
if (this.target() === null)
|
|
27070
|
-
throw new UserError(`Toyon ${this.d.running} is the newest version`);
|
|
27329
|
+
if (this.target() === null) {
|
|
27330
|
+
throw new UserError(`Toyon ${this.d.running} is the newest version ${registryHost(this.registryName)} lists`);
|
|
27331
|
+
}
|
|
27071
27332
|
}
|
|
27072
27333
|
install() {
|
|
27073
27334
|
return this.d.method;
|
|
@@ -27088,7 +27349,12 @@ class UpdateService {
|
|
|
27088
27349
|
};
|
|
27089
27350
|
}
|
|
27090
27351
|
status() {
|
|
27091
|
-
return {
|
|
27352
|
+
return {
|
|
27353
|
+
managedBy: this.d.managedBy,
|
|
27354
|
+
registry: this.d.method === "none" || this.d.managedBy ? null : this.registryName,
|
|
27355
|
+
unreachable: this.unreachable,
|
|
27356
|
+
latest: this.latest
|
|
27357
|
+
};
|
|
27092
27358
|
}
|
|
27093
27359
|
refuseManaged() {
|
|
27094
27360
|
if (this.d.managedBy === "policy")
|
|
@@ -27149,10 +27415,10 @@ class UpdateService {
|
|
|
27149
27415
|
this.announce();
|
|
27150
27416
|
return;
|
|
27151
27417
|
}
|
|
27152
|
-
await this.refresh();
|
|
27418
|
+
await this.refresh(true);
|
|
27153
27419
|
}
|
|
27154
27420
|
this.wanted = false;
|
|
27155
|
-
const refused = this.d.restarter.request();
|
|
27421
|
+
const refused = await this.d.restarter.request();
|
|
27156
27422
|
if (refused) {
|
|
27157
27423
|
this.failed = { version: target ?? this.d.running, line: refused, command: "toyon restart" };
|
|
27158
27424
|
log.warn("update", `restart refused: ${refused}`);
|
|
@@ -27296,16 +27562,16 @@ class BackendShare {
|
|
|
27296
27562
|
|
|
27297
27563
|
// ../daemon/src/worktrees/cache.ts
|
|
27298
27564
|
import { createHash as createHash3 } from "crypto";
|
|
27299
|
-
import { existsSync as
|
|
27565
|
+
import { existsSync as existsSync25, readFileSync as readFileSync16 } from "fs";
|
|
27300
27566
|
import { mkdir as mkdir4, readdir as readdir2, rename, rm as rm3, stat as stat3, utimes, writeFile as writeFile2 } from "fs/promises";
|
|
27301
|
-
import { join as
|
|
27567
|
+
import { join as join22 } from "path";
|
|
27302
27568
|
|
|
27303
27569
|
// ../daemon/src/core/clone.ts
|
|
27304
27570
|
import { mkdir as mkdir3, rm as rm2 } from "fs/promises";
|
|
27305
|
-
import { dirname as
|
|
27571
|
+
import { dirname as dirname9 } from "path";
|
|
27306
27572
|
async function cloneTree(src, dst) {
|
|
27307
27573
|
const started = Date.now();
|
|
27308
|
-
await mkdir3(
|
|
27574
|
+
await mkdir3(dirname9(dst), { recursive: true });
|
|
27309
27575
|
const attempts = [
|
|
27310
27576
|
["clonefile", ["-Rc", src, dst]],
|
|
27311
27577
|
["reflink", ["-R", "--reflink=auto", src, dst]],
|
|
@@ -27313,7 +27579,7 @@ async function cloneTree(src, dst) {
|
|
|
27313
27579
|
];
|
|
27314
27580
|
let error = "";
|
|
27315
27581
|
for (const [how, args] of attempts) {
|
|
27316
|
-
const r = await run("cp", args,
|
|
27582
|
+
const r = await run("cp", args, dirname9(dst));
|
|
27317
27583
|
if (r.ok)
|
|
27318
27584
|
return { ok: true, how, ms: Date.now() - started };
|
|
27319
27585
|
error = r.err.split(`
|
|
@@ -27329,7 +27595,7 @@ var STALE_MS = 30 * 24 * 3600000;
|
|
|
27329
27595
|
var TMP = ".tmp-";
|
|
27330
27596
|
var MANIFEST = "manifest.json";
|
|
27331
27597
|
function defaultTools(dir) {
|
|
27332
|
-
const has = (...files) => files.some((f) =>
|
|
27598
|
+
const has = (...files) => files.some((f) => existsSync25(join22(dir, f)));
|
|
27333
27599
|
const out = [];
|
|
27334
27600
|
if (has("bun.lock", "bun.lockb"))
|
|
27335
27601
|
out.push("bun --version");
|
|
@@ -27342,7 +27608,7 @@ function defaultTools(dir) {
|
|
|
27342
27608
|
return out;
|
|
27343
27609
|
}
|
|
27344
27610
|
function withSidecars(root, rel) {
|
|
27345
|
-
return [rel, ...SIDECARS.map((s) => rel + s)].filter((n) =>
|
|
27611
|
+
return [rel, ...SIDECARS.map((s) => rel + s)].filter((n) => existsSync25(join22(root, n)));
|
|
27346
27612
|
}
|
|
27347
27613
|
var entryName = (base, at) => `${base.slice(0, 12)}-${at.toString(36)}`;
|
|
27348
27614
|
async function listEntries(familyDir) {
|
|
@@ -27357,8 +27623,8 @@ async function listEntries(familyDir) {
|
|
|
27357
27623
|
const m = /^([0-9a-f]{12})-([0-9a-z]+)$/.exec(name);
|
|
27358
27624
|
if (!m?.[1] || !m[2])
|
|
27359
27625
|
continue;
|
|
27360
|
-
const dir =
|
|
27361
|
-
if (!
|
|
27626
|
+
const dir = join22(familyDir, name);
|
|
27627
|
+
if (!existsSync25(join22(dir, MANIFEST)))
|
|
27362
27628
|
continue;
|
|
27363
27629
|
out.push({ dir, base: m[1], at: Number.parseInt(m[2], 36) });
|
|
27364
27630
|
}
|
|
@@ -27388,7 +27654,7 @@ class ArtifactCache {
|
|
|
27388
27654
|
try {
|
|
27389
27655
|
const family = await this.family(wt, repo, policy);
|
|
27390
27656
|
const base = await this.baseCommit(wt, repo);
|
|
27391
|
-
const entries = await listEntries(
|
|
27657
|
+
const entries = await listEntries(join22(this.d.paths.cacheDir, repo.id, family.key));
|
|
27392
27658
|
const entry = entries.find((e) => e.base === base.slice(0, 12)) ?? entries[0];
|
|
27393
27659
|
if (!entry) {
|
|
27394
27660
|
say(`cache: nothing kept for these lockfiles and tools on ${family.platform}`);
|
|
@@ -27396,14 +27662,14 @@ class ArtifactCache {
|
|
|
27396
27662
|
}
|
|
27397
27663
|
const restored = [];
|
|
27398
27664
|
for (const rel of policy.paths) {
|
|
27399
|
-
if (
|
|
27665
|
+
if (existsSync25(join22(wt.path, rel)))
|
|
27400
27666
|
continue;
|
|
27401
27667
|
const names = withSidecars(entry.dir, rel);
|
|
27402
27668
|
if (names.length === 0)
|
|
27403
27669
|
continue;
|
|
27404
27670
|
let whole = true;
|
|
27405
27671
|
for (const name of names) {
|
|
27406
|
-
const r = await (this.d.clone ?? cloneTree)(
|
|
27672
|
+
const r = await (this.d.clone ?? cloneTree)(join22(entry.dir, name), join22(wt.path, name));
|
|
27407
27673
|
if (r.ok)
|
|
27408
27674
|
continue;
|
|
27409
27675
|
say(`cache: could not restore ${name}: ${r.error}`);
|
|
@@ -27437,12 +27703,12 @@ class ArtifactCache {
|
|
|
27437
27703
|
let tmp;
|
|
27438
27704
|
let slot;
|
|
27439
27705
|
try {
|
|
27440
|
-
const present = policy.paths.filter((rel) =>
|
|
27706
|
+
const present = policy.paths.filter((rel) => existsSync25(join22(wt.path, rel)));
|
|
27441
27707
|
if (present.length === 0)
|
|
27442
27708
|
return;
|
|
27443
27709
|
const family = await this.family(wt, repo, policy);
|
|
27444
27710
|
const base = await this.baseCommit(wt, repo);
|
|
27445
|
-
const familyDir =
|
|
27711
|
+
const familyDir = join22(this.d.paths.cacheDir, repo.id, family.key);
|
|
27446
27712
|
const key = `${familyDir}/${base.slice(0, 12)}`;
|
|
27447
27713
|
const kept = (await listEntries(familyDir)).find((e) => e.base === base.slice(0, 12));
|
|
27448
27714
|
if (kept || this.making.has(key)) {
|
|
@@ -27453,21 +27719,21 @@ class ArtifactCache {
|
|
|
27453
27719
|
this.making.add(key);
|
|
27454
27720
|
const at = Date.now();
|
|
27455
27721
|
const name = entryName(base, at);
|
|
27456
|
-
tmp =
|
|
27722
|
+
tmp = join22(familyDir, `${TMP}${name}`);
|
|
27457
27723
|
await mkdir4(tmp, { recursive: true });
|
|
27458
27724
|
let how = "clonefile";
|
|
27459
27725
|
for (const rel of present) {
|
|
27460
27726
|
for (const n of withSidecars(wt.path, rel)) {
|
|
27461
|
-
const r = await (this.d.clone ?? cloneTree)(
|
|
27727
|
+
const r = await (this.d.clone ?? cloneTree)(join22(wt.path, n), join22(tmp, n));
|
|
27462
27728
|
if (!r.ok)
|
|
27463
27729
|
throw new Error(`could not keep ${n}: ${r.error}`);
|
|
27464
27730
|
how = r.how;
|
|
27465
27731
|
}
|
|
27466
27732
|
}
|
|
27467
27733
|
const manifest = { at, base, platform: family.platform, tools: family.tools, paths: present };
|
|
27468
|
-
await writeFile2(
|
|
27734
|
+
await writeFile2(join22(tmp, MANIFEST), `${JSON.stringify(manifest, null, 2)}
|
|
27469
27735
|
`);
|
|
27470
|
-
await rename(tmp,
|
|
27736
|
+
await rename(tmp, join22(familyDir, name));
|
|
27471
27737
|
tmp = undefined;
|
|
27472
27738
|
log.info(worktreeId, `cache: kept ${list(present)} for ${base.slice(0, 7)} via ${how} in ${Date.now() - started}ms`);
|
|
27473
27739
|
await this.prune(familyDir);
|
|
@@ -27495,7 +27761,7 @@ class ArtifactCache {
|
|
|
27495
27761
|
return;
|
|
27496
27762
|
}
|
|
27497
27763
|
for (const repoId of repoIds) {
|
|
27498
|
-
const repoDir =
|
|
27764
|
+
const repoDir = join22(root, repoId);
|
|
27499
27765
|
if (!this.d.state.repo(repoId)) {
|
|
27500
27766
|
await rm3(repoDir, { recursive: true, force: true });
|
|
27501
27767
|
continue;
|
|
@@ -27507,7 +27773,7 @@ class ArtifactCache {
|
|
|
27507
27773
|
continue;
|
|
27508
27774
|
}
|
|
27509
27775
|
for (const family of families) {
|
|
27510
|
-
const familyDir =
|
|
27776
|
+
const familyDir = join22(repoDir, family);
|
|
27511
27777
|
let names;
|
|
27512
27778
|
try {
|
|
27513
27779
|
names = await readdir2(familyDir);
|
|
@@ -27516,7 +27782,7 @@ class ArtifactCache {
|
|
|
27516
27782
|
}
|
|
27517
27783
|
for (const n of names)
|
|
27518
27784
|
if (n.startsWith(TMP))
|
|
27519
|
-
await rm3(
|
|
27785
|
+
await rm3(join22(familyDir, n), { recursive: true, force: true });
|
|
27520
27786
|
const entries = await listEntries(familyDir);
|
|
27521
27787
|
let newest = 0;
|
|
27522
27788
|
for (const e of entries) {
|
|
@@ -27536,7 +27802,7 @@ class ArtifactCache {
|
|
|
27536
27802
|
for (const f of policy.key) {
|
|
27537
27803
|
h.update(`\x00${f}\x00`);
|
|
27538
27804
|
try {
|
|
27539
|
-
h.update(readFileSync16(
|
|
27805
|
+
h.update(readFileSync16(join22(wt.path, f)));
|
|
27540
27806
|
} catch {
|
|
27541
27807
|
h.update("(absent)");
|
|
27542
27808
|
}
|
|
@@ -27602,11 +27868,9 @@ function saidRows(entries) {
|
|
|
27602
27868
|
}
|
|
27603
27869
|
return out;
|
|
27604
27870
|
}
|
|
27605
|
-
function
|
|
27606
|
-
const
|
|
27607
|
-
|
|
27608
|
-
return [];
|
|
27609
|
-
return [...new Set(needle.split(" "))];
|
|
27871
|
+
function needleTerms(query) {
|
|
27872
|
+
const terms = queryTerms(query);
|
|
27873
|
+
return terms.join(" ").length < MIN_QUERY ? [] : terms;
|
|
27610
27874
|
}
|
|
27611
27875
|
function snippet(text, ranges) {
|
|
27612
27876
|
const first = Math.min(...ranges.map(([at]) => at));
|
|
@@ -27622,13 +27886,13 @@ function snippet(text, ranges) {
|
|
|
27622
27886
|
}
|
|
27623
27887
|
return { text: `${head}${text.slice(start, end)}${tail}`, match };
|
|
27624
27888
|
}
|
|
27625
|
-
function searchRows(rows,
|
|
27889
|
+
function searchRows(rows, terms, chat, limit = CHAT_HITS_PER_WORKTREE) {
|
|
27626
27890
|
const hits = [];
|
|
27627
|
-
if (
|
|
27891
|
+
if (terms.length === 0)
|
|
27628
27892
|
return { hits, more: false };
|
|
27629
27893
|
for (let i = rows.length - 1;i >= 0; i--) {
|
|
27630
27894
|
const row = rows[i];
|
|
27631
|
-
const ranges =
|
|
27895
|
+
const ranges = termRanges(row.lower, terms);
|
|
27632
27896
|
if (!ranges)
|
|
27633
27897
|
continue;
|
|
27634
27898
|
if (hits.length === limit)
|
|
@@ -27637,13 +27901,13 @@ function searchRows(rows, words, chat, limit = CHAT_HITS_PER_WORKTREE) {
|
|
|
27637
27901
|
}
|
|
27638
27902
|
return { hits, more: false };
|
|
27639
27903
|
}
|
|
27640
|
-
function
|
|
27904
|
+
function termRanges(lower, terms) {
|
|
27641
27905
|
const ranges = [];
|
|
27642
|
-
for (const
|
|
27643
|
-
const at = lower.indexOf(
|
|
27906
|
+
for (const term of terms) {
|
|
27907
|
+
const at = lower.indexOf(term);
|
|
27644
27908
|
if (at < 0)
|
|
27645
27909
|
return null;
|
|
27646
|
-
ranges.push([at,
|
|
27910
|
+
ranges.push([at, term.length]);
|
|
27647
27911
|
}
|
|
27648
27912
|
return ranges;
|
|
27649
27913
|
}
|
|
@@ -27656,8 +27920,8 @@ class ChatSearch {
|
|
|
27656
27920
|
}
|
|
27657
27921
|
async search(repoId, query) {
|
|
27658
27922
|
this.d.state.requireRepo(repoId);
|
|
27659
|
-
const
|
|
27660
|
-
if (
|
|
27923
|
+
const terms = needleTerms(query);
|
|
27924
|
+
if (terms.length === 0)
|
|
27661
27925
|
return { hits: [], truncated: false };
|
|
27662
27926
|
const chats = [
|
|
27663
27927
|
...this.d.state.worktrees.filter((w) => w.repoId === repoId && w.kind === "worktree").map((w) => ({ id: w.id, archived: false, path: this.d.transcriptPath(w.id) })),
|
|
@@ -27666,7 +27930,7 @@ class ChatSearch {
|
|
|
27666
27930
|
const read = new Set;
|
|
27667
27931
|
const found = await Promise.all(chats.map(async (c) => {
|
|
27668
27932
|
const rows = await this.rowsOf(c.id, c.archived, c.path, read);
|
|
27669
|
-
return searchRows(rows,
|
|
27933
|
+
return searchRows(rows, terms, { worktreeId: c.id, archived: c.archived });
|
|
27670
27934
|
}));
|
|
27671
27935
|
for (const key of this.said.keys())
|
|
27672
27936
|
if (!read.has(key))
|
|
@@ -27946,7 +28210,7 @@ async function recentSubjects(path, defaultBr) {
|
|
|
27946
28210
|
}
|
|
27947
28211
|
|
|
27948
28212
|
// ../daemon/src/worktrees/prs.ts
|
|
27949
|
-
var
|
|
28213
|
+
var POLL_MS3 = 3 * 60000;
|
|
27950
28214
|
|
|
27951
28215
|
class PrService {
|
|
27952
28216
|
d;
|
|
@@ -27955,7 +28219,7 @@ class PrService {
|
|
|
27955
28219
|
constructor(d) {
|
|
27956
28220
|
this.d = d;
|
|
27957
28221
|
d.hub.on("repoTick", (repoId) => this.refreshRepo(repoId));
|
|
27958
|
-
this.timer = setInterval(() => this.refreshAll(), d.everyMs ??
|
|
28222
|
+
this.timer = setInterval(() => this.refreshAll(), d.everyMs ?? POLL_MS3);
|
|
27959
28223
|
this.timer.unref?.();
|
|
27960
28224
|
}
|
|
27961
28225
|
stop() {
|
|
@@ -28057,7 +28321,7 @@ async function mergedBranches(repoPath, defaultBr) {
|
|
|
28057
28321
|
|
|
28058
28322
|
// ../daemon/src/worktrees/discover.ts
|
|
28059
28323
|
import { createHash as createHash4 } from "crypto";
|
|
28060
|
-
import { basename as
|
|
28324
|
+
import { basename as basename7 } from "path";
|
|
28061
28325
|
|
|
28062
28326
|
// ../daemon/src/git/worktrees.ts
|
|
28063
28327
|
function parseWorktreeList(out) {
|
|
@@ -28146,7 +28410,7 @@ function subtractKnown(repoId, listed, known) {
|
|
|
28146
28410
|
rows.push({
|
|
28147
28411
|
id: discoveredId(g.path),
|
|
28148
28412
|
repoId,
|
|
28149
|
-
name: g.branch ??
|
|
28413
|
+
name: g.branch ?? basename7(g.path),
|
|
28150
28414
|
path: g.path,
|
|
28151
28415
|
...g.branch ? { branch: g.branch } : {},
|
|
28152
28416
|
...g.locked ? { locked: true } : {},
|
|
@@ -28308,9 +28572,9 @@ function rankRefs({ refs, merged, prs, query, defaultBranch, rowIdForPath }) {
|
|
|
28308
28572
|
}
|
|
28309
28573
|
|
|
28310
28574
|
// ../daemon/src/worktrees/service.ts
|
|
28311
|
-
import { cpSync, existsSync as
|
|
28575
|
+
import { cpSync, existsSync as existsSync28, readFileSync as readFileSync18, rmSync as rmSync5 } from "fs";
|
|
28312
28576
|
import { rm as rm4 } from "fs/promises";
|
|
28313
|
-
import { dirname as
|
|
28577
|
+
import { dirname as dirname11, join as join25 } from "path";
|
|
28314
28578
|
|
|
28315
28579
|
// ../daemon/src/git/archive.ts
|
|
28316
28580
|
import { rmSync as rmSync3 } from "fs";
|
|
@@ -28463,12 +28727,31 @@ async function requireClean(worktreePath) {
|
|
|
28463
28727
|
}
|
|
28464
28728
|
return null;
|
|
28465
28729
|
}
|
|
28466
|
-
|
|
28730
|
+
function overwritten(text) {
|
|
28731
|
+
const files = [];
|
|
28732
|
+
let listing = false;
|
|
28733
|
+
for (const line of text.split(`
|
|
28734
|
+
`)) {
|
|
28735
|
+
if (/would be overwritten by (merge|checkout):\s*$/.test(line)) {
|
|
28736
|
+
listing = true;
|
|
28737
|
+
} else if (listing && line.startsWith("\t")) {
|
|
28738
|
+
files.push(line.trim());
|
|
28739
|
+
} else {
|
|
28740
|
+
listing = false;
|
|
28741
|
+
}
|
|
28742
|
+
}
|
|
28743
|
+
return files;
|
|
28744
|
+
}
|
|
28745
|
+
function inTheWay(defaultBr, what, files) {
|
|
28746
|
+
const shown = files.length > 3 ? `${files.slice(0, 3).join(", ")} and ${files.length - 3} more` : files.join(", ");
|
|
28747
|
+
return `${defaultBr} has uncommitted files the ${what} would overwrite (${shown}): commit or stash them there first`;
|
|
28748
|
+
}
|
|
28749
|
+
async function requireMainReady(repoPath, defaultBr, clean) {
|
|
28467
28750
|
const current = await git(repoPath, "branch", "--show-current");
|
|
28468
28751
|
if (current.out !== defaultBr) {
|
|
28469
28752
|
return { ok: false, message: `main checkout is on '${current.out}', not ${defaultBr}; switch it first` };
|
|
28470
28753
|
}
|
|
28471
|
-
if ((await statusFiles(repoPath)).length > 0) {
|
|
28754
|
+
if (clean && (await statusFiles(repoPath)).length > 0) {
|
|
28472
28755
|
return { ok: false, message: `${defaultBr} has uncommitted changes: commit or stash them there first` };
|
|
28473
28756
|
}
|
|
28474
28757
|
return null;
|
|
@@ -28508,14 +28791,18 @@ async function landLocally(worktreePath, branch, repoPath, defaultBr, method, me
|
|
|
28508
28791
|
const { ahead } = await aheadBehind(worktreePath, defaultBr);
|
|
28509
28792
|
if (ahead === 0)
|
|
28510
28793
|
return { ok: false, message: `nothing to merge: no commits ahead of ${defaultBr}` };
|
|
28511
|
-
const mErr = await requireMainReady(repoPath, defaultBr);
|
|
28794
|
+
const mErr = await requireMainReady(repoPath, defaultBr, method === "squash");
|
|
28512
28795
|
if (mErr)
|
|
28513
28796
|
return mErr;
|
|
28514
28797
|
if (method === "rebase") {
|
|
28515
28798
|
w.step(`moving ${defaultBr} onto the branch`);
|
|
28516
28799
|
const ff = await w.git(repoPath, ["merge", "--ff-only", branch]);
|
|
28517
|
-
if (!ff.ok)
|
|
28800
|
+
if (!ff.ok) {
|
|
28801
|
+
const files = overwritten(ff.text);
|
|
28802
|
+
if (files.length)
|
|
28803
|
+
return { ok: false, message: inTheWay(defaultBr, "landing", files) };
|
|
28518
28804
|
return { ok: false, message: `${branch} is not a fast-forward of ${defaultBr}: take ${defaultBr} in first` };
|
|
28805
|
+
}
|
|
28519
28806
|
return { ok: true, message: `${defaultBr} moved onto ${branch}` };
|
|
28520
28807
|
}
|
|
28521
28808
|
if (method === "squash") {
|
|
@@ -28534,8 +28821,12 @@ async function landLocally(worktreePath, branch, repoPath, defaultBr, method, me
|
|
|
28534
28821
|
}
|
|
28535
28822
|
w.step(`merging into ${defaultBr}`);
|
|
28536
28823
|
const m = await w.git(repoPath, ["merge", "--no-ff", "--no-edit", branch]);
|
|
28537
|
-
if (!m.ok)
|
|
28824
|
+
if (!m.ok) {
|
|
28825
|
+
const files = overwritten(m.text);
|
|
28826
|
+
if (files.length)
|
|
28827
|
+
return { ok: false, message: inTheWay(defaultBr, "landing", files) };
|
|
28538
28828
|
return mergeFailure(repoPath, m, `merge conflicts with ${defaultBr}: take ${defaultBr} in first`);
|
|
28829
|
+
}
|
|
28539
28830
|
return { ok: true, message: `merged ${branch} into ${defaultBr}` };
|
|
28540
28831
|
}
|
|
28541
28832
|
async function squashMessage(worktreePath, defaultBr, suggested) {
|
|
@@ -28561,22 +28852,11 @@ async function fastForwardFetched(repoPath, defaultBr) {
|
|
|
28561
28852
|
}
|
|
28562
28853
|
if (behind === 0)
|
|
28563
28854
|
return { ok: true, moved: false, message: "already up to date with origin" };
|
|
28564
|
-
if ((await statusFiles(repoPath)).length > 0) {
|
|
28565
|
-
return {
|
|
28566
|
-
ok: false,
|
|
28567
|
-
stale: "dirty",
|
|
28568
|
-
message: `${defaultBr} has uncommitted changes: commit or stash them there first`
|
|
28569
|
-
};
|
|
28570
|
-
}
|
|
28571
28855
|
const m = await git(repoPath, "merge", "--ff-only", "@{upstream}");
|
|
28572
28856
|
if (!m.ok) {
|
|
28573
|
-
const
|
|
28857
|
+
const files = overwritten(`${m.out}
|
|
28574
28858
|
${m.err}`);
|
|
28575
|
-
return
|
|
28576
|
-
ok: false,
|
|
28577
|
-
stale: "dirty",
|
|
28578
|
-
message: `${defaultBr} here has edits the pull would overwrite; commit or stash them first`
|
|
28579
|
-
} : { ok: false, stale: "diverged", message: `${defaultBr} has diverged from origin; reconcile it in a terminal` };
|
|
28859
|
+
return files.length ? { ok: false, stale: "dirty", message: inTheWay(defaultBr, "pull", files) } : { ok: false, stale: "diverged", message: `${defaultBr} has diverged from origin; reconcile it in a terminal` };
|
|
28580
28860
|
}
|
|
28581
28861
|
return { ok: true, moved: true, message: `pulled ${behind} commit(s) from origin` };
|
|
28582
28862
|
}
|
|
@@ -28753,8 +29033,8 @@ function compareUrl(remoteUrl, base, branch) {
|
|
|
28753
29033
|
}
|
|
28754
29034
|
|
|
28755
29035
|
// ../daemon/src/worktrees/archive.ts
|
|
28756
|
-
import { existsSync as
|
|
28757
|
-
import { basename as
|
|
29036
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync7, readdirSync as readdirSync7, readFileSync as readFileSync17, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
29037
|
+
import { basename as basename8, dirname as dirname10, join as join23 } from "path";
|
|
28758
29038
|
class WorktreeArchive {
|
|
28759
29039
|
dir;
|
|
28760
29040
|
records = new Map;
|
|
@@ -28763,7 +29043,7 @@ class WorktreeArchive {
|
|
|
28763
29043
|
mkdirSync7(dir, { recursive: true });
|
|
28764
29044
|
for (const id of readdirSync7(dir)) {
|
|
28765
29045
|
const file = this.filesOf(id).record;
|
|
28766
|
-
if (!
|
|
29046
|
+
if (!existsSync26(file))
|
|
28767
29047
|
continue;
|
|
28768
29048
|
try {
|
|
28769
29049
|
this.records.set(id, JSON.parse(readFileSync17(file, "utf8")));
|
|
@@ -28812,8 +29092,8 @@ class WorktreeArchive {
|
|
|
28812
29092
|
planFile(id, rel) {
|
|
28813
29093
|
if (!this.records.has(id) || !isPlanPath(rel))
|
|
28814
29094
|
return null;
|
|
28815
|
-
const file =
|
|
28816
|
-
if (!
|
|
29095
|
+
const file = join23(this.filesOf(id).plans, basename8(rel));
|
|
29096
|
+
if (!existsSync26(file))
|
|
28817
29097
|
return null;
|
|
28818
29098
|
try {
|
|
28819
29099
|
return readFileSync17(file, "utf8");
|
|
@@ -28823,13 +29103,13 @@ class WorktreeArchive {
|
|
|
28823
29103
|
}
|
|
28824
29104
|
}
|
|
28825
29105
|
filesOf(id) {
|
|
28826
|
-
const dir =
|
|
29106
|
+
const dir = join23(this.dir, id);
|
|
28827
29107
|
return {
|
|
28828
29108
|
dir,
|
|
28829
|
-
record:
|
|
28830
|
-
transcript:
|
|
28831
|
-
attachments:
|
|
28832
|
-
plans:
|
|
29109
|
+
record: join23(dir, "record.json"),
|
|
29110
|
+
transcript: join23(dir, "transcript.jsonl"),
|
|
29111
|
+
attachments: join23(dir, "attachments"),
|
|
29112
|
+
plans: join23(dir, "plans")
|
|
28833
29113
|
};
|
|
28834
29114
|
}
|
|
28835
29115
|
}
|
|
@@ -28848,12 +29128,13 @@ function summarize(r, repoId, transcript) {
|
|
|
28848
29128
|
...r.sessionId ? { sessionId: r.sessionId } : {},
|
|
28849
29129
|
...r.kept?.snapshot ? { uncommitted: true, ...r.kept.dirty ? { dirty: r.kept.dirty } : {} } : {},
|
|
28850
29130
|
...r.worktree.landed ? { landed: true } : {},
|
|
29131
|
+
...r.worktree.lands?.length ? { landedAt: r.worktree.lands[r.worktree.lands.length - 1]?.at } : {},
|
|
28851
29132
|
...r.cost !== undefined ? { cost: r.cost } : {},
|
|
28852
29133
|
...r.auto ? { auto: r.auto } : {}
|
|
28853
29134
|
};
|
|
28854
29135
|
}
|
|
28855
29136
|
function lastUsage(transcript) {
|
|
28856
|
-
if (!
|
|
29137
|
+
if (!existsSync26(transcript))
|
|
28857
29138
|
return null;
|
|
28858
29139
|
const lines = readFileSync17(transcript, "utf8").split(`
|
|
28859
29140
|
`);
|
|
@@ -28869,7 +29150,7 @@ function lastUsage(transcript) {
|
|
|
28869
29150
|
return null;
|
|
28870
29151
|
}
|
|
28871
29152
|
function firstPrompt(transcript) {
|
|
28872
|
-
if (!
|
|
29153
|
+
if (!existsSync26(transcript))
|
|
28873
29154
|
return;
|
|
28874
29155
|
for (const line of readFileSync17(transcript, "utf8").split(`
|
|
28875
29156
|
`)) {
|
|
@@ -28884,9 +29165,9 @@ function firstPrompt(transcript) {
|
|
|
28884
29165
|
return;
|
|
28885
29166
|
}
|
|
28886
29167
|
function move(from, to) {
|
|
28887
|
-
if (!
|
|
29168
|
+
if (!existsSync26(from))
|
|
28888
29169
|
return;
|
|
28889
|
-
mkdirSync7(
|
|
29170
|
+
mkdirSync7(dirname10(to), { recursive: true });
|
|
28890
29171
|
rmSync4(to, { recursive: true, force: true });
|
|
28891
29172
|
renameSync4(from, to);
|
|
28892
29173
|
}
|
|
@@ -28949,8 +29230,8 @@ class ArchivedGit {
|
|
|
28949
29230
|
}
|
|
28950
29231
|
|
|
28951
29232
|
// ../daemon/src/worktrees/spare.ts
|
|
28952
|
-
import { existsSync as
|
|
28953
|
-
import { basename as
|
|
29233
|
+
import { existsSync as existsSync27 } from "fs";
|
|
29234
|
+
import { basename as basename9, join as join24 } from "path";
|
|
28954
29235
|
class SparePool {
|
|
28955
29236
|
d;
|
|
28956
29237
|
inflight = new Map;
|
|
@@ -28969,7 +29250,7 @@ class SparePool {
|
|
|
28969
29250
|
if (!text.trim())
|
|
28970
29251
|
return;
|
|
28971
29252
|
const wt = this.d.state.worktree(boxId);
|
|
28972
|
-
if (wt?.kind !== "spare" || !
|
|
29253
|
+
if (wt?.kind !== "spare" || !existsSync27(wt.path))
|
|
28973
29254
|
return;
|
|
28974
29255
|
const agent = this.d.runtime.ensureAgent(wt).agent;
|
|
28975
29256
|
if (agent.runningAgent === null)
|
|
@@ -29014,11 +29295,11 @@ class SparePool {
|
|
|
29014
29295
|
return this.record(repoId, repo).then((wt) => this.warmUp(wt, repo, {}));
|
|
29015
29296
|
}
|
|
29016
29297
|
async record(repoId, repo) {
|
|
29017
|
-
const { id, dir } = freeSlot(
|
|
29298
|
+
const { id, dir } = freeSlot(join24(this.d.paths.worktreesDir, repo.name));
|
|
29018
29299
|
const wt = {
|
|
29019
29300
|
id,
|
|
29020
29301
|
repoId,
|
|
29021
|
-
path:
|
|
29302
|
+
path: join24(this.d.paths.worktreesDir, repo.name, dir),
|
|
29022
29303
|
branch: repo.defaultBranch,
|
|
29023
29304
|
kind: "spare",
|
|
29024
29305
|
phase: "reserved",
|
|
@@ -29033,7 +29314,7 @@ class SparePool {
|
|
|
29033
29314
|
const repoId = repo.id;
|
|
29034
29315
|
let cut = () => {};
|
|
29035
29316
|
const added = (async () => {
|
|
29036
|
-
if (
|
|
29317
|
+
if (existsSync27(wt.path))
|
|
29037
29318
|
return;
|
|
29038
29319
|
await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "--detach", wt.path, baseOf(repo)));
|
|
29039
29320
|
})();
|
|
@@ -29081,7 +29362,7 @@ class SparePool {
|
|
|
29081
29362
|
}
|
|
29082
29363
|
log.warn(wt.repoId, "spare warm-up failed; rolling back", e);
|
|
29083
29364
|
this.d.carryDraft?.(wt.id, wt.repoId);
|
|
29084
|
-
if (
|
|
29365
|
+
if (existsSync27(wt.path)) {
|
|
29085
29366
|
await this.d.discard(wt.id).catch((re) => log.warn(wt.repoId, "spare rollback failed", re));
|
|
29086
29367
|
return;
|
|
29087
29368
|
}
|
|
@@ -29138,12 +29419,12 @@ class SparePool {
|
|
|
29138
29419
|
const running = this.inflight.get(repoId);
|
|
29139
29420
|
await running?.added.catch(() => {});
|
|
29140
29421
|
running?.cut();
|
|
29141
|
-
if (wt.kind !== "spare" || !
|
|
29422
|
+
if (wt.kind !== "spare" || !existsSync27(wt.path))
|
|
29142
29423
|
return null;
|
|
29143
29424
|
}
|
|
29144
29425
|
await this.refreshing.get(repoId);
|
|
29145
29426
|
const repo = this.d.state.requireRepo(repoId);
|
|
29146
|
-
const branch = `toyon/${
|
|
29427
|
+
const branch = `toyon/${basename9(wt.path)}`;
|
|
29147
29428
|
await withRepoLock(repo.path, () => gitOrThrow(wt.path, "switch", "-c", branch));
|
|
29148
29429
|
wt.kind = "worktree";
|
|
29149
29430
|
delete wt.phase;
|
|
@@ -29526,8 +29807,8 @@ class WorktreeService {
|
|
|
29526
29807
|
if (!await isGitRepo(repo.path)) {
|
|
29527
29808
|
throw new UserError(`git no longer reads ${repo.name} as a checkout, so there is nothing to start a worktree from. If \`git config core.bare\` says true there, \`git config --unset core.bare\` puts it back.`);
|
|
29528
29809
|
}
|
|
29529
|
-
const { id, dir } = freeSlot(
|
|
29530
|
-
const wtPath =
|
|
29810
|
+
const { id, dir } = freeSlot(join25(this.d.paths.worktreesDir, repo.name));
|
|
29811
|
+
const wtPath = join25(this.d.paths.worktreesDir, repo.name, dir);
|
|
29531
29812
|
const branch = `toyon/${dir}`;
|
|
29532
29813
|
await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "--no-track", "-b", branch, wtPath, baseOf(repo)));
|
|
29533
29814
|
const wt = {
|
|
@@ -29593,7 +29874,7 @@ class WorktreeService {
|
|
|
29593
29874
|
};
|
|
29594
29875
|
const carried = await git(repo.path, "ls-tree", "-r", "--name-only", `${sha}^3`);
|
|
29595
29876
|
const restored = (carried.ok ? carried.out.split(`
|
|
29596
|
-
`).filter(Boolean) : []).filter((p) => !
|
|
29877
|
+
`).filter(Boolean) : []).filter((p) => !existsSync28(join25(wt.path, p)));
|
|
29597
29878
|
const applied = await git(wt.path, "stash", "apply", sha);
|
|
29598
29879
|
if (applied.ok) {
|
|
29599
29880
|
const ref = await entry();
|
|
@@ -29629,9 +29910,9 @@ class WorktreeService {
|
|
|
29629
29910
|
const branch = kind === "pr" ? `pr/${number}` : ref;
|
|
29630
29911
|
const title = kind === "pr" ? `pr-${number}` : cleanTitle2(ref) || "branch";
|
|
29631
29912
|
let slug = branchSlug(title) || "branch";
|
|
29632
|
-
if (
|
|
29913
|
+
if (existsSync28(join25(this.d.paths.worktreesDir, repo.name, slug)))
|
|
29633
29914
|
slug = `${slug}-${shortId().slice(0, 4)}`;
|
|
29634
|
-
const wtPath =
|
|
29915
|
+
const wtPath = join25(this.d.paths.worktreesDir, repo.name, slug);
|
|
29635
29916
|
await withRepoLock(repo.path, async () => {
|
|
29636
29917
|
const out = (await listWorktrees(repo.path)).find((w) => w.branch === branch);
|
|
29637
29918
|
if (out)
|
|
@@ -29877,7 +30158,7 @@ class WorktreeService {
|
|
|
29877
30158
|
}
|
|
29878
30159
|
async teardown(wt, repo) {
|
|
29879
30160
|
const cmds = repo.config.teardown ?? [];
|
|
29880
|
-
if (cmds.length === 0 || !
|
|
30161
|
+
if (cmds.length === 0 || !existsSync28(wt.path))
|
|
29881
30162
|
return;
|
|
29882
30163
|
for (const cmd of cmds) {
|
|
29883
30164
|
const code = await runSetup(cmd, wt.path, (line) => this.d.hub.emit("log", wt.id, "setup", line), worktreeEnv(wt, repo), { timeoutMs: timeoutFor(repo.config, "setup") });
|
|
@@ -29886,7 +30167,7 @@ class WorktreeService {
|
|
|
29886
30167
|
}
|
|
29887
30168
|
}
|
|
29888
30169
|
async keep(repo, wt) {
|
|
29889
|
-
const index =
|
|
30170
|
+
const index = join25(this.d.paths.archiveDir, `${wt.id}.index`);
|
|
29890
30171
|
const kept = await keepState(repo.path, wt.path, archiveRef(wt.id), index) ?? await this.keepUnlinked(repo, wt);
|
|
29891
30172
|
if (!kept)
|
|
29892
30173
|
log.warn(wt.id, "could not keep its git state");
|
|
@@ -29918,16 +30199,16 @@ class WorktreeService {
|
|
|
29918
30199
|
throw new Error(`git worktree remove --force ${wt.path} failed (${r.exit}): ${r.err}`);
|
|
29919
30200
|
if (await deleteTree(wt.path))
|
|
29920
30201
|
return;
|
|
29921
|
-
await run("chmod", ["-R", "u+w", wt.path],
|
|
30202
|
+
await run("chmod", ["-R", "u+w", wt.path], dirname11(wt.path));
|
|
29922
30203
|
if (!await deleteTree(wt.path))
|
|
29923
30204
|
log.warn(wt.id, `its directory could not be deleted and stays at ${wt.path}`);
|
|
29924
30205
|
}
|
|
29925
30206
|
plansScratch(worktreeId) {
|
|
29926
|
-
return
|
|
30207
|
+
return join25(this.d.paths.archiveDir, `${worktreeId}.plans`);
|
|
29927
30208
|
}
|
|
29928
30209
|
keepPlans(wt) {
|
|
29929
|
-
const from =
|
|
29930
|
-
if (!
|
|
30210
|
+
const from = join25(wt.path, PLANS_DIR);
|
|
30211
|
+
if (!existsSync28(from))
|
|
29931
30212
|
return;
|
|
29932
30213
|
try {
|
|
29933
30214
|
cpSync(from, this.plansScratch(wt.id), { recursive: true });
|
|
@@ -30015,7 +30296,7 @@ class WorktreeService {
|
|
|
30015
30296
|
}
|
|
30016
30297
|
archivedAttachment(archiveId, file) {
|
|
30017
30298
|
const files = this.archive.chatFiles(archiveId);
|
|
30018
|
-
return files && isAttachmentFile(file) ?
|
|
30299
|
+
return files && isAttachmentFile(file) ? join25(files.attachments, file) : null;
|
|
30019
30300
|
}
|
|
30020
30301
|
archivedGit(archiveId) {
|
|
30021
30302
|
const rec = this.archive.get(archiveId);
|
|
@@ -30058,7 +30339,7 @@ class WorktreeService {
|
|
|
30058
30339
|
throw new UserError(`${old.title} was archived without its commits, so there is nothing to restore`);
|
|
30059
30340
|
if (rec.auto)
|
|
30060
30341
|
log.info(old.id, `restoring a worktree that archived itself (${rec.auto})`);
|
|
30061
|
-
const path =
|
|
30342
|
+
const path = existsSync28(old.path) ? join25(dirname11(old.path), freeSlot(dirname11(old.path)).dir) : old.path;
|
|
30062
30343
|
const branch = await withRepoLock(repo.path, async () => {
|
|
30063
30344
|
if (!await commitOf(repo.path, archiveRef(old.id))) {
|
|
30064
30345
|
throw new UserError(`${old.title}'s kept commits are gone from git`);
|
|
@@ -30095,7 +30376,7 @@ class WorktreeService {
|
|
|
30095
30376
|
...createdBy ? { createdBy } : {}
|
|
30096
30377
|
};
|
|
30097
30378
|
try {
|
|
30098
|
-
this.archive.take(old.id, { ...this.chatFiles(old.id), plans:
|
|
30379
|
+
this.archive.take(old.id, { ...this.chatFiles(old.id), plans: join25(path, PLANS_DIR) });
|
|
30099
30380
|
} catch (e) {
|
|
30100
30381
|
log.warn(old.id, "could not move its chat back out of the archive", e);
|
|
30101
30382
|
}
|
|
@@ -30106,6 +30387,13 @@ class WorktreeService {
|
|
|
30106
30387
|
log.warn(old.id, `could not drop its archive ref: ${dropped.err}`);
|
|
30107
30388
|
this.countsCache.delete(wt.id);
|
|
30108
30389
|
this.launch(wt, repo, repo.path);
|
|
30390
|
+
this.d.runtime.ensureAgent(wt).agent.note({
|
|
30391
|
+
type: "restored",
|
|
30392
|
+
archivedAt: rec.archivedAt,
|
|
30393
|
+
branch,
|
|
30394
|
+
uncommitted: !!kept.snapshot,
|
|
30395
|
+
ts: Date.now()
|
|
30396
|
+
});
|
|
30109
30397
|
if (message)
|
|
30110
30398
|
this.d.runtime.ensureAgent(wt).agent.send(message.text, { attachments: message.attachments });
|
|
30111
30399
|
this.d.hub.emit("archiveChanged", repo.id);
|
|
@@ -30117,12 +30405,12 @@ class WorktreeService {
|
|
|
30117
30405
|
return;
|
|
30118
30406
|
const repo = this.repoOf(rec);
|
|
30119
30407
|
const repoPath = repo?.path ?? rec.repoPath;
|
|
30120
|
-
if (rec.kept &&
|
|
30408
|
+
if (rec.kept && existsSync28(repoPath)) {
|
|
30121
30409
|
const r = await git(repoPath, "update-ref", "-d", archiveRef(archiveId));
|
|
30122
30410
|
if (!r.ok)
|
|
30123
30411
|
log.warn(archiveId, `could not drop its archive ref: ${r.err}`);
|
|
30124
30412
|
}
|
|
30125
|
-
if (rec.worktree.lands?.length &&
|
|
30413
|
+
if (rec.worktree.lands?.length && existsSync28(repoPath))
|
|
30126
30414
|
await dropLandRefs(repoPath, archiveId);
|
|
30127
30415
|
this.archive.delete(archiveId);
|
|
30128
30416
|
this.d.drafts?.drop(archiveId);
|
|
@@ -30222,9 +30510,9 @@ class WorktreeService {
|
|
|
30222
30510
|
}
|
|
30223
30511
|
}
|
|
30224
30512
|
async mirrorBaseIgnore(repo) {
|
|
30225
|
-
const src =
|
|
30226
|
-
const tracked =
|
|
30227
|
-
const mirror =
|
|
30513
|
+
const src = join25(repo.path, ".gitignore");
|
|
30514
|
+
const tracked = existsSync28(src) && (await git(repo.path, "ls-files", "--", ".gitignore")).out !== "";
|
|
30515
|
+
const mirror = existsSync28(src) && !tracked;
|
|
30228
30516
|
const lines = mirror ? readFileSync18(src, "utf8").split(`
|
|
30229
30517
|
`).filter((l) => l.trim() !== "") : [];
|
|
30230
30518
|
await excludeBlock(repo.path, BASE_IGNORE, lines);
|
|
@@ -30237,9 +30525,9 @@ class WorktreeService {
|
|
|
30237
30525
|
await this.d.cache.restore(wt, repo, (line) => this.d.hub.emit("log", wt.id, "setup", line));
|
|
30238
30526
|
run.finish(0);
|
|
30239
30527
|
}
|
|
30240
|
-
const srcNm =
|
|
30241
|
-
const dstNm =
|
|
30242
|
-
if (
|
|
30528
|
+
const srcNm = join25(depsSource, "node_modules");
|
|
30529
|
+
const dstNm = join25(wt.path, "node_modules");
|
|
30530
|
+
if (existsSync28(srcNm) && !existsSync28(dstNm)) {
|
|
30243
30531
|
const r = await cloneTree(srcNm, dstNm);
|
|
30244
30532
|
if (r.ok)
|
|
30245
30533
|
this.d.hub.emit("log", wt.id, "setup", `deps via ${r.how} in ${r.ms}ms`);
|
|
@@ -30247,10 +30535,10 @@ class WorktreeService {
|
|
|
30247
30535
|
log.warn(wt.id, "could not copy node_modules from the base checkout", r.error);
|
|
30248
30536
|
}
|
|
30249
30537
|
for (const f of LOCAL_CONFIG_FILES) {
|
|
30250
|
-
const src =
|
|
30251
|
-
if (!
|
|
30538
|
+
const src = join25(depsSource, f);
|
|
30539
|
+
if (!existsSync28(src) || existsSync28(join25(wt.path, f)))
|
|
30252
30540
|
continue;
|
|
30253
|
-
const r = await run("cp", [src,
|
|
30541
|
+
const r = await run("cp", [src, join25(wt.path, f)], wt.path);
|
|
30254
30542
|
if (r.ok)
|
|
30255
30543
|
this.d.hub.emit("log", wt.id, "setup", `copied ${f}`);
|
|
30256
30544
|
else
|
|
@@ -30719,12 +31007,6 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
30719
31007
|
const repo = this.d.state.requireRepo(wt.repoId);
|
|
30720
31008
|
const w = { step: (name) => this.step(worktreeId, name), git: UNWATCHED.git };
|
|
30721
31009
|
return this.ship(worktreeId, "pull-main", async () => {
|
|
30722
|
-
if ((await statusFiles(repo.path)).length > 0) {
|
|
30723
|
-
return {
|
|
30724
|
-
ok: false,
|
|
30725
|
-
message: `${repo.defaultBranch} has uncommitted changes: commit or stash them there first`
|
|
30726
|
-
};
|
|
30727
|
-
}
|
|
30728
31010
|
const result = await this.trunk.pull(repo.id, w);
|
|
30729
31011
|
if (result.ok) {
|
|
30730
31012
|
this.invalidateCounts();
|
|
@@ -31153,7 +31435,7 @@ class ArchiveSweep {
|
|
|
31153
31435
|
// ../daemon/src/index.ts
|
|
31154
31436
|
process.on("unhandledRejection", (e) => log.error("daemon", "unhandled rejection", e));
|
|
31155
31437
|
process.on("uncaughtException", (e) => log.error("daemon", "uncaught exception", e));
|
|
31156
|
-
var here =
|
|
31438
|
+
var here = dirname12(fileURLToPath(import.meta.url));
|
|
31157
31439
|
var {
|
|
31158
31440
|
shellDist: SHELL_DIST,
|
|
31159
31441
|
bridgeJs: BRIDGE_JS,
|
|
@@ -31291,9 +31573,12 @@ var restarter = new Restarter({
|
|
|
31291
31573
|
hub,
|
|
31292
31574
|
state,
|
|
31293
31575
|
runtime,
|
|
31294
|
-
refusal: () => {
|
|
31576
|
+
refusal: async () => {
|
|
31295
31577
|
const can = restartable();
|
|
31296
|
-
|
|
31578
|
+
if (!can.ok)
|
|
31579
|
+
return can.reason;
|
|
31580
|
+
const runs = await replacementRuns();
|
|
31581
|
+
return runs.ok ? null : runs.reason;
|
|
31297
31582
|
},
|
|
31298
31583
|
go: () => {
|
|
31299
31584
|
setTimeout(() => fireAndForget("daemon", shutdown("restart", { respawn: true }), "restart"), 100);
|
|
@@ -31309,6 +31594,7 @@ var update = new UpdateService({
|
|
|
31309
31594
|
restarter,
|
|
31310
31595
|
installed: async () => installedPackage ? readVersion(installedPackage) : null,
|
|
31311
31596
|
latest: latestVersion,
|
|
31597
|
+
registry: npmRegistry,
|
|
31312
31598
|
command: (version) => installCommand(method, version),
|
|
31313
31599
|
install: runInstall,
|
|
31314
31600
|
busy: () => runtime.anyBusy(),
|
|
@@ -31440,7 +31726,12 @@ async function shutdown(signal, opts = {}) {
|
|
|
31440
31726
|
if (opts.respawn) {
|
|
31441
31727
|
await Bun.sleep(250);
|
|
31442
31728
|
log.info("daemon", "starting the replacement");
|
|
31443
|
-
|
|
31729
|
+
try {
|
|
31730
|
+
respawn(paths.logFile);
|
|
31731
|
+
} catch (e) {
|
|
31732
|
+
log.error("daemon", "the replacement did not start; run `toyon` to start it again", e);
|
|
31733
|
+
process.exit(1);
|
|
31734
|
+
}
|
|
31444
31735
|
}
|
|
31445
31736
|
process.exit(0);
|
|
31446
31737
|
}
|