toyon 0.12.0 → 0.13.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/dist/bridge.js +1 -1
- package/dist/cli.js +19 -25
- package/dist/daemon.js +1048 -821
- package/dist/shell/assets/{Editor-ofBOHWJt.js → Editor-CUcgZuoM.js} +3 -3
- package/dist/shell/assets/{XTerm-C_Gv5uo2.js → XTerm-V6sBDIe-.js} +1 -1
- package/dist/shell/assets/{cssMode-LqxC-sXw.js → cssMode-CSyiAvIn.js} +1 -1
- package/dist/shell/assets/{freemarker2-DUqPZFAL.js → freemarker2-C4-FNK3V.js} +1 -1
- package/dist/shell/assets/{grammar-CFa79rtH.js → grammar-DzHb7e7O.js} +1 -1
- package/dist/shell/assets/{handlebars-CF9us5mA.js → handlebars-DcjVaOCM.js} +1 -1
- package/dist/shell/assets/{html-SO575qLW.js → html-Csl8cogA.js} +1 -1
- package/dist/shell/assets/{htmlMode-XUsZRqmU.js → htmlMode-rewe9UaO.js} +1 -1
- package/dist/shell/assets/index-Bkn7zNFw.js +267 -0
- package/dist/shell/assets/index-iVaPr_If.css +1 -0
- package/dist/shell/assets/{javascript-4ER2NhcO.js → javascript--pKV-E-G.js} +1 -1
- package/dist/shell/assets/{jsonMode-D-UOjsYN.js → jsonMode-zPVc0E6l.js} +1 -1
- package/dist/shell/assets/{liquid-CMXc0SdF.js → liquid-jZT5xQG-.js} +1 -1
- package/dist/shell/assets/{lspLanguageFeatures-D4Z0LyWk.js → lspLanguageFeatures-BuiqKs90.js} +1 -1
- package/dist/shell/assets/{mdx-Dc4E4gPJ.js → mdx-DwnR-ia2.js} +1 -1
- package/dist/shell/assets/{python-DhCnlsga.js → python-DU3pl9Zu.js} +1 -1
- package/dist/shell/assets/{razor-CGRu_Zyc.js → razor-BzVUPqHs.js} +1 -1
- package/dist/shell/assets/{tsMode-BoDg-EAl.js → tsMode-ONmZhHHZ.js} +1 -1
- package/dist/shell/assets/{xml-Dl50xhA0.js → xml-BXW7X3BT.js} +1 -1
- package/dist/shell/assets/{yaml-CoqiVhBi.js → yaml-DFE79ex1.js} +1 -1
- package/dist/shell/index.html +17 -10
- package/dist/shell/manifest.json +1 -1
- package/package.json +1 -1
- package/dist/shell/assets/index-BYJYiHrs.css +0 -1
- package/dist/shell/assets/index-Brx-QOIQ.js +0 -266
package/dist/daemon.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../daemon/src/index.ts
|
|
3
|
-
import { rmSync as rmSync9, writeFileSync as
|
|
4
|
-
import { dirname as dirname13, join as
|
|
3
|
+
import { rmSync as rmSync9, writeFileSync as writeFileSync13 } from "fs";
|
|
4
|
+
import { dirname as dirname13, join as join31 } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
|
|
7
7
|
// ../shared/src/protocol/events.ts
|
|
@@ -358,10 +358,13 @@ var DAEMON_FILES = {
|
|
|
358
358
|
};
|
|
359
359
|
function machineLabel(hostname, remote) {
|
|
360
360
|
const of = (name) => name.split(".")[0]?.toLowerCase() ?? "";
|
|
361
|
-
if (remote && isTailnetName(remote.host))
|
|
361
|
+
if (remote && (isTailnetName(remote.host) || isFlyName(remote.host)))
|
|
362
362
|
return of(remote.host) || of(hostname);
|
|
363
363
|
return of(hostname) || "toyon";
|
|
364
364
|
}
|
|
365
|
+
function isFlyName(host) {
|
|
366
|
+
return host.toLowerCase().endsWith(".fly.dev");
|
|
367
|
+
}
|
|
365
368
|
function isShellOrigin(origin) {
|
|
366
369
|
let url;
|
|
367
370
|
try {
|
|
@@ -6599,7 +6602,7 @@ ${text.slice(text.length - (CONTEXT_CHARS - HEAD_CHARS))}`;
|
|
|
6599
6602
|
}
|
|
6600
6603
|
|
|
6601
6604
|
// ../shared/src/protocol/limits.ts
|
|
6602
|
-
var PROTOCOL_VERSION =
|
|
6605
|
+
var PROTOCOL_VERSION = 105;
|
|
6603
6606
|
var PAGE_ERROR_MAX_CHARS = 2000;
|
|
6604
6607
|
var PAGE_TITLE_MAX_CHARS = 1000;
|
|
6605
6608
|
var RENDER_ERRORS_MAX = 10;
|
|
@@ -6924,6 +6927,7 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
6924
6927
|
object({ t: literal("commit"), worktreeId: id, message: string2().max(5000) }),
|
|
6925
6928
|
object({ t: literal("graft"), targetId: id, sourceIds: array(id).min(1).max(20) }),
|
|
6926
6929
|
object({ t: literal("sync-main"), worktreeId: id }),
|
|
6930
|
+
object({ t: literal("unqueue-sync"), worktreeId: id }),
|
|
6927
6931
|
object({ t: literal("run-after-land"), repoId: id }),
|
|
6928
6932
|
object({ t: literal("restart-daemon") }),
|
|
6929
6933
|
object({ t: literal("forget-machine"), origin: string2().max(2048) }),
|
|
@@ -8406,7 +8410,7 @@ function readinessOf(json) {
|
|
|
8406
8410
|
line: "this machine is waiting to be approved in the Tailscale admin console"
|
|
8407
8411
|
};
|
|
8408
8412
|
case "Stopped":
|
|
8409
|
-
return { state: "
|
|
8413
|
+
return { state: "disconnected", name, line: "Tailscale is disconnected; run `tailscale up`, then this again" };
|
|
8410
8414
|
default:
|
|
8411
8415
|
return {
|
|
8412
8416
|
state: "not-running",
|
|
@@ -8524,7 +8528,7 @@ async function turnRemoteOff(ts, daemonPort, file, was) {
|
|
|
8524
8528
|
// ../daemon/package.json
|
|
8525
8529
|
var package_default = {
|
|
8526
8530
|
name: "@toyon/daemon",
|
|
8527
|
-
version: "0.
|
|
8531
|
+
version: "0.13.0",
|
|
8528
8532
|
private: true,
|
|
8529
8533
|
type: "module",
|
|
8530
8534
|
main: "src/index.ts",
|
|
@@ -14848,6 +14852,562 @@ function unformatOutput(output) {
|
|
|
14848
14852
|
|
|
14849
14853
|
// ../daemon/src/agent/policy.ts
|
|
14850
14854
|
import { isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
|
|
14855
|
+
|
|
14856
|
+
// ../daemon/src/agent/acp/background.ts
|
|
14857
|
+
import { existsSync as existsSync10, statSync as statSync4 } from "fs";
|
|
14858
|
+
import { open as open2 } from "fs/promises";
|
|
14859
|
+
import { homedir as homedir5 } from "os";
|
|
14860
|
+
import { basename as basename3, dirname as dirname4, join as join8 } from "path";
|
|
14861
|
+
var STARTED = /Command running in background with ID: (\S+)\. Output is being written to: (\S+)\./;
|
|
14862
|
+
function backgroundStart(input, output) {
|
|
14863
|
+
const flag = input && typeof input === "object" ? input.run_in_background : undefined;
|
|
14864
|
+
if (flag !== true)
|
|
14865
|
+
return null;
|
|
14866
|
+
const m = STARTED.exec(output);
|
|
14867
|
+
return m ? { taskId: m[1], file: m[2] } : null;
|
|
14868
|
+
}
|
|
14869
|
+
function spawnDetached(output) {
|
|
14870
|
+
return output.includes("Async agent launched successfully");
|
|
14871
|
+
}
|
|
14872
|
+
function claudeConfigDir() {
|
|
14873
|
+
return process.env.CLAUDE_CONFIG_DIR || join8(homedir5(), ".claude");
|
|
14874
|
+
}
|
|
14875
|
+
function sessionLogFor(file, configDir = claudeConfigDir()) {
|
|
14876
|
+
const tasks = dirname4(file);
|
|
14877
|
+
if (basename3(tasks) !== "tasks")
|
|
14878
|
+
return null;
|
|
14879
|
+
const session = dirname4(tasks);
|
|
14880
|
+
const path = join8(configDir, "projects", basename3(dirname4(session)), `${basename3(session)}.jsonl`);
|
|
14881
|
+
return existsSync10(path) ? path : null;
|
|
14882
|
+
}
|
|
14883
|
+
function taskEnd(text, toolId) {
|
|
14884
|
+
const at = text.indexOf(`<tool-use-id>${toolId}</tool-use-id>`);
|
|
14885
|
+
if (at === -1)
|
|
14886
|
+
return null;
|
|
14887
|
+
const tail = text.slice(at, at + 2000);
|
|
14888
|
+
const status = /<status>(\w+)<\/status>/.exec(tail)?.[1] ?? "completed";
|
|
14889
|
+
const exit = /exit code (\d+)/.exec(tail)?.[1];
|
|
14890
|
+
return { status, exit: exit === undefined ? null : Number(exit) };
|
|
14891
|
+
}
|
|
14892
|
+
var POLL_MS = 500;
|
|
14893
|
+
var DEFAULT_BASH_TIMEOUT_MS = 120000;
|
|
14894
|
+
var GRACE_MS = 60000;
|
|
14895
|
+
var READ_CHUNK = 1 << 20;
|
|
14896
|
+
var CARRY = 4000;
|
|
14897
|
+
|
|
14898
|
+
class BackgroundTasks {
|
|
14899
|
+
deps;
|
|
14900
|
+
watched = new Map;
|
|
14901
|
+
constructor(deps) {
|
|
14902
|
+
this.deps = deps;
|
|
14903
|
+
}
|
|
14904
|
+
get size() {
|
|
14905
|
+
return this.watched.size;
|
|
14906
|
+
}
|
|
14907
|
+
start(toolId, start, input) {
|
|
14908
|
+
const logPath = sessionLogFor(start.file, this.deps.configDir);
|
|
14909
|
+
if (!logPath)
|
|
14910
|
+
return false;
|
|
14911
|
+
const timeout = input?.timeout;
|
|
14912
|
+
const ceiling = (typeof timeout === "number" ? timeout : DEFAULT_BASH_TIMEOUT_MS) + (this.deps.graceMs ?? GRACE_MS);
|
|
14913
|
+
const w = {
|
|
14914
|
+
toolId,
|
|
14915
|
+
file: start.file,
|
|
14916
|
+
log: logPath,
|
|
14917
|
+
read: 0,
|
|
14918
|
+
logRead: statSync4(logPath).size,
|
|
14919
|
+
carry: "",
|
|
14920
|
+
decoder: new TextDecoder,
|
|
14921
|
+
text: "",
|
|
14922
|
+
truncated: false,
|
|
14923
|
+
ceiling: Date.now() + ceiling,
|
|
14924
|
+
timer: null
|
|
14925
|
+
};
|
|
14926
|
+
this.watched.set(toolId, w);
|
|
14927
|
+
this.schedule(w);
|
|
14928
|
+
return true;
|
|
14929
|
+
}
|
|
14930
|
+
endAll(reason) {
|
|
14931
|
+
for (const w of [...this.watched.values()]) {
|
|
14932
|
+
fireAndForget("background", this.finish(w, null, reason, true), `ending ${w.toolId}`);
|
|
14933
|
+
}
|
|
14934
|
+
}
|
|
14935
|
+
schedule(w) {
|
|
14936
|
+
w.timer = setTimeout(() => this.poll(w), this.deps.pollMs ?? POLL_MS);
|
|
14937
|
+
w.timer.unref?.();
|
|
14938
|
+
}
|
|
14939
|
+
async poll(w) {
|
|
14940
|
+
w.timer = null;
|
|
14941
|
+
try {
|
|
14942
|
+
await this.drain(w);
|
|
14943
|
+
const fresh = await readSince(w.log, w.logRead);
|
|
14944
|
+
w.logRead += fresh.length;
|
|
14945
|
+
const text = w.carry + fresh.toString("utf8");
|
|
14946
|
+
w.carry = text.slice(-CARRY);
|
|
14947
|
+
const end = fresh.length > 0 ? taskEnd(text, w.toolId) : null;
|
|
14948
|
+
if (end)
|
|
14949
|
+
return this.finish(w, end);
|
|
14950
|
+
if (Date.now() >= w.ceiling)
|
|
14951
|
+
return this.finish(w, null, "no word of its end; the row stops watching");
|
|
14952
|
+
} catch (e) {
|
|
14953
|
+
log.warn("background", `watching ${w.toolId} failed`, e);
|
|
14954
|
+
}
|
|
14955
|
+
if (this.watched.has(w.toolId))
|
|
14956
|
+
this.schedule(w);
|
|
14957
|
+
}
|
|
14958
|
+
async drain(w) {
|
|
14959
|
+
const fresh = await readSince(w.file, w.read);
|
|
14960
|
+
w.read += fresh.length;
|
|
14961
|
+
if (fresh.length === 0 || w.truncated)
|
|
14962
|
+
return;
|
|
14963
|
+
const chunk = w.decoder.decode(fresh, { stream: true });
|
|
14964
|
+
const take = chunk.slice(0, OUTPUT_CAP - w.text.length);
|
|
14965
|
+
w.text += take;
|
|
14966
|
+
if (take.length < chunk.length)
|
|
14967
|
+
w.truncated = true;
|
|
14968
|
+
if (take)
|
|
14969
|
+
this.deps.note({ type: "tool-delta", toolId: w.toolId, text: take });
|
|
14970
|
+
}
|
|
14971
|
+
async finish(w, end, note, killed = false) {
|
|
14972
|
+
if (!this.watched.delete(w.toolId))
|
|
14973
|
+
return;
|
|
14974
|
+
if (w.timer)
|
|
14975
|
+
clearTimeout(w.timer);
|
|
14976
|
+
try {
|
|
14977
|
+
await this.drain(w);
|
|
14978
|
+
} catch (e) {
|
|
14979
|
+
log.warn("background", `reading the last of ${w.toolId} failed`, e);
|
|
14980
|
+
}
|
|
14981
|
+
const exit = end ? end.exit ?? (end.status === "completed" ? 0 : null) : null;
|
|
14982
|
+
const lines = [formatOutput(w.text, exit ?? 0, w.truncated)];
|
|
14983
|
+
if (end && exit === null)
|
|
14984
|
+
lines.push(end.status);
|
|
14985
|
+
if (note)
|
|
14986
|
+
lines.push(note);
|
|
14987
|
+
this.deps.note({
|
|
14988
|
+
type: "tool-end",
|
|
14989
|
+
toolId: w.toolId,
|
|
14990
|
+
output: lines.filter(Boolean).join(`
|
|
14991
|
+
`),
|
|
14992
|
+
isError: killed || end !== null && (exit !== 0 || end.status !== "completed")
|
|
14993
|
+
});
|
|
14994
|
+
this.deps.onEnd?.();
|
|
14995
|
+
}
|
|
14996
|
+
}
|
|
14997
|
+
async function readSince(path, offset) {
|
|
14998
|
+
let fh;
|
|
14999
|
+
try {
|
|
15000
|
+
fh = await open2(path, "r");
|
|
15001
|
+
} catch (e) {
|
|
15002
|
+
if (e.code === "ENOENT")
|
|
15003
|
+
return Buffer.alloc(0);
|
|
15004
|
+
throw e;
|
|
15005
|
+
}
|
|
15006
|
+
try {
|
|
15007
|
+
const { size } = await fh.stat();
|
|
15008
|
+
if (size <= offset)
|
|
15009
|
+
return Buffer.alloc(0);
|
|
15010
|
+
const buf = Buffer.alloc(Math.min(size - offset, READ_CHUNK));
|
|
15011
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, offset);
|
|
15012
|
+
return buf.subarray(0, bytesRead);
|
|
15013
|
+
} finally {
|
|
15014
|
+
await fh.close();
|
|
15015
|
+
}
|
|
15016
|
+
}
|
|
15017
|
+
|
|
15018
|
+
// ../daemon/src/agent/acp/diff.ts
|
|
15019
|
+
function diffOps(a, b) {
|
|
15020
|
+
return diffSeq(a, b).map((op) => ({ mark: op.mark, text: op.value }));
|
|
15021
|
+
}
|
|
15022
|
+
function unifiedDiff(oldText, newText, context = 3) {
|
|
15023
|
+
const a = oldText === "" ? [] : oldText.split(`
|
|
15024
|
+
`);
|
|
15025
|
+
const b = newText === "" ? [] : newText.split(`
|
|
15026
|
+
`);
|
|
15027
|
+
const ops = diffOps(a, b);
|
|
15028
|
+
const oldNo = [];
|
|
15029
|
+
const newNo = [];
|
|
15030
|
+
let o = 1;
|
|
15031
|
+
let n = 1;
|
|
15032
|
+
for (const op of ops) {
|
|
15033
|
+
oldNo.push(o);
|
|
15034
|
+
newNo.push(n);
|
|
15035
|
+
if (op.mark !== "+")
|
|
15036
|
+
o++;
|
|
15037
|
+
if (op.mark !== "-")
|
|
15038
|
+
n++;
|
|
15039
|
+
}
|
|
15040
|
+
const out = [];
|
|
15041
|
+
let k = 0;
|
|
15042
|
+
while (k < ops.length) {
|
|
15043
|
+
if (ops[k]?.mark === " ") {
|
|
15044
|
+
k++;
|
|
15045
|
+
continue;
|
|
15046
|
+
}
|
|
15047
|
+
const start = k;
|
|
15048
|
+
let end = k;
|
|
15049
|
+
let j = k;
|
|
15050
|
+
while (j < ops.length) {
|
|
15051
|
+
if (ops[j]?.mark !== " ") {
|
|
15052
|
+
end = j;
|
|
15053
|
+
j++;
|
|
15054
|
+
continue;
|
|
15055
|
+
}
|
|
15056
|
+
let run = 0;
|
|
15057
|
+
while (j + run < ops.length && ops[j + run]?.mark === " ")
|
|
15058
|
+
run++;
|
|
15059
|
+
if (run > context * 2 || j + run >= ops.length)
|
|
15060
|
+
break;
|
|
15061
|
+
j += run;
|
|
15062
|
+
}
|
|
15063
|
+
const from = Math.max(0, start - context);
|
|
15064
|
+
const to = Math.min(ops.length, end + context + 1);
|
|
15065
|
+
const body = ops.slice(from, to);
|
|
15066
|
+
const oldCount = body.filter((op) => op.mark !== "+").length;
|
|
15067
|
+
const newCount = body.filter((op) => op.mark !== "-").length;
|
|
15068
|
+
out.push(`@@ -${oldNo[from]},${oldCount} +${newNo[from]},${newCount} @@`, ...body.map((op) => `${op.mark}${op.text}`));
|
|
15069
|
+
k = to;
|
|
15070
|
+
}
|
|
15071
|
+
return out.join(`
|
|
15072
|
+
`);
|
|
15073
|
+
}
|
|
15074
|
+
|
|
15075
|
+
// ../daemon/src/agent/acp/map.ts
|
|
15076
|
+
function spawnOf(meta) {
|
|
15077
|
+
const claude = asRecord2(asRecord2(meta).claudeCode);
|
|
15078
|
+
const parentToolId = typeof claude.parentToolUseId === "string" ? claude.parentToolUseId : undefined;
|
|
15079
|
+
const codex = asRecord2(asRecord2(meta).codex).subagent;
|
|
15080
|
+
const spawns = claude.toolName === "Agent" || claude.toolName === "Task";
|
|
15081
|
+
const subagent = spawns || claude.subagent === true || Object.keys(asRecord2(codex)).length > 0;
|
|
15082
|
+
return { ...parentToolId ? { parentToolId } : {}, ...subagent ? { subagent: true } : {} };
|
|
15083
|
+
}
|
|
15084
|
+
function asRecord2(v) {
|
|
15085
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
15086
|
+
}
|
|
15087
|
+
var NETWORK_ASK = "SandboxNetworkAccess";
|
|
15088
|
+
function isNetworkAsk(update) {
|
|
15089
|
+
return update.name === NETWORK_ASK || asRecord2(asRecord2(update._meta).claudeCode).toolName === NETWORK_ASK;
|
|
15090
|
+
}
|
|
15091
|
+
function isToolSearch(update) {
|
|
15092
|
+
return update.name === TOOL_SEARCH || asRecord2(asRecord2(update._meta).claudeCode).toolName === TOOL_SEARCH;
|
|
15093
|
+
}
|
|
15094
|
+
function toolSearchQuery(rawInput) {
|
|
15095
|
+
const query = asRecord2(rawInput).query;
|
|
15096
|
+
if (typeof query !== "string")
|
|
15097
|
+
return "";
|
|
15098
|
+
const names = /^select:(.*)$/.exec(query.trim());
|
|
15099
|
+
if (!names)
|
|
15100
|
+
return query.trim();
|
|
15101
|
+
return (names[1] ?? "").split(",").map((s) => s.trim()).filter(Boolean).join(", ");
|
|
15102
|
+
}
|
|
15103
|
+
var TOYON_TOOL = new RegExp(`^(?:mcp__${TOYON_MCP_SERVER}__|${TOYON_MCP_SERVER}/)([A-Za-z0-9_-]+)$`);
|
|
15104
|
+
function toyonTool(update) {
|
|
15105
|
+
const meta = asRecord2(asRecord2(update._meta).claudeCode).toolName;
|
|
15106
|
+
for (const spelled of [update.name ?? "", typeof meta === "string" ? meta : ""]) {
|
|
15107
|
+
const m = TOYON_TOOL.exec(spelled);
|
|
15108
|
+
if (m)
|
|
15109
|
+
return m[1];
|
|
15110
|
+
}
|
|
15111
|
+
return null;
|
|
15112
|
+
}
|
|
15113
|
+
function toyonTitle(tool, rawInput, title) {
|
|
15114
|
+
if (tool !== HANDOFF_TOOL)
|
|
15115
|
+
return title && toyonTool({ name: title }) === null ? title : tool;
|
|
15116
|
+
const project = asRecord2(rawInput).project;
|
|
15117
|
+
return typeof project === "string" && project.trim() ? `Continue in ${project.trim()}` : "Continue in another project";
|
|
15118
|
+
}
|
|
15119
|
+
function heading(update) {
|
|
15120
|
+
const name = typeof update.name === "string" ? update.name : "";
|
|
15121
|
+
const host = asRecord2(update.rawInput).host;
|
|
15122
|
+
if (isNetworkAsk(update) && typeof host === "string")
|
|
15123
|
+
return { name: "", title: host, kind: "fetch" };
|
|
15124
|
+
if (isToolSearch(update)) {
|
|
15125
|
+
return { name: TOOL_SEARCH, title: toolSearchQuery(update.rawInput) || TOOL_SEARCH, kind: "search" };
|
|
15126
|
+
}
|
|
15127
|
+
const own = toyonTool(update);
|
|
15128
|
+
if (own)
|
|
15129
|
+
return { name: own, title: toyonTitle(own, update.rawInput, update.title), kind: "other" };
|
|
15130
|
+
return { name, title: update.title, ...update.kind ? { kind: update.kind } : {} };
|
|
15131
|
+
}
|
|
15132
|
+
function endOfAsk(req, res, memos) {
|
|
15133
|
+
const memo = memos.get(req.toolCall.toolCallId);
|
|
15134
|
+
if (req.toolCall.name !== NETWORK_ASK || !memo || memo.ended)
|
|
15135
|
+
return null;
|
|
15136
|
+
const outcome = res.outcome;
|
|
15137
|
+
const picked = outcome.outcome === "selected" ? req.options.find((o) => o.optionId === outcome.optionId) : undefined;
|
|
15138
|
+
const allowed = picked?.kind === "allow_once" || picked?.kind === "allow_always";
|
|
15139
|
+
memo.ended = true;
|
|
15140
|
+
return {
|
|
15141
|
+
type: "tool-end",
|
|
15142
|
+
toolId: req.toolCall.toolCallId,
|
|
15143
|
+
output: allowed ? "allowed" : "refused",
|
|
15144
|
+
isError: !allowed
|
|
15145
|
+
};
|
|
15146
|
+
}
|
|
15147
|
+
function release(toolId, memo) {
|
|
15148
|
+
const held = memo.held;
|
|
15149
|
+
if (!held)
|
|
15150
|
+
return null;
|
|
15151
|
+
delete memo.held;
|
|
15152
|
+
if (!emptyInput(memo.input))
|
|
15153
|
+
memo.writing = false;
|
|
15154
|
+
return memo.ended ? null : { type: "tool-update", toolId, ...held };
|
|
15155
|
+
}
|
|
15156
|
+
function preempted(memos) {
|
|
15157
|
+
for (const memo of memos.values()) {
|
|
15158
|
+
if (!memo.ended && memo.writing && !memo.parent)
|
|
15159
|
+
memo.cut = true;
|
|
15160
|
+
}
|
|
15161
|
+
}
|
|
15162
|
+
function abandoned(memos, parent, opening = false) {
|
|
15163
|
+
const out = [];
|
|
15164
|
+
for (const [toolId, memo] of memos) {
|
|
15165
|
+
if (memo.ended || !memo.writing || memo.parent !== parent)
|
|
15166
|
+
continue;
|
|
15167
|
+
if (opening && memo.held && !memo.cut) {
|
|
15168
|
+
const sent = release(toolId, memo);
|
|
15169
|
+
if (sent)
|
|
15170
|
+
out.push(sent);
|
|
15171
|
+
continue;
|
|
15172
|
+
}
|
|
15173
|
+
memo.ended = true;
|
|
15174
|
+
out.push({ type: "tool-end", toolId });
|
|
15175
|
+
}
|
|
15176
|
+
return out;
|
|
15177
|
+
}
|
|
15178
|
+
function mapUpdate(update, memos, tag, sink, background) {
|
|
15179
|
+
switch (update.sessionUpdate) {
|
|
15180
|
+
case "agent_message_chunk": {
|
|
15181
|
+
const { parentToolId } = spawnOf(update._meta);
|
|
15182
|
+
const out = abandoned(memos, parentToolId);
|
|
15183
|
+
const text = textOf(update.content, tag, "message");
|
|
15184
|
+
if (text === null)
|
|
15185
|
+
return out;
|
|
15186
|
+
out.push(parentToolId ? { type: "tool-delta", toolId: parentToolId, text } : { type: "text-delta", text, ...update.messageId ? { messageId: update.messageId } : {} });
|
|
15187
|
+
return out;
|
|
15188
|
+
}
|
|
15189
|
+
case "agent_thought_chunk": {
|
|
15190
|
+
const { parentToolId } = spawnOf(update._meta);
|
|
15191
|
+
const out = abandoned(memos, parentToolId);
|
|
15192
|
+
if (parentToolId)
|
|
15193
|
+
return out;
|
|
15194
|
+
const text = textOf(update.content, tag, "thought");
|
|
15195
|
+
if (text !== null)
|
|
15196
|
+
out.push({ type: "thinking-delta", text });
|
|
15197
|
+
return out;
|
|
15198
|
+
}
|
|
15199
|
+
case "tool_call": {
|
|
15200
|
+
const spawn = spawnOf(update._meta);
|
|
15201
|
+
const out = abandoned(memos, spawn.parentToolId, true);
|
|
15202
|
+
const input = update.rawInput ?? { locations: update.locations ?? [] };
|
|
15203
|
+
const head = heading(update);
|
|
15204
|
+
const memo = {
|
|
15205
|
+
...head,
|
|
15206
|
+
input,
|
|
15207
|
+
content: update.content ?? [],
|
|
15208
|
+
rawOutput: update.rawOutput,
|
|
15209
|
+
ended: false,
|
|
15210
|
+
writing: writingCall(head, input),
|
|
15211
|
+
...spawn.parentToolId ? { parent: spawn.parentToolId } : {},
|
|
15212
|
+
...spawn.subagent ? { spawns: true } : {}
|
|
15213
|
+
};
|
|
15214
|
+
memos.set(update.toolCallId, memo);
|
|
15215
|
+
out.push({
|
|
15216
|
+
type: "tool-start",
|
|
15217
|
+
toolId: update.toolCallId,
|
|
15218
|
+
name: memo.name,
|
|
15219
|
+
input,
|
|
15220
|
+
...memo.kind ? { kind: memo.kind } : {},
|
|
15221
|
+
title: memo.title,
|
|
15222
|
+
...spawn
|
|
15223
|
+
});
|
|
15224
|
+
if (update.status === "completed" || update.status === "failed")
|
|
15225
|
+
out.push(endOf(update.toolCallId, memo, update.status, sink, background));
|
|
15226
|
+
return out;
|
|
15227
|
+
}
|
|
15228
|
+
case "tool_call_update": {
|
|
15229
|
+
let memo = memos.get(update.toolCallId);
|
|
15230
|
+
const out = [];
|
|
15231
|
+
if (!memo) {
|
|
15232
|
+
const spawn = spawnOf(update._meta);
|
|
15233
|
+
const input = update.rawInput ?? { locations: update.locations ?? [] };
|
|
15234
|
+
memo = {
|
|
15235
|
+
name: update.name ?? "",
|
|
15236
|
+
title: update.title ?? update.name ?? "tool",
|
|
15237
|
+
...update.kind ? { kind: update.kind } : {},
|
|
15238
|
+
input,
|
|
15239
|
+
content: [],
|
|
15240
|
+
ended: false,
|
|
15241
|
+
writing: writingCall({ name: update.name ?? "", kind: update.kind ?? undefined }, input),
|
|
15242
|
+
...spawn.parentToolId ? { parent: spawn.parentToolId } : {},
|
|
15243
|
+
...spawn.subagent ? { spawns: true } : {}
|
|
15244
|
+
};
|
|
15245
|
+
memos.set(update.toolCallId, memo);
|
|
15246
|
+
out.push({
|
|
15247
|
+
type: "tool-start",
|
|
15248
|
+
toolId: update.toolCallId,
|
|
15249
|
+
name: memo.name,
|
|
15250
|
+
input,
|
|
15251
|
+
...memo.kind ? { kind: memo.kind } : {},
|
|
15252
|
+
title: memo.title,
|
|
15253
|
+
...spawn
|
|
15254
|
+
});
|
|
15255
|
+
}
|
|
15256
|
+
const refined = { ...memo.held };
|
|
15257
|
+
const title = memo.name === TOOL_SEARCH ? toolSearchQuery(update.rawInput) : memo.name === HANDOFF_TOOL ? update.rawInput !== undefined ? toyonTitle(HANDOFF_TOOL, update.rawInput, update.title) : undefined : update.title;
|
|
15258
|
+
if (title && title !== memo.title)
|
|
15259
|
+
refined.title = memo.title = title;
|
|
15260
|
+
if (update.name && update.name !== memo.name && toyonTool(update) !== memo.name)
|
|
15261
|
+
refined.name = memo.name = update.name;
|
|
15262
|
+
if (update.kind && update.kind !== memo.kind)
|
|
15263
|
+
refined.kind = memo.kind = update.kind;
|
|
15264
|
+
if (update.rawInput !== undefined)
|
|
15265
|
+
refined.input = memo.input = update.rawInput;
|
|
15266
|
+
if (update.rawInput !== undefined && update.content === undefined && update.status === undefined) {
|
|
15267
|
+
if (out.length === 0)
|
|
15268
|
+
memo.held = refined;
|
|
15269
|
+
return out;
|
|
15270
|
+
}
|
|
15271
|
+
delete memo.held;
|
|
15272
|
+
if (!emptyInput(memo.input))
|
|
15273
|
+
memo.writing = false;
|
|
15274
|
+
if (Object.keys(refined).length > 0 && !memo.ended && out.length === 0) {
|
|
15275
|
+
out.push({ type: "tool-update", toolId: update.toolCallId, ...refined });
|
|
15276
|
+
}
|
|
15277
|
+
if (update.content)
|
|
15278
|
+
memo.content = [...memo.content, ...update.content];
|
|
15279
|
+
if (update.rawOutput !== undefined)
|
|
15280
|
+
memo.rawOutput = update.rawOutput;
|
|
15281
|
+
if ((update.status === "completed" || update.status === "failed") && !memo.ended) {
|
|
15282
|
+
out.push(endOf(update.toolCallId, memo, update.status, sink, background));
|
|
15283
|
+
}
|
|
15284
|
+
return out;
|
|
15285
|
+
}
|
|
15286
|
+
case "config_option_update": {
|
|
15287
|
+
const values = currentValues(readOptions(update.configOptions));
|
|
15288
|
+
return Object.keys(values).length > 0 ? [{ type: "session-info", sessionId: "", ...values }] : [];
|
|
15289
|
+
}
|
|
15290
|
+
case "usage_update": {
|
|
15291
|
+
const cost = update.cost && update.cost.currency === "USD" ? update.cost.amount : undefined;
|
|
15292
|
+
return [
|
|
15293
|
+
{
|
|
15294
|
+
type: "usage",
|
|
15295
|
+
used: update.used,
|
|
15296
|
+
size: update.size,
|
|
15297
|
+
...cost !== undefined ? { cost } : {},
|
|
15298
|
+
ts: Date.now()
|
|
15299
|
+
}
|
|
15300
|
+
];
|
|
15301
|
+
}
|
|
15302
|
+
default:
|
|
15303
|
+
log.debug(tag, `acp: ignoring ${update.sessionUpdate}`);
|
|
15304
|
+
return [];
|
|
15305
|
+
}
|
|
15306
|
+
}
|
|
15307
|
+
function textOf(content, tag, what) {
|
|
15308
|
+
if (content.type === "text" && typeof content.text === "string")
|
|
15309
|
+
return content.text;
|
|
15310
|
+
log.debug(tag, `acp: dropping non-text ${what} block (${content.type})`);
|
|
15311
|
+
return null;
|
|
15312
|
+
}
|
|
15313
|
+
function endOf(toolId, memo, status, sink, background) {
|
|
15314
|
+
memo.ended = true;
|
|
15315
|
+
const output = summarizeToolOutput(memo.content, memo.rawOutput);
|
|
15316
|
+
if (status === "completed" && memo.kind === "execute" && background) {
|
|
15317
|
+
const start = backgroundStart(memo.input, output);
|
|
15318
|
+
if (start && background(toolId, start, memo.input))
|
|
15319
|
+
return { type: "tool-update", toolId, background: true };
|
|
15320
|
+
}
|
|
15321
|
+
const images = sink ? toolImages(memo.content, sink) : [];
|
|
15322
|
+
const detached = status === "completed" && !!memo.spawns && spawnDetached(wholeText(memo.content, memo.rawOutput));
|
|
15323
|
+
return {
|
|
15324
|
+
type: "tool-end",
|
|
15325
|
+
toolId,
|
|
15326
|
+
output,
|
|
15327
|
+
isError: status === "failed" || exitCodeOf(memo.rawOutput) > 0,
|
|
15328
|
+
...detached ? { detached: true } : {},
|
|
15329
|
+
...images.length ? { images } : {}
|
|
15330
|
+
};
|
|
15331
|
+
}
|
|
15332
|
+
function wholeText(content, rawOutput) {
|
|
15333
|
+
const parts = content.flatMap((c) => c.type === "content" && c.content.type === "text" ? [c.content.text] : []);
|
|
15334
|
+
if (typeof rawOutput === "string")
|
|
15335
|
+
parts.push(rawOutput);
|
|
15336
|
+
return parts.join(`
|
|
15337
|
+
`);
|
|
15338
|
+
}
|
|
15339
|
+
function toolImages(content, sink) {
|
|
15340
|
+
const out = [];
|
|
15341
|
+
for (const c of content) {
|
|
15342
|
+
if (c.type !== "content" || c.content.type !== "image")
|
|
15343
|
+
continue;
|
|
15344
|
+
const img = toolImage(c.content.data, c.content.mimeType);
|
|
15345
|
+
if (!img)
|
|
15346
|
+
continue;
|
|
15347
|
+
sink(img.ref.file, img.bytes);
|
|
15348
|
+
out.push(img.ref);
|
|
15349
|
+
}
|
|
15350
|
+
return out;
|
|
15351
|
+
}
|
|
15352
|
+
function exitCodeOf(rawOutput) {
|
|
15353
|
+
const exit = rawOutput?.metadata?.exit;
|
|
15354
|
+
return typeof exit === "number" ? exit : 0;
|
|
15355
|
+
}
|
|
15356
|
+
function summarizeToolOutput(content, rawOutput) {
|
|
15357
|
+
const parts = [];
|
|
15358
|
+
let pictured = false;
|
|
15359
|
+
for (const c of content) {
|
|
15360
|
+
if (c.type === "content") {
|
|
15361
|
+
if (c.content.type === "text")
|
|
15362
|
+
parts.push(c.content.text);
|
|
15363
|
+
else if (c.content.type === "image")
|
|
15364
|
+
pictured = true;
|
|
15365
|
+
} else if (c.type === "diff") {
|
|
15366
|
+
const body = unifiedDiff(c.oldText ?? "", c.newText);
|
|
15367
|
+
if (body)
|
|
15368
|
+
parts.push(`\`\`\`diff
|
|
15369
|
+
${body}
|
|
15370
|
+
\`\`\``);
|
|
15371
|
+
}
|
|
15372
|
+
}
|
|
15373
|
+
if (parts.length > 0)
|
|
15374
|
+
return truncate(parts.join(`
|
|
15375
|
+
`));
|
|
15376
|
+
if (pictured)
|
|
15377
|
+
return "";
|
|
15378
|
+
if (typeof rawOutput === "string")
|
|
15379
|
+
return truncate(rawOutput);
|
|
15380
|
+
if (rawOutput && typeof rawOutput === "object") {
|
|
15381
|
+
const rec = rawOutput;
|
|
15382
|
+
for (const k of ["formatted_output", "output", "stdout", "text"])
|
|
15383
|
+
if (typeof rec[k] === "string")
|
|
15384
|
+
return truncate(rec[k]);
|
|
15385
|
+
return truncate(JSON.stringify(rawOutput, null, 1));
|
|
15386
|
+
}
|
|
15387
|
+
return "";
|
|
15388
|
+
}
|
|
15389
|
+
function truncate(s, max = 4000) {
|
|
15390
|
+
return s.length > max ? `${s.slice(0, max)}
|
|
15391
|
+
\u2026 (${s.length - max} more chars)` : s;
|
|
15392
|
+
}
|
|
15393
|
+
function mapCommands(cmds) {
|
|
15394
|
+
const seen = new Set;
|
|
15395
|
+
return cmds.filter((c) => {
|
|
15396
|
+
if (typeof c.name !== "string" || c.name.length === 0 || c.name.length > 120 || seen.has(c.name))
|
|
15397
|
+
return false;
|
|
15398
|
+
seen.add(c.name);
|
|
15399
|
+
return true;
|
|
15400
|
+
}).slice(0, 300).map((c) => ({
|
|
15401
|
+
name: c.name,
|
|
15402
|
+
description: c.description ?? "",
|
|
15403
|
+
...c.input?.hint ? { hint: c.input.hint } : {}
|
|
15404
|
+
}));
|
|
15405
|
+
}
|
|
15406
|
+
function mapStopReason(r) {
|
|
15407
|
+
return r === "cancelled" ? "interrupted" : r;
|
|
15408
|
+
}
|
|
15409
|
+
|
|
15410
|
+
// ../daemon/src/agent/policy.ts
|
|
14851
15411
|
var NON_WRITE_KINDS = new Set(["execute", "read", "search", "fetch", "think"]);
|
|
14852
15412
|
function requestedPaths(req) {
|
|
14853
15413
|
const raw = [];
|
|
@@ -14876,6 +15436,8 @@ function decide(req, bounds, cwd, mode = "auto", sandboxed = true) {
|
|
|
14876
15436
|
const commandAsks = kind === "execute" && !sandboxed;
|
|
14877
15437
|
if (kind === "switch_mode")
|
|
14878
15438
|
return { kind: "prompt" };
|
|
15439
|
+
if (toyonTool(req.toolCall) !== null)
|
|
15440
|
+
return { kind: "allow" };
|
|
14879
15441
|
const asks = mode !== "auto";
|
|
14880
15442
|
if (paths.length === 0) {
|
|
14881
15443
|
if (kind === "execute")
|
|
@@ -15014,7 +15576,7 @@ function fixWhy(f) {
|
|
|
15014
15576
|
}
|
|
15015
15577
|
function pageFixPrompt(errors) {
|
|
15016
15578
|
return [
|
|
15017
|
-
"
|
|
15579
|
+
"Since your last edits the preview threw this in the user's browser:",
|
|
15018
15580
|
"",
|
|
15019
15581
|
...errors.map((e) => `- ${e}`),
|
|
15020
15582
|
"",
|
|
@@ -15227,11 +15789,11 @@ ${a.text}
|
|
|
15227
15789
|
}
|
|
15228
15790
|
|
|
15229
15791
|
// ../daemon/src/agent/transcript.ts
|
|
15230
|
-
import { existsSync as
|
|
15792
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
15231
15793
|
import { appendFile } from "fs/promises";
|
|
15232
|
-
import { join as
|
|
15794
|
+
import { join as join9 } from "path";
|
|
15233
15795
|
function transcriptPathFor(transcriptsDir, worktreeId) {
|
|
15234
|
-
return
|
|
15796
|
+
return join9(transcriptsDir, `${worktreeId}.jsonl`);
|
|
15235
15797
|
}
|
|
15236
15798
|
function coalesce(entries) {
|
|
15237
15799
|
const out = [];
|
|
@@ -15285,7 +15847,7 @@ class Transcript {
|
|
|
15285
15847
|
this.seq = (this.entries.at(-1)?.seq ?? -1) + 1;
|
|
15286
15848
|
}
|
|
15287
15849
|
read() {
|
|
15288
|
-
return
|
|
15850
|
+
return existsSync11(this.path) ? parseTranscript(readFileSync9(this.path, "utf8"), this.tag) : [];
|
|
15289
15851
|
}
|
|
15290
15852
|
append(event) {
|
|
15291
15853
|
const entry = { seq: this.seq++, event };
|
|
@@ -15382,168 +15944,6 @@ async function cleanUp(ctx, a, sessionId) {
|
|
|
15382
15944
|
await ctx.request(methods.agent.session.delete, { sessionId }).catch(quietly("delete"));
|
|
15383
15945
|
}
|
|
15384
15946
|
|
|
15385
|
-
// ../daemon/src/agent/acp/background.ts
|
|
15386
|
-
import { existsSync as existsSync11, statSync as statSync4 } from "fs";
|
|
15387
|
-
import { open as open2 } from "fs/promises";
|
|
15388
|
-
import { homedir as homedir5 } from "os";
|
|
15389
|
-
import { basename as basename3, dirname as dirname4, join as join9 } from "path";
|
|
15390
|
-
var STARTED = /Command running in background with ID: (\S+)\. Output is being written to: (\S+)\./;
|
|
15391
|
-
function backgroundStart(input, output) {
|
|
15392
|
-
const flag = input && typeof input === "object" ? input.run_in_background : undefined;
|
|
15393
|
-
if (flag !== true)
|
|
15394
|
-
return null;
|
|
15395
|
-
const m = STARTED.exec(output);
|
|
15396
|
-
return m ? { taskId: m[1], file: m[2] } : null;
|
|
15397
|
-
}
|
|
15398
|
-
function spawnDetached(output) {
|
|
15399
|
-
return output.includes("Async agent launched successfully");
|
|
15400
|
-
}
|
|
15401
|
-
function claudeConfigDir() {
|
|
15402
|
-
return process.env.CLAUDE_CONFIG_DIR || join9(homedir5(), ".claude");
|
|
15403
|
-
}
|
|
15404
|
-
function sessionLogFor(file, configDir = claudeConfigDir()) {
|
|
15405
|
-
const tasks = dirname4(file);
|
|
15406
|
-
if (basename3(tasks) !== "tasks")
|
|
15407
|
-
return null;
|
|
15408
|
-
const session = dirname4(tasks);
|
|
15409
|
-
const path = join9(configDir, "projects", basename3(dirname4(session)), `${basename3(session)}.jsonl`);
|
|
15410
|
-
return existsSync11(path) ? path : null;
|
|
15411
|
-
}
|
|
15412
|
-
function taskEnd(text, toolId) {
|
|
15413
|
-
const at = text.indexOf(`<tool-use-id>${toolId}</tool-use-id>`);
|
|
15414
|
-
if (at === -1)
|
|
15415
|
-
return null;
|
|
15416
|
-
const tail = text.slice(at, at + 2000);
|
|
15417
|
-
const status = /<status>(\w+)<\/status>/.exec(tail)?.[1] ?? "completed";
|
|
15418
|
-
const exit = /exit code (\d+)/.exec(tail)?.[1];
|
|
15419
|
-
return { status, exit: exit === undefined ? null : Number(exit) };
|
|
15420
|
-
}
|
|
15421
|
-
var POLL_MS = 500;
|
|
15422
|
-
var DEFAULT_BASH_TIMEOUT_MS = 120000;
|
|
15423
|
-
var GRACE_MS = 60000;
|
|
15424
|
-
var READ_CHUNK = 1 << 20;
|
|
15425
|
-
var CARRY = 4000;
|
|
15426
|
-
|
|
15427
|
-
class BackgroundTasks {
|
|
15428
|
-
deps;
|
|
15429
|
-
watched = new Map;
|
|
15430
|
-
constructor(deps) {
|
|
15431
|
-
this.deps = deps;
|
|
15432
|
-
}
|
|
15433
|
-
get size() {
|
|
15434
|
-
return this.watched.size;
|
|
15435
|
-
}
|
|
15436
|
-
start(toolId, start, input) {
|
|
15437
|
-
const logPath = sessionLogFor(start.file, this.deps.configDir);
|
|
15438
|
-
if (!logPath)
|
|
15439
|
-
return false;
|
|
15440
|
-
const timeout = input?.timeout;
|
|
15441
|
-
const ceiling = (typeof timeout === "number" ? timeout : DEFAULT_BASH_TIMEOUT_MS) + (this.deps.graceMs ?? GRACE_MS);
|
|
15442
|
-
const w = {
|
|
15443
|
-
toolId,
|
|
15444
|
-
file: start.file,
|
|
15445
|
-
log: logPath,
|
|
15446
|
-
read: 0,
|
|
15447
|
-
logRead: statSync4(logPath).size,
|
|
15448
|
-
carry: "",
|
|
15449
|
-
decoder: new TextDecoder,
|
|
15450
|
-
text: "",
|
|
15451
|
-
truncated: false,
|
|
15452
|
-
ceiling: Date.now() + ceiling,
|
|
15453
|
-
timer: null
|
|
15454
|
-
};
|
|
15455
|
-
this.watched.set(toolId, w);
|
|
15456
|
-
this.schedule(w);
|
|
15457
|
-
return true;
|
|
15458
|
-
}
|
|
15459
|
-
endAll(reason) {
|
|
15460
|
-
for (const w of [...this.watched.values()]) {
|
|
15461
|
-
fireAndForget("background", this.finish(w, null, reason, true), `ending ${w.toolId}`);
|
|
15462
|
-
}
|
|
15463
|
-
}
|
|
15464
|
-
schedule(w) {
|
|
15465
|
-
w.timer = setTimeout(() => this.poll(w), this.deps.pollMs ?? POLL_MS);
|
|
15466
|
-
w.timer.unref?.();
|
|
15467
|
-
}
|
|
15468
|
-
async poll(w) {
|
|
15469
|
-
w.timer = null;
|
|
15470
|
-
try {
|
|
15471
|
-
await this.drain(w);
|
|
15472
|
-
const fresh = await readSince(w.log, w.logRead);
|
|
15473
|
-
w.logRead += fresh.length;
|
|
15474
|
-
const text = w.carry + fresh.toString("utf8");
|
|
15475
|
-
w.carry = text.slice(-CARRY);
|
|
15476
|
-
const end = fresh.length > 0 ? taskEnd(text, w.toolId) : null;
|
|
15477
|
-
if (end)
|
|
15478
|
-
return this.finish(w, end);
|
|
15479
|
-
if (Date.now() >= w.ceiling)
|
|
15480
|
-
return this.finish(w, null, "no word of its end; the row stops watching");
|
|
15481
|
-
} catch (e) {
|
|
15482
|
-
log.warn("background", `watching ${w.toolId} failed`, e);
|
|
15483
|
-
}
|
|
15484
|
-
if (this.watched.has(w.toolId))
|
|
15485
|
-
this.schedule(w);
|
|
15486
|
-
}
|
|
15487
|
-
async drain(w) {
|
|
15488
|
-
const fresh = await readSince(w.file, w.read);
|
|
15489
|
-
w.read += fresh.length;
|
|
15490
|
-
if (fresh.length === 0 || w.truncated)
|
|
15491
|
-
return;
|
|
15492
|
-
const chunk = w.decoder.decode(fresh, { stream: true });
|
|
15493
|
-
const take = chunk.slice(0, OUTPUT_CAP - w.text.length);
|
|
15494
|
-
w.text += take;
|
|
15495
|
-
if (take.length < chunk.length)
|
|
15496
|
-
w.truncated = true;
|
|
15497
|
-
if (take)
|
|
15498
|
-
this.deps.note({ type: "tool-delta", toolId: w.toolId, text: take });
|
|
15499
|
-
}
|
|
15500
|
-
async finish(w, end, note, killed = false) {
|
|
15501
|
-
if (!this.watched.delete(w.toolId))
|
|
15502
|
-
return;
|
|
15503
|
-
if (w.timer)
|
|
15504
|
-
clearTimeout(w.timer);
|
|
15505
|
-
try {
|
|
15506
|
-
await this.drain(w);
|
|
15507
|
-
} catch (e) {
|
|
15508
|
-
log.warn("background", `reading the last of ${w.toolId} failed`, e);
|
|
15509
|
-
}
|
|
15510
|
-
const exit = end ? end.exit ?? (end.status === "completed" ? 0 : null) : null;
|
|
15511
|
-
const lines = [formatOutput(w.text, exit ?? 0, w.truncated)];
|
|
15512
|
-
if (end && exit === null)
|
|
15513
|
-
lines.push(end.status);
|
|
15514
|
-
if (note)
|
|
15515
|
-
lines.push(note);
|
|
15516
|
-
this.deps.note({
|
|
15517
|
-
type: "tool-end",
|
|
15518
|
-
toolId: w.toolId,
|
|
15519
|
-
output: lines.filter(Boolean).join(`
|
|
15520
|
-
`),
|
|
15521
|
-
isError: killed || end !== null && (exit !== 0 || end.status !== "completed")
|
|
15522
|
-
});
|
|
15523
|
-
this.deps.onEnd?.();
|
|
15524
|
-
}
|
|
15525
|
-
}
|
|
15526
|
-
async function readSince(path, offset) {
|
|
15527
|
-
let fh;
|
|
15528
|
-
try {
|
|
15529
|
-
fh = await open2(path, "r");
|
|
15530
|
-
} catch (e) {
|
|
15531
|
-
if (e.code === "ENOENT")
|
|
15532
|
-
return Buffer.alloc(0);
|
|
15533
|
-
throw e;
|
|
15534
|
-
}
|
|
15535
|
-
try {
|
|
15536
|
-
const { size } = await fh.stat();
|
|
15537
|
-
if (size <= offset)
|
|
15538
|
-
return Buffer.alloc(0);
|
|
15539
|
-
const buf = Buffer.alloc(Math.min(size - offset, READ_CHUNK));
|
|
15540
|
-
const { bytesRead } = await fh.read(buf, 0, buf.length, offset);
|
|
15541
|
-
return buf.subarray(0, bytesRead);
|
|
15542
|
-
} finally {
|
|
15543
|
-
await fh.close();
|
|
15544
|
-
}
|
|
15545
|
-
}
|
|
15546
|
-
|
|
15547
15947
|
// ../daemon/src/agent/acp/elicit.ts
|
|
15548
15948
|
var CODEX_META = "codex";
|
|
15549
15949
|
var CLAUDE_CUSTOM_KEY = /^(question_\d+)_custom$/;
|
|
@@ -15657,398 +16057,6 @@ function toContent(form, answers) {
|
|
|
15657
16057
|
return out;
|
|
15658
16058
|
}
|
|
15659
16059
|
|
|
15660
|
-
// ../daemon/src/agent/acp/diff.ts
|
|
15661
|
-
function diffOps(a, b) {
|
|
15662
|
-
return diffSeq(a, b).map((op) => ({ mark: op.mark, text: op.value }));
|
|
15663
|
-
}
|
|
15664
|
-
function unifiedDiff(oldText, newText, context = 3) {
|
|
15665
|
-
const a = oldText === "" ? [] : oldText.split(`
|
|
15666
|
-
`);
|
|
15667
|
-
const b = newText === "" ? [] : newText.split(`
|
|
15668
|
-
`);
|
|
15669
|
-
const ops = diffOps(a, b);
|
|
15670
|
-
const oldNo = [];
|
|
15671
|
-
const newNo = [];
|
|
15672
|
-
let o = 1;
|
|
15673
|
-
let n = 1;
|
|
15674
|
-
for (const op of ops) {
|
|
15675
|
-
oldNo.push(o);
|
|
15676
|
-
newNo.push(n);
|
|
15677
|
-
if (op.mark !== "+")
|
|
15678
|
-
o++;
|
|
15679
|
-
if (op.mark !== "-")
|
|
15680
|
-
n++;
|
|
15681
|
-
}
|
|
15682
|
-
const out = [];
|
|
15683
|
-
let k = 0;
|
|
15684
|
-
while (k < ops.length) {
|
|
15685
|
-
if (ops[k]?.mark === " ") {
|
|
15686
|
-
k++;
|
|
15687
|
-
continue;
|
|
15688
|
-
}
|
|
15689
|
-
const start = k;
|
|
15690
|
-
let end = k;
|
|
15691
|
-
let j = k;
|
|
15692
|
-
while (j < ops.length) {
|
|
15693
|
-
if (ops[j]?.mark !== " ") {
|
|
15694
|
-
end = j;
|
|
15695
|
-
j++;
|
|
15696
|
-
continue;
|
|
15697
|
-
}
|
|
15698
|
-
let run = 0;
|
|
15699
|
-
while (j + run < ops.length && ops[j + run]?.mark === " ")
|
|
15700
|
-
run++;
|
|
15701
|
-
if (run > context * 2 || j + run >= ops.length)
|
|
15702
|
-
break;
|
|
15703
|
-
j += run;
|
|
15704
|
-
}
|
|
15705
|
-
const from = Math.max(0, start - context);
|
|
15706
|
-
const to = Math.min(ops.length, end + context + 1);
|
|
15707
|
-
const body = ops.slice(from, to);
|
|
15708
|
-
const oldCount = body.filter((op) => op.mark !== "+").length;
|
|
15709
|
-
const newCount = body.filter((op) => op.mark !== "-").length;
|
|
15710
|
-
out.push(`@@ -${oldNo[from]},${oldCount} +${newNo[from]},${newCount} @@`, ...body.map((op) => `${op.mark}${op.text}`));
|
|
15711
|
-
k = to;
|
|
15712
|
-
}
|
|
15713
|
-
return out.join(`
|
|
15714
|
-
`);
|
|
15715
|
-
}
|
|
15716
|
-
|
|
15717
|
-
// ../daemon/src/agent/acp/map.ts
|
|
15718
|
-
function spawnOf(meta) {
|
|
15719
|
-
const claude = asRecord2(asRecord2(meta).claudeCode);
|
|
15720
|
-
const parentToolId = typeof claude.parentToolUseId === "string" ? claude.parentToolUseId : undefined;
|
|
15721
|
-
const codex = asRecord2(asRecord2(meta).codex).subagent;
|
|
15722
|
-
const spawns = claude.toolName === "Agent" || claude.toolName === "Task";
|
|
15723
|
-
const subagent = spawns || claude.subagent === true || Object.keys(asRecord2(codex)).length > 0;
|
|
15724
|
-
return { ...parentToolId ? { parentToolId } : {}, ...subagent ? { subagent: true } : {} };
|
|
15725
|
-
}
|
|
15726
|
-
function asRecord2(v) {
|
|
15727
|
-
return v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
15728
|
-
}
|
|
15729
|
-
var NETWORK_ASK = "SandboxNetworkAccess";
|
|
15730
|
-
function isNetworkAsk(update) {
|
|
15731
|
-
return update.name === NETWORK_ASK || asRecord2(asRecord2(update._meta).claudeCode).toolName === NETWORK_ASK;
|
|
15732
|
-
}
|
|
15733
|
-
function isToolSearch(update) {
|
|
15734
|
-
return update.name === TOOL_SEARCH || asRecord2(asRecord2(update._meta).claudeCode).toolName === TOOL_SEARCH;
|
|
15735
|
-
}
|
|
15736
|
-
function toolSearchQuery(rawInput) {
|
|
15737
|
-
const query = asRecord2(rawInput).query;
|
|
15738
|
-
if (typeof query !== "string")
|
|
15739
|
-
return "";
|
|
15740
|
-
const names = /^select:(.*)$/.exec(query.trim());
|
|
15741
|
-
if (!names)
|
|
15742
|
-
return query.trim();
|
|
15743
|
-
return (names[1] ?? "").split(",").map((s) => s.trim()).filter(Boolean).join(", ");
|
|
15744
|
-
}
|
|
15745
|
-
var TOYON_TOOL = new RegExp(`^(?:mcp__${TOYON_MCP_SERVER}__|${TOYON_MCP_SERVER}/)([A-Za-z0-9_-]+)$`);
|
|
15746
|
-
function toyonTool(update) {
|
|
15747
|
-
const meta = asRecord2(asRecord2(update._meta).claudeCode).toolName;
|
|
15748
|
-
for (const spelled of [update.name ?? "", typeof meta === "string" ? meta : ""]) {
|
|
15749
|
-
const m = TOYON_TOOL.exec(spelled);
|
|
15750
|
-
if (m)
|
|
15751
|
-
return m[1];
|
|
15752
|
-
}
|
|
15753
|
-
return null;
|
|
15754
|
-
}
|
|
15755
|
-
function toyonTitle(tool, rawInput, title) {
|
|
15756
|
-
if (tool !== HANDOFF_TOOL)
|
|
15757
|
-
return title && toyonTool({ name: title }) === null ? title : tool;
|
|
15758
|
-
const project = asRecord2(rawInput).project;
|
|
15759
|
-
return typeof project === "string" && project.trim() ? `Continue in ${project.trim()}` : "Continue in another project";
|
|
15760
|
-
}
|
|
15761
|
-
function heading(update) {
|
|
15762
|
-
const name = typeof update.name === "string" ? update.name : "";
|
|
15763
|
-
const host = asRecord2(update.rawInput).host;
|
|
15764
|
-
if (isNetworkAsk(update) && typeof host === "string")
|
|
15765
|
-
return { name: "", title: host, kind: "fetch" };
|
|
15766
|
-
if (isToolSearch(update)) {
|
|
15767
|
-
return { name: TOOL_SEARCH, title: toolSearchQuery(update.rawInput) || TOOL_SEARCH, kind: "search" };
|
|
15768
|
-
}
|
|
15769
|
-
const own = toyonTool(update);
|
|
15770
|
-
if (own)
|
|
15771
|
-
return { name: own, title: toyonTitle(own, update.rawInput, update.title), kind: "other" };
|
|
15772
|
-
return { name, title: update.title, ...update.kind ? { kind: update.kind } : {} };
|
|
15773
|
-
}
|
|
15774
|
-
function endOfAsk(req, res, memos) {
|
|
15775
|
-
const memo = memos.get(req.toolCall.toolCallId);
|
|
15776
|
-
if (req.toolCall.name !== NETWORK_ASK || !memo || memo.ended)
|
|
15777
|
-
return null;
|
|
15778
|
-
const outcome = res.outcome;
|
|
15779
|
-
const picked = outcome.outcome === "selected" ? req.options.find((o) => o.optionId === outcome.optionId) : undefined;
|
|
15780
|
-
const allowed = picked?.kind === "allow_once" || picked?.kind === "allow_always";
|
|
15781
|
-
memo.ended = true;
|
|
15782
|
-
return {
|
|
15783
|
-
type: "tool-end",
|
|
15784
|
-
toolId: req.toolCall.toolCallId,
|
|
15785
|
-
output: allowed ? "allowed" : "refused",
|
|
15786
|
-
isError: !allowed
|
|
15787
|
-
};
|
|
15788
|
-
}
|
|
15789
|
-
function release(toolId, memo) {
|
|
15790
|
-
const held = memo.held;
|
|
15791
|
-
if (!held)
|
|
15792
|
-
return null;
|
|
15793
|
-
delete memo.held;
|
|
15794
|
-
if (!emptyInput(memo.input))
|
|
15795
|
-
memo.writing = false;
|
|
15796
|
-
return memo.ended ? null : { type: "tool-update", toolId, ...held };
|
|
15797
|
-
}
|
|
15798
|
-
function preempted(memos) {
|
|
15799
|
-
for (const memo of memos.values()) {
|
|
15800
|
-
if (!memo.ended && memo.writing && !memo.parent)
|
|
15801
|
-
memo.cut = true;
|
|
15802
|
-
}
|
|
15803
|
-
}
|
|
15804
|
-
function abandoned(memos, parent, opening = false) {
|
|
15805
|
-
const out = [];
|
|
15806
|
-
for (const [toolId, memo] of memos) {
|
|
15807
|
-
if (memo.ended || !memo.writing || memo.parent !== parent)
|
|
15808
|
-
continue;
|
|
15809
|
-
if (opening && memo.held && !memo.cut) {
|
|
15810
|
-
const sent = release(toolId, memo);
|
|
15811
|
-
if (sent)
|
|
15812
|
-
out.push(sent);
|
|
15813
|
-
continue;
|
|
15814
|
-
}
|
|
15815
|
-
memo.ended = true;
|
|
15816
|
-
out.push({ type: "tool-end", toolId });
|
|
15817
|
-
}
|
|
15818
|
-
return out;
|
|
15819
|
-
}
|
|
15820
|
-
function mapUpdate(update, memos, tag, sink, background) {
|
|
15821
|
-
switch (update.sessionUpdate) {
|
|
15822
|
-
case "agent_message_chunk": {
|
|
15823
|
-
const { parentToolId } = spawnOf(update._meta);
|
|
15824
|
-
const out = abandoned(memos, parentToolId);
|
|
15825
|
-
const text = textOf(update.content, tag, "message");
|
|
15826
|
-
if (text === null)
|
|
15827
|
-
return out;
|
|
15828
|
-
out.push(parentToolId ? { type: "tool-delta", toolId: parentToolId, text } : { type: "text-delta", text, ...update.messageId ? { messageId: update.messageId } : {} });
|
|
15829
|
-
return out;
|
|
15830
|
-
}
|
|
15831
|
-
case "agent_thought_chunk": {
|
|
15832
|
-
const { parentToolId } = spawnOf(update._meta);
|
|
15833
|
-
const out = abandoned(memos, parentToolId);
|
|
15834
|
-
if (parentToolId)
|
|
15835
|
-
return out;
|
|
15836
|
-
const text = textOf(update.content, tag, "thought");
|
|
15837
|
-
if (text !== null)
|
|
15838
|
-
out.push({ type: "thinking-delta", text });
|
|
15839
|
-
return out;
|
|
15840
|
-
}
|
|
15841
|
-
case "tool_call": {
|
|
15842
|
-
const spawn = spawnOf(update._meta);
|
|
15843
|
-
const out = abandoned(memos, spawn.parentToolId, true);
|
|
15844
|
-
const input = update.rawInput ?? { locations: update.locations ?? [] };
|
|
15845
|
-
const head = heading(update);
|
|
15846
|
-
const memo = {
|
|
15847
|
-
...head,
|
|
15848
|
-
input,
|
|
15849
|
-
content: update.content ?? [],
|
|
15850
|
-
rawOutput: update.rawOutput,
|
|
15851
|
-
ended: false,
|
|
15852
|
-
writing: writingCall(head, input),
|
|
15853
|
-
...spawn.parentToolId ? { parent: spawn.parentToolId } : {},
|
|
15854
|
-
...spawn.subagent ? { spawns: true } : {}
|
|
15855
|
-
};
|
|
15856
|
-
memos.set(update.toolCallId, memo);
|
|
15857
|
-
out.push({
|
|
15858
|
-
type: "tool-start",
|
|
15859
|
-
toolId: update.toolCallId,
|
|
15860
|
-
name: memo.name,
|
|
15861
|
-
input,
|
|
15862
|
-
...memo.kind ? { kind: memo.kind } : {},
|
|
15863
|
-
title: memo.title,
|
|
15864
|
-
...spawn
|
|
15865
|
-
});
|
|
15866
|
-
if (update.status === "completed" || update.status === "failed")
|
|
15867
|
-
out.push(endOf(update.toolCallId, memo, update.status, sink, background));
|
|
15868
|
-
return out;
|
|
15869
|
-
}
|
|
15870
|
-
case "tool_call_update": {
|
|
15871
|
-
let memo = memos.get(update.toolCallId);
|
|
15872
|
-
const out = [];
|
|
15873
|
-
if (!memo) {
|
|
15874
|
-
const spawn = spawnOf(update._meta);
|
|
15875
|
-
const input = update.rawInput ?? { locations: update.locations ?? [] };
|
|
15876
|
-
memo = {
|
|
15877
|
-
name: update.name ?? "",
|
|
15878
|
-
title: update.title ?? update.name ?? "tool",
|
|
15879
|
-
...update.kind ? { kind: update.kind } : {},
|
|
15880
|
-
input,
|
|
15881
|
-
content: [],
|
|
15882
|
-
ended: false,
|
|
15883
|
-
writing: writingCall({ name: update.name ?? "", kind: update.kind ?? undefined }, input),
|
|
15884
|
-
...spawn.parentToolId ? { parent: spawn.parentToolId } : {},
|
|
15885
|
-
...spawn.subagent ? { spawns: true } : {}
|
|
15886
|
-
};
|
|
15887
|
-
memos.set(update.toolCallId, memo);
|
|
15888
|
-
out.push({
|
|
15889
|
-
type: "tool-start",
|
|
15890
|
-
toolId: update.toolCallId,
|
|
15891
|
-
name: memo.name,
|
|
15892
|
-
input,
|
|
15893
|
-
...memo.kind ? { kind: memo.kind } : {},
|
|
15894
|
-
title: memo.title,
|
|
15895
|
-
...spawn
|
|
15896
|
-
});
|
|
15897
|
-
}
|
|
15898
|
-
const refined = { ...memo.held };
|
|
15899
|
-
const title = memo.name === TOOL_SEARCH ? toolSearchQuery(update.rawInput) : memo.name === HANDOFF_TOOL ? update.rawInput !== undefined ? toyonTitle(HANDOFF_TOOL, update.rawInput, update.title) : undefined : update.title;
|
|
15900
|
-
if (title && title !== memo.title)
|
|
15901
|
-
refined.title = memo.title = title;
|
|
15902
|
-
if (update.name && update.name !== memo.name && toyonTool(update) !== memo.name)
|
|
15903
|
-
refined.name = memo.name = update.name;
|
|
15904
|
-
if (update.kind && update.kind !== memo.kind)
|
|
15905
|
-
refined.kind = memo.kind = update.kind;
|
|
15906
|
-
if (update.rawInput !== undefined)
|
|
15907
|
-
refined.input = memo.input = update.rawInput;
|
|
15908
|
-
if (update.rawInput !== undefined && update.content === undefined && update.status === undefined) {
|
|
15909
|
-
if (out.length === 0)
|
|
15910
|
-
memo.held = refined;
|
|
15911
|
-
return out;
|
|
15912
|
-
}
|
|
15913
|
-
delete memo.held;
|
|
15914
|
-
if (!emptyInput(memo.input))
|
|
15915
|
-
memo.writing = false;
|
|
15916
|
-
if (Object.keys(refined).length > 0 && !memo.ended && out.length === 0) {
|
|
15917
|
-
out.push({ type: "tool-update", toolId: update.toolCallId, ...refined });
|
|
15918
|
-
}
|
|
15919
|
-
if (update.content)
|
|
15920
|
-
memo.content = [...memo.content, ...update.content];
|
|
15921
|
-
if (update.rawOutput !== undefined)
|
|
15922
|
-
memo.rawOutput = update.rawOutput;
|
|
15923
|
-
if ((update.status === "completed" || update.status === "failed") && !memo.ended) {
|
|
15924
|
-
out.push(endOf(update.toolCallId, memo, update.status, sink, background));
|
|
15925
|
-
}
|
|
15926
|
-
return out;
|
|
15927
|
-
}
|
|
15928
|
-
case "config_option_update": {
|
|
15929
|
-
const values = currentValues(readOptions(update.configOptions));
|
|
15930
|
-
return Object.keys(values).length > 0 ? [{ type: "session-info", sessionId: "", ...values }] : [];
|
|
15931
|
-
}
|
|
15932
|
-
case "usage_update": {
|
|
15933
|
-
const cost = update.cost && update.cost.currency === "USD" ? update.cost.amount : undefined;
|
|
15934
|
-
return [
|
|
15935
|
-
{
|
|
15936
|
-
type: "usage",
|
|
15937
|
-
used: update.used,
|
|
15938
|
-
size: update.size,
|
|
15939
|
-
...cost !== undefined ? { cost } : {},
|
|
15940
|
-
ts: Date.now()
|
|
15941
|
-
}
|
|
15942
|
-
];
|
|
15943
|
-
}
|
|
15944
|
-
default:
|
|
15945
|
-
log.debug(tag, `acp: ignoring ${update.sessionUpdate}`);
|
|
15946
|
-
return [];
|
|
15947
|
-
}
|
|
15948
|
-
}
|
|
15949
|
-
function textOf(content, tag, what) {
|
|
15950
|
-
if (content.type === "text" && typeof content.text === "string")
|
|
15951
|
-
return content.text;
|
|
15952
|
-
log.debug(tag, `acp: dropping non-text ${what} block (${content.type})`);
|
|
15953
|
-
return null;
|
|
15954
|
-
}
|
|
15955
|
-
function endOf(toolId, memo, status, sink, background) {
|
|
15956
|
-
memo.ended = true;
|
|
15957
|
-
const output = summarizeToolOutput(memo.content, memo.rawOutput);
|
|
15958
|
-
if (status === "completed" && memo.kind === "execute" && background) {
|
|
15959
|
-
const start = backgroundStart(memo.input, output);
|
|
15960
|
-
if (start && background(toolId, start, memo.input))
|
|
15961
|
-
return { type: "tool-update", toolId, background: true };
|
|
15962
|
-
}
|
|
15963
|
-
const images = sink ? toolImages(memo.content, sink) : [];
|
|
15964
|
-
const detached = status === "completed" && !!memo.spawns && spawnDetached(wholeText(memo.content, memo.rawOutput));
|
|
15965
|
-
return {
|
|
15966
|
-
type: "tool-end",
|
|
15967
|
-
toolId,
|
|
15968
|
-
output,
|
|
15969
|
-
isError: status === "failed" || exitCodeOf(memo.rawOutput) > 0,
|
|
15970
|
-
...detached ? { detached: true } : {},
|
|
15971
|
-
...images.length ? { images } : {}
|
|
15972
|
-
};
|
|
15973
|
-
}
|
|
15974
|
-
function wholeText(content, rawOutput) {
|
|
15975
|
-
const parts = content.flatMap((c) => c.type === "content" && c.content.type === "text" ? [c.content.text] : []);
|
|
15976
|
-
if (typeof rawOutput === "string")
|
|
15977
|
-
parts.push(rawOutput);
|
|
15978
|
-
return parts.join(`
|
|
15979
|
-
`);
|
|
15980
|
-
}
|
|
15981
|
-
function toolImages(content, sink) {
|
|
15982
|
-
const out = [];
|
|
15983
|
-
for (const c of content) {
|
|
15984
|
-
if (c.type !== "content" || c.content.type !== "image")
|
|
15985
|
-
continue;
|
|
15986
|
-
const img = toolImage(c.content.data, c.content.mimeType);
|
|
15987
|
-
if (!img)
|
|
15988
|
-
continue;
|
|
15989
|
-
sink(img.ref.file, img.bytes);
|
|
15990
|
-
out.push(img.ref);
|
|
15991
|
-
}
|
|
15992
|
-
return out;
|
|
15993
|
-
}
|
|
15994
|
-
function exitCodeOf(rawOutput) {
|
|
15995
|
-
const exit = rawOutput?.metadata?.exit;
|
|
15996
|
-
return typeof exit === "number" ? exit : 0;
|
|
15997
|
-
}
|
|
15998
|
-
function summarizeToolOutput(content, rawOutput) {
|
|
15999
|
-
const parts = [];
|
|
16000
|
-
let pictured = false;
|
|
16001
|
-
for (const c of content) {
|
|
16002
|
-
if (c.type === "content") {
|
|
16003
|
-
if (c.content.type === "text")
|
|
16004
|
-
parts.push(c.content.text);
|
|
16005
|
-
else if (c.content.type === "image")
|
|
16006
|
-
pictured = true;
|
|
16007
|
-
} else if (c.type === "diff") {
|
|
16008
|
-
const body = unifiedDiff(c.oldText ?? "", c.newText);
|
|
16009
|
-
if (body)
|
|
16010
|
-
parts.push(`\`\`\`diff
|
|
16011
|
-
${body}
|
|
16012
|
-
\`\`\``);
|
|
16013
|
-
}
|
|
16014
|
-
}
|
|
16015
|
-
if (parts.length > 0)
|
|
16016
|
-
return truncate(parts.join(`
|
|
16017
|
-
`));
|
|
16018
|
-
if (pictured)
|
|
16019
|
-
return "";
|
|
16020
|
-
if (typeof rawOutput === "string")
|
|
16021
|
-
return truncate(rawOutput);
|
|
16022
|
-
if (rawOutput && typeof rawOutput === "object") {
|
|
16023
|
-
const rec = rawOutput;
|
|
16024
|
-
for (const k of ["formatted_output", "output", "stdout", "text"])
|
|
16025
|
-
if (typeof rec[k] === "string")
|
|
16026
|
-
return truncate(rec[k]);
|
|
16027
|
-
return truncate(JSON.stringify(rawOutput, null, 1));
|
|
16028
|
-
}
|
|
16029
|
-
return "";
|
|
16030
|
-
}
|
|
16031
|
-
function truncate(s, max = 4000) {
|
|
16032
|
-
return s.length > max ? `${s.slice(0, max)}
|
|
16033
|
-
\u2026 (${s.length - max} more chars)` : s;
|
|
16034
|
-
}
|
|
16035
|
-
function mapCommands(cmds) {
|
|
16036
|
-
const seen = new Set;
|
|
16037
|
-
return cmds.filter((c) => {
|
|
16038
|
-
if (typeof c.name !== "string" || c.name.length === 0 || c.name.length > 120 || seen.has(c.name))
|
|
16039
|
-
return false;
|
|
16040
|
-
seen.add(c.name);
|
|
16041
|
-
return true;
|
|
16042
|
-
}).slice(0, 300).map((c) => ({
|
|
16043
|
-
name: c.name,
|
|
16044
|
-
description: c.description ?? "",
|
|
16045
|
-
...c.input?.hint ? { hint: c.input.hint } : {}
|
|
16046
|
-
}));
|
|
16047
|
-
}
|
|
16048
|
-
function mapStopReason(r) {
|
|
16049
|
-
return r === "cancelled" ? "interrupted" : r;
|
|
16050
|
-
}
|
|
16051
|
-
|
|
16052
16060
|
// ../daemon/src/agent/acp/ratelimit.ts
|
|
16053
16061
|
var RATE_LIMIT_META = "_claude/rateLimit";
|
|
16054
16062
|
var STATUSES = new Set(["allowed", "allowed_warning", "rejected"]);
|
|
@@ -16123,7 +16131,10 @@ class AcpSession {
|
|
|
16123
16131
|
running = false;
|
|
16124
16132
|
interrupted = false;
|
|
16125
16133
|
stopped = false;
|
|
16126
|
-
|
|
16134
|
+
holds = 0;
|
|
16135
|
+
get held() {
|
|
16136
|
+
return this.holds > 0;
|
|
16137
|
+
}
|
|
16127
16138
|
conn = null;
|
|
16128
16139
|
connecting = null;
|
|
16129
16140
|
opening = null;
|
|
@@ -16316,14 +16327,14 @@ class AcpSession {
|
|
|
16316
16327
|
fireAndForget(this.d.worktreeId, this.drain(), "agent drain");
|
|
16317
16328
|
}
|
|
16318
16329
|
hold() {
|
|
16319
|
-
this.
|
|
16330
|
+
this.holds++;
|
|
16320
16331
|
let released = false;
|
|
16321
16332
|
return () => {
|
|
16322
16333
|
if (released)
|
|
16323
16334
|
return;
|
|
16324
16335
|
released = true;
|
|
16325
|
-
this.
|
|
16326
|
-
if (!this.running && this.queue.length > 0)
|
|
16336
|
+
this.holds--;
|
|
16337
|
+
if (!this.held && !this.running && this.queue.length > 0)
|
|
16327
16338
|
fireAndForget(this.d.worktreeId, this.drain(), "agent drain");
|
|
16328
16339
|
};
|
|
16329
16340
|
}
|
|
@@ -19714,32 +19725,83 @@ function makePlanner(agents) {
|
|
|
19714
19725
|
return async (request, cwd, agentId) => parsePlan(await askFreshAgent(agents, agentId, cwd, PLAN_SYSTEM, planPrompt(request), { quick: "prefer" }));
|
|
19715
19726
|
}
|
|
19716
19727
|
|
|
19728
|
+
// ../daemon/src/agent/transcriptForward.ts
|
|
19729
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
19730
|
+
import { join as join12 } from "path";
|
|
19731
|
+
function forwardLine(line) {
|
|
19732
|
+
if (!line.includes('"page-error"'))
|
|
19733
|
+
return null;
|
|
19734
|
+
let entry;
|
|
19735
|
+
try {
|
|
19736
|
+
entry = JSON.parse(line);
|
|
19737
|
+
} catch {
|
|
19738
|
+
return null;
|
|
19739
|
+
}
|
|
19740
|
+
const e = entry.event;
|
|
19741
|
+
if (e?.type !== "page-error" || Array.isArray(e.errors) || typeof e.message !== "string")
|
|
19742
|
+
return null;
|
|
19743
|
+
const { message, ...rest } = e;
|
|
19744
|
+
return JSON.stringify({ ...entry, event: { ...rest, errors: [message] } });
|
|
19745
|
+
}
|
|
19746
|
+
function forwardText(text) {
|
|
19747
|
+
let changed = 0;
|
|
19748
|
+
const lines = text.split(`
|
|
19749
|
+
`).map((line) => {
|
|
19750
|
+
const next = forwardLine(line);
|
|
19751
|
+
if (next === null)
|
|
19752
|
+
return line;
|
|
19753
|
+
changed++;
|
|
19754
|
+
return next;
|
|
19755
|
+
});
|
|
19756
|
+
return { text: changed ? lines.join(`
|
|
19757
|
+
`) : text, changed };
|
|
19758
|
+
}
|
|
19759
|
+
function forwardTranscripts(dir) {
|
|
19760
|
+
let files = 0;
|
|
19761
|
+
let rows = 0;
|
|
19762
|
+
for (const name of readdirSync5(dir)) {
|
|
19763
|
+
if (!name.endsWith(".jsonl"))
|
|
19764
|
+
continue;
|
|
19765
|
+
const path = join12(dir, name);
|
|
19766
|
+
const { text, changed } = forwardText(readFileSync11(path, "utf8"));
|
|
19767
|
+
if (!changed)
|
|
19768
|
+
continue;
|
|
19769
|
+
const tmp = `${path}.tmp`;
|
|
19770
|
+
writeFileSync6(tmp, text);
|
|
19771
|
+
renameSync2(tmp, path);
|
|
19772
|
+
files++;
|
|
19773
|
+
rows += changed;
|
|
19774
|
+
}
|
|
19775
|
+
if (files)
|
|
19776
|
+
log.info("transcripts", `rewrote ${rows} page-error row(s) in ${files} transcript(s) to the list shape`);
|
|
19777
|
+
}
|
|
19778
|
+
|
|
19717
19779
|
// ../daemon/src/core/assets.ts
|
|
19718
|
-
import { existsSync as existsSync14, readdirSync as
|
|
19719
|
-
import { join as
|
|
19780
|
+
import { existsSync as existsSync14, readdirSync as readdirSync6, rmSync as rmSync3, statSync as statSync5 } from "fs";
|
|
19781
|
+
import { join as join13, resolve as resolve5 } from "path";
|
|
19720
19782
|
function locateAssets(here, env = process.env) {
|
|
19721
|
-
const packaged = existsSync14(
|
|
19783
|
+
const packaged = existsSync14(join13(here, "shell", "index.html"));
|
|
19722
19784
|
return {
|
|
19723
|
-
shellDist: env.TOYON_SHELL_DIST ?? (packaged ?
|
|
19724
|
-
bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ?
|
|
19725
|
-
helperStub: packaged ?
|
|
19726
|
-
cliEntry: packaged ?
|
|
19785
|
+
shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join13(here, "shell") : join13(here, "../../shell/dist")),
|
|
19786
|
+
bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join13(here, "bridge.js") : join13(here, "../../bridge/dist/bridge.js")),
|
|
19787
|
+
helperStub: packaged ? join13(here, "helper-stub") : join13(here, "../dist/helper-stub"),
|
|
19788
|
+
cliEntry: packaged ? join13(here, "cli.js") : join13(here, "../../cli/src/cli.ts"),
|
|
19727
19789
|
sourceRoot: packaged ? null : resolve5(here, "../../.."),
|
|
19728
|
-
packageJson: packaged ?
|
|
19790
|
+
packageJson: packaged ? join13(here, "..", "package.json") : null
|
|
19729
19791
|
};
|
|
19730
19792
|
}
|
|
19731
19793
|
var KEEP_MS = 3 * 24 * 60 * 60 * 1000;
|
|
19732
19794
|
function pruneAssets(shellDist) {
|
|
19733
|
-
const dir =
|
|
19795
|
+
const dir = join13(shellDist, "assets");
|
|
19734
19796
|
let names;
|
|
19735
19797
|
try {
|
|
19736
|
-
names =
|
|
19798
|
+
names = readdirSync6(dir);
|
|
19737
19799
|
} catch {
|
|
19738
19800
|
return 0;
|
|
19739
19801
|
}
|
|
19740
19802
|
const found = [];
|
|
19741
19803
|
for (const name of names) {
|
|
19742
|
-
const path =
|
|
19804
|
+
const path = join13(dir, name);
|
|
19743
19805
|
try {
|
|
19744
19806
|
found.push({ path, mtime: statSync5(path).mtimeMs });
|
|
19745
19807
|
} catch {}
|
|
@@ -20281,7 +20343,7 @@ class GrantCodes {
|
|
|
20281
20343
|
import { createHash as createHash3 } from "crypto";
|
|
20282
20344
|
import { existsSync as existsSync16 } from "fs";
|
|
20283
20345
|
import { chmod, copyFile as copyFile3, mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
20284
|
-
import { basename as basename4, dirname as dirname5, join as
|
|
20346
|
+
import { basename as basename4, dirname as dirname5, join as join14 } from "path";
|
|
20285
20347
|
var EXECUTABLE = "Toyon";
|
|
20286
20348
|
function helperWanted(where) {
|
|
20287
20349
|
return where.platform === "darwin" && !where.cloud && where.port === DAEMON_DEFAULT_PORT && where.home === undefined;
|
|
@@ -20364,19 +20426,19 @@ class Helper {
|
|
|
20364
20426
|
return "skipped";
|
|
20365
20427
|
const stubHash = createHash3("sha256").update(await readFile2(spec.stub)).digest("hex").slice(0, 16);
|
|
20366
20428
|
const { plist, script } = helperFiles(spec, stubHash);
|
|
20367
|
-
const contents =
|
|
20368
|
-
const resources =
|
|
20369
|
-
const stub =
|
|
20370
|
-
const scriptFile =
|
|
20429
|
+
const contents = join14(spec.dir, "Contents");
|
|
20430
|
+
const resources = join14(contents, "Resources");
|
|
20431
|
+
const stub = join14(contents, "MacOS", EXECUTABLE);
|
|
20432
|
+
const scriptFile = join14(resources, "launch.sh");
|
|
20371
20433
|
if (existsSync16(stub) && existsSync16(scriptFile) && await readFile2(scriptFile, "utf8") === script)
|
|
20372
20434
|
return "kept";
|
|
20373
|
-
await mkdir2(
|
|
20435
|
+
await mkdir2(join14(contents, "MacOS"), { recursive: true });
|
|
20374
20436
|
await mkdir2(resources, { recursive: true });
|
|
20375
|
-
await writeFile2(
|
|
20437
|
+
await writeFile2(join14(contents, "Info.plist"), plist);
|
|
20376
20438
|
await writeFile2(scriptFile, script, { mode: 493 });
|
|
20377
20439
|
await copyFile3(spec.stub, stub);
|
|
20378
20440
|
await chmod(stub, 493);
|
|
20379
|
-
if (!existsSync16(
|
|
20441
|
+
if (!existsSync16(join14(resources, "AppIcon.icns")))
|
|
20380
20442
|
await this.drawIcon(resources);
|
|
20381
20443
|
await this.run(["xattr", "-cr", spec.dir]);
|
|
20382
20444
|
if (!await this.run(["codesign", "--force", "--deep", "-s", "-", spec.dir])) {
|
|
@@ -20387,16 +20449,16 @@ class Helper {
|
|
|
20387
20449
|
}
|
|
20388
20450
|
async drawIcon(resources) {
|
|
20389
20451
|
const svg = this.spec.icon;
|
|
20390
|
-
const tmp =
|
|
20391
|
-
const set =
|
|
20452
|
+
const tmp = join14(resources, "iconset.tmp");
|
|
20453
|
+
const set = join14(tmp, "AppIcon.iconset");
|
|
20392
20454
|
await mkdir2(set, { recursive: true });
|
|
20393
20455
|
await this.run(["qlmanage", "-t", "-s", "1024", "-o", tmp, svg]);
|
|
20394
|
-
const big =
|
|
20456
|
+
const big = join14(tmp, `${basename4(svg)}.png`);
|
|
20395
20457
|
if (existsSync16(big)) {
|
|
20396
20458
|
for (const size of [16, 32, 64, 128, 256, 512, 1024]) {
|
|
20397
|
-
await this.run(["sips", "-z", String(size), String(size), big, "--out",
|
|
20459
|
+
await this.run(["sips", "-z", String(size), String(size), big, "--out", join14(set, `icon_${size}x${size}.png`)]);
|
|
20398
20460
|
}
|
|
20399
|
-
await this.run(["iconutil", "-c", "icns", set, "-o",
|
|
20461
|
+
await this.run(["iconutil", "-c", "icns", set, "-o", join14(resources, "AppIcon.icns")]);
|
|
20400
20462
|
}
|
|
20401
20463
|
await rm(tmp, { recursive: true, force: true });
|
|
20402
20464
|
}
|
|
@@ -20620,7 +20682,7 @@ class Origins {
|
|
|
20620
20682
|
}
|
|
20621
20683
|
|
|
20622
20684
|
// ../daemon/src/core/remoteSwitch.ts
|
|
20623
|
-
import { writeFileSync as
|
|
20685
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
20624
20686
|
class RemoteSwitch {
|
|
20625
20687
|
d;
|
|
20626
20688
|
constructor(d) {
|
|
@@ -20653,7 +20715,7 @@ class RemoteSwitch {
|
|
|
20653
20715
|
const accepted = acceptRemote(view, this.d.policy);
|
|
20654
20716
|
if (typeof accepted === "string")
|
|
20655
20717
|
throw new UserError(accepted);
|
|
20656
|
-
|
|
20718
|
+
writeFileSync7(this.d.file, `${JSON.stringify(accepted, null, 2)}
|
|
20657
20719
|
`);
|
|
20658
20720
|
return this.live(accepted);
|
|
20659
20721
|
}
|
|
@@ -20835,7 +20897,7 @@ class SelfWatch {
|
|
|
20835
20897
|
|
|
20836
20898
|
// ../daemon/src/core/state.ts
|
|
20837
20899
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
20838
|
-
import { existsSync as existsSync17, readFileSync as
|
|
20900
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
20839
20901
|
var TRUSTED_MAX = 16;
|
|
20840
20902
|
var DAEMON_GROUPS = "daemon";
|
|
20841
20903
|
var GROUPS_SAVE_MS = 250;
|
|
@@ -20848,7 +20910,7 @@ function loadState(paths) {
|
|
|
20848
20910
|
return structuredClone(empty);
|
|
20849
20911
|
let raw;
|
|
20850
20912
|
try {
|
|
20851
|
-
raw =
|
|
20913
|
+
raw = readFileSync12(STATE_FILE, "utf8");
|
|
20852
20914
|
} catch (e) {
|
|
20853
20915
|
log.error("state", `cannot read ${STATE_FILE}; starting empty`, e);
|
|
20854
20916
|
return structuredClone(empty);
|
|
@@ -20859,7 +20921,7 @@ function loadState(paths) {
|
|
|
20859
20921
|
} catch (e) {
|
|
20860
20922
|
const backup = `${STATE_FILE}.corrupt-${Date.now()}`;
|
|
20861
20923
|
try {
|
|
20862
|
-
|
|
20924
|
+
writeFileSync8(backup, raw);
|
|
20863
20925
|
} catch (be) {
|
|
20864
20926
|
log.warn("state", "could not write the corrupt-state backup", be);
|
|
20865
20927
|
}
|
|
@@ -20890,16 +20952,16 @@ function loadState(paths) {
|
|
|
20890
20952
|
}
|
|
20891
20953
|
function saveState(paths, state) {
|
|
20892
20954
|
const tmp = `${paths.stateFile}.tmp`;
|
|
20893
|
-
|
|
20894
|
-
|
|
20955
|
+
writeFileSync8(tmp, JSON.stringify(state, null, 2));
|
|
20956
|
+
renameSync3(tmp, paths.stateFile);
|
|
20895
20957
|
}
|
|
20896
20958
|
function loadOrCreateToken(paths) {
|
|
20897
20959
|
ensureDirs(paths);
|
|
20898
20960
|
const TOKEN_FILE = paths.tokenFile;
|
|
20899
20961
|
if (existsSync17(TOKEN_FILE))
|
|
20900
|
-
return
|
|
20962
|
+
return readFileSync12(TOKEN_FILE, "utf8").trim();
|
|
20901
20963
|
const token = randomBytes6(32).toString("hex");
|
|
20902
|
-
|
|
20964
|
+
writeFileSync8(TOKEN_FILE, token, { mode: 384 });
|
|
20903
20965
|
return token;
|
|
20904
20966
|
}
|
|
20905
20967
|
|
|
@@ -20985,6 +21047,10 @@ class StateStore {
|
|
|
20985
21047
|
this.save();
|
|
20986
21048
|
}
|
|
20987
21049
|
removeWorktree(id) {
|
|
21050
|
+
const wt = this.state.worktrees.find((w) => w.id === id);
|
|
21051
|
+
const repo = wt && wt.kind !== "spare" ? this.repo(wt.repoId) : undefined;
|
|
21052
|
+
if (wt && repo)
|
|
21053
|
+
repo.promptedAt = Math.max(repo.promptedAt ?? 0, sentAt(wt));
|
|
20988
21054
|
this.state.worktrees = this.state.worktrees.filter((w) => w.id !== id);
|
|
20989
21055
|
delete this.state.sessions[id];
|
|
20990
21056
|
if (this.state.seen)
|
|
@@ -21168,7 +21234,7 @@ class StateStore {
|
|
|
21168
21234
|
|
|
21169
21235
|
// ../daemon/src/core/tailnet.ts
|
|
21170
21236
|
var PHONE_OS = new Set(["android", "ios"]);
|
|
21171
|
-
function
|
|
21237
|
+
function tailnetListed(json) {
|
|
21172
21238
|
let st;
|
|
21173
21239
|
try {
|
|
21174
21240
|
st = JSON.parse(json);
|
|
@@ -21183,12 +21249,31 @@ function tailnetPeers(json) {
|
|
|
21183
21249
|
continue;
|
|
21184
21250
|
const host = dnsName(p.DNSName);
|
|
21185
21251
|
const name = p.HostName || host.split(".")[0] || "a device";
|
|
21186
|
-
peers.push({
|
|
21252
|
+
peers.push({
|
|
21253
|
+
name,
|
|
21254
|
+
host,
|
|
21255
|
+
ip: p.TailscaleIPs?.[0] ?? null,
|
|
21256
|
+
os: (p.OS ?? "").toLowerCase(),
|
|
21257
|
+
online: p.Online === true
|
|
21258
|
+
});
|
|
21187
21259
|
}
|
|
21188
|
-
return peers;
|
|
21260
|
+
return { here: st.Self?.Online !== false, peers };
|
|
21189
21261
|
}
|
|
21190
21262
|
var isPhone = (p) => PHONE_OS.has(p.os);
|
|
21263
|
+
function loginUrl(json) {
|
|
21264
|
+
try {
|
|
21265
|
+
const st = JSON.parse(json);
|
|
21266
|
+
return typeof st?.AuthURL === "string" && st.AuthURL !== "" ? st.AuthURL : null;
|
|
21267
|
+
} catch {
|
|
21268
|
+
return null;
|
|
21269
|
+
}
|
|
21270
|
+
}
|
|
21271
|
+
var lastLine2 = (r) => (r.err.trim() || r.out.trim()).split(`
|
|
21272
|
+
`).at(-1) ?? "tailscale failed";
|
|
21273
|
+
var LOGIN_URL_WAIT_MS = 4000;
|
|
21274
|
+
var LOGIN_URL_POLL_MS = 300;
|
|
21191
21275
|
var PROBE_TIMEOUT_MS2 = 2000;
|
|
21276
|
+
var ANSWERED_RECENTLY_MS = 3 * 60000;
|
|
21192
21277
|
var probe = async (origin, named) => {
|
|
21193
21278
|
try {
|
|
21194
21279
|
const r = await fetch(`${origin}/health`, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS2) });
|
|
@@ -21205,6 +21290,8 @@ var probe = async (origin, named) => {
|
|
|
21205
21290
|
var PING_TIMEOUT = "2s";
|
|
21206
21291
|
var pingArgs = (ip) => ["ping", "--until-direct=false", "--c", "1", "--timeout", PING_TIMEOUT, ip];
|
|
21207
21292
|
var STATUS_FRESH_MS = 5000;
|
|
21293
|
+
var VIEW_FRESH_MS = 4000;
|
|
21294
|
+
var VIEW_STALE_MAX_MS = 60000;
|
|
21208
21295
|
var READINESS_EVERY_MS = 60000;
|
|
21209
21296
|
class Tailnet {
|
|
21210
21297
|
d;
|
|
@@ -21212,6 +21299,9 @@ class Tailnet {
|
|
|
21212
21299
|
statusAt = 0;
|
|
21213
21300
|
status = null;
|
|
21214
21301
|
timer = null;
|
|
21302
|
+
answered = new Map;
|
|
21303
|
+
lastView = null;
|
|
21304
|
+
viewing = null;
|
|
21215
21305
|
constructor(d) {
|
|
21216
21306
|
this.d = d;
|
|
21217
21307
|
}
|
|
@@ -21229,7 +21319,9 @@ class Tailnet {
|
|
|
21229
21319
|
return this.readinessNow;
|
|
21230
21320
|
}
|
|
21231
21321
|
async refresh() {
|
|
21232
|
-
|
|
21322
|
+
return this.noteReadiness(await this.statusJson(true));
|
|
21323
|
+
}
|
|
21324
|
+
noteReadiness(json) {
|
|
21233
21325
|
const next = this.d.ts === null ? {
|
|
21234
21326
|
state: "not-installed",
|
|
21235
21327
|
name: null,
|
|
@@ -21241,6 +21333,32 @@ class Tailnet {
|
|
|
21241
21333
|
this.d.hub.emit("tailscaleChanged");
|
|
21242
21334
|
return next;
|
|
21243
21335
|
}
|
|
21336
|
+
async up() {
|
|
21337
|
+
const ts = this.d.ts;
|
|
21338
|
+
if (ts === null)
|
|
21339
|
+
return "Tailscale is not installed here";
|
|
21340
|
+
const r = await ts(["up"]);
|
|
21341
|
+
await this.refresh();
|
|
21342
|
+
return r.ok ? null : lastLine2(r);
|
|
21343
|
+
}
|
|
21344
|
+
async login() {
|
|
21345
|
+
const ts = this.d.ts;
|
|
21346
|
+
if (ts === null)
|
|
21347
|
+
return { why: "Tailscale is not installed here" };
|
|
21348
|
+
fireAndForget("tailscale", ts(["login"]), "tailscale login");
|
|
21349
|
+
const sleep = this.d.sleep ?? ((ms) => Bun.sleep(ms));
|
|
21350
|
+
const now = this.d.now ?? Date.now;
|
|
21351
|
+
const until = now() + LOGIN_URL_WAIT_MS;
|
|
21352
|
+
for (;; ) {
|
|
21353
|
+
const json = await this.statusJson(true);
|
|
21354
|
+
const url = json === null ? null : loginUrl(json);
|
|
21355
|
+
if (url !== null)
|
|
21356
|
+
return { url };
|
|
21357
|
+
if (now() >= until)
|
|
21358
|
+
return { why: "Tailscale did not offer a sign-in link; run `tailscale login` in a terminal" };
|
|
21359
|
+
await sleep(LOGIN_URL_POLL_MS);
|
|
21360
|
+
}
|
|
21361
|
+
}
|
|
21244
21362
|
statusJson(fresh = false) {
|
|
21245
21363
|
const ts = this.d.ts;
|
|
21246
21364
|
if (ts === null)
|
|
@@ -21252,26 +21370,50 @@ class Tailnet {
|
|
|
21252
21370
|
}
|
|
21253
21371
|
return this.status;
|
|
21254
21372
|
}
|
|
21255
|
-
async
|
|
21373
|
+
async listed() {
|
|
21256
21374
|
const json = await this.statusJson();
|
|
21257
|
-
|
|
21375
|
+
this.noteReadiness(json);
|
|
21376
|
+
return json === null ? null : tailnetListed(json);
|
|
21258
21377
|
}
|
|
21259
|
-
async
|
|
21378
|
+
async view() {
|
|
21379
|
+
const now = (this.d.now ?? Date.now)();
|
|
21380
|
+
const last = this.lastView;
|
|
21381
|
+
if (last && now - last.at < VIEW_FRESH_MS)
|
|
21382
|
+
return last.view;
|
|
21383
|
+
if (this.viewing === null) {
|
|
21384
|
+
this.viewing = this.makeView().finally(() => {
|
|
21385
|
+
this.viewing = null;
|
|
21386
|
+
});
|
|
21387
|
+
}
|
|
21388
|
+
if (last && now - last.at < VIEW_STALE_MAX_MS) {
|
|
21389
|
+
fireAndForget("tailnet", this.viewing, "tailnet view");
|
|
21390
|
+
return last.view;
|
|
21391
|
+
}
|
|
21392
|
+
return this.viewing;
|
|
21393
|
+
}
|
|
21394
|
+
async makeView() {
|
|
21260
21395
|
const ts = this.d.ts;
|
|
21261
|
-
const
|
|
21262
|
-
if (
|
|
21263
|
-
return null;
|
|
21264
|
-
return Promise.all(peers.filter(isPhone).map(async (p) => ({
|
|
21265
|
-
name: p.name,
|
|
21266
|
-
online: p.ip !== null && (await ts(pingArgs(p.ip))).ok
|
|
21267
|
-
})));
|
|
21268
|
-
}
|
|
21269
|
-
async machines() {
|
|
21270
|
-
const peers = await this.peers();
|
|
21271
|
-
if (peers === null)
|
|
21396
|
+
const listed = await this.listed();
|
|
21397
|
+
if (listed === null || ts === null)
|
|
21272
21398
|
return null;
|
|
21273
21399
|
const health = this.d.health ?? probe;
|
|
21274
|
-
|
|
21400
|
+
const now = (this.d.now ?? Date.now)();
|
|
21401
|
+
const devices = await Promise.all(listed.peers.map(async (p) => {
|
|
21402
|
+
const kind = isPhone(p) ? "phone" : "machine";
|
|
21403
|
+
const ask = listed.here;
|
|
21404
|
+
const [reached, got] = await Promise.all([
|
|
21405
|
+
ask && p.ip !== null ? ts(pingArgs(p.ip)).then((r) => r.ok) : false,
|
|
21406
|
+
ask && kind === "machine" && p.host !== "" ? health(`https://${p.host}`, true) : null
|
|
21407
|
+
]);
|
|
21408
|
+
if (got)
|
|
21409
|
+
this.answered.set(p.host, { machine: got.machine, at: now });
|
|
21410
|
+
const last = kind === "machine" ? this.answered.get(p.host) : undefined;
|
|
21411
|
+
const toyon = got ? { machine: got.machine, answering: true } : last && now - last.at < ANSWERED_RECENTLY_MS ? { machine: last.machine, answering: false } : null;
|
|
21412
|
+
return { name: p.name, host: p.host, kind, reached, toyon };
|
|
21413
|
+
}));
|
|
21414
|
+
const view = { here: listed.here, devices };
|
|
21415
|
+
this.lastView = { view, at: now };
|
|
21416
|
+
return view;
|
|
21275
21417
|
}
|
|
21276
21418
|
isToyon(origin) {
|
|
21277
21419
|
return (this.d.health ?? probe)(origin, false).then((r) => r !== null);
|
|
@@ -21279,7 +21421,7 @@ class Tailnet {
|
|
|
21279
21421
|
}
|
|
21280
21422
|
|
|
21281
21423
|
// ../daemon/src/design/service.ts
|
|
21282
|
-
import { join as
|
|
21424
|
+
import { join as join15 } from "path";
|
|
21283
21425
|
|
|
21284
21426
|
// ../daemon/src/design/scan.ts
|
|
21285
21427
|
var COLOR = /^(#|rgba?\(|hsla?\(|oklch\(|oklab\(|lab\(|lch\(|color\(|color-mix\(|light-dark\()/i;
|
|
@@ -21729,7 +21871,7 @@ class DesignService {
|
|
|
21729
21871
|
async function readAll(root, paths) {
|
|
21730
21872
|
const out = [];
|
|
21731
21873
|
for (const path of paths) {
|
|
21732
|
-
const file = Bun.file(
|
|
21874
|
+
const file = Bun.file(join15(root, path));
|
|
21733
21875
|
if (file.size === 0 || file.size > MAX_FILE_BYTES)
|
|
21734
21876
|
continue;
|
|
21735
21877
|
try {
|
|
@@ -21844,14 +21986,14 @@ function typicalUse(classes) {
|
|
|
21844
21986
|
var normal = (s) => s.replace(/[-_]/g, "").toLowerCase();
|
|
21845
21987
|
async function loadProjectTypescript(root) {
|
|
21846
21988
|
try {
|
|
21847
|
-
return await import(
|
|
21989
|
+
return await import(join15(root, "node_modules", "typescript", "lib", "typescript.js"));
|
|
21848
21990
|
} catch {
|
|
21849
21991
|
return null;
|
|
21850
21992
|
}
|
|
21851
21993
|
}
|
|
21852
21994
|
|
|
21853
21995
|
// ../daemon/src/drafts/store.ts
|
|
21854
|
-
import { existsSync as existsSync18, readFileSync as
|
|
21996
|
+
import { existsSync as existsSync18, readFileSync as readFileSync13, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
21855
21997
|
var SAVE_DELAY_MS = 1000;
|
|
21856
21998
|
|
|
21857
21999
|
class DraftStore {
|
|
@@ -21991,7 +22133,7 @@ function readRecord(file) {
|
|
|
21991
22133
|
if (!existsSync18(file))
|
|
21992
22134
|
return [];
|
|
21993
22135
|
try {
|
|
21994
|
-
const raw = JSON.parse(
|
|
22136
|
+
const raw = JSON.parse(readFileSync13(file, "utf8"));
|
|
21995
22137
|
return raw && typeof raw === "object" ? Object.entries(raw) : [];
|
|
21996
22138
|
} catch (e) {
|
|
21997
22139
|
log.warn("drafts", `skipped an unreadable ${file}`, e);
|
|
@@ -22001,8 +22143,8 @@ function readRecord(file) {
|
|
|
22001
22143
|
function writeAtomic(file, value) {
|
|
22002
22144
|
const tmp = `${file}.tmp`;
|
|
22003
22145
|
try {
|
|
22004
|
-
|
|
22005
|
-
|
|
22146
|
+
writeFileSync9(tmp, JSON.stringify(value));
|
|
22147
|
+
renameSync4(tmp, file);
|
|
22006
22148
|
} catch (e) {
|
|
22007
22149
|
log.error("drafts", `could not write ${file}`, e);
|
|
22008
22150
|
}
|
|
@@ -22251,10 +22393,10 @@ import { basename as basename5, resolve as resolve7 } from "path";
|
|
|
22251
22393
|
import { existsSync as existsSync19, statSync as statSync6 } from "fs";
|
|
22252
22394
|
import { readdir } from "fs/promises";
|
|
22253
22395
|
import { homedir as homedir8 } from "os";
|
|
22254
|
-
import { dirname as dirname6, join as
|
|
22396
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
22255
22397
|
var MAX_ENTRIES = 40;
|
|
22256
22398
|
function expandTilde(raw) {
|
|
22257
|
-
return raw === "~" || raw.startsWith("~/") ?
|
|
22399
|
+
return raw === "~" || raw.startsWith("~/") ? join16(homedir8(), raw.slice(1)) : raw;
|
|
22258
22400
|
}
|
|
22259
22401
|
function collapseTilde(abs) {
|
|
22260
22402
|
const home = homedir8();
|
|
@@ -22265,7 +22407,7 @@ function holdsNothing(names) {
|
|
|
22265
22407
|
}
|
|
22266
22408
|
async function describeFolder(abs) {
|
|
22267
22409
|
const path = collapseTilde(abs);
|
|
22268
|
-
if (existsSync19(
|
|
22410
|
+
if (existsSync19(join16(abs, ".git")))
|
|
22269
22411
|
return { path, kind: "project" };
|
|
22270
22412
|
try {
|
|
22271
22413
|
return { path, kind: holdsNothing(await readdir(abs)) ? "empty" : "folder" };
|
|
@@ -22282,7 +22424,7 @@ function describe2(typed, parent) {
|
|
|
22282
22424
|
return {
|
|
22283
22425
|
exists: existsSync19(typed),
|
|
22284
22426
|
isDir,
|
|
22285
|
-
isRepo: isDir && existsSync19(
|
|
22427
|
+
isRepo: isDir && existsSync19(join16(typed, ".git")),
|
|
22286
22428
|
parentExists: existsSync19(parent)
|
|
22287
22429
|
};
|
|
22288
22430
|
}
|
|
@@ -22307,8 +22449,8 @@ async function browsePath(raw) {
|
|
|
22307
22449
|
continue;
|
|
22308
22450
|
if (!name.toLowerCase().startsWith(prefix))
|
|
22309
22451
|
continue;
|
|
22310
|
-
const abs =
|
|
22311
|
-
entries.push({ path: collapseTilde(abs), name, isRepo: existsSync19(
|
|
22452
|
+
const abs = join16(dir, name);
|
|
22453
|
+
entries.push({ path: collapseTilde(abs), name, isRepo: existsSync19(join16(abs, ".git")) });
|
|
22312
22454
|
}
|
|
22313
22455
|
entries.sort((a, b) => a.isRepo !== b.isRepo ? a.isRepo ? -1 : 1 : a.name.localeCompare(b.name));
|
|
22314
22456
|
return { entries: entries.slice(0, MAX_ENTRIES), target };
|
|
@@ -22422,7 +22564,7 @@ function withFetchLock(repoPath, fn) {
|
|
|
22422
22564
|
var fileLockKey = (absPath) => `file:${absPath}`;
|
|
22423
22565
|
|
|
22424
22566
|
// ../daemon/src/git/status.ts
|
|
22425
|
-
import { join as
|
|
22567
|
+
import { join as join17 } from "path";
|
|
22426
22568
|
async function treeFingerprint(worktreePath) {
|
|
22427
22569
|
const [head, diff, untracked] = await Promise.all([
|
|
22428
22570
|
git(worktreePath, "rev-parse", "HEAD"),
|
|
@@ -22432,7 +22574,7 @@ async function treeFingerprint(worktreePath) {
|
|
|
22432
22574
|
const others = await Promise.all(untracked.out.split(`
|
|
22433
22575
|
`).filter(Boolean).map(async (p) => {
|
|
22434
22576
|
try {
|
|
22435
|
-
const f = Bun.file(
|
|
22577
|
+
const f = Bun.file(join17(worktreePath, p));
|
|
22436
22578
|
const body = f.size > 2000000 ? String(f.size) : Bun.hash(await f.arrayBuffer()).toString(36);
|
|
22437
22579
|
return `${p}:${body}`;
|
|
22438
22580
|
} catch {
|
|
@@ -22517,7 +22659,7 @@ async function numstat(worktreePath, ...range) {
|
|
|
22517
22659
|
}
|
|
22518
22660
|
async function untrackedLines(worktreePath, file) {
|
|
22519
22661
|
try {
|
|
22520
|
-
const f = Bun.file(
|
|
22662
|
+
const f = Bun.file(join17(worktreePath, file));
|
|
22521
22663
|
if (f.size > 2000000)
|
|
22522
22664
|
return {};
|
|
22523
22665
|
const buf = new Uint8Array(await f.arrayBuffer());
|
|
@@ -23283,7 +23425,7 @@ function prNumberOf(url) {
|
|
|
23283
23425
|
|
|
23284
23426
|
// ../daemon/src/preview/errors.ts
|
|
23285
23427
|
var KEPT = 5;
|
|
23286
|
-
var
|
|
23428
|
+
var ROWS_MAX = 3;
|
|
23287
23429
|
var TWICE_MS = 1000;
|
|
23288
23430
|
|
|
23289
23431
|
class PageErrorService {
|
|
@@ -23301,18 +23443,28 @@ class PageErrorService {
|
|
|
23301
23443
|
of(worktreeId) {
|
|
23302
23444
|
let h = this.held.get(worktreeId);
|
|
23303
23445
|
if (!h) {
|
|
23304
|
-
h = { recent: [],
|
|
23446
|
+
h = { recent: [], running: false, noted: new Set, rows: 0 };
|
|
23305
23447
|
this.held.set(worktreeId, h);
|
|
23306
23448
|
}
|
|
23307
23449
|
return h;
|
|
23308
23450
|
}
|
|
23309
23451
|
heard(worktreeId, event) {
|
|
23310
23452
|
const h = this.of(worktreeId);
|
|
23311
|
-
|
|
23312
|
-
|
|
23313
|
-
|
|
23314
|
-
|
|
23315
|
-
|
|
23453
|
+
switch (event.type) {
|
|
23454
|
+
case "user-message":
|
|
23455
|
+
case "fix-asked":
|
|
23456
|
+
h.noted = new Set;
|
|
23457
|
+
h.rows = 0;
|
|
23458
|
+
break;
|
|
23459
|
+
case "turn-start":
|
|
23460
|
+
h.running = true;
|
|
23461
|
+
break;
|
|
23462
|
+
case "turn-end":
|
|
23463
|
+
case "agent-error":
|
|
23464
|
+
case "agent-auth-required":
|
|
23465
|
+
h.running = false;
|
|
23466
|
+
this.flush(worktreeId, h, event.ts);
|
|
23467
|
+
break;
|
|
23316
23468
|
}
|
|
23317
23469
|
}
|
|
23318
23470
|
report(worktreeId, message) {
|
|
@@ -23328,13 +23480,20 @@ class PageErrorService {
|
|
|
23328
23480
|
h.recent.push({ message, ts: now });
|
|
23329
23481
|
if (h.recent.length > KEPT)
|
|
23330
23482
|
h.recent.splice(0, h.recent.length - KEPT);
|
|
23331
|
-
if (!h.
|
|
23483
|
+
if (!h.running)
|
|
23484
|
+
this.flush(worktreeId, h, now);
|
|
23485
|
+
}
|
|
23486
|
+
flush(worktreeId, h, ts) {
|
|
23487
|
+
const fresh = [...new Set(h.recent.map((e) => e.message))].filter((m) => !h.noted.has(m));
|
|
23488
|
+
if (fresh.length === 0 || h.rows >= ROWS_MAX)
|
|
23332
23489
|
return;
|
|
23333
23490
|
const agent = this.d.runtime.agentFor(worktreeId);
|
|
23334
23491
|
if (!agent)
|
|
23335
23492
|
return;
|
|
23336
|
-
|
|
23337
|
-
|
|
23493
|
+
for (const m of fresh)
|
|
23494
|
+
h.noted.add(m);
|
|
23495
|
+
h.rows++;
|
|
23496
|
+
agent.note({ type: "page-error", errors: fresh, ts });
|
|
23338
23497
|
}
|
|
23339
23498
|
loaded(worktreeId) {
|
|
23340
23499
|
const h = this.held.get(worktreeId);
|
|
@@ -23598,9 +23757,9 @@ class AfterLand {
|
|
|
23598
23757
|
}
|
|
23599
23758
|
|
|
23600
23759
|
// ../daemon/src/repos/registry.ts
|
|
23601
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync7, rmSync as rmSync4, writeFileSync as
|
|
23760
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
23602
23761
|
import { homedir as homedir10 } from "os";
|
|
23603
|
-
import { basename as basename7, dirname as dirname8, join as
|
|
23762
|
+
import { basename as basename7, dirname as dirname8, join as join21 } from "path";
|
|
23604
23763
|
|
|
23605
23764
|
// ../daemon/src/git/base.ts
|
|
23606
23765
|
async function resolveBase(repoPath, defaultBranch, route) {
|
|
@@ -23611,8 +23770,8 @@ async function resolveBase(repoPath, defaultBranch, route) {
|
|
|
23611
23770
|
}
|
|
23612
23771
|
|
|
23613
23772
|
// ../daemon/src/repos/config.ts
|
|
23614
|
-
import { closeSync, existsSync as existsSync20, openSync as openSync2, readdirSync as
|
|
23615
|
-
import { join as
|
|
23773
|
+
import { closeSync, existsSync as existsSync20, openSync as openSync2, readdirSync as readdirSync7, readFileSync as readFileSync14, readSync, statSync as statSync7 } from "fs";
|
|
23774
|
+
import { join as join18 } from "path";
|
|
23616
23775
|
|
|
23617
23776
|
// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
23618
23777
|
function createScanner(text, ignoreTrivia = false) {
|
|
@@ -24976,7 +25135,7 @@ function applyEdits(text, edits) {
|
|
|
24976
25135
|
|
|
24977
25136
|
// ../daemon/src/repos/config.ts
|
|
24978
25137
|
var PLACES = [CONFIG_FILES.folder, CONFIG_FILES.root];
|
|
24979
|
-
var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync20(
|
|
25138
|
+
var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync20(join18(repoPath, rel)));
|
|
24980
25139
|
var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
24981
25140
|
function parseSettings(text) {
|
|
24982
25141
|
const errors = [];
|
|
@@ -25001,7 +25160,7 @@ function readConfigFile(repoPath) {
|
|
|
25001
25160
|
for (const rel of files) {
|
|
25002
25161
|
let raw;
|
|
25003
25162
|
try {
|
|
25004
|
-
raw = parseSettings(
|
|
25163
|
+
raw = parseSettings(readFileSync14(join18(repoPath, rel), "utf8"));
|
|
25005
25164
|
} catch (e) {
|
|
25006
25165
|
return { ok: false, reason: `${rel} is not valid JSON: ${e instanceof Error ? e.message : String(e)}` };
|
|
25007
25166
|
}
|
|
@@ -25060,12 +25219,12 @@ function configTarget(repoPath, made) {
|
|
|
25060
25219
|
}
|
|
25061
25220
|
function configBody(repoPath, rel, config) {
|
|
25062
25221
|
const place = PLACES.find((p) => p.local === rel);
|
|
25063
|
-
const abs = place &&
|
|
25222
|
+
const abs = place && join18(repoPath, place.shared);
|
|
25064
25223
|
if (!abs || !existsSync20(abs))
|
|
25065
25224
|
return config;
|
|
25066
25225
|
let shared;
|
|
25067
25226
|
try {
|
|
25068
|
-
shared = parseSettings(
|
|
25227
|
+
shared = parseSettings(readFileSync14(abs, "utf8"));
|
|
25069
25228
|
} catch {
|
|
25070
25229
|
return config;
|
|
25071
25230
|
}
|
|
@@ -25078,7 +25237,7 @@ function configText(repoPath, rel, config) {
|
|
|
25078
25237
|
let text;
|
|
25079
25238
|
let was;
|
|
25080
25239
|
try {
|
|
25081
|
-
text =
|
|
25240
|
+
text = readFileSync14(join18(repoPath, rel), "utf8");
|
|
25082
25241
|
was = parseSettings(text);
|
|
25083
25242
|
} catch {
|
|
25084
25243
|
return fresh;
|
|
@@ -25108,9 +25267,9 @@ function detectConfig(repoPath) {
|
|
|
25108
25267
|
return { config: explicit.config, needsSetup: false };
|
|
25109
25268
|
if (explicit)
|
|
25110
25269
|
log.warn("config", `${repoPath}: ${explicit.reason}; detecting instead`);
|
|
25111
|
-
const pkgPath =
|
|
25270
|
+
const pkgPath = join18(repoPath, "package.json");
|
|
25112
25271
|
if (existsSync20(pkgPath)) {
|
|
25113
|
-
const pkg = JSON.parse(
|
|
25272
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
|
|
25114
25273
|
const scripts = pkg.scripts ?? {};
|
|
25115
25274
|
const runner = detectRunner(repoPath, pkg.packageManager);
|
|
25116
25275
|
const pages = detectPages(repoPath, scripts, runner);
|
|
@@ -25131,7 +25290,7 @@ function detectConfig(repoPath) {
|
|
|
25131
25290
|
};
|
|
25132
25291
|
}
|
|
25133
25292
|
}
|
|
25134
|
-
if (existsSync20(
|
|
25293
|
+
if (existsSync20(join18(repoPath, "start.sh"))) {
|
|
25135
25294
|
return { config: { run: { app: "./start.sh" } }, needsSetup: true, from: "start.sh" };
|
|
25136
25295
|
}
|
|
25137
25296
|
const assumed = assumeNothingToRun(repoPath);
|
|
@@ -25174,24 +25333,24 @@ function assumeNothingToRun(repoPath) {
|
|
|
25174
25333
|
return marker && !servesPage(repoPath) ? marker : undefined;
|
|
25175
25334
|
}
|
|
25176
25335
|
function markerIn(repoPath) {
|
|
25177
|
-
const named = NO_PAGE_MARKERS.find((f) => existsSync20(
|
|
25336
|
+
const named = NO_PAGE_MARKERS.find((f) => existsSync20(join18(repoPath, f)));
|
|
25178
25337
|
if (named)
|
|
25179
25338
|
return named;
|
|
25180
25339
|
let entries;
|
|
25181
25340
|
try {
|
|
25182
|
-
entries =
|
|
25341
|
+
entries = readdirSync7(repoPath);
|
|
25183
25342
|
} catch {
|
|
25184
25343
|
return;
|
|
25185
25344
|
}
|
|
25186
25345
|
return entries.find((f) => NO_PAGE_EXTENSIONS.some((ext) => f.endsWith(ext)));
|
|
25187
25346
|
}
|
|
25188
25347
|
function servesPage(repoPath) {
|
|
25189
|
-
if (WEB_FILES.some((f) => existsSync20(
|
|
25348
|
+
if (WEB_FILES.some((f) => existsSync20(join18(repoPath, f))))
|
|
25190
25349
|
return true;
|
|
25191
|
-
if (/^web:/m.test(head(
|
|
25350
|
+
if (/^web:/m.test(head(join18(repoPath, "Procfile"))))
|
|
25192
25351
|
return true;
|
|
25193
25352
|
for (const [files, names] of WEB_DEPENDENCIES) {
|
|
25194
|
-
const text = files.map((f) => head(
|
|
25353
|
+
const text = files.map((f) => head(join18(repoPath, f))).join(`
|
|
25195
25354
|
`);
|
|
25196
25355
|
if (text.trim() && names.some((n) => mentions(text, n)))
|
|
25197
25356
|
return true;
|
|
@@ -25201,14 +25360,14 @@ function servesPage(repoPath) {
|
|
|
25201
25360
|
function frontEndBeside(repoPath) {
|
|
25202
25361
|
let dirs;
|
|
25203
25362
|
try {
|
|
25204
|
-
dirs =
|
|
25363
|
+
dirs = readdirSync7(repoPath, { withFileTypes: true });
|
|
25205
25364
|
} catch {
|
|
25206
25365
|
return false;
|
|
25207
25366
|
}
|
|
25208
25367
|
return dirs.some((d) => {
|
|
25209
25368
|
if (!d.isDirectory() || d.name.startsWith(".") || NOT_A_PACKAGE.has(d.name))
|
|
25210
25369
|
return false;
|
|
25211
|
-
const pkg = head(
|
|
25370
|
+
const pkg = head(join18(repoPath, d.name, "package.json"));
|
|
25212
25371
|
if (!pkg)
|
|
25213
25372
|
return false;
|
|
25214
25373
|
try {
|
|
@@ -25264,11 +25423,11 @@ function isLocalDatabase(url) {
|
|
|
25264
25423
|
}
|
|
25265
25424
|
function detectServices(repoPath) {
|
|
25266
25425
|
const out = {};
|
|
25267
|
-
const compose = COMPOSE_FILES.find((f) => existsSync20(
|
|
25426
|
+
const compose = COMPOSE_FILES.find((f) => existsSync20(join18(repoPath, f)));
|
|
25268
25427
|
if (compose)
|
|
25269
25428
|
out.compose = compose;
|
|
25270
25429
|
for (const file of ENV_FILES) {
|
|
25271
|
-
const text = head(
|
|
25430
|
+
const text = head(join18(repoPath, file));
|
|
25272
25431
|
if (!text)
|
|
25273
25432
|
continue;
|
|
25274
25433
|
for (const name of DB_URL_NAMES) {
|
|
@@ -25292,7 +25451,7 @@ function inferPaths(cmd, repoPath, depth = 1) {
|
|
|
25292
25451
|
return;
|
|
25293
25452
|
let dir = false;
|
|
25294
25453
|
try {
|
|
25295
|
-
dir = statSync7(
|
|
25454
|
+
dir = statSync7(join18(repoPath, clean)).isDirectory();
|
|
25296
25455
|
} catch {
|
|
25297
25456
|
return;
|
|
25298
25457
|
}
|
|
@@ -25303,25 +25462,25 @@ function inferPaths(cmd, repoPath, depth = 1) {
|
|
|
25303
25462
|
const t = tokens[i];
|
|
25304
25463
|
if (t === "cd") {
|
|
25305
25464
|
const dir = tokens[i + 1];
|
|
25306
|
-
if (dir && !dir.startsWith("-") && existsSync20(
|
|
25465
|
+
if (dir && !dir.startsWith("-") && existsSync20(join18(repoPath, dir)))
|
|
25307
25466
|
add(dir);
|
|
25308
25467
|
continue;
|
|
25309
25468
|
}
|
|
25310
25469
|
if (t.startsWith("-") || t.startsWith("$"))
|
|
25311
25470
|
continue;
|
|
25312
25471
|
const mod = /^([A-Za-z_]\w*)(?:\.\w+)*:\w+$/.exec(t);
|
|
25313
|
-
if (mod?.[1] && existsSync20(
|
|
25472
|
+
if (mod?.[1] && existsSync20(join18(repoPath, mod[1]))) {
|
|
25314
25473
|
add(mod[1]);
|
|
25315
25474
|
continue;
|
|
25316
25475
|
}
|
|
25317
25476
|
const rel = t.replace(/^\.\//, "");
|
|
25318
25477
|
if (rel.startsWith("/") || rel.startsWith("..") || rel.includes("="))
|
|
25319
25478
|
continue;
|
|
25320
|
-
if (!existsSync20(
|
|
25479
|
+
if (!existsSync20(join18(repoPath, rel)))
|
|
25321
25480
|
continue;
|
|
25322
25481
|
add(rel);
|
|
25323
25482
|
if (depth > 0 && rel.endsWith(".sh")) {
|
|
25324
|
-
for (const p of inferPaths(head(
|
|
25483
|
+
for (const p of inferPaths(head(join18(repoPath, rel)), repoPath, depth - 1))
|
|
25325
25484
|
out.add(p);
|
|
25326
25485
|
}
|
|
25327
25486
|
}
|
|
@@ -25344,31 +25503,31 @@ function procCommand(runner, script, body) {
|
|
|
25344
25503
|
return `${base}${runner === "npm" ? " --" : ""} ${flag}`;
|
|
25345
25504
|
}
|
|
25346
25505
|
function detectPages(repoPath, scripts, runner) {
|
|
25347
|
-
if (!scripts.build || !existsSync20(
|
|
25506
|
+
if (!scripts.build || !existsSync20(join18(repoPath, "functions")))
|
|
25348
25507
|
return null;
|
|
25349
|
-
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) =>
|
|
25508
|
+
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join18(repoPath, f)).find((f) => existsSync20(f));
|
|
25350
25509
|
if (!cfg)
|
|
25351
25510
|
return null;
|
|
25352
|
-
const out =
|
|
25511
|
+
const out = readFileSync14(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
|
|
25353
25512
|
if (!out)
|
|
25354
25513
|
return null;
|
|
25355
25514
|
return { web: `${runner} run build && npx wrangler pages dev ${out} --ip 127.0.0.1 --port $PORT` };
|
|
25356
25515
|
}
|
|
25357
25516
|
function detectRunner(repoPath, packageManager) {
|
|
25358
|
-
if (existsSync20(
|
|
25517
|
+
if (existsSync20(join18(repoPath, "bun.lock")) || existsSync20(join18(repoPath, "bun.lockb")))
|
|
25359
25518
|
return "bun";
|
|
25360
|
-
if (existsSync20(
|
|
25519
|
+
if (existsSync20(join18(repoPath, "pnpm-lock.yaml")))
|
|
25361
25520
|
return "pnpm";
|
|
25362
|
-
if (existsSync20(
|
|
25521
|
+
if (existsSync20(join18(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
|
|
25363
25522
|
return "yarn";
|
|
25364
25523
|
return "npm";
|
|
25365
25524
|
}
|
|
25366
25525
|
|
|
25367
25526
|
// ../daemon/src/repos/create.ts
|
|
25368
|
-
import { existsSync as existsSync21,
|
|
25527
|
+
import { existsSync as existsSync21, realpathSync as realpathSync2, statSync as statSync8 } from "fs";
|
|
25369
25528
|
import { appendFile as appendFile2, mkdir as mkdir3, rm as rm2 } from "fs/promises";
|
|
25370
25529
|
import { homedir as homedir9 } from "os";
|
|
25371
|
-
import { dirname as dirname7, join as
|
|
25530
|
+
import { dirname as dirname7, join as join19, resolve as resolve8 } from "path";
|
|
25372
25531
|
function isInside(dir, parent) {
|
|
25373
25532
|
const p = parent.replace(/\/+$/, "");
|
|
25374
25533
|
return dir === p || dir.startsWith(`${p}/`);
|
|
@@ -25379,31 +25538,28 @@ function planProject(opts, home = homedir9()) {
|
|
|
25379
25538
|
throw new UserError(nameError);
|
|
25380
25539
|
const first = firstProjectsFolder(opts.parent, home);
|
|
25381
25540
|
const parent = first ?? resolveParent(opts.parent);
|
|
25382
|
-
const dir =
|
|
25541
|
+
const dir = join19(parent, opts.name.trim());
|
|
25383
25542
|
if (existsSync21(dir))
|
|
25384
25543
|
throw new UserError(`${dir} already exists`);
|
|
25385
25544
|
return { parent, dir, makeParent: first !== null };
|
|
25386
25545
|
}
|
|
25387
25546
|
function firstProjectsFolder(raw, home) {
|
|
25388
25547
|
const typed = expandTilde(raw.trim()).replace(/\/+$/, "");
|
|
25389
|
-
if (typed !==
|
|
25548
|
+
if (typed !== join19(home, PROJECTS_FOLDER) || existsSync21(typed) || !existsSync21(home))
|
|
25390
25549
|
return null;
|
|
25391
|
-
return
|
|
25550
|
+
return join19(realpathSync2(home), PROJECTS_FOLDER);
|
|
25392
25551
|
}
|
|
25393
25552
|
function planInPlace(opts) {
|
|
25394
25553
|
const name = opts.name.trim();
|
|
25395
25554
|
if (!name || name === "." || name === ".." || name.includes("/")) {
|
|
25396
25555
|
throw new UserError(`${opts.name} is not a folder name`);
|
|
25397
25556
|
}
|
|
25398
|
-
const typed =
|
|
25557
|
+
const typed = join19(resolveParent(opts.parent), name);
|
|
25399
25558
|
if (!existsSync21(typed) || !statSync8(typed).isDirectory())
|
|
25400
25559
|
throw new UserError(`${typed} is not a folder`);
|
|
25401
25560
|
const dir = realpathSync2(typed);
|
|
25402
|
-
if (existsSync21(
|
|
25561
|
+
if (existsSync21(join19(dir, ".git")))
|
|
25403
25562
|
throw new UserError(`${dir} is already a project; open it instead`);
|
|
25404
|
-
if (!holdsNothing(readdirSync7(dir))) {
|
|
25405
|
-
throw new UserError(`${dir} is not empty; only an empty folder can become a project where it is`);
|
|
25406
|
-
}
|
|
25407
25563
|
return { parent: dirname7(dir), dir, makeParent: false };
|
|
25408
25564
|
}
|
|
25409
25565
|
function resolveParent(raw) {
|
|
@@ -25423,7 +25579,7 @@ async function createRepoDir(opts, home = homedir9()) {
|
|
|
25423
25579
|
await mkdir3(parent);
|
|
25424
25580
|
await mkdir3(dir);
|
|
25425
25581
|
try {
|
|
25426
|
-
await
|
|
25582
|
+
await initWithFirstCommit(dir);
|
|
25427
25583
|
} catch (e) {
|
|
25428
25584
|
await undoCreate(dir);
|
|
25429
25585
|
throw e;
|
|
@@ -25434,7 +25590,7 @@ async function initRepoInPlace(opts) {
|
|
|
25434
25590
|
const { dir } = planInPlace(opts);
|
|
25435
25591
|
await requireGitIdentity(dir);
|
|
25436
25592
|
try {
|
|
25437
|
-
await
|
|
25593
|
+
await initWithFirstCommit(dir);
|
|
25438
25594
|
} catch (e) {
|
|
25439
25595
|
await undoInit(dir);
|
|
25440
25596
|
throw e;
|
|
@@ -25445,7 +25601,7 @@ var LOCAL_EXCLUDES = [
|
|
|
25445
25601
|
".DS_Store",
|
|
25446
25602
|
"node_modules/"
|
|
25447
25603
|
];
|
|
25448
|
-
async function
|
|
25604
|
+
async function initWithFirstCommit(dir) {
|
|
25449
25605
|
await ok(git(dir, "init", "-q"), "git init");
|
|
25450
25606
|
const exclude = await git(dir, "rev-parse", "--git-path", "info/exclude");
|
|
25451
25607
|
if (!exclude.ok)
|
|
@@ -25456,7 +25612,8 @@ async function initWithEmptyCommit(dir) {
|
|
|
25456
25612
|
${LOCAL_EXCLUDES.join(`
|
|
25457
25613
|
`)}
|
|
25458
25614
|
`);
|
|
25459
|
-
await ok(git(dir, "
|
|
25615
|
+
await ok(git(dir, "add", "-A"), "git add");
|
|
25616
|
+
await ok(git(dir, "-c", "commit.gpgsign=false", "commit", "--no-verify", "--allow-empty", "-qm", "initial commit"), "git commit");
|
|
25460
25617
|
}
|
|
25461
25618
|
async function cloneInto(plan, name, url, opts = {}) {
|
|
25462
25619
|
if (!url)
|
|
@@ -25490,13 +25647,13 @@ async function undoCreate(dir) {
|
|
|
25490
25647
|
}
|
|
25491
25648
|
async function undoInit(dir) {
|
|
25492
25649
|
try {
|
|
25493
|
-
await rm2(
|
|
25650
|
+
await rm2(join19(dir, ".git"), { recursive: true, force: true });
|
|
25494
25651
|
} catch (e) {
|
|
25495
25652
|
log.warn(dir, "could not take back a half-made .git", e);
|
|
25496
25653
|
}
|
|
25497
25654
|
}
|
|
25498
25655
|
async function unmakeProject(dir, made) {
|
|
25499
|
-
const target = made === "folder" ? dir :
|
|
25656
|
+
const target = made === "folder" ? dir : join19(dir, ".git");
|
|
25500
25657
|
try {
|
|
25501
25658
|
await rm2(target, { recursive: true, force: true });
|
|
25502
25659
|
} catch (e) {
|
|
@@ -25527,7 +25684,7 @@ async function ok(p, what) {
|
|
|
25527
25684
|
|
|
25528
25685
|
// ../daemon/src/repos/watcher.ts
|
|
25529
25686
|
import { existsSync as existsSync22, watch } from "fs";
|
|
25530
|
-
import { basename as basename6, join as
|
|
25687
|
+
import { basename as basename6, join as join20 } from "path";
|
|
25531
25688
|
function watchDefaultBranch(repoPath, branch, onMove) {
|
|
25532
25689
|
const last = { local: null, upstream: null };
|
|
25533
25690
|
const read = async () => {
|
|
@@ -25580,13 +25737,13 @@ function watchDefaultBranch(repoPath, branch, onMove) {
|
|
|
25580
25737
|
]);
|
|
25581
25738
|
if (stopped)
|
|
25582
25739
|
return;
|
|
25583
|
-
const gitDir = common.ok && common.out ? common.out :
|
|
25740
|
+
const gitDir = common.ok && common.out ? common.out : join20(repoPath, ".git");
|
|
25584
25741
|
tryWatch(gitDir);
|
|
25585
|
-
tryWatch(
|
|
25742
|
+
tryWatch(join20(gitDir, "refs", "heads"));
|
|
25586
25743
|
if (!remote.ok || !remote.out)
|
|
25587
25744
|
return;
|
|
25588
|
-
const remotes =
|
|
25589
|
-
const mine =
|
|
25745
|
+
const remotes = join20(gitDir, "refs", "remotes");
|
|
25746
|
+
const mine = join20(remotes, remote.out);
|
|
25590
25747
|
let inner = null;
|
|
25591
25748
|
let mid = null;
|
|
25592
25749
|
const armInner = () => {
|
|
@@ -25602,7 +25759,7 @@ function watchDefaultBranch(repoPath, branch, onMove) {
|
|
|
25602
25759
|
}) : null;
|
|
25603
25760
|
armInner();
|
|
25604
25761
|
};
|
|
25605
|
-
tryWatch(
|
|
25762
|
+
tryWatch(join20(gitDir, "refs"), (_event, filename) => {
|
|
25606
25763
|
if (!filename || filename === "remotes")
|
|
25607
25764
|
armMid();
|
|
25608
25765
|
schedule();
|
|
@@ -25631,7 +25788,7 @@ function watchWorktreeDir(repoPath, onChange) {
|
|
|
25631
25788
|
}, 300);
|
|
25632
25789
|
};
|
|
25633
25790
|
const arm = (commonDir) => {
|
|
25634
|
-
const dir =
|
|
25791
|
+
const dir = join20(commonDir, "worktrees");
|
|
25635
25792
|
const armInner = () => {
|
|
25636
25793
|
inner?.close();
|
|
25637
25794
|
inner = null;
|
|
@@ -25674,7 +25831,7 @@ function watchWorktreeDir(repoPath, onChange) {
|
|
|
25674
25831
|
function watchConfigFile(repoPath, onChange) {
|
|
25675
25832
|
const atRoot = new Set([CONFIG_FILES.root.shared, CONFIG_FILES.root.local, CONFIG_DIR]);
|
|
25676
25833
|
const inDir = new Set([CONFIG_FILES.folder.shared, CONFIG_FILES.folder.local].map((rel) => basename6(rel)));
|
|
25677
|
-
const dir =
|
|
25834
|
+
const dir = join20(repoPath, CONFIG_DIR);
|
|
25678
25835
|
let timer = null;
|
|
25679
25836
|
let outer = null;
|
|
25680
25837
|
let inner = null;
|
|
@@ -25811,7 +25968,7 @@ class RepoRegistry {
|
|
|
25811
25968
|
return `${repo.name} is set up now`;
|
|
25812
25969
|
if (main.promptedAt || main.lastTurn)
|
|
25813
25970
|
return `${repo.name} has a chat now`;
|
|
25814
|
-
if (existsSync23(
|
|
25971
|
+
if (existsSync23(join21(repo.path, ".claude")))
|
|
25815
25972
|
return `an agent has already run in ${repo.name}`;
|
|
25816
25973
|
if ((await statusFiles(repo.path)).length > 0 || !await treeEmpty(repo.path)) {
|
|
25817
25974
|
return `${repo.name} has files in it now`;
|
|
@@ -25821,9 +25978,13 @@ class RepoRegistry {
|
|
|
25821
25978
|
return null;
|
|
25822
25979
|
}
|
|
25823
25980
|
refuseIfManaged(dir) {
|
|
25824
|
-
const
|
|
25825
|
-
if (
|
|
25981
|
+
const records = [...this.d.state.repos, ...this.d.state.worktrees];
|
|
25982
|
+
if (records.some((r) => isInside(dir, r.path))) {
|
|
25826
25983
|
throw new UserError(`${dir} is inside a project Toyon already manages`);
|
|
25984
|
+
}
|
|
25985
|
+
if (records.some((r) => isInside(r.path, dir))) {
|
|
25986
|
+
throw new UserError(`${dir} holds projects Toyon already manages`);
|
|
25987
|
+
}
|
|
25827
25988
|
}
|
|
25828
25989
|
get pending() {
|
|
25829
25990
|
return [...this.imports.values()].map((i) => i.pending).sort((a, b) => a.startedAt - b.startedAt);
|
|
@@ -25962,10 +26123,10 @@ class RepoRegistry {
|
|
|
25962
26123
|
throw new UserError(current.reason);
|
|
25963
26124
|
const was = configTarget(repo.path, !!repo.made);
|
|
25964
26125
|
const rel = configSibling(was, kind);
|
|
25965
|
-
const moved = rel !== was && existsSync23(
|
|
26126
|
+
const moved = rel !== was && existsSync23(join21(repo.path, was));
|
|
25966
26127
|
if (moved && !(kind === "local" && await isTracked(repo.path, was))) {
|
|
25967
26128
|
try {
|
|
25968
|
-
rmSync4(
|
|
26129
|
+
rmSync4(join21(repo.path, was));
|
|
25969
26130
|
} catch (e) {
|
|
25970
26131
|
log.warn(repoId, `could not remove ${was}`, e);
|
|
25971
26132
|
}
|
|
@@ -25974,10 +26135,10 @@ class RepoRegistry {
|
|
|
25974
26135
|
}
|
|
25975
26136
|
if (isLocalConfigFile(rel))
|
|
25976
26137
|
await excludeFromGit(repo.path, rel);
|
|
25977
|
-
const file =
|
|
26138
|
+
const file = join21(repo.path, rel);
|
|
25978
26139
|
try {
|
|
25979
26140
|
mkdirSync7(dirname8(file), { recursive: true });
|
|
25980
|
-
|
|
26141
|
+
writeFileSync10(file, configText(repo.path, rel, config));
|
|
25981
26142
|
} catch (e) {
|
|
25982
26143
|
log.warn(repoId, `could not write ${rel}`, e);
|
|
25983
26144
|
}
|
|
@@ -26078,7 +26239,7 @@ class RepoRegistry {
|
|
|
26078
26239
|
}
|
|
26079
26240
|
placeConfig(repo) {
|
|
26080
26241
|
const rel = configTarget(repo.path, !!repo.made);
|
|
26081
|
-
if (isLocalConfigFile(rel) && existsSync23(
|
|
26242
|
+
if (isLocalConfigFile(rel) && existsSync23(join21(repo.path, rel))) {
|
|
26082
26243
|
fireAndForget(repo.id, excludeFromGit(repo.path, rel), "exclude local settings");
|
|
26083
26244
|
}
|
|
26084
26245
|
if (repo.configFile === rel)
|
|
@@ -26170,7 +26331,7 @@ class RepoRegistry {
|
|
|
26170
26331
|
}
|
|
26171
26332
|
|
|
26172
26333
|
// ../daemon/src/routes/service.ts
|
|
26173
|
-
import { join as
|
|
26334
|
+
import { join as join22 } from "path";
|
|
26174
26335
|
|
|
26175
26336
|
// ../daemon/src/routes/fileRouters.ts
|
|
26176
26337
|
var SKIP = /(^|\/)(node_modules|dist|build|out|coverage|\.next|\.nuxt|\.output|\.svelte-kit|\.vercel|\.netlify)\//;
|
|
@@ -27370,7 +27531,7 @@ class RouteService {
|
|
|
27370
27531
|
return mergeRoutes(fileRoutes(paths, roots), code);
|
|
27371
27532
|
}
|
|
27372
27533
|
async cachedRead(root, path, had, kept, decode) {
|
|
27373
|
-
const abs =
|
|
27534
|
+
const abs = join22(root, path);
|
|
27374
27535
|
const stat = await this.fs.stat(abs);
|
|
27375
27536
|
if (!stat)
|
|
27376
27537
|
return null;
|
|
@@ -27395,7 +27556,7 @@ class RouteService {
|
|
|
27395
27556
|
}
|
|
27396
27557
|
}
|
|
27397
27558
|
async hash(root, file) {
|
|
27398
|
-
const abs =
|
|
27559
|
+
const abs = join22(root, file);
|
|
27399
27560
|
const stat = await this.fs.stat(abs);
|
|
27400
27561
|
if (!stat)
|
|
27401
27562
|
return null;
|
|
@@ -27657,7 +27818,7 @@ function isAlive(pid) {
|
|
|
27657
27818
|
}
|
|
27658
27819
|
|
|
27659
27820
|
// ../daemon/src/runtime/bridge-script.ts
|
|
27660
|
-
import { existsSync as existsSync24, readFileSync as
|
|
27821
|
+
import { existsSync as existsSync24, readFileSync as readFileSync15, statSync as statSync9 } from "fs";
|
|
27661
27822
|
var LEARNED_MAX = 8;
|
|
27662
27823
|
|
|
27663
27824
|
class BridgeScript {
|
|
@@ -27696,7 +27857,7 @@ class BridgeScript {
|
|
|
27696
27857
|
this.stamp = stamp;
|
|
27697
27858
|
const origins = [...this.origins, ...this.learned];
|
|
27698
27859
|
this.cache = `window.__toyonShellOrigins=${JSON.stringify(origins)};
|
|
27699
|
-
${
|
|
27860
|
+
${readFileSync15(this.path, "utf8")}`;
|
|
27700
27861
|
return this.cache;
|
|
27701
27862
|
}
|
|
27702
27863
|
}
|
|
@@ -27720,9 +27881,9 @@ import { homedir as homedir12, hostname as hostname2 } from "os";
|
|
|
27720
27881
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
27721
27882
|
|
|
27722
27883
|
// ../daemon/src/agent/config.ts
|
|
27723
|
-
import { existsSync as existsSync25, readFileSync as
|
|
27884
|
+
import { existsSync as existsSync25, readFileSync as readFileSync16 } from "fs";
|
|
27724
27885
|
import { homedir as homedir11 } from "os";
|
|
27725
|
-
import { join as
|
|
27886
|
+
import { join as join23 } from "path";
|
|
27726
27887
|
function agentConfigFiles(spec, repoPath, home = homedir11()) {
|
|
27727
27888
|
const file = (id, label, path) => ({
|
|
27728
27889
|
id,
|
|
@@ -27732,30 +27893,30 @@ function agentConfigFiles(spec, repoPath, home = homedir11()) {
|
|
|
27732
27893
|
});
|
|
27733
27894
|
if (spec.id === "claude") {
|
|
27734
27895
|
return [
|
|
27735
|
-
file("user-config", "user config (MCP servers, projects)",
|
|
27736
|
-
file("user-settings", "user settings (permissions, hooks)",
|
|
27737
|
-
...repoPath ? [file("project-mcp", "project MCP servers",
|
|
27738
|
-
...repoPath ? [file("project-settings", "project settings",
|
|
27896
|
+
file("user-config", "user config (MCP servers, projects)", join23(home, ".claude.json")),
|
|
27897
|
+
file("user-settings", "user settings (permissions, hooks)", join23(home, ".claude", "settings.json")),
|
|
27898
|
+
...repoPath ? [file("project-mcp", "project MCP servers", join23(repoPath, ".mcp.json"))] : [],
|
|
27899
|
+
...repoPath ? [file("project-settings", "project settings", join23(repoPath, ".claude", "settings.json"))] : []
|
|
27739
27900
|
];
|
|
27740
27901
|
}
|
|
27741
27902
|
if (spec.id === "codex")
|
|
27742
|
-
return [file("user-config", "config",
|
|
27903
|
+
return [file("user-config", "config", join23(home, ".codex", "config.toml"))];
|
|
27743
27904
|
return [];
|
|
27744
27905
|
}
|
|
27745
27906
|
function describeAgentConfig(spec, repoPath, home = homedir11()) {
|
|
27746
27907
|
const files = agentConfigFiles(spec, repoPath, home);
|
|
27747
27908
|
const servers = [];
|
|
27748
27909
|
if (spec.id === "claude") {
|
|
27749
|
-
const user = readJson(
|
|
27910
|
+
const user = readJson(join23(home, ".claude.json"));
|
|
27750
27911
|
servers.push(...serversOf(user?.mcpServers, "user"));
|
|
27751
27912
|
if (repoPath) {
|
|
27752
27913
|
const projects = user?.projects;
|
|
27753
27914
|
const mine = projects?.[repoPath];
|
|
27754
27915
|
servers.push(...serversOf(mine?.mcpServers, "local"));
|
|
27755
|
-
servers.push(...serversOf(readJson(
|
|
27916
|
+
servers.push(...serversOf(readJson(join23(repoPath, ".mcp.json"))?.mcpServers, "project"));
|
|
27756
27917
|
}
|
|
27757
27918
|
} else if (spec.id === "codex") {
|
|
27758
|
-
servers.push(...codexServers(
|
|
27919
|
+
servers.push(...codexServers(join23(home, ".codex", "config.toml")));
|
|
27759
27920
|
}
|
|
27760
27921
|
return { agent: spec.id, files, servers };
|
|
27761
27922
|
}
|
|
@@ -27763,7 +27924,7 @@ function readJson(path) {
|
|
|
27763
27924
|
if (!existsSync25(path))
|
|
27764
27925
|
return null;
|
|
27765
27926
|
try {
|
|
27766
|
-
const v = JSON.parse(
|
|
27927
|
+
const v = JSON.parse(readFileSync16(path, "utf8"));
|
|
27767
27928
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
27768
27929
|
} catch (e) {
|
|
27769
27930
|
log.warn("agent-config", `${path} is not valid JSON`, e);
|
|
@@ -27785,7 +27946,7 @@ function codexServers(path) {
|
|
|
27785
27946
|
return [];
|
|
27786
27947
|
const out = [];
|
|
27787
27948
|
let current = null;
|
|
27788
|
-
for (const raw of
|
|
27949
|
+
for (const raw of readFileSync16(path, "utf8").split(`
|
|
27789
27950
|
`)) {
|
|
27790
27951
|
const line = raw.trim();
|
|
27791
27952
|
const header = /^\[mcp_servers\.("?)([^\]"]+)\1\]$/.exec(line);
|
|
@@ -28579,6 +28740,9 @@ var handlers = {
|
|
|
28579
28740
|
const { result } = await s.worktrees.sync(msg.worktreeId);
|
|
28580
28741
|
await notify(s, ctx, msg.worktreeId, shipped(msg.worktreeId, result.ok, result.message, result.asked ? { asked: true } : {}));
|
|
28581
28742
|
},
|
|
28743
|
+
"unqueue-sync"(msg, _ctx, s) {
|
|
28744
|
+
s.worktrees.unqueueSync(msg.worktreeId);
|
|
28745
|
+
},
|
|
28582
28746
|
async "pull-main"(msg, ctx, s) {
|
|
28583
28747
|
const result = await s.worktrees.pull(msg.worktreeId);
|
|
28584
28748
|
await notify(s, ctx, msg.worktreeId, shipped(msg.worktreeId, result.ok, result.message));
|
|
@@ -28954,7 +29118,7 @@ async function dispatch(msg, ctx, s) {
|
|
|
28954
29118
|
// ../daemon/src/server/http.ts
|
|
28955
29119
|
import { existsSync as existsSync26 } from "fs";
|
|
28956
29120
|
import { hostname } from "os";
|
|
28957
|
-
import { join as
|
|
29121
|
+
import { join as join24 } from "path";
|
|
28958
29122
|
|
|
28959
29123
|
// ../daemon/src/server/cors.ts
|
|
28960
29124
|
var ALLOW_HEADERS = "authorization, content-type";
|
|
@@ -29006,7 +29170,7 @@ function pairingRoutes(d) {
|
|
|
29006
29170
|
const body = state === "let-in" ? { state, token: d.token } : { state };
|
|
29007
29171
|
return json(body, headers);
|
|
29008
29172
|
}
|
|
29009
|
-
if (!url.pathname.match(/^\/(knocks|knock\/[A-Za-z0-9_-]+\/answer|
|
|
29173
|
+
if (!url.pathname.match(/^\/(knocks|knock\/[A-Za-z0-9_-]+\/answer|tailnet|tailscale|remote|machines)$/))
|
|
29010
29174
|
return;
|
|
29011
29175
|
if (!d.authed(req))
|
|
29012
29176
|
return unauthorized();
|
|
@@ -29027,14 +29191,18 @@ function pairingRoutes(d) {
|
|
|
29027
29191
|
d.knocks.answer(answered, body.letIn);
|
|
29028
29192
|
return new Response(null, { status: 204 });
|
|
29029
29193
|
}
|
|
29030
|
-
if (url.pathname === "/phones" && req.method === "GET") {
|
|
29031
|
-
const body = remote && isTailnetName(remote.host) ? await d.tailnet.phones() : null;
|
|
29032
|
-
return json(body);
|
|
29033
|
-
}
|
|
29034
29194
|
if (url.pathname === "/tailnet" && req.method === "GET") {
|
|
29035
|
-
const body = await d.tailnet.
|
|
29195
|
+
const body = await d.tailnet.view();
|
|
29036
29196
|
return json(body);
|
|
29037
29197
|
}
|
|
29198
|
+
if (url.pathname === "/tailscale" && req.method === "POST") {
|
|
29199
|
+
const body = await req.json().catch(() => ({}));
|
|
29200
|
+
if (body.up === true)
|
|
29201
|
+
return json({ refused: await d.tailnet.up() });
|
|
29202
|
+
if (body.login === true)
|
|
29203
|
+
return json(await d.tailnet.login());
|
|
29204
|
+
return new Response("say up or login", { status: 400 });
|
|
29205
|
+
}
|
|
29038
29206
|
if (url.pathname === "/remote" && req.method === "POST") {
|
|
29039
29207
|
const body = await req.json().catch(() => ({}));
|
|
29040
29208
|
try {
|
|
@@ -29285,7 +29453,7 @@ function createFetch(opts) {
|
|
|
29285
29453
|
});
|
|
29286
29454
|
}
|
|
29287
29455
|
const rel = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
29288
|
-
const file =
|
|
29456
|
+
const file = join24(opts.shellDist, rel.replaceAll("..", ""));
|
|
29289
29457
|
const hashed = rel.startsWith("/assets/");
|
|
29290
29458
|
if (existsSync26(file) && Bun.file(file).size > 0) {
|
|
29291
29459
|
if (rel === "/manifest.json") {
|
|
@@ -29306,7 +29474,7 @@ function createFetch(opts) {
|
|
|
29306
29474
|
}
|
|
29307
29475
|
if (hashed)
|
|
29308
29476
|
return new Response("not found", { status: 404 });
|
|
29309
|
-
const index =
|
|
29477
|
+
const index = join24(opts.shellDist, "index.html");
|
|
29310
29478
|
if (existsSync26(index))
|
|
29311
29479
|
return new Response(Bun.file(index), { headers: { "cache-control": NO_STORE2 } });
|
|
29312
29480
|
return new Response("Toyon daemon running; shell not built (run: bun run build)", { status: 200 });
|
|
@@ -29681,7 +29849,7 @@ function startServer(opts) {
|
|
|
29681
29849
|
bootstrap: helloFrame,
|
|
29682
29850
|
manifestColors: () => {
|
|
29683
29851
|
const { surface0, surface1 } = s.themes.current().colors;
|
|
29684
|
-
return { bar:
|
|
29852
|
+
return { bar: surface0, ground: surface1 };
|
|
29685
29853
|
},
|
|
29686
29854
|
open: (path) => s.opens.open(path),
|
|
29687
29855
|
restart: (now) => s.restarter.request({ now }),
|
|
@@ -29799,16 +29967,16 @@ function startServer(opts) {
|
|
|
29799
29967
|
}
|
|
29800
29968
|
|
|
29801
29969
|
// ../daemon/src/themes/store.ts
|
|
29802
|
-
import { existsSync as existsSync27, readdirSync as readdirSync8, readFileSync as
|
|
29970
|
+
import { existsSync as existsSync27, readdirSync as readdirSync8, readFileSync as readFileSync17, statSync as statSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
29803
29971
|
import { homedir as homedir13 } from "os";
|
|
29804
|
-
import { basename as basename8, dirname as dirname9, join as
|
|
29972
|
+
import { basename as basename8, dirname as dirname9, join as join25, resolve as resolve9 } from "path";
|
|
29805
29973
|
var home = homedir13();
|
|
29806
29974
|
var defaultExtensionDirs = [
|
|
29807
|
-
|
|
29808
|
-
|
|
29809
|
-
|
|
29810
|
-
|
|
29811
|
-
|
|
29975
|
+
join25(home, ".vscode", "extensions"),
|
|
29976
|
+
join25(home, ".vscode-insiders", "extensions"),
|
|
29977
|
+
join25(home, ".vscode-oss", "extensions"),
|
|
29978
|
+
join25(home, ".cursor", "extensions"),
|
|
29979
|
+
join25(home, ".windsurf", "extensions"),
|
|
29812
29980
|
"/Applications/Visual Studio Code.app/Contents/Resources/app/extensions",
|
|
29813
29981
|
"/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/extensions",
|
|
29814
29982
|
"/Applications/Cursor.app/Contents/Resources/app/extensions",
|
|
@@ -29892,14 +30060,14 @@ class ThemeStore {
|
|
|
29892
30060
|
name: json?.name ?? base,
|
|
29893
30061
|
source: "file"
|
|
29894
30062
|
});
|
|
29895
|
-
|
|
30063
|
+
writeFileSync11(join25(this.themesDir, `${base}.json`), `${JSON.stringify(theme, null, 2)}
|
|
29896
30064
|
`);
|
|
29897
30065
|
this.load();
|
|
29898
30066
|
return theme;
|
|
29899
30067
|
}
|
|
29900
30068
|
}
|
|
29901
30069
|
function readJsonc(file) {
|
|
29902
|
-
return parse6(
|
|
30070
|
+
return parse6(readFileSync17(file, "utf8"), [], { allowTrailingComma: true });
|
|
29903
30071
|
}
|
|
29904
30072
|
function readVscodeTheme(file, depth = 0) {
|
|
29905
30073
|
const json = readJsonc(file);
|
|
@@ -29927,7 +30095,7 @@ function loadDir(dir) {
|
|
|
29927
30095
|
for (const f of readdirSync8(dir)) {
|
|
29928
30096
|
if (!/\.jsonc?$/i.test(f))
|
|
29929
30097
|
continue;
|
|
29930
|
-
const file =
|
|
30098
|
+
const file = join25(dir, f);
|
|
29931
30099
|
const id = `file:${slug(f.replace(/\.(json|jsonc)$/i, ""))}`;
|
|
29932
30100
|
try {
|
|
29933
30101
|
const json = readJsonc(file);
|
|
@@ -29953,21 +30121,21 @@ function discover(dir) {
|
|
|
29953
30121
|
return out;
|
|
29954
30122
|
}
|
|
29955
30123
|
for (const name of entries) {
|
|
29956
|
-
const extDir =
|
|
29957
|
-
const pkgFile =
|
|
30124
|
+
const extDir = join25(dir, name);
|
|
30125
|
+
const pkgFile = join25(extDir, "package.json");
|
|
29958
30126
|
try {
|
|
29959
30127
|
if (!statSync10(extDir).isDirectory() || !existsSync27(pkgFile))
|
|
29960
30128
|
continue;
|
|
29961
|
-
const pkg = JSON.parse(
|
|
30129
|
+
const pkg = JSON.parse(readFileSync17(pkgFile, "utf8"));
|
|
29962
30130
|
const themes = pkg.contributes?.themes;
|
|
29963
30131
|
if (!Array.isArray(themes))
|
|
29964
30132
|
continue;
|
|
29965
30133
|
const ext = `${pkg.publisher ?? "local"}.${pkg.name ?? name}`;
|
|
29966
30134
|
let nls = {};
|
|
29967
|
-
const nlsFile =
|
|
30135
|
+
const nlsFile = join25(extDir, "package.nls.json");
|
|
29968
30136
|
if (existsSync27(nlsFile)) {
|
|
29969
30137
|
try {
|
|
29970
|
-
nls = JSON.parse(
|
|
30138
|
+
nls = JSON.parse(readFileSync17(nlsFile, "utf8"));
|
|
29971
30139
|
} catch {}
|
|
29972
30140
|
}
|
|
29973
30141
|
for (const c of themes) {
|
|
@@ -30379,9 +30547,9 @@ class BackendShare {
|
|
|
30379
30547
|
|
|
30380
30548
|
// ../daemon/src/worktrees/cache.ts
|
|
30381
30549
|
import { createHash as createHash4 } from "crypto";
|
|
30382
|
-
import { existsSync as existsSync28, readFileSync as
|
|
30550
|
+
import { existsSync as existsSync28, readFileSync as readFileSync18 } from "fs";
|
|
30383
30551
|
import { mkdir as mkdir5, readdir as readdir2, rename, rm as rm4, stat as stat4, utimes, writeFile as writeFile3 } from "fs/promises";
|
|
30384
|
-
import { join as
|
|
30552
|
+
import { join as join26 } from "path";
|
|
30385
30553
|
|
|
30386
30554
|
// ../daemon/src/core/clone.ts
|
|
30387
30555
|
import { mkdir as mkdir4, rm as rm3 } from "fs/promises";
|
|
@@ -30412,7 +30580,7 @@ var STALE_MS = 30 * 24 * 3600000;
|
|
|
30412
30580
|
var TMP = ".tmp-";
|
|
30413
30581
|
var MANIFEST = "manifest.json";
|
|
30414
30582
|
function defaultTools(dir) {
|
|
30415
|
-
const has = (...files) => files.some((f) => existsSync28(
|
|
30583
|
+
const has = (...files) => files.some((f) => existsSync28(join26(dir, f)));
|
|
30416
30584
|
const out = [];
|
|
30417
30585
|
if (has("bun.lock", "bun.lockb"))
|
|
30418
30586
|
out.push("bun --version");
|
|
@@ -30425,7 +30593,7 @@ function defaultTools(dir) {
|
|
|
30425
30593
|
return out;
|
|
30426
30594
|
}
|
|
30427
30595
|
function withSidecars(root, rel) {
|
|
30428
|
-
return [rel, ...SIDECARS.map((s) => rel + s)].filter((n) => existsSync28(
|
|
30596
|
+
return [rel, ...SIDECARS.map((s) => rel + s)].filter((n) => existsSync28(join26(root, n)));
|
|
30429
30597
|
}
|
|
30430
30598
|
var entryName = (base, at) => `${base.slice(0, 12)}-${at.toString(36)}`;
|
|
30431
30599
|
async function listEntries(familyDir) {
|
|
@@ -30440,8 +30608,8 @@ async function listEntries(familyDir) {
|
|
|
30440
30608
|
const m = /^([0-9a-f]{12})-([0-9a-z]+)$/.exec(name);
|
|
30441
30609
|
if (!m?.[1] || !m[2])
|
|
30442
30610
|
continue;
|
|
30443
|
-
const dir =
|
|
30444
|
-
if (!existsSync28(
|
|
30611
|
+
const dir = join26(familyDir, name);
|
|
30612
|
+
if (!existsSync28(join26(dir, MANIFEST)))
|
|
30445
30613
|
continue;
|
|
30446
30614
|
out.push({ dir, base: m[1], at: Number.parseInt(m[2], 36) });
|
|
30447
30615
|
}
|
|
@@ -30471,7 +30639,7 @@ class ArtifactCache {
|
|
|
30471
30639
|
try {
|
|
30472
30640
|
const family = await this.family(wt, repo, policy);
|
|
30473
30641
|
const base = await this.baseCommit(wt, repo);
|
|
30474
|
-
const entries = await listEntries(
|
|
30642
|
+
const entries = await listEntries(join26(this.d.paths.cacheDir, repo.id, family.key));
|
|
30475
30643
|
const entry = entries.find((e) => e.base === base.slice(0, 12)) ?? entries[0];
|
|
30476
30644
|
if (!entry) {
|
|
30477
30645
|
say(`cache: nothing kept for these lockfiles and tools on ${family.platform}`);
|
|
@@ -30479,14 +30647,14 @@ class ArtifactCache {
|
|
|
30479
30647
|
}
|
|
30480
30648
|
const restored = [];
|
|
30481
30649
|
for (const rel of policy.paths) {
|
|
30482
|
-
if (existsSync28(
|
|
30650
|
+
if (existsSync28(join26(wt.path, rel)))
|
|
30483
30651
|
continue;
|
|
30484
30652
|
const names = withSidecars(entry.dir, rel);
|
|
30485
30653
|
if (names.length === 0)
|
|
30486
30654
|
continue;
|
|
30487
30655
|
let whole = true;
|
|
30488
30656
|
for (const name of names) {
|
|
30489
|
-
const r = await (this.d.clone ?? cloneTree)(
|
|
30657
|
+
const r = await (this.d.clone ?? cloneTree)(join26(entry.dir, name), join26(wt.path, name));
|
|
30490
30658
|
if (r.ok)
|
|
30491
30659
|
continue;
|
|
30492
30660
|
say(`cache: could not restore ${name}: ${r.error}`);
|
|
@@ -30520,12 +30688,12 @@ class ArtifactCache {
|
|
|
30520
30688
|
let tmp;
|
|
30521
30689
|
let slot;
|
|
30522
30690
|
try {
|
|
30523
|
-
const present = policy.paths.filter((rel) => existsSync28(
|
|
30691
|
+
const present = policy.paths.filter((rel) => existsSync28(join26(wt.path, rel)));
|
|
30524
30692
|
if (present.length === 0)
|
|
30525
30693
|
return;
|
|
30526
30694
|
const family = await this.family(wt, repo, policy);
|
|
30527
30695
|
const base = await this.baseCommit(wt, repo);
|
|
30528
|
-
const familyDir =
|
|
30696
|
+
const familyDir = join26(this.d.paths.cacheDir, repo.id, family.key);
|
|
30529
30697
|
const key = `${familyDir}/${base.slice(0, 12)}`;
|
|
30530
30698
|
const kept = (await listEntries(familyDir)).find((e) => e.base === base.slice(0, 12));
|
|
30531
30699
|
if (kept || this.making.has(key)) {
|
|
@@ -30536,21 +30704,21 @@ class ArtifactCache {
|
|
|
30536
30704
|
this.making.add(key);
|
|
30537
30705
|
const at = Date.now();
|
|
30538
30706
|
const name = entryName(base, at);
|
|
30539
|
-
tmp =
|
|
30707
|
+
tmp = join26(familyDir, `${TMP}${name}`);
|
|
30540
30708
|
await mkdir5(tmp, { recursive: true });
|
|
30541
30709
|
let how = "clonefile";
|
|
30542
30710
|
for (const rel of present) {
|
|
30543
30711
|
for (const n of withSidecars(wt.path, rel)) {
|
|
30544
|
-
const r = await (this.d.clone ?? cloneTree)(
|
|
30712
|
+
const r = await (this.d.clone ?? cloneTree)(join26(wt.path, n), join26(tmp, n));
|
|
30545
30713
|
if (!r.ok)
|
|
30546
30714
|
throw new Error(`could not keep ${n}: ${r.error}`);
|
|
30547
30715
|
how = r.how;
|
|
30548
30716
|
}
|
|
30549
30717
|
}
|
|
30550
30718
|
const manifest = { at, base, platform: family.platform, tools: family.tools, paths: present };
|
|
30551
|
-
await writeFile3(
|
|
30719
|
+
await writeFile3(join26(tmp, MANIFEST), `${JSON.stringify(manifest, null, 2)}
|
|
30552
30720
|
`);
|
|
30553
|
-
await rename(tmp,
|
|
30721
|
+
await rename(tmp, join26(familyDir, name));
|
|
30554
30722
|
tmp = undefined;
|
|
30555
30723
|
log.info(worktreeId, `cache: kept ${list(present)} for ${base.slice(0, 7)} via ${how} in ${Date.now() - started}ms`);
|
|
30556
30724
|
await this.prune(familyDir);
|
|
@@ -30578,7 +30746,7 @@ class ArtifactCache {
|
|
|
30578
30746
|
return;
|
|
30579
30747
|
}
|
|
30580
30748
|
for (const repoId of repoIds) {
|
|
30581
|
-
const repoDir =
|
|
30749
|
+
const repoDir = join26(root, repoId);
|
|
30582
30750
|
if (!this.d.state.repo(repoId)) {
|
|
30583
30751
|
await rm4(repoDir, { recursive: true, force: true });
|
|
30584
30752
|
continue;
|
|
@@ -30590,7 +30758,7 @@ class ArtifactCache {
|
|
|
30590
30758
|
continue;
|
|
30591
30759
|
}
|
|
30592
30760
|
for (const family of families) {
|
|
30593
|
-
const familyDir =
|
|
30761
|
+
const familyDir = join26(repoDir, family);
|
|
30594
30762
|
let names;
|
|
30595
30763
|
try {
|
|
30596
30764
|
names = await readdir2(familyDir);
|
|
@@ -30599,7 +30767,7 @@ class ArtifactCache {
|
|
|
30599
30767
|
}
|
|
30600
30768
|
for (const n of names)
|
|
30601
30769
|
if (n.startsWith(TMP))
|
|
30602
|
-
await rm4(
|
|
30770
|
+
await rm4(join26(familyDir, n), { recursive: true, force: true });
|
|
30603
30771
|
const entries = await listEntries(familyDir);
|
|
30604
30772
|
let newest = 0;
|
|
30605
30773
|
for (const e of entries) {
|
|
@@ -30619,7 +30787,7 @@ class ArtifactCache {
|
|
|
30619
30787
|
for (const f of policy.key) {
|
|
30620
30788
|
h.update(`\x00${f}\x00`);
|
|
30621
30789
|
try {
|
|
30622
|
-
h.update(
|
|
30790
|
+
h.update(readFileSync18(join26(wt.path, f)));
|
|
30623
30791
|
} catch {
|
|
30624
30792
|
h.update("(absent)");
|
|
30625
30793
|
}
|
|
@@ -30824,7 +30992,7 @@ function lastPageErrors(entries) {
|
|
|
30824
30992
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
30825
30993
|
const e = entries[i].event;
|
|
30826
30994
|
if (e.type === "page-error") {
|
|
30827
|
-
errors.unshift(e.
|
|
30995
|
+
errors.unshift(...e.errors);
|
|
30828
30996
|
continue;
|
|
30829
30997
|
}
|
|
30830
30998
|
if (e.type === "user-message" || e.type === "fix-asked")
|
|
@@ -31837,9 +32005,9 @@ function rankRefs({ refs, merged, prs, query, defaultBranch, rowIdForPath }) {
|
|
|
31837
32005
|
}
|
|
31838
32006
|
|
|
31839
32007
|
// ../daemon/src/worktrees/service.ts
|
|
31840
|
-
import { cpSync, existsSync as existsSync33, readFileSync as
|
|
32008
|
+
import { cpSync, existsSync as existsSync33, readFileSync as readFileSync20, rmSync as rmSync8 } from "fs";
|
|
31841
32009
|
import { rm as rm5 } from "fs/promises";
|
|
31842
|
-
import { dirname as dirname12, join as
|
|
32010
|
+
import { dirname as dirname12, join as join30 } from "path";
|
|
31843
32011
|
|
|
31844
32012
|
// ../daemon/src/git/archive.ts
|
|
31845
32013
|
import { rmSync as rmSync5 } from "fs";
|
|
@@ -32415,7 +32583,7 @@ function compareUrl(remoteUrl, base, branch) {
|
|
|
32415
32583
|
|
|
32416
32584
|
// ../daemon/src/git/turn.ts
|
|
32417
32585
|
import { copyFileSync, existsSync as existsSync30, rmSync as rmSync6 } from "fs";
|
|
32418
|
-
import { join as
|
|
32586
|
+
import { join as join27 } from "path";
|
|
32419
32587
|
var turnRef = (worktreeId, which) => `refs/toyon/turns/${worktreeId}/${which}`;
|
|
32420
32588
|
var SNAPSHOT_SUBJECT2 = "toyon: tree at a send";
|
|
32421
32589
|
var queue = new Map;
|
|
@@ -32433,7 +32601,7 @@ async function treeNow(wtPath, head, dirty) {
|
|
|
32433
32601
|
const dir = await git(wtPath, "rev-parse", "--absolute-git-dir");
|
|
32434
32602
|
if (!dir.ok || !dir.out)
|
|
32435
32603
|
return null;
|
|
32436
|
-
index =
|
|
32604
|
+
index = join27(dir.out, "toyon-turn-index");
|
|
32437
32605
|
}
|
|
32438
32606
|
const env = { GIT_INDEX_FILE: index };
|
|
32439
32607
|
const writeTree = async () => {
|
|
@@ -32456,7 +32624,7 @@ async function treeNow(wtPath, head, dirty) {
|
|
|
32456
32624
|
}
|
|
32457
32625
|
synced.delete(wtPath);
|
|
32458
32626
|
if (!existsSync30(index)) {
|
|
32459
|
-
const own =
|
|
32627
|
+
const own = join27(index, "..", "index");
|
|
32460
32628
|
if (existsSync30(own))
|
|
32461
32629
|
copyFileSync(own, index);
|
|
32462
32630
|
else if (!(await run(GIT, ["read-tree", "HEAD"], wtPath, env)).ok)
|
|
@@ -32538,8 +32706,8 @@ async function dropTurnRefs(repoPath, worktreeId) {
|
|
|
32538
32706
|
}
|
|
32539
32707
|
|
|
32540
32708
|
// ../daemon/src/worktrees/archive.ts
|
|
32541
|
-
import { existsSync as existsSync31, mkdirSync as mkdirSync8, readdirSync as readdirSync9, readFileSync as
|
|
32542
|
-
import { basename as basename10, dirname as dirname11, join as
|
|
32709
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync8, readdirSync as readdirSync9, readFileSync as readFileSync19, renameSync as renameSync5, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
|
|
32710
|
+
import { basename as basename10, dirname as dirname11, join as join28 } from "path";
|
|
32543
32711
|
class WorktreeArchive {
|
|
32544
32712
|
dir;
|
|
32545
32713
|
records = new Map;
|
|
@@ -32551,7 +32719,7 @@ class WorktreeArchive {
|
|
|
32551
32719
|
if (!existsSync31(file))
|
|
32552
32720
|
continue;
|
|
32553
32721
|
try {
|
|
32554
|
-
this.records.set(id, JSON.parse(
|
|
32722
|
+
this.records.set(id, JSON.parse(readFileSync19(file, "utf8")));
|
|
32555
32723
|
} catch (e) {
|
|
32556
32724
|
log.warn("archive", `skipped an unreadable record: ${file}`, e);
|
|
32557
32725
|
}
|
|
@@ -32566,7 +32734,7 @@ class WorktreeArchive {
|
|
|
32566
32734
|
move(from.transcript, at.transcript);
|
|
32567
32735
|
move(from.attachments, at.attachments);
|
|
32568
32736
|
move(from.plans, at.plans);
|
|
32569
|
-
|
|
32737
|
+
writeFileSync12(at.record, JSON.stringify(rec, null, 2));
|
|
32570
32738
|
this.records.set(rec.worktree.id, rec);
|
|
32571
32739
|
return { transcript: at.transcript, attachments: at.attachments, plans: at.plans };
|
|
32572
32740
|
}
|
|
@@ -32595,7 +32763,7 @@ class WorktreeArchive {
|
|
|
32595
32763
|
const rec = this.records.get(id);
|
|
32596
32764
|
if (!rec)
|
|
32597
32765
|
return;
|
|
32598
|
-
|
|
32766
|
+
writeFileSync12(this.filesOf(id).record, JSON.stringify(rec, null, 2));
|
|
32599
32767
|
}
|
|
32600
32768
|
chatFiles(id) {
|
|
32601
32769
|
if (!this.records.has(id))
|
|
@@ -32606,24 +32774,24 @@ class WorktreeArchive {
|
|
|
32606
32774
|
planFile(id, rel) {
|
|
32607
32775
|
if (!this.records.has(id) || !isPlanPath(rel))
|
|
32608
32776
|
return null;
|
|
32609
|
-
const file =
|
|
32777
|
+
const file = join28(this.filesOf(id).plans, basename10(rel));
|
|
32610
32778
|
if (!existsSync31(file))
|
|
32611
32779
|
return null;
|
|
32612
32780
|
try {
|
|
32613
|
-
return
|
|
32781
|
+
return readFileSync19(file, "utf8");
|
|
32614
32782
|
} catch (e) {
|
|
32615
32783
|
log.warn(id, `could not read its archived plan ${rel}`, e);
|
|
32616
32784
|
return null;
|
|
32617
32785
|
}
|
|
32618
32786
|
}
|
|
32619
32787
|
filesOf(id) {
|
|
32620
|
-
const dir =
|
|
32788
|
+
const dir = join28(this.dir, id);
|
|
32621
32789
|
return {
|
|
32622
32790
|
dir,
|
|
32623
|
-
record:
|
|
32624
|
-
transcript:
|
|
32625
|
-
attachments:
|
|
32626
|
-
plans:
|
|
32791
|
+
record: join28(dir, "record.json"),
|
|
32792
|
+
transcript: join28(dir, "transcript.jsonl"),
|
|
32793
|
+
attachments: join28(dir, "attachments"),
|
|
32794
|
+
plans: join28(dir, "plans")
|
|
32627
32795
|
};
|
|
32628
32796
|
}
|
|
32629
32797
|
}
|
|
@@ -32650,7 +32818,7 @@ function summarize(r, repoId, transcript) {
|
|
|
32650
32818
|
function lastUsage(transcript) {
|
|
32651
32819
|
if (!existsSync31(transcript))
|
|
32652
32820
|
return null;
|
|
32653
|
-
const lines =
|
|
32821
|
+
const lines = readFileSync19(transcript, "utf8").split(`
|
|
32654
32822
|
`);
|
|
32655
32823
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
32656
32824
|
if (!lines[i]?.includes('"usage"'))
|
|
@@ -32666,7 +32834,7 @@ function lastUsage(transcript) {
|
|
|
32666
32834
|
function firstPrompt(transcript) {
|
|
32667
32835
|
if (!existsSync31(transcript))
|
|
32668
32836
|
return;
|
|
32669
|
-
for (const line of
|
|
32837
|
+
for (const line of readFileSync19(transcript, "utf8").split(`
|
|
32670
32838
|
`)) {
|
|
32671
32839
|
if (!line.includes('"user-message"'))
|
|
32672
32840
|
continue;
|
|
@@ -32683,7 +32851,7 @@ function move(from, to) {
|
|
|
32683
32851
|
return;
|
|
32684
32852
|
mkdirSync8(dirname11(to), { recursive: true });
|
|
32685
32853
|
rmSync7(to, { recursive: true, force: true });
|
|
32686
|
-
|
|
32854
|
+
renameSync5(from, to);
|
|
32687
32855
|
}
|
|
32688
32856
|
|
|
32689
32857
|
// ../daemon/src/worktrees/archivedGit.ts
|
|
@@ -32851,7 +33019,7 @@ async function fillLandMarks(jobs, read, width = 8) {
|
|
|
32851
33019
|
|
|
32852
33020
|
// ../daemon/src/worktrees/spare.ts
|
|
32853
33021
|
import { existsSync as existsSync32 } from "fs";
|
|
32854
|
-
import { basename as basename11, join as
|
|
33022
|
+
import { basename as basename11, join as join29 } from "path";
|
|
32855
33023
|
class SparePool {
|
|
32856
33024
|
d;
|
|
32857
33025
|
inflight = new Map;
|
|
@@ -32915,11 +33083,11 @@ class SparePool {
|
|
|
32915
33083
|
return this.record(repoId, repo).then((wt) => this.warmUp(wt, repo, {}));
|
|
32916
33084
|
}
|
|
32917
33085
|
async record(repoId, repo) {
|
|
32918
|
-
const { id, dir } = freeSlot(
|
|
33086
|
+
const { id, dir } = freeSlot(join29(this.d.paths.worktreesDir, repo.name));
|
|
32919
33087
|
const wt = {
|
|
32920
33088
|
id,
|
|
32921
33089
|
repoId,
|
|
32922
|
-
path:
|
|
33090
|
+
path: join29(this.d.paths.worktreesDir, repo.name, dir),
|
|
32923
33091
|
branch: repo.defaultBranch,
|
|
32924
33092
|
kind: "spare",
|
|
32925
33093
|
phase: "reserved",
|
|
@@ -33308,6 +33476,7 @@ class WorktreeService {
|
|
|
33308
33476
|
spare;
|
|
33309
33477
|
runs;
|
|
33310
33478
|
ops = new Map;
|
|
33479
|
+
syncQueued = new Set;
|
|
33311
33480
|
trunk;
|
|
33312
33481
|
archive;
|
|
33313
33482
|
archiving = new Map;
|
|
@@ -33379,6 +33548,8 @@ class WorktreeService {
|
|
|
33379
33548
|
if (wt && prompt)
|
|
33380
33549
|
this.scheduleNaming(wt, prompt, wt.variant);
|
|
33381
33550
|
}
|
|
33551
|
+
if (turn.end !== "asking")
|
|
33552
|
+
this.syncAfterTurn(worktreeId);
|
|
33382
33553
|
if (turn.end !== "done" && turn.end !== "stopped")
|
|
33383
33554
|
return;
|
|
33384
33555
|
for (const p of d.runtime.get(worktreeId)?.procs?.states() ?? []) {
|
|
@@ -33446,8 +33617,8 @@ class WorktreeService {
|
|
|
33446
33617
|
if (!await isGitRepo(repo.path)) {
|
|
33447
33618
|
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.`);
|
|
33448
33619
|
}
|
|
33449
|
-
const { id, dir } = freeSlot(
|
|
33450
|
-
const wtPath =
|
|
33620
|
+
const { id, dir } = freeSlot(join30(this.d.paths.worktreesDir, repo.name));
|
|
33621
|
+
const wtPath = join30(this.d.paths.worktreesDir, repo.name, dir);
|
|
33451
33622
|
const branch = `toyon/${dir}`;
|
|
33452
33623
|
await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "--no-track", "-b", branch, wtPath, baseOf(repo)));
|
|
33453
33624
|
const wt = {
|
|
@@ -33524,7 +33695,7 @@ class WorktreeService {
|
|
|
33524
33695
|
};
|
|
33525
33696
|
const carried = await git(repo.path, "ls-tree", "-r", "--name-only", `${sha}^3`);
|
|
33526
33697
|
const restored = (carried.ok ? carried.out.split(`
|
|
33527
|
-
`).filter(Boolean) : []).filter((p) => !existsSync33(
|
|
33698
|
+
`).filter(Boolean) : []).filter((p) => !existsSync33(join30(wt.path, p)));
|
|
33528
33699
|
const applied = await git(wt.path, "stash", "apply", sha);
|
|
33529
33700
|
if (applied.ok) {
|
|
33530
33701
|
const ref = await entry();
|
|
@@ -33564,9 +33735,9 @@ class WorktreeService {
|
|
|
33564
33735
|
const branch = kind === "pr" ? `pr/${number}` : ref;
|
|
33565
33736
|
const title = kind === "pr" ? `pr-${number}` : cleanTitle2(ref) || "branch";
|
|
33566
33737
|
let slug = branchSlug(title) || "branch";
|
|
33567
|
-
if (existsSync33(
|
|
33738
|
+
if (existsSync33(join30(this.d.paths.worktreesDir, repo.name, slug)))
|
|
33568
33739
|
slug = `${slug}-${shortId().slice(0, 4)}`;
|
|
33569
|
-
const wtPath =
|
|
33740
|
+
const wtPath = join30(this.d.paths.worktreesDir, repo.name, slug);
|
|
33570
33741
|
await withRepoLock(repo.path, async () => {
|
|
33571
33742
|
const out = (await listWorktrees(repo.path)).find((w) => w.branch === branch);
|
|
33572
33743
|
if (out)
|
|
@@ -33768,6 +33939,7 @@ class WorktreeService {
|
|
|
33768
33939
|
}
|
|
33769
33940
|
async takeDown(wt, archive, reason) {
|
|
33770
33941
|
this.going.add(wt.id);
|
|
33942
|
+
this.syncQueued.delete(wt.id);
|
|
33771
33943
|
try {
|
|
33772
33944
|
return await this.removeAndRecord(wt, archive, reason);
|
|
33773
33945
|
} finally {
|
|
@@ -33799,7 +33971,10 @@ class WorktreeService {
|
|
|
33799
33971
|
return k;
|
|
33800
33972
|
});
|
|
33801
33973
|
const sessionId = this.d.state.session(worktreeId);
|
|
33974
|
+
const placed = repo.promptedAt;
|
|
33802
33975
|
this.d.state.removeWorktree(worktreeId);
|
|
33976
|
+
if (repo.promptedAt !== placed)
|
|
33977
|
+
this.d.hub.emit("reposChanged");
|
|
33803
33978
|
releasePort(wt.proxyPort);
|
|
33804
33979
|
let archived = null;
|
|
33805
33980
|
await dropTurnRefs(repo.path, worktreeId);
|
|
@@ -33824,7 +33999,7 @@ class WorktreeService {
|
|
|
33824
33999
|
}
|
|
33825
34000
|
}
|
|
33826
34001
|
async keep(repo, wt) {
|
|
33827
|
-
const index =
|
|
34002
|
+
const index = join30(this.d.paths.archiveDir, `${wt.id}.index`);
|
|
33828
34003
|
const kept = await keepState(repo.path, wt.path, archiveRef(wt.id), index) ?? await this.keepUnlinked(repo, wt);
|
|
33829
34004
|
if (!kept)
|
|
33830
34005
|
log.warn(wt.id, "could not keep its git state");
|
|
@@ -33861,10 +34036,10 @@ class WorktreeService {
|
|
|
33861
34036
|
log.warn(wt.id, `its directory could not be deleted and stays at ${wt.path}`);
|
|
33862
34037
|
}
|
|
33863
34038
|
plansScratch(worktreeId) {
|
|
33864
|
-
return
|
|
34039
|
+
return join30(this.d.paths.archiveDir, `${worktreeId}.plans`);
|
|
33865
34040
|
}
|
|
33866
34041
|
keepPlans(wt) {
|
|
33867
|
-
const from =
|
|
34042
|
+
const from = join30(wt.path, PLANS_DIR);
|
|
33868
34043
|
if (!existsSync33(from))
|
|
33869
34044
|
return;
|
|
33870
34045
|
try {
|
|
@@ -33931,6 +34106,24 @@ class WorktreeService {
|
|
|
33931
34106
|
});
|
|
33932
34107
|
this.archiveChat(wt, repo, kept, sessionId);
|
|
33933
34108
|
}
|
|
34109
|
+
fillSentStamps() {
|
|
34110
|
+
let filled = 0;
|
|
34111
|
+
for (const repo of this.d.state.repos) {
|
|
34112
|
+
if (repo.promptedAt !== undefined)
|
|
34113
|
+
continue;
|
|
34114
|
+
let at = 0;
|
|
34115
|
+
for (const rec of this.archive.recordsOf(repo))
|
|
34116
|
+
at = Math.max(at, sentAt(rec.worktree));
|
|
34117
|
+
if (at === 0)
|
|
34118
|
+
continue;
|
|
34119
|
+
repo.promptedAt = at;
|
|
34120
|
+
filled++;
|
|
34121
|
+
}
|
|
34122
|
+
if (filled === 0)
|
|
34123
|
+
return;
|
|
34124
|
+
this.d.state.save();
|
|
34125
|
+
log.info("daemon", `placed ${filled} projects by what their archives were sent`);
|
|
34126
|
+
}
|
|
33934
34127
|
async fillLandMarks() {
|
|
33935
34128
|
const jobs = [];
|
|
33936
34129
|
const rows = new Set;
|
|
@@ -33986,7 +34179,7 @@ class WorktreeService {
|
|
|
33986
34179
|
}
|
|
33987
34180
|
archivedAttachment(archiveId, file) {
|
|
33988
34181
|
const files = this.archive.chatFiles(archiveId);
|
|
33989
|
-
return files && isAttachmentFile(file) ?
|
|
34182
|
+
return files && isAttachmentFile(file) ? join30(files.attachments, file) : null;
|
|
33990
34183
|
}
|
|
33991
34184
|
archivedGit(archiveId) {
|
|
33992
34185
|
const rec = this.archive.get(archiveId);
|
|
@@ -34041,7 +34234,7 @@ class WorktreeService {
|
|
|
34041
34234
|
throw new UserError(`${old.title} was archived without its commits, so there is nothing to restore`);
|
|
34042
34235
|
if (rec.auto)
|
|
34043
34236
|
log.info(old.id, `restoring a worktree that archived itself (${rec.auto})`);
|
|
34044
|
-
const path = existsSync33(old.path) ?
|
|
34237
|
+
const path = existsSync33(old.path) ? join30(dirname12(old.path), freeSlot(dirname12(old.path)).dir) : old.path;
|
|
34045
34238
|
const branch = await withRepoLock(repo.path, async () => {
|
|
34046
34239
|
if (!await commitOf(repo.path, archiveRef(old.id))) {
|
|
34047
34240
|
throw new UserError(`${old.title}'s kept commits are gone from git`);
|
|
@@ -34079,7 +34272,7 @@ class WorktreeService {
|
|
|
34079
34272
|
...createdBy ? { createdBy } : {}
|
|
34080
34273
|
};
|
|
34081
34274
|
try {
|
|
34082
|
-
this.archive.take(old.id, { ...this.chatFiles(old.id), plans:
|
|
34275
|
+
this.archive.take(old.id, { ...this.chatFiles(old.id), plans: join30(path, PLANS_DIR) });
|
|
34083
34276
|
} catch (e) {
|
|
34084
34277
|
log.warn(old.id, "could not move its chat back out of the archive", e);
|
|
34085
34278
|
}
|
|
@@ -34221,10 +34414,10 @@ class WorktreeService {
|
|
|
34221
34414
|
}
|
|
34222
34415
|
}
|
|
34223
34416
|
async mirrorBaseIgnore(repo) {
|
|
34224
|
-
const src =
|
|
34417
|
+
const src = join30(repo.path, ".gitignore");
|
|
34225
34418
|
const tracked = existsSync33(src) && (await git(repo.path, "ls-files", "--", ".gitignore")).out !== "";
|
|
34226
34419
|
const mirror = existsSync33(src) && !tracked;
|
|
34227
|
-
const lines = mirror ?
|
|
34420
|
+
const lines = mirror ? readFileSync20(src, "utf8").split(`
|
|
34228
34421
|
`).filter((l) => l.trim() !== "") : [];
|
|
34229
34422
|
await excludeBlock(repo.path, BASE_IGNORE, lines);
|
|
34230
34423
|
}
|
|
@@ -34236,8 +34429,8 @@ class WorktreeService {
|
|
|
34236
34429
|
await this.d.cache.restore(wt, repo, (line) => this.d.hub.emit("log", wt.id, "setup", line));
|
|
34237
34430
|
run.finish(0);
|
|
34238
34431
|
}
|
|
34239
|
-
const srcNm =
|
|
34240
|
-
const dstNm =
|
|
34432
|
+
const srcNm = join30(depsSource, "node_modules");
|
|
34433
|
+
const dstNm = join30(wt.path, "node_modules");
|
|
34241
34434
|
if (existsSync33(srcNm) && !existsSync33(dstNm)) {
|
|
34242
34435
|
const r = await cloneTree(srcNm, dstNm);
|
|
34243
34436
|
if (r.ok)
|
|
@@ -34246,10 +34439,10 @@ class WorktreeService {
|
|
|
34246
34439
|
log.warn(wt.id, "could not copy node_modules from the base checkout", r.error);
|
|
34247
34440
|
}
|
|
34248
34441
|
for (const f of LOCAL_CONFIG_FILES) {
|
|
34249
|
-
const src =
|
|
34250
|
-
if (!existsSync33(src) || existsSync33(
|
|
34442
|
+
const src = join30(depsSource, f);
|
|
34443
|
+
if (!existsSync33(src) || existsSync33(join30(wt.path, f)))
|
|
34251
34444
|
continue;
|
|
34252
|
-
const r = await run("cp", [src,
|
|
34445
|
+
const r = await run("cp", [src, join30(wt.path, f)], wt.path);
|
|
34253
34446
|
if (r.ok)
|
|
34254
34447
|
this.d.hub.emit("log", wt.id, "setup", `copied ${f}`);
|
|
34255
34448
|
else
|
|
@@ -34502,6 +34695,9 @@ ${wt.landing.body}` : s;
|
|
|
34502
34695
|
this.ops.delete(id);
|
|
34503
34696
|
release?.();
|
|
34504
34697
|
this.d.hub.emit("worktreesChanged");
|
|
34698
|
+
const status = this.d.runtime.agentFor(id)?.status;
|
|
34699
|
+
if (status !== "working" && status !== "waiting")
|
|
34700
|
+
this.syncAfterTurn(id);
|
|
34505
34701
|
}
|
|
34506
34702
|
}
|
|
34507
34703
|
step(id, name) {
|
|
@@ -34775,8 +34971,11 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
34775
34971
|
const own = r.wt ? hasOwnBranch(r.wt) : false;
|
|
34776
34972
|
const carry = !!r.wt;
|
|
34777
34973
|
const status = this.d.runtime.agentFor(worktreeId)?.status;
|
|
34778
|
-
if (carry && (status === "working" || status === "waiting") && (await statusFiles(r.path)).length > 0)
|
|
34779
|
-
|
|
34974
|
+
if (carry && (status === "working" || status === "waiting") && (await statusFiles(r.path)).length > 0) {
|
|
34975
|
+
this.syncQueued.add(worktreeId);
|
|
34976
|
+
this.d.hub.emit("worktreesChanged");
|
|
34977
|
+
return { result: { ok: true, message: `${r.name} syncs from ${r.base} once the turn ends` }, base: r.base };
|
|
34978
|
+
}
|
|
34780
34979
|
const w = r.wt ? this.landWatch(r.wt) : UNWATCHED;
|
|
34781
34980
|
const result = await this.ship(worktreeId, "sync-main", async () => {
|
|
34782
34981
|
if (r.wt) {
|
|
@@ -34794,6 +34993,31 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
34794
34993
|
});
|
|
34795
34994
|
return { result: r.wt ? this.fixShip(r.wt, result, r.base) : result, base: r.base };
|
|
34796
34995
|
}
|
|
34996
|
+
unqueueSync(worktreeId) {
|
|
34997
|
+
if (!this.syncQueued.delete(worktreeId))
|
|
34998
|
+
return;
|
|
34999
|
+
this.d.hub.emit("worktreesChanged");
|
|
35000
|
+
}
|
|
35001
|
+
syncAfterTurn(worktreeId) {
|
|
35002
|
+
if (!this.syncQueued.has(worktreeId))
|
|
35003
|
+
return;
|
|
35004
|
+
if (this.ops.has(worktreeId))
|
|
35005
|
+
return;
|
|
35006
|
+
this.syncQueued.delete(worktreeId);
|
|
35007
|
+
const release = this.d.runtime.agentFor(worktreeId)?.hold();
|
|
35008
|
+
this.d.hub.emit("worktreesChanged");
|
|
35009
|
+
fireAndForget(worktreeId, (async () => {
|
|
35010
|
+
try {
|
|
35011
|
+
const { result } = await this.sync(worktreeId);
|
|
35012
|
+
if (!result.ok && !result.asked)
|
|
35013
|
+
this.d.hub.emit("failed", worktreeId, result.message);
|
|
35014
|
+
} catch (e) {
|
|
35015
|
+
this.d.hub.emit("failed", worktreeId, e instanceof Error ? e.message : String(e));
|
|
35016
|
+
} finally {
|
|
35017
|
+
release?.();
|
|
35018
|
+
}
|
|
35019
|
+
})(), "sync after the turn");
|
|
35020
|
+
}
|
|
34797
35021
|
async pull(worktreeId) {
|
|
34798
35022
|
const wt = this.d.state.requireWorktree(worktreeId);
|
|
34799
35023
|
if (!isMain(wt))
|
|
@@ -35152,7 +35376,8 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
|
|
|
35152
35376
|
usage: this.usageFor(wt.id),
|
|
35153
35377
|
transcript: transcriptPathFor(this.d.paths.transcriptsDir, wt.id),
|
|
35154
35378
|
sessionId: this.d.state.session(wt.id),
|
|
35155
|
-
shipping: this.ops.get(wt.id)
|
|
35379
|
+
shipping: this.ops.get(wt.id),
|
|
35380
|
+
syncQueued: this.syncQueued.has(wt.id) || undefined
|
|
35156
35381
|
};
|
|
35157
35382
|
});
|
|
35158
35383
|
}
|
|
@@ -35279,16 +35504,17 @@ if (pruned > 0)
|
|
|
35279
35504
|
var managed = await loadManaged();
|
|
35280
35505
|
var paths = makePaths();
|
|
35281
35506
|
ensureDirs(paths);
|
|
35507
|
+
forwardTranscripts(paths.transcriptsDir);
|
|
35282
35508
|
var token = loadOrCreateToken(paths);
|
|
35283
35509
|
var port = Number(process.env.TOYON_PORT ?? DAEMON_DEFAULT_PORT);
|
|
35284
|
-
var helperApp =
|
|
35510
|
+
var helperApp = join31(paths.home, DAEMON_FILES.helper);
|
|
35285
35511
|
var helper = helperWanted({ platform: process.platform, cloud: cloud.enabled, port, home: process.env.TOYON_HOME }) ? new Helper({
|
|
35286
35512
|
dir: helperApp,
|
|
35287
35513
|
stub: HELPER_STUB,
|
|
35288
35514
|
bun: process.execPath,
|
|
35289
35515
|
cli: CLI_ENTRY,
|
|
35290
|
-
log:
|
|
35291
|
-
icon:
|
|
35516
|
+
log: join31(paths.home, DAEMON_FILES.launcherLog),
|
|
35517
|
+
icon: join31(SHELL_DIST, "icon.svg"),
|
|
35292
35518
|
version: package_default.version
|
|
35293
35519
|
}) : null;
|
|
35294
35520
|
if (helper) {
|
|
@@ -35505,6 +35731,7 @@ var keepAwake = new KeepAwake({
|
|
|
35505
35731
|
answerable: () => setting.get() !== null
|
|
35506
35732
|
});
|
|
35507
35733
|
await worktrees.fillLandMarks();
|
|
35734
|
+
worktrees.fillSentStamps();
|
|
35508
35735
|
var {
|
|
35509
35736
|
server,
|
|
35510
35737
|
branded,
|
|
@@ -35581,7 +35808,7 @@ hub.on("remoteChanged", () => {
|
|
|
35581
35808
|
fireAndForget("remote", tailnet.refresh(), "ask Tailscale");
|
|
35582
35809
|
});
|
|
35583
35810
|
fireAndForget("remote", tailnet.start(), "ask Tailscale");
|
|
35584
|
-
|
|
35811
|
+
writeFileSync13(paths.pidFile, `${process.pid}
|
|
35585
35812
|
`);
|
|
35586
35813
|
var stopLagSampler = startLagSampler();
|
|
35587
35814
|
await runtime.reclaimOrphans();
|