toyon 0.15.0 → 0.16.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/CHANGELOG.md +27 -0
- package/dist/bridge.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/daemon.js +606 -720
- package/dist/shell/assets/{Editor-jjf6IGvI.js → Editor-BATywBIx.js} +3 -3
- package/dist/shell/assets/{XTerm-ClBPMY5e.js → XTerm-So8-Vafj.js} +1 -1
- package/dist/shell/assets/{cssMode-CgTk4Slg.js → cssMode-DlHcrkXj.js} +1 -1
- package/dist/shell/assets/{freemarker2-B4Eirl4Y.js → freemarker2-D3xOnVxc.js} +1 -1
- package/dist/shell/assets/{grammar-E8nWrhy3.js → grammar-RZJ5oG33.js} +1 -1
- package/dist/shell/assets/{handlebars-C6uEGz8T.js → handlebars-UbIG7080.js} +1 -1
- package/dist/shell/assets/{html-tDAdsEYh.js → html-D92eLK6U.js} +1 -1
- package/dist/shell/assets/{htmlMode-DXGTr81h.js → htmlMode-DsZIJzoj.js} +1 -1
- package/dist/shell/assets/index-BvYzBRq_.css +1 -0
- package/dist/shell/assets/index-CHQwm2uN.js +268 -0
- package/dist/shell/assets/{javascript-BeM3ebwd.js → javascript-DUkgFtV2.js} +1 -1
- package/dist/shell/assets/{jsonMode-CXStIFU4.js → jsonMode-BiIooPg9.js} +1 -1
- package/dist/shell/assets/{liquid-2UNKoWGQ.js → liquid-DFFjZ5TH.js} +1 -1
- package/dist/shell/assets/{lspLanguageFeatures-G4xsCwWV.js → lspLanguageFeatures-C7Ug4qkg.js} +1 -1
- package/dist/shell/assets/{mdx-CoF6St9y.js → mdx-DdjSgAuG.js} +1 -1
- package/dist/shell/assets/{python-rULGuAEe.js → python-DPUF4OK9.js} +1 -1
- package/dist/shell/assets/{razor-0I41g6kD.js → razor-IRYJ0pOA.js} +1 -1
- package/dist/shell/assets/{tsMode-CyMsmAXC.js → tsMode-CljE7MPO.js} +1 -1
- package/dist/shell/assets/{xml-BNhTd1tm.js → xml-D8Zv3Gd0.js} +1 -1
- package/dist/shell/assets/{yaml-Cp69rkq5.js → yaml-Dxx9PL0B.js} +1 -1
- package/dist/shell/index.html +2 -2
- package/package.json +1 -1
- package/dist/shell/assets/index-1FLxRByi.js +0 -267
- package/dist/shell/assets/index-D-4W2x1P.css +0 -1
package/dist/daemon.js
CHANGED
|
@@ -11,14 +11,10 @@ var TOOL_SEARCH = "ToolSearch";
|
|
|
11
11
|
var ASK_TOOL = "AskUserQuestion";
|
|
12
12
|
var TOYON_MCP_SERVER = "toyon";
|
|
13
13
|
var HANDOFF_TOOL = "handoff";
|
|
14
|
-
var PREVIEW_TOOL = "preview";
|
|
15
14
|
|
|
16
15
|
// ../shared/src/agent-tools.ts
|
|
17
16
|
var EDIT_KINDS = new Set(["edit", "delete", "move", "execute"]);
|
|
18
17
|
var EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit", "Bash"]);
|
|
19
|
-
function isEditTool(ev) {
|
|
20
|
-
return ev.kind ? EDIT_KINDS.has(ev.kind) : EDIT_TOOLS.has(ev.name);
|
|
21
|
-
}
|
|
22
18
|
var WRITE_KINDS = new Set(["edit", "delete", "move"]);
|
|
23
19
|
var WRITE_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
|
|
24
20
|
function isWriteTool(ev) {
|
|
@@ -6602,11 +6598,9 @@ ${text.slice(text.length - (CONTEXT_CHARS - HEAD_CHARS))}`;
|
|
|
6602
6598
|
}
|
|
6603
6599
|
|
|
6604
6600
|
// ../shared/src/protocol/limits.ts
|
|
6605
|
-
var PROTOCOL_VERSION =
|
|
6601
|
+
var PROTOCOL_VERSION = 112;
|
|
6606
6602
|
var PAGE_ERROR_MAX_CHARS = 2000;
|
|
6607
6603
|
var PAGE_TITLE_MAX_CHARS = 1000;
|
|
6608
|
-
var RENDER_ERRORS_MAX = 10;
|
|
6609
|
-
var PAGE_URL_MAX_CHARS = 2048;
|
|
6610
6604
|
var FILE_MAX_CHARS = 5000000;
|
|
6611
6605
|
var IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
6612
6606
|
var IMAGE_MAX_EDGE = 2576;
|
|
@@ -6962,6 +6956,7 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
6962
6956
|
object({ t: literal("continue"), worktreeId: id }),
|
|
6963
6957
|
object({ t: literal("reveal"), worktreeId: id, path: relPath.optional() }),
|
|
6964
6958
|
object({ t: literal("reveal-path"), path: relPath }),
|
|
6959
|
+
object({ t: literal("reveal-attachment"), worktreeId: id, file: string2().min(1).max(120) }),
|
|
6965
6960
|
object({ t: literal("stop-agent"), worktreeId: id }),
|
|
6966
6961
|
object({ t: literal("seen"), worktreeId: id }),
|
|
6967
6962
|
object({ t: literal("mark-unread"), worktreeId: id }),
|
|
@@ -6978,17 +6973,6 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
6978
6973
|
object({ t: literal("forget-visit"), repoId: id, path: routePath }),
|
|
6979
6974
|
object({ t: literal("page-error"), worktreeId: id, message: string2().min(1).max(PAGE_ERROR_MAX_CHARS) }),
|
|
6980
6975
|
object({ t: literal("page-loaded"), worktreeId: id }),
|
|
6981
|
-
object({ t: literal("fix-page"), worktreeId: id }),
|
|
6982
|
-
object({
|
|
6983
|
-
t: literal("rendered"),
|
|
6984
|
-
worktreeId: id,
|
|
6985
|
-
id: string2().min(1).max(64),
|
|
6986
|
-
ok: boolean2(),
|
|
6987
|
-
reason: string2().max(200).optional(),
|
|
6988
|
-
url: string2().max(PAGE_URL_MAX_CHARS).optional(),
|
|
6989
|
-
title: pageTitle.optional(),
|
|
6990
|
-
errors: array(string2().max(PAGE_ERROR_MAX_CHARS)).max(RENDER_ERRORS_MAX)
|
|
6991
|
-
}),
|
|
6992
6976
|
object({ t: literal("pick-variant"), worktreeId: id }),
|
|
6993
6977
|
object({
|
|
6994
6978
|
t: literal("unqueue"),
|
|
@@ -7060,6 +7044,7 @@ var clientMsgSchema = discriminatedUnion("t", [
|
|
|
7060
7044
|
}),
|
|
7061
7045
|
object({ t: literal("handoff-ask"), worktreeId: id, repoId: id, text: prompt.optional() }),
|
|
7062
7046
|
object({ t: literal("agent-logout"), agent: id }),
|
|
7047
|
+
object({ t: literal("agent-check"), agent: id }),
|
|
7063
7048
|
object({ t: literal("agent-config"), agent: id, repoId: id.optional() }),
|
|
7064
7049
|
object({ t: literal("reveal-agent-file"), agent: id, file: string2().max(64), repoId: id.optional() }),
|
|
7065
7050
|
object({ t: literal("import-theme"), name: string2().max(300), source: string2().max(2000000) }),
|
|
@@ -8529,7 +8514,7 @@ async function turnRemoteOff(ts, daemonPort, file, was) {
|
|
|
8529
8514
|
// ../daemon/package.json
|
|
8530
8515
|
var package_default = {
|
|
8531
8516
|
name: "@toyon/daemon",
|
|
8532
|
-
version: "0.
|
|
8517
|
+
version: "0.16.0",
|
|
8533
8518
|
private: true,
|
|
8534
8519
|
type: "module",
|
|
8535
8520
|
main: "src/index.ts",
|
|
@@ -8545,6 +8530,10 @@ var package_default = {
|
|
|
8545
8530
|
}
|
|
8546
8531
|
};
|
|
8547
8532
|
|
|
8533
|
+
// ../daemon/src/agent/acp/transport.ts
|
|
8534
|
+
import { spawn } from "child_process";
|
|
8535
|
+
import { Readable, Writable } from "stream";
|
|
8536
|
+
|
|
8548
8537
|
// ../../node_modules/.bun/@agentclientprotocol+sdk@1.4.0+ab3ed8e9421bfd7b/node_modules/@agentclientprotocol/sdk/dist/schema/index.js
|
|
8549
8538
|
var AGENT_METHODS = {
|
|
8550
8539
|
initialize: "initialize",
|
|
@@ -12621,16 +12610,6 @@ var legacyClientNotificationMethods = new Set([
|
|
|
12621
12610
|
CLIENT_METHODS.elicitation_complete
|
|
12622
12611
|
]);
|
|
12623
12612
|
|
|
12624
|
-
// ../daemon/src/core/errors.ts
|
|
12625
|
-
class UserError extends Error {
|
|
12626
|
-
delivered;
|
|
12627
|
-
constructor(message, about = {}) {
|
|
12628
|
-
super(message);
|
|
12629
|
-
this.name = "UserError";
|
|
12630
|
-
this.delivered = about.delivered ?? false;
|
|
12631
|
-
}
|
|
12632
|
-
}
|
|
12633
|
-
|
|
12634
12613
|
// ../daemon/src/core/log.ts
|
|
12635
12614
|
var LEVELS = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
12636
12615
|
function envLevel() {
|
|
@@ -12676,131 +12655,6 @@ function fireAndForget(tag, p, what = "background task") {
|
|
|
12676
12655
|
p.catch((e) => log.warn(tag, `${what} failed`, e));
|
|
12677
12656
|
}
|
|
12678
12657
|
|
|
12679
|
-
// ../daemon/src/agent/acp/authstatus.ts
|
|
12680
|
-
var AUTH_STATUS_UPDATE_METHOD = "_auth/status_update";
|
|
12681
|
-
var KINDS = new Set(["none", "api_key", "account", "external", "gateway"]);
|
|
12682
|
-
function supportsLogout(caps) {
|
|
12683
|
-
return !!caps?.auth?.logout;
|
|
12684
|
-
}
|
|
12685
|
-
var str = (v) => typeof v === "string" && v.trim() ? v : undefined;
|
|
12686
|
-
function parseAuthStatus(params) {
|
|
12687
|
-
const status = params?.authStatus;
|
|
12688
|
-
if (!status || typeof status !== "object")
|
|
12689
|
-
return null;
|
|
12690
|
-
const kind = status.kind;
|
|
12691
|
-
if (!KINDS.has(kind))
|
|
12692
|
-
return null;
|
|
12693
|
-
const account = status.account ?? undefined;
|
|
12694
|
-
const fields = account ? {
|
|
12695
|
-
...str(account.email) ? { email: str(account.email) } : {},
|
|
12696
|
-
...str(account.organization) ? { organization: str(account.organization) } : {},
|
|
12697
|
-
...str(account.plan) ? { plan: str(account.plan) } : {}
|
|
12698
|
-
} : {};
|
|
12699
|
-
return {
|
|
12700
|
-
kind,
|
|
12701
|
-
label: str(status.label) ?? (kind === "none" ? "not logged in" : "logged in"),
|
|
12702
|
-
...str(status.detail) ? { detail: str(status.detail) } : {},
|
|
12703
|
-
...Object.keys(fields).length ? { account: fields } : {}
|
|
12704
|
-
};
|
|
12705
|
-
}
|
|
12706
|
-
|
|
12707
|
-
// ../daemon/src/agent/accounts.ts
|
|
12708
|
-
class AgentAccounts {
|
|
12709
|
-
d;
|
|
12710
|
-
known = new Map;
|
|
12711
|
-
busy = new Map;
|
|
12712
|
-
onChange = null;
|
|
12713
|
-
constructor(d) {
|
|
12714
|
-
this.d = d;
|
|
12715
|
-
}
|
|
12716
|
-
observe(agentId, o) {
|
|
12717
|
-
const before = this.known.get(agentId) ?? { canLogout: false, status: undefined };
|
|
12718
|
-
const entry = { ...before };
|
|
12719
|
-
if (o.canLogout !== undefined)
|
|
12720
|
-
entry.canLogout = o.canLogout;
|
|
12721
|
-
if (o.status)
|
|
12722
|
-
entry.status = o.status;
|
|
12723
|
-
this.known.set(agentId, entry);
|
|
12724
|
-
if (before.canLogout !== entry.canLogout || !same(before.status, entry.status))
|
|
12725
|
-
this.onChange?.();
|
|
12726
|
-
}
|
|
12727
|
-
observeLimits(agentId, limits) {
|
|
12728
|
-
const before = this.known.get(agentId) ?? { canLogout: false, status: undefined };
|
|
12729
|
-
const entry = { ...before, limits };
|
|
12730
|
-
this.known.set(agentId, entry);
|
|
12731
|
-
if (!sameLimits(before.limits, limits))
|
|
12732
|
-
this.onChange?.();
|
|
12733
|
-
}
|
|
12734
|
-
describe(infos) {
|
|
12735
|
-
return infos.map((info) => {
|
|
12736
|
-
const k = this.known.get(info.id);
|
|
12737
|
-
if (!k)
|
|
12738
|
-
return info;
|
|
12739
|
-
return {
|
|
12740
|
-
...info,
|
|
12741
|
-
...k.status ? { auth: k.status } : {},
|
|
12742
|
-
...k.canLogout ? { canLogout: true } : {},
|
|
12743
|
-
...k.limits ? { limits: k.limits } : {}
|
|
12744
|
-
};
|
|
12745
|
-
});
|
|
12746
|
-
}
|
|
12747
|
-
logout(agentId) {
|
|
12748
|
-
const running = this.busy.get(agentId);
|
|
12749
|
-
if (running)
|
|
12750
|
-
return running;
|
|
12751
|
-
const p = this.runLogout(agentId).finally(() => this.busy.delete(agentId));
|
|
12752
|
-
this.busy.set(agentId, p);
|
|
12753
|
-
return p;
|
|
12754
|
-
}
|
|
12755
|
-
async runLogout(agentId) {
|
|
12756
|
-
const spec = this.d.require(agentId);
|
|
12757
|
-
let pushed = null;
|
|
12758
|
-
let signingOut = false;
|
|
12759
|
-
const app = client({ name: "toyon" }).onNotification(AUTH_STATUS_UPDATE_METHOD, parseAuthStatus, (c) => {
|
|
12760
|
-
if (c.params && signingOut)
|
|
12761
|
-
pushed = c.params;
|
|
12762
|
-
});
|
|
12763
|
-
const link = await this.d.connect(app, spec);
|
|
12764
|
-
try {
|
|
12765
|
-
const init = await link.conn.agent.request(methods.agent.initialize, {
|
|
12766
|
-
protocolVersion: PROTOCOL_VERSION2,
|
|
12767
|
-
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false } },
|
|
12768
|
-
clientInfo: { name: "toyon", version: "0" }
|
|
12769
|
-
});
|
|
12770
|
-
if (!supportsLogout(init.agentCapabilities))
|
|
12771
|
-
throw new UserError(`${spec.name} cannot be signed out from here`);
|
|
12772
|
-
signingOut = true;
|
|
12773
|
-
await link.conn.agent.request(methods.agent.logout, {});
|
|
12774
|
-
const entry = this.known.get(agentId);
|
|
12775
|
-
this.known.set(agentId, { canLogout: true, ...pushed ? { status: pushed } : {} });
|
|
12776
|
-
if (!entry || !same(entry.status, pushed ?? undefined))
|
|
12777
|
-
this.onChange?.();
|
|
12778
|
-
log.info("agents", `${agentId} signed out`);
|
|
12779
|
-
} catch (e) {
|
|
12780
|
-
if (e instanceof UserError)
|
|
12781
|
-
throw e;
|
|
12782
|
-
throw new UserError(`${spec.name} could not be signed out: ${e instanceof Error ? e.message : String(e)}`);
|
|
12783
|
-
} finally {
|
|
12784
|
-
await link.kill();
|
|
12785
|
-
}
|
|
12786
|
-
}
|
|
12787
|
-
}
|
|
12788
|
-
function sameLimits(a, b) {
|
|
12789
|
-
if (!a || !b)
|
|
12790
|
-
return a === b;
|
|
12791
|
-
const win = (x) => x ? `${x.used}@${x.resetsAt}` : "";
|
|
12792
|
-
return a.status === b.status && a.binding === b.binding && win(a.windows.five_hour) === win(b.windows.five_hour) && win(a.windows.seven_day) === win(b.windows.seven_day);
|
|
12793
|
-
}
|
|
12794
|
-
function same(a, b) {
|
|
12795
|
-
if (!a || !b)
|
|
12796
|
-
return a === b;
|
|
12797
|
-
return a.kind === b.kind && a.label === b.label && a.detail === b.detail && a.account?.email === b.account?.email && a.account?.organization === b.account?.organization && a.account?.plan === b.account?.plan;
|
|
12798
|
-
}
|
|
12799
|
-
|
|
12800
|
-
// ../daemon/src/agent/acp/transport.ts
|
|
12801
|
-
import { spawn } from "child_process";
|
|
12802
|
-
import { Readable, Writable } from "stream";
|
|
12803
|
-
|
|
12804
12658
|
// ../daemon/src/runtime/kill.ts
|
|
12805
12659
|
var KILL_GRACE_MS = 3000;
|
|
12806
12660
|
var EXIT_GRACE_MS = 200;
|
|
@@ -12904,6 +12758,16 @@ import { createHash } from "crypto";
|
|
|
12904
12758
|
import { copyFile as copyFile2, mkdir, readFile, stat, writeFile } from "fs/promises";
|
|
12905
12759
|
import { join as join2 } from "path";
|
|
12906
12760
|
|
|
12761
|
+
// ../daemon/src/core/errors.ts
|
|
12762
|
+
class UserError extends Error {
|
|
12763
|
+
delivered;
|
|
12764
|
+
constructor(message, about = {}) {
|
|
12765
|
+
super(message);
|
|
12766
|
+
this.name = "UserError";
|
|
12767
|
+
this.delivered = about.delivered ?? false;
|
|
12768
|
+
}
|
|
12769
|
+
}
|
|
12770
|
+
|
|
12907
12771
|
// ../daemon/src/agent/uploads.ts
|
|
12908
12772
|
import { randomBytes } from "crypto";
|
|
12909
12773
|
import { mkdirSync, readdirSync, rmSync as rmSync2 } from "fs";
|
|
@@ -13188,13 +13052,7 @@ class AttachmentStore {
|
|
|
13188
13052
|
const { n: _n, ...pick } = ref;
|
|
13189
13053
|
return pick;
|
|
13190
13054
|
}
|
|
13191
|
-
|
|
13192
|
-
for (const at of [this.fileFor(worktreeId, ref.file), archived?.(ref.file) ?? null]) {
|
|
13193
|
-
if (at && await Bun.file(at).exists()) {
|
|
13194
|
-
path = at;
|
|
13195
|
-
break;
|
|
13196
|
-
}
|
|
13197
|
-
}
|
|
13055
|
+
const path = await this.locate(worktreeId, ref.file, archived);
|
|
13198
13056
|
if (!path)
|
|
13199
13057
|
return null;
|
|
13200
13058
|
if (ref.kind === "paste") {
|
|
@@ -13212,6 +13070,13 @@ class AttachmentStore {
|
|
|
13212
13070
|
const { name, bytes, width, height } = ref;
|
|
13213
13071
|
return { kind: "image", upload, name, mimeType, bytes, width, height };
|
|
13214
13072
|
}
|
|
13073
|
+
async locate(worktreeId, file, archived) {
|
|
13074
|
+
for (const at of [this.fileFor(worktreeId, file), archived?.(file) ?? null]) {
|
|
13075
|
+
if (at && await Bun.file(at).exists())
|
|
13076
|
+
return at;
|
|
13077
|
+
}
|
|
13078
|
+
return null;
|
|
13079
|
+
}
|
|
13215
13080
|
async write(worktreeId, file, fill) {
|
|
13216
13081
|
const wtDir = attachmentsDirFor(this.dir, worktreeId);
|
|
13217
13082
|
const path = join2(wtDir, file);
|
|
@@ -13596,6 +13461,37 @@ async function readCapped(req, max) {
|
|
|
13596
13461
|
return Buffer.concat(chunks).toString("utf8");
|
|
13597
13462
|
}
|
|
13598
13463
|
|
|
13464
|
+
// ../daemon/src/agent/acp/authstatus.ts
|
|
13465
|
+
var AUTH_STATUS_UPDATE_METHOD = "_auth/status_update";
|
|
13466
|
+
var KINDS = new Set(["none", "api_key", "account", "external", "gateway"]);
|
|
13467
|
+
function supportsLogout(caps) {
|
|
13468
|
+
return !!caps?.auth?.logout;
|
|
13469
|
+
}
|
|
13470
|
+
var str = (v) => typeof v === "string" && v.trim() ? v : undefined;
|
|
13471
|
+
function parseAuthStatus(params) {
|
|
13472
|
+
const status = params?.authStatus;
|
|
13473
|
+
if (!status || typeof status !== "object")
|
|
13474
|
+
return null;
|
|
13475
|
+
const kind = status.kind;
|
|
13476
|
+
if (!KINDS.has(kind))
|
|
13477
|
+
return null;
|
|
13478
|
+
const account = status.account ?? undefined;
|
|
13479
|
+
const fields = account ? {
|
|
13480
|
+
...str(account.email) ? { email: str(account.email) } : {},
|
|
13481
|
+
...str(account.organization) ? { organization: str(account.organization) } : {},
|
|
13482
|
+
...str(account.plan) ? { plan: str(account.plan) } : {}
|
|
13483
|
+
} : {};
|
|
13484
|
+
return {
|
|
13485
|
+
kind,
|
|
13486
|
+
label: str(status.label) ?? (kind === "none" ? "not logged in" : "logged in"),
|
|
13487
|
+
...str(status.detail) ? { detail: str(status.detail) } : {},
|
|
13488
|
+
...Object.keys(fields).length ? { account: fields } : {}
|
|
13489
|
+
};
|
|
13490
|
+
}
|
|
13491
|
+
function isAuthRequired(e) {
|
|
13492
|
+
return e instanceof RequestError && e.code === -32000;
|
|
13493
|
+
}
|
|
13494
|
+
|
|
13599
13495
|
// ../daemon/src/agent/acp/options.ts
|
|
13600
13496
|
var OPTION_FIELDS = { model: "model", thought_level: "effort" };
|
|
13601
13497
|
var OPTION_CATEGORIES = Object.keys(OPTION_FIELDS);
|
|
@@ -13648,37 +13544,125 @@ function currentValues(options) {
|
|
|
13648
13544
|
return out;
|
|
13649
13545
|
}
|
|
13650
13546
|
|
|
13651
|
-
// ../daemon/src/agent/
|
|
13652
|
-
|
|
13547
|
+
// ../daemon/src/agent/readiness.ts
|
|
13548
|
+
var CHECK_COOLDOWN_MS = 30000;
|
|
13549
|
+
|
|
13550
|
+
class AgentReadiness {
|
|
13653
13551
|
d;
|
|
13654
|
-
|
|
13552
|
+
known = new Map;
|
|
13553
|
+
inflight = new Map;
|
|
13554
|
+
logouts = new Map;
|
|
13555
|
+
onChange = null;
|
|
13556
|
+
now;
|
|
13655
13557
|
constructor(d) {
|
|
13656
13558
|
this.d = d;
|
|
13559
|
+
this.now = d.now ?? Date.now;
|
|
13657
13560
|
}
|
|
13658
|
-
|
|
13561
|
+
entry(agentId) {
|
|
13562
|
+
let e = this.known.get(agentId);
|
|
13563
|
+
if (!e) {
|
|
13564
|
+
e = { canLogout: false };
|
|
13565
|
+
this.known.set(agentId, e);
|
|
13566
|
+
}
|
|
13567
|
+
return e;
|
|
13568
|
+
}
|
|
13569
|
+
observe(agentId, o) {
|
|
13570
|
+
const e = this.entry(agentId);
|
|
13571
|
+
const before = { ...e };
|
|
13572
|
+
if (o.canLogout !== undefined)
|
|
13573
|
+
e.canLogout = o.canLogout;
|
|
13574
|
+
if (o.status) {
|
|
13575
|
+
e.status = o.status;
|
|
13576
|
+
if (o.status.kind !== "none")
|
|
13577
|
+
delete e.failure;
|
|
13578
|
+
}
|
|
13579
|
+
if (before.canLogout !== e.canLogout || !same(before.status, e.status) || before.failure !== e.failure) {
|
|
13580
|
+
this.onChange?.();
|
|
13581
|
+
}
|
|
13582
|
+
}
|
|
13583
|
+
observeLimits(agentId, limits) {
|
|
13584
|
+
const e = this.entry(agentId);
|
|
13585
|
+
const before = e.limits;
|
|
13586
|
+
e.limits = limits;
|
|
13587
|
+
if (!sameLimits(before, limits))
|
|
13588
|
+
this.onChange?.();
|
|
13589
|
+
}
|
|
13590
|
+
describe() {
|
|
13591
|
+
return this.d.infos().map((info) => {
|
|
13592
|
+
const e = this.known.get(info.id);
|
|
13593
|
+
const models = this.d.options(info.id, "model");
|
|
13594
|
+
const efforts = this.d.options(info.id, "thought_level");
|
|
13595
|
+
return {
|
|
13596
|
+
...info,
|
|
13597
|
+
...models.length > 0 ? { models } : {},
|
|
13598
|
+
...efforts.length > 0 ? { efforts } : {},
|
|
13599
|
+
...this.d.quick(info.id) ? { quick: true } : {},
|
|
13600
|
+
...e?.status ? { auth: e.status } : {},
|
|
13601
|
+
...e?.canLogout ? { canLogout: true } : {},
|
|
13602
|
+
...e?.limits ? { limits: e.limits } : {},
|
|
13603
|
+
...e?.failure ? { checkFailed: e.failure } : {}
|
|
13604
|
+
};
|
|
13605
|
+
});
|
|
13606
|
+
}
|
|
13607
|
+
positive(agentId) {
|
|
13608
|
+
const e = this.known.get(agentId);
|
|
13609
|
+
return this.d.options(agentId, "model").length > 0 && !e?.failure && e?.status?.kind !== "none";
|
|
13610
|
+
}
|
|
13611
|
+
check(agentId) {
|
|
13612
|
+
const info = this.d.infos().find((a) => a.id === agentId);
|
|
13613
|
+
if (!info?.available)
|
|
13614
|
+
return Promise.resolve();
|
|
13615
|
+
const running = this.inflight.get(agentId);
|
|
13616
|
+
if (running)
|
|
13617
|
+
return running;
|
|
13618
|
+
if (this.positive(agentId))
|
|
13619
|
+
return Promise.resolve();
|
|
13620
|
+
const e = this.known.get(agentId);
|
|
13621
|
+
if (e?.checkedAt !== undefined && this.now() - e.checkedAt < CHECK_COOLDOWN_MS)
|
|
13622
|
+
return Promise.resolve();
|
|
13623
|
+
const p = this.run(agentId).finally(() => {
|
|
13624
|
+
if (this.inflight.get(agentId) === p)
|
|
13625
|
+
this.inflight.delete(agentId);
|
|
13626
|
+
});
|
|
13627
|
+
this.inflight.set(agentId, p);
|
|
13628
|
+
return p;
|
|
13629
|
+
}
|
|
13630
|
+
async checkMissing() {
|
|
13659
13631
|
for (const a of this.d.infos()) {
|
|
13660
|
-
if (
|
|
13661
|
-
|
|
13662
|
-
this.tried.add(a.id);
|
|
13663
|
-
await this.probe(a.id);
|
|
13632
|
+
if (a.available && this.d.options(a.id, "model").length === 0)
|
|
13633
|
+
await this.check(a.id);
|
|
13664
13634
|
}
|
|
13665
13635
|
}
|
|
13666
|
-
async
|
|
13667
|
-
const tag = `
|
|
13668
|
-
const
|
|
13636
|
+
async run(agentId) {
|
|
13637
|
+
const tag = `check:${agentId}`;
|
|
13638
|
+
const found = {};
|
|
13639
|
+
let spec;
|
|
13640
|
+
try {
|
|
13641
|
+
spec = this.d.require(agentId);
|
|
13642
|
+
} catch (e) {
|
|
13643
|
+
this.finish(agentId, firstLine(e), found);
|
|
13644
|
+
return;
|
|
13645
|
+
}
|
|
13646
|
+
const app = client({ name: "toyon" }).onNotification(methods.client.session.update, () => {}).onNotification(AUTH_STATUS_UPDATE_METHOD, parseAuthStatus, (c) => {
|
|
13647
|
+
if (c.params)
|
|
13648
|
+
found.status = c.params;
|
|
13649
|
+
});
|
|
13669
13650
|
let link;
|
|
13670
13651
|
try {
|
|
13671
|
-
link = await this.d.connect(app,
|
|
13652
|
+
link = await this.d.connect(app, spec);
|
|
13672
13653
|
} catch (e) {
|
|
13673
|
-
log.warn(tag, "could not start the agent
|
|
13654
|
+
log.warn(tag, "could not start the agent", e);
|
|
13655
|
+
this.finish(agentId, firstLine(e), found);
|
|
13674
13656
|
return;
|
|
13675
13657
|
}
|
|
13658
|
+
let failure;
|
|
13676
13659
|
try {
|
|
13677
13660
|
const init = await link.conn.agent.request(methods.agent.initialize, {
|
|
13678
13661
|
protocolVersion: PROTOCOL_VERSION2,
|
|
13679
13662
|
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false } },
|
|
13680
13663
|
clientInfo: { name: "toyon", version: "0" }
|
|
13681
13664
|
});
|
|
13665
|
+
found.canLogout = supportsLogout(init.agentCapabilities);
|
|
13682
13666
|
const s = await link.conn.agent.request(methods.agent.session.new, { cwd: this.d.cwd, mcpServers: [] });
|
|
13683
13667
|
for (const [category, opt] of readOptions(s.configOptions))
|
|
13684
13668
|
this.d.learned(agentId, category, opt.choices);
|
|
@@ -13688,18 +13672,97 @@ class OptionProbe {
|
|
|
13688
13672
|
});
|
|
13689
13673
|
}
|
|
13690
13674
|
} catch (e) {
|
|
13691
|
-
|
|
13675
|
+
failure = isAuthRequired(e) ? spec.loginHint : firstLine(e);
|
|
13676
|
+
log.info(tag, `could not read its models: ${failure}`);
|
|
13692
13677
|
} finally {
|
|
13693
13678
|
link.conn.close();
|
|
13694
13679
|
await link.kill();
|
|
13695
13680
|
}
|
|
13681
|
+
this.finish(agentId, failure, found);
|
|
13682
|
+
}
|
|
13683
|
+
finish(agentId, failure, found) {
|
|
13684
|
+
const e = this.entry(agentId);
|
|
13685
|
+
const before = { ...e };
|
|
13686
|
+
if (found.canLogout !== undefined)
|
|
13687
|
+
e.canLogout = found.canLogout;
|
|
13688
|
+
if (found.status)
|
|
13689
|
+
e.status = found.status;
|
|
13690
|
+
e.failure = failure;
|
|
13691
|
+
e.checkedAt = this.now();
|
|
13692
|
+
if (before.canLogout !== e.canLogout || !same(before.status, e.status) || before.failure !== e.failure) {
|
|
13693
|
+
this.onChange?.();
|
|
13694
|
+
}
|
|
13695
|
+
}
|
|
13696
|
+
logout(agentId) {
|
|
13697
|
+
const running = this.logouts.get(agentId);
|
|
13698
|
+
if (running)
|
|
13699
|
+
return running;
|
|
13700
|
+
const prior = this.inflight.get(agentId) ?? Promise.resolve();
|
|
13701
|
+
const p = prior.then(() => this.runLogout(agentId)).finally(() => {
|
|
13702
|
+
this.logouts.delete(agentId);
|
|
13703
|
+
this.inflight.delete(agentId);
|
|
13704
|
+
});
|
|
13705
|
+
this.logouts.set(agentId, p);
|
|
13706
|
+
this.inflight.set(agentId, p.catch(() => {
|
|
13707
|
+
return;
|
|
13708
|
+
}));
|
|
13709
|
+
return p;
|
|
13710
|
+
}
|
|
13711
|
+
async runLogout(agentId) {
|
|
13712
|
+
const spec = this.d.require(agentId);
|
|
13713
|
+
let pushed = null;
|
|
13714
|
+
let signingOut = false;
|
|
13715
|
+
const app = client({ name: "toyon" }).onNotification(AUTH_STATUS_UPDATE_METHOD, parseAuthStatus, (c) => {
|
|
13716
|
+
if (c.params && signingOut)
|
|
13717
|
+
pushed = c.params;
|
|
13718
|
+
});
|
|
13719
|
+
const link = await this.d.connect(app, spec);
|
|
13720
|
+
try {
|
|
13721
|
+
const init = await link.conn.agent.request(methods.agent.initialize, {
|
|
13722
|
+
protocolVersion: PROTOCOL_VERSION2,
|
|
13723
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false } },
|
|
13724
|
+
clientInfo: { name: "toyon", version: "0" }
|
|
13725
|
+
});
|
|
13726
|
+
if (!supportsLogout(init.agentCapabilities))
|
|
13727
|
+
throw new UserError(`${spec.name} cannot be signed out from here`);
|
|
13728
|
+
signingOut = true;
|
|
13729
|
+
await link.conn.agent.request(methods.agent.logout, {});
|
|
13730
|
+
const before = this.known.get(agentId);
|
|
13731
|
+
this.known.set(agentId, { canLogout: true, ...pushed ? { status: pushed } : {} });
|
|
13732
|
+
if (!before || !same(before.status, pushed ?? undefined) || before.failure !== undefined)
|
|
13733
|
+
this.onChange?.();
|
|
13734
|
+
log.info("agents", `${agentId} signed out`);
|
|
13735
|
+
} catch (e) {
|
|
13736
|
+
if (e instanceof UserError)
|
|
13737
|
+
throw e;
|
|
13738
|
+
throw new UserError(`${spec.name} could not be signed out: ${e instanceof Error ? e.message : String(e)}`);
|
|
13739
|
+
} finally {
|
|
13740
|
+
await link.kill();
|
|
13741
|
+
}
|
|
13696
13742
|
}
|
|
13697
13743
|
}
|
|
13744
|
+
function firstLine(e) {
|
|
13745
|
+
const details = e instanceof RequestError ? e.data?.details : undefined;
|
|
13746
|
+
const message = typeof details === "string" ? details : e instanceof Error ? e.message : String(e);
|
|
13747
|
+
return message.replace(/^Internal error: /, "").split(`
|
|
13748
|
+
`)[0] ?? message;
|
|
13749
|
+
}
|
|
13750
|
+
function sameLimits(a, b) {
|
|
13751
|
+
if (!a || !b)
|
|
13752
|
+
return a === b;
|
|
13753
|
+
const win = (x) => x ? `${x.used}@${x.resetsAt}` : "";
|
|
13754
|
+
return a.status === b.status && a.binding === b.binding && win(a.windows.five_hour) === win(b.windows.five_hour) && win(a.windows.seven_day) === win(b.windows.seven_day);
|
|
13755
|
+
}
|
|
13756
|
+
function same(a, b) {
|
|
13757
|
+
if (!a || !b)
|
|
13758
|
+
return a === b;
|
|
13759
|
+
return a.kind === b.kind && a.label === b.label && a.detail === b.detail && a.account?.email === b.account?.email && a.account?.organization === b.account?.organization && a.account?.plan === b.account?.plan;
|
|
13760
|
+
}
|
|
13698
13761
|
|
|
13699
13762
|
// ../daemon/src/agent/registry.ts
|
|
13700
|
-
import { existsSync as
|
|
13763
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
13701
13764
|
import { homedir as homedir3 } from "os";
|
|
13702
|
-
import { dirname as dirname3, isAbsolute, join as
|
|
13765
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join7, resolve as resolve3 } from "path";
|
|
13703
13766
|
|
|
13704
13767
|
// ../daemon/src/core/cloud.ts
|
|
13705
13768
|
var enabled = process.env.TOYON_CLOUD === "1";
|
|
@@ -14181,9 +14244,9 @@ function opencodeEnv() {
|
|
|
14181
14244
|
}
|
|
14182
14245
|
|
|
14183
14246
|
// ../daemon/src/agent/sandbox.ts
|
|
14184
|
-
import { existsSync as
|
|
14247
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
14185
14248
|
import { homedir as homedir2, tmpdir } from "os";
|
|
14186
|
-
import { join as
|
|
14249
|
+
import { join as join6, resolve as resolve2 } from "path";
|
|
14187
14250
|
|
|
14188
14251
|
// ../daemon/src/core/paths.ts
|
|
14189
14252
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
@@ -14308,6 +14371,76 @@ function within2(path, root) {
|
|
|
14308
14371
|
return path === root || path.startsWith(root + sep);
|
|
14309
14372
|
}
|
|
14310
14373
|
|
|
14374
|
+
// ../daemon/src/agent/planDoc.ts
|
|
14375
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
14376
|
+
import { basename as basename2, isAbsolute, join as join5, relative, sep as sep2 } from "path";
|
|
14377
|
+
var PLANS_DIR = join5(".toyon", "plans");
|
|
14378
|
+
var PLAN_NAME = /^[A-Za-z0-9][\w.-]*\.md$/;
|
|
14379
|
+
function isPlanPath(rel) {
|
|
14380
|
+
return rel.startsWith(`${PLANS_DIR}/`) && PLAN_NAME.test(rel.slice(PLANS_DIR.length + 1));
|
|
14381
|
+
}
|
|
14382
|
+
function ownPlanPath(cwd, file) {
|
|
14383
|
+
const dir = canonical(join5(cwd, PLANS_DIR));
|
|
14384
|
+
const abs = canonical(isAbsolute(file) ? file : join5(cwd, file));
|
|
14385
|
+
if (!within2(abs, dir) || relative(dir, abs).includes(sep2))
|
|
14386
|
+
return null;
|
|
14387
|
+
return PLAN_NAME.test(basename2(abs)) ? join5(PLANS_DIR, basename2(abs)) : null;
|
|
14388
|
+
}
|
|
14389
|
+
async function excludePlans(cwd) {
|
|
14390
|
+
await excludeFromGit(cwd, `${PLANS_DIR}/`);
|
|
14391
|
+
}
|
|
14392
|
+
function lastNumber(dir) {
|
|
14393
|
+
if (!existsSync7(dir))
|
|
14394
|
+
return 0;
|
|
14395
|
+
let max = 0;
|
|
14396
|
+
for (const name of readdirSync3(dir)) {
|
|
14397
|
+
const n = Number(/^(\d+)\.md$/.exec(name)?.[1]);
|
|
14398
|
+
if (n > max)
|
|
14399
|
+
max = n;
|
|
14400
|
+
}
|
|
14401
|
+
return max;
|
|
14402
|
+
}
|
|
14403
|
+
function sameAsLast(cwd, rel, markdown) {
|
|
14404
|
+
try {
|
|
14405
|
+
return readFileSync6(join5(cwd, rel), "utf8").trim() === markdown.trim();
|
|
14406
|
+
} catch (e) {
|
|
14407
|
+
log.warn(cwd, `could not read ${rel}`, e);
|
|
14408
|
+
return false;
|
|
14409
|
+
}
|
|
14410
|
+
}
|
|
14411
|
+
async function writePlanDoc(cwd, markdown) {
|
|
14412
|
+
const dir = join5(cwd, PLANS_DIR);
|
|
14413
|
+
const last = lastNumber(dir);
|
|
14414
|
+
if (last > 0) {
|
|
14415
|
+
const newest = join5(PLANS_DIR, `${last}.md`);
|
|
14416
|
+
if (sameAsLast(cwd, newest, markdown))
|
|
14417
|
+
return newest;
|
|
14418
|
+
}
|
|
14419
|
+
const rel = join5(PLANS_DIR, `${last + 1}.md`);
|
|
14420
|
+
try {
|
|
14421
|
+
mkdirSync4(dir, { recursive: true });
|
|
14422
|
+
writeFileSync3(join5(cwd, rel), markdown.endsWith(`
|
|
14423
|
+
`) ? markdown : `${markdown}
|
|
14424
|
+
`);
|
|
14425
|
+
} catch (e) {
|
|
14426
|
+
log.warn(cwd, `could not write ${rel}; the plan stays on the card`, e);
|
|
14427
|
+
return null;
|
|
14428
|
+
}
|
|
14429
|
+
await excludePlans(cwd);
|
|
14430
|
+
return rel;
|
|
14431
|
+
}
|
|
14432
|
+
function planEdited(cwd, rel, proposed) {
|
|
14433
|
+
const file = join5(cwd, rel);
|
|
14434
|
+
if (!existsSync7(file))
|
|
14435
|
+
return false;
|
|
14436
|
+
try {
|
|
14437
|
+
return readFileSync6(file, "utf8").trim() !== proposed.trim();
|
|
14438
|
+
} catch (e) {
|
|
14439
|
+
log.warn(cwd, `could not read ${rel}`, e);
|
|
14440
|
+
return false;
|
|
14441
|
+
}
|
|
14442
|
+
}
|
|
14443
|
+
|
|
14311
14444
|
// ../daemon/src/agent/sandbox.ts
|
|
14312
14445
|
var CACHE_DIRS = [".bun", ".npm", ".cache", ".yarn", ".pnpm-store", "Library/Caches"].map((d) => resolve2(homedir2(), d));
|
|
14313
14446
|
var AGENT_SETTINGS = [".claude", ".codex", ".opencode", "opencode.json", "opencode.jsonc"];
|
|
@@ -14336,9 +14469,10 @@ async function prepareLaunch(cwd, spec) {
|
|
|
14336
14469
|
log.warn(cwd, `agent ${spec.id} runs without an OS sandbox`);
|
|
14337
14470
|
return { bounds, env: setup?.env ?? {} };
|
|
14338
14471
|
}
|
|
14339
|
-
var SETTINGS_REL =
|
|
14472
|
+
var SETTINGS_REL = join6(".claude", "settings.local.json");
|
|
14340
14473
|
function claudeLocalSettings(b) {
|
|
14341
14474
|
return {
|
|
14475
|
+
plansDirectory: PLANS_DIR,
|
|
14342
14476
|
sandbox: {
|
|
14343
14477
|
enabled: true,
|
|
14344
14478
|
failIfUnavailable: true,
|
|
@@ -14350,12 +14484,12 @@ function claudeLocalSettings(b) {
|
|
|
14350
14484
|
};
|
|
14351
14485
|
}
|
|
14352
14486
|
async function writeClaudeLocalSettings(cwd, b) {
|
|
14353
|
-
const file =
|
|
14354
|
-
|
|
14487
|
+
const file = join6(cwd, SETTINGS_REL);
|
|
14488
|
+
mkdirSync5(join6(cwd, ".claude"), { recursive: true });
|
|
14355
14489
|
let existing = {};
|
|
14356
|
-
if (
|
|
14490
|
+
if (existsSync8(file)) {
|
|
14357
14491
|
try {
|
|
14358
|
-
const parsed = JSON.parse(
|
|
14492
|
+
const parsed = JSON.parse(readFileSync7(file, "utf8"));
|
|
14359
14493
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
14360
14494
|
existing = parsed;
|
|
14361
14495
|
} catch (e) {
|
|
@@ -14368,13 +14502,20 @@ async function writeClaudeLocalSettings(cwd, b) {
|
|
|
14368
14502
|
const perms = existing.permissions && typeof existing.permissions === "object" ? existing.permissions : {};
|
|
14369
14503
|
const theirs = Array.isArray(perms.deny) ? perms.deny.filter((r) => typeof r === "string") : [];
|
|
14370
14504
|
const deny = uniq2([...theirs, ...ours.permissions.deny]);
|
|
14371
|
-
const next = {
|
|
14505
|
+
const next = {
|
|
14506
|
+
...existing,
|
|
14507
|
+
sandbox: ours.sandbox,
|
|
14508
|
+
permissions: { ...perms, ...ours.permissions, deny },
|
|
14509
|
+
plansDirectory: ours.plansDirectory
|
|
14510
|
+
};
|
|
14372
14511
|
const tmp = `${file}.tmp`;
|
|
14373
|
-
|
|
14512
|
+
writeFileSync4(tmp, `${JSON.stringify(next, null, 2)}
|
|
14374
14513
|
`);
|
|
14375
14514
|
renameSync(tmp, file);
|
|
14376
|
-
if (
|
|
14515
|
+
if (existsSync8(join6(cwd, ".git"))) {
|
|
14377
14516
|
await excludeFromGit(cwd, SETTINGS_REL);
|
|
14517
|
+
await excludePlans(cwd);
|
|
14518
|
+
}
|
|
14378
14519
|
}
|
|
14379
14520
|
|
|
14380
14521
|
// ../daemon/src/agent/registry.ts
|
|
@@ -14433,7 +14574,7 @@ var BUILTIN_AGENTS = [
|
|
|
14433
14574
|
systemPrompt: "prompt-prefix",
|
|
14434
14575
|
modes: { plan: "plan", build: "build" },
|
|
14435
14576
|
quickModel: opencodeQuickModel,
|
|
14436
|
-
loginHint: "OpenCode has no provider logged in; run
|
|
14577
|
+
loginHint: "OpenCode has no provider logged in; run opencode auth login in the terminal"
|
|
14437
14578
|
}
|
|
14438
14579
|
];
|
|
14439
14580
|
function installErrorLine(stderr) {
|
|
@@ -14520,21 +14661,21 @@ class AgentRegistry {
|
|
|
14520
14661
|
const name = this.packageName(spec);
|
|
14521
14662
|
if (!name || spec.run.kind === "command")
|
|
14522
14663
|
return null;
|
|
14523
|
-
const pkgDir =
|
|
14664
|
+
const pkgDir = join7(this.dir, spec.id, "node_modules", name);
|
|
14524
14665
|
if (spec.run.kind === "npm-native") {
|
|
14525
|
-
const bin =
|
|
14526
|
-
return
|
|
14666
|
+
const bin = join7(pkgDir, spec.run.bin);
|
|
14667
|
+
return existsSync9(bin) ? bin : null;
|
|
14527
14668
|
}
|
|
14528
|
-
const pkgJson =
|
|
14529
|
-
if (!
|
|
14669
|
+
const pkgJson = join7(pkgDir, "package.json");
|
|
14670
|
+
if (!existsSync9(pkgJson))
|
|
14530
14671
|
return null;
|
|
14531
14672
|
try {
|
|
14532
|
-
const meta = JSON.parse(
|
|
14673
|
+
const meta = JSON.parse(readFileSync8(pkgJson, "utf8"));
|
|
14533
14674
|
const rel = typeof meta.bin === "string" ? meta.bin : meta.bin?.[spec.run.bin];
|
|
14534
14675
|
if (!rel)
|
|
14535
14676
|
return null;
|
|
14536
|
-
const script =
|
|
14537
|
-
return
|
|
14677
|
+
const script = join7(dirname3(pkgJson), rel);
|
|
14678
|
+
return existsSync9(script) ? script : null;
|
|
14538
14679
|
} catch (e) {
|
|
14539
14680
|
log.warn("agents", `cannot read ${pkgJson}`, e);
|
|
14540
14681
|
return null;
|
|
@@ -14547,7 +14688,7 @@ class AgentRegistry {
|
|
|
14547
14688
|
const boxed = spec.confinement === "none" ? null : this.sandbox.problem();
|
|
14548
14689
|
if (spec.run.kind === "command") {
|
|
14549
14690
|
const cmd = spec.run.command;
|
|
14550
|
-
const found =
|
|
14691
|
+
const found = isAbsolute2(cmd) ? existsSync9(cmd) : Bun.which(cmd);
|
|
14551
14692
|
return { reason: found ? boxed : `${cmd} not found on PATH` };
|
|
14552
14693
|
}
|
|
14553
14694
|
if (!this.packageName(spec))
|
|
@@ -14594,11 +14735,11 @@ class AgentRegistry {
|
|
|
14594
14735
|
return Promise.resolve();
|
|
14595
14736
|
}
|
|
14596
14737
|
const { version } = spec.run;
|
|
14597
|
-
const dir =
|
|
14738
|
+
const dir = join7(this.dir, id);
|
|
14598
14739
|
const p = (async () => {
|
|
14599
14740
|
try {
|
|
14600
|
-
|
|
14601
|
-
|
|
14741
|
+
mkdirSync6(dir, { recursive: true });
|
|
14742
|
+
writeFileSync5(join7(dir, "package.json"), `${JSON.stringify({ name: `toyon-agent-${id}`, private: true, dependencies: { [pkg]: version } }, null, 2)}
|
|
14602
14743
|
`);
|
|
14603
14744
|
log.info("agents", `installing ${pkg}@${version} for ${id}`);
|
|
14604
14745
|
const r = await this.installer(dir, pkg, version);
|
|
@@ -14629,9 +14770,9 @@ class AgentRegistry {
|
|
|
14629
14770
|
const name = this.packageName(spec);
|
|
14630
14771
|
if (!name)
|
|
14631
14772
|
return null;
|
|
14632
|
-
const pkgJson =
|
|
14773
|
+
const pkgJson = join7(this.dir, spec.id, "node_modules", name, "package.json");
|
|
14633
14774
|
try {
|
|
14634
|
-
return JSON.parse(
|
|
14775
|
+
return JSON.parse(readFileSync8(pkgJson, "utf8")).version ?? null;
|
|
14635
14776
|
} catch {
|
|
14636
14777
|
return null;
|
|
14637
14778
|
}
|
|
@@ -14684,7 +14825,8 @@ class AgentRegistry {
|
|
|
14684
14825
|
...this.installing.has(spec.id) ? { installing: true } : {},
|
|
14685
14826
|
...install !== undefined ? { install } : {},
|
|
14686
14827
|
...spec.onDemand ? { onDemand: true } : {},
|
|
14687
|
-
sandboxed: spec.confinement !== "none"
|
|
14828
|
+
sandboxed: spec.confinement !== "none",
|
|
14829
|
+
loginHint: spec.loginHint
|
|
14688
14830
|
};
|
|
14689
14831
|
});
|
|
14690
14832
|
}
|
|
@@ -14752,7 +14894,7 @@ function managedSpecs(specs, managed) {
|
|
|
14752
14894
|
return managed.agents === null ? kept : kept.filter((s) => managed.agents?.includes(s.id));
|
|
14753
14895
|
}
|
|
14754
14896
|
function loadAgentRegistry(agentsFile, agentsDir, sandbox, managed = MANAGED_DEFAULTS) {
|
|
14755
|
-
const specs = managed.customAgents &&
|
|
14897
|
+
const specs = managed.customAgents && existsSync9(agentsFile) ? parseAgentsFile(readFileSync8(agentsFile, "utf8"), BUILTIN_AGENTS) : BUILTIN_AGENTS;
|
|
14756
14898
|
return new AgentRegistry(managedSpecs(specs, managed), agentsDir, undefined, undefined, sandbox);
|
|
14757
14899
|
}
|
|
14758
14900
|
|
|
@@ -14785,19 +14927,19 @@ function orderPlanOptions(options) {
|
|
|
14785
14927
|
}
|
|
14786
14928
|
|
|
14787
14929
|
// ../daemon/src/agent/origins.ts
|
|
14788
|
-
import { existsSync as
|
|
14930
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
|
|
14789
14931
|
import { homedir as homedir4 } from "os";
|
|
14790
|
-
import { basename as
|
|
14932
|
+
import { basename as basename3, join as join8 } from "path";
|
|
14791
14933
|
function commandFolders(spec, cwd, home = homedir4()) {
|
|
14792
14934
|
if (spec.id === "claude") {
|
|
14793
14935
|
return [
|
|
14794
|
-
{ origin: "project", skills:
|
|
14795
|
-
{ origin: "user", skills:
|
|
14796
|
-
...pluginPaths(spec.meta).map((p) => ({ origin: "plugin", skills:
|
|
14936
|
+
{ origin: "project", skills: join8(cwd, ".claude", "skills"), commands: join8(cwd, ".claude", "commands") },
|
|
14937
|
+
{ origin: "user", skills: join8(home, ".claude", "skills"), commands: join8(home, ".claude", "commands") },
|
|
14938
|
+
...pluginPaths(spec.meta).map((p) => ({ origin: "plugin", skills: join8(p, "skills"), commands: join8(p, "commands") }))
|
|
14797
14939
|
];
|
|
14798
14940
|
}
|
|
14799
14941
|
if (spec.id === "codex")
|
|
14800
|
-
return [{ origin: "user", commands:
|
|
14942
|
+
return [{ origin: "user", commands: join8(home, ".codex", "prompts") }];
|
|
14801
14943
|
return [];
|
|
14802
14944
|
}
|
|
14803
14945
|
function readCommandOrigins(folders) {
|
|
@@ -14837,23 +14979,23 @@ function pluginPaths(meta) {
|
|
|
14837
14979
|
}
|
|
14838
14980
|
function skillNames(dir) {
|
|
14839
14981
|
return entries2(dir).flatMap((e) => {
|
|
14840
|
-
const file =
|
|
14841
|
-
if (!
|
|
14982
|
+
const file = join8(dir, e, "SKILL.md");
|
|
14983
|
+
if (!existsSync10(file))
|
|
14842
14984
|
return [];
|
|
14843
14985
|
return [frontMatterName(file) ?? e];
|
|
14844
14986
|
});
|
|
14845
14987
|
}
|
|
14846
14988
|
function commandNames(dir) {
|
|
14847
14989
|
return entries2(dir).flatMap((e) => {
|
|
14848
|
-
const path =
|
|
14990
|
+
const path = join8(dir, e);
|
|
14849
14991
|
if (e.endsWith(".md"))
|
|
14850
|
-
return [
|
|
14992
|
+
return [basename3(e, ".md")];
|
|
14851
14993
|
return isDir(path) ? commandNames(path) : [];
|
|
14852
14994
|
});
|
|
14853
14995
|
}
|
|
14854
14996
|
function frontMatterName(file) {
|
|
14855
14997
|
try {
|
|
14856
|
-
const text =
|
|
14998
|
+
const text = readFileSync9(file, "utf8");
|
|
14857
14999
|
if (!text.startsWith("---"))
|
|
14858
15000
|
return null;
|
|
14859
15001
|
const end = text.indexOf(`
|
|
@@ -14867,10 +15009,10 @@ function frontMatterName(file) {
|
|
|
14867
15009
|
}
|
|
14868
15010
|
}
|
|
14869
15011
|
function entries2(dir) {
|
|
14870
|
-
if (!
|
|
15012
|
+
if (!existsSync10(dir))
|
|
14871
15013
|
return [];
|
|
14872
15014
|
try {
|
|
14873
|
-
return
|
|
15015
|
+
return readdirSync4(dir).filter((e) => !e.startsWith("."));
|
|
14874
15016
|
} catch (e) {
|
|
14875
15017
|
log.warn("origins", `${dir} could not be listed`, e);
|
|
14876
15018
|
return [];
|
|
@@ -14914,13 +15056,13 @@ function unformatOutput(output) {
|
|
|
14914
15056
|
}
|
|
14915
15057
|
|
|
14916
15058
|
// ../daemon/src/agent/policy.ts
|
|
14917
|
-
import { isAbsolute as
|
|
15059
|
+
import { isAbsolute as isAbsolute3, resolve as resolve4 } from "path";
|
|
14918
15060
|
|
|
14919
15061
|
// ../daemon/src/agent/acp/background.ts
|
|
14920
|
-
import { existsSync as
|
|
15062
|
+
import { existsSync as existsSync11, statSync as statSync4 } from "fs";
|
|
14921
15063
|
import { open as open2 } from "fs/promises";
|
|
14922
15064
|
import { homedir as homedir5 } from "os";
|
|
14923
|
-
import { basename as
|
|
15065
|
+
import { basename as basename4, dirname as dirname4, join as join9 } from "path";
|
|
14924
15066
|
var STARTED = /Command running in background with ID: (\S+)\. Output is being written to: (\S+)\./;
|
|
14925
15067
|
function backgroundStart(input, output) {
|
|
14926
15068
|
const flag = input && typeof input === "object" ? input.run_in_background : undefined;
|
|
@@ -14933,15 +15075,15 @@ function spawnDetached(output) {
|
|
|
14933
15075
|
return output.includes("Async agent launched successfully");
|
|
14934
15076
|
}
|
|
14935
15077
|
function claudeConfigDir() {
|
|
14936
|
-
return process.env.CLAUDE_CONFIG_DIR ||
|
|
15078
|
+
return process.env.CLAUDE_CONFIG_DIR || join9(homedir5(), ".claude");
|
|
14937
15079
|
}
|
|
14938
15080
|
function sessionLogFor(file, configDir = claudeConfigDir()) {
|
|
14939
15081
|
const tasks = dirname4(file);
|
|
14940
|
-
if (
|
|
15082
|
+
if (basename4(tasks) !== "tasks")
|
|
14941
15083
|
return null;
|
|
14942
15084
|
const session = dirname4(tasks);
|
|
14943
|
-
const path =
|
|
14944
|
-
return
|
|
15085
|
+
const path = join9(configDir, "projects", basename4(dirname4(session)), `${basename4(session)}.jsonl`);
|
|
15086
|
+
return existsSync11(path) ? path : null;
|
|
14945
15087
|
}
|
|
14946
15088
|
function taskEnd(text, toolId) {
|
|
14947
15089
|
const at = text.indexOf(`<tool-use-id>${toolId}</tool-use-id>`);
|
|
@@ -15520,7 +15662,7 @@ function decide(req, bounds, cwd, mode = "auto", sandboxed = true) {
|
|
|
15520
15662
|
return { kind: "allow" };
|
|
15521
15663
|
}
|
|
15522
15664
|
for (const raw of paths) {
|
|
15523
|
-
const target = canonical(
|
|
15665
|
+
const target = canonical(isAbsolute3(raw) ? raw : resolve4(cwd, raw));
|
|
15524
15666
|
if (bounds.denyRead.some((d) => within2(target, d))) {
|
|
15525
15667
|
return {
|
|
15526
15668
|
kind: "reject",
|
|
@@ -15586,7 +15728,6 @@ var SYSTEM_APPEND = [
|
|
|
15586
15728
|
"`@some/path` in a message means read that file or directory first; `@changes` means this worktree's uncommitted files, which `git status` lists.",
|
|
15587
15729
|
'Toyon previews the project in a browser panel by running the commands in its settings file, .toyon/settings.json (or toyon.json at the repo root, in a repo that keeps it there), shaped like {"setup": ["bun install"], "run": {"web": "bun run dev --port $PORT"}, "check": "bun run check", "land": {"route": "merge"}}: setup runs once in every new worktree, teardown (optional) runs once when a worktree is removed, every command in run keeps running and must listen on $PORT, which toyon sets differently for each worktree, check (optional) runs after each of your turns and must pass before the work can land, and land (optional) says how the user lands work on main: route "merge" (onto main here), "push" (onto main here, then pushed) or "pr" (a pull request); method "merge", "squash" or "rebase" for how the commits arrive on main; automerge true when GitHub should merge the pull request itself; timeouts (optional) gives setup, check and commit each a ceiling as a duration like "30m" (ten minutes each when unset), for a suite that runs longer. A settings.local.json beside it (toyon.local.json at the root) holds one person\'s overrides and is never committed. Both files may carry comments and trailing commas; leave any you find.',
|
|
15588
15730
|
"Toyon runs those commands itself in every worktree, and the user watches the result live in a preview beside this chat, so never start a dev server or open a browser of your own to check your work. Each message says where this worktree's preview answers; fetch a page from there when you want to see it.",
|
|
15589
|
-
"To see whether a page renders without throwing, call the `preview` tool with its path when you have one: a browser the user has open loads it beside what they see and reports what it threw.",
|
|
15590
15731
|
"When you scaffold a project, write its .gitignore (dependencies, build output, local env files) before installing anything, so an install never leaves thousands of untracked files for the user to wade through or commit.",
|
|
15591
15732
|
"When you scaffold a project or change how it installs or starts, finish by updating the settings file Toyon already reads, or writing .toyon/settings.json when there is none, so the preview can run it."
|
|
15592
15733
|
].join(" ");
|
|
@@ -15599,7 +15740,6 @@ function failedRun(f) {
|
|
|
15599
15740
|
case "conflict":
|
|
15600
15741
|
return f.step;
|
|
15601
15742
|
case "preview":
|
|
15602
|
-
case "page":
|
|
15603
15743
|
return;
|
|
15604
15744
|
}
|
|
15605
15745
|
}
|
|
@@ -15615,8 +15755,6 @@ function fixPrompt(f) {
|
|
|
15615
15755
|
return commandFixPrompt(f.command);
|
|
15616
15756
|
case "preview":
|
|
15617
15757
|
return procFixPrompt(f.procs, f.log);
|
|
15618
|
-
case "page":
|
|
15619
|
-
return pageFixPrompt(f.errors);
|
|
15620
15758
|
}
|
|
15621
15759
|
}
|
|
15622
15760
|
var WHY_COMMAND_CHARS = 60;
|
|
@@ -15635,20 +15773,8 @@ function fixWhy(f) {
|
|
|
15635
15773
|
}
|
|
15636
15774
|
case "preview":
|
|
15637
15775
|
return "the dev server is not reachable";
|
|
15638
|
-
case "page":
|
|
15639
|
-
return "the preview threw";
|
|
15640
15776
|
}
|
|
15641
15777
|
}
|
|
15642
|
-
function pageFixPrompt(errors) {
|
|
15643
|
-
return [
|
|
15644
|
-
"Since your last edits the preview threw this in the user's browser:",
|
|
15645
|
-
"",
|
|
15646
|
-
...errors.map((e) => `- ${e}`),
|
|
15647
|
-
"",
|
|
15648
|
-
"Find the cause and fix it, inside this worktree. If your change did not cause it, say what did instead of changing anything."
|
|
15649
|
-
].join(`
|
|
15650
|
-
`);
|
|
15651
|
-
}
|
|
15652
15778
|
function failureContext(f) {
|
|
15653
15779
|
const run = failedRun(f);
|
|
15654
15780
|
if (!run)
|
|
@@ -15854,11 +15980,11 @@ ${a.text}
|
|
|
15854
15980
|
}
|
|
15855
15981
|
|
|
15856
15982
|
// ../daemon/src/agent/transcript.ts
|
|
15857
|
-
import { existsSync as
|
|
15983
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
15858
15984
|
import { appendFile } from "fs/promises";
|
|
15859
|
-
import { join as
|
|
15985
|
+
import { join as join10 } from "path";
|
|
15860
15986
|
function transcriptPathFor(transcriptsDir, worktreeId) {
|
|
15861
|
-
return
|
|
15987
|
+
return join10(transcriptsDir, `${worktreeId}.jsonl`);
|
|
15862
15988
|
}
|
|
15863
15989
|
function coalesce(entries) {
|
|
15864
15990
|
const out = [];
|
|
@@ -15912,7 +16038,7 @@ class Transcript {
|
|
|
15912
16038
|
this.seq = (this.entries.at(-1)?.seq ?? -1) + 1;
|
|
15913
16039
|
}
|
|
15914
16040
|
read() {
|
|
15915
|
-
return
|
|
16041
|
+
return existsSync12(this.path) ? parseTranscript(readFileSync10(this.path, "utf8"), this.tag) : [];
|
|
15916
16042
|
}
|
|
15917
16043
|
append(event) {
|
|
15918
16044
|
const entry = { seq: this.seq++, event };
|
|
@@ -16685,12 +16811,7 @@ class AcpSession {
|
|
|
16685
16811
|
this.emit({ type: "turn-end", stopReason: mapStopReason(res.stopReason), ts: Date.now() });
|
|
16686
16812
|
}
|
|
16687
16813
|
contextFor(item) {
|
|
16688
|
-
return ambientBlock([
|
|
16689
|
-
...item.context ?? [],
|
|
16690
|
-
this.d.preview?.(),
|
|
16691
|
-
item.asked === "page" ? undefined : this.d.pageErrors?.(),
|
|
16692
|
-
...this.landedSince()
|
|
16693
|
-
]);
|
|
16814
|
+
return ambientBlock([...item.context ?? [], this.d.preview?.(), this.d.pageErrors?.(), ...this.landedSince()]);
|
|
16694
16815
|
}
|
|
16695
16816
|
landedSince() {
|
|
16696
16817
|
const entries = this.log.entries;
|
|
@@ -17004,7 +17125,8 @@ class AcpSession {
|
|
|
17004
17125
|
const plan = params.toolCall.kind === "switch_mode";
|
|
17005
17126
|
const parts = permissionParts(params);
|
|
17006
17127
|
const detail = parts.detail ?? "";
|
|
17007
|
-
const
|
|
17128
|
+
const ownFile = plan ? stringField(params.toolCall.rawInput, "planFilePath") : undefined;
|
|
17129
|
+
const planPath = plan ? await this.d.onPlan?.(detail, ownFile) ?? null : null;
|
|
17008
17130
|
const offered = plan ? orderPlanOptions(params.options) : params.options.filter((o) => o.kind !== "allow_always");
|
|
17009
17131
|
const choices = offered.map((o) => ({ id: o.optionId, name: o.name, kind: o.kind }));
|
|
17010
17132
|
return this.openAsk((id) => ({
|
|
@@ -17175,9 +17297,6 @@ class AcpSession {
|
|
|
17175
17297
|
this.noteProcess(conn.link, false);
|
|
17176
17298
|
}
|
|
17177
17299
|
}
|
|
17178
|
-
function isAuthRequired(e) {
|
|
17179
|
-
return e instanceof RequestError && e.code === -32000;
|
|
17180
|
-
}
|
|
17181
17300
|
function takesHttpMcp(init) {
|
|
17182
17301
|
const http = init.agentCapabilities?.mcpCapabilities?.http;
|
|
17183
17302
|
return http === true || typeof http === "object" && http !== null;
|
|
@@ -17254,66 +17373,6 @@ function sameCommands(a, b) {
|
|
|
17254
17373
|
return a.length === b.length && a.every((c, i) => c.name === b[i]?.name && c.description === b[i]?.description && c.hint === b[i]?.hint && c.origin === b[i]?.origin);
|
|
17255
17374
|
}
|
|
17256
17375
|
|
|
17257
|
-
// ../daemon/src/agent/planDoc.ts
|
|
17258
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
17259
|
-
import { join as join10 } from "path";
|
|
17260
|
-
var PLANS_DIR = join10(".toyon", "plans");
|
|
17261
|
-
var PLAN_NAME = /^(\d+)\.md$/;
|
|
17262
|
-
function isPlanPath(rel) {
|
|
17263
|
-
return rel.startsWith(`${PLANS_DIR}/`) && PLAN_NAME.test(rel.slice(PLANS_DIR.length + 1));
|
|
17264
|
-
}
|
|
17265
|
-
function lastNumber(dir) {
|
|
17266
|
-
if (!existsSync12(dir))
|
|
17267
|
-
return 0;
|
|
17268
|
-
let max = 0;
|
|
17269
|
-
for (const name of readdirSync4(dir)) {
|
|
17270
|
-
const n = Number(PLAN_NAME.exec(name)?.[1]);
|
|
17271
|
-
if (n > max)
|
|
17272
|
-
max = n;
|
|
17273
|
-
}
|
|
17274
|
-
return max;
|
|
17275
|
-
}
|
|
17276
|
-
function sameAsLast(cwd, rel, markdown) {
|
|
17277
|
-
try {
|
|
17278
|
-
return readFileSync10(join10(cwd, rel), "utf8").trim() === markdown.trim();
|
|
17279
|
-
} catch (e) {
|
|
17280
|
-
log.warn(cwd, `could not read ${rel}`, e);
|
|
17281
|
-
return false;
|
|
17282
|
-
}
|
|
17283
|
-
}
|
|
17284
|
-
async function writePlanDoc(cwd, markdown) {
|
|
17285
|
-
const dir = join10(cwd, PLANS_DIR);
|
|
17286
|
-
const last = lastNumber(dir);
|
|
17287
|
-
if (last > 0) {
|
|
17288
|
-
const newest = join10(PLANS_DIR, `${last}.md`);
|
|
17289
|
-
if (sameAsLast(cwd, newest, markdown))
|
|
17290
|
-
return newest;
|
|
17291
|
-
}
|
|
17292
|
-
const rel = join10(PLANS_DIR, `${last + 1}.md`);
|
|
17293
|
-
try {
|
|
17294
|
-
mkdirSync6(dir, { recursive: true });
|
|
17295
|
-
writeFileSync5(join10(cwd, rel), markdown.endsWith(`
|
|
17296
|
-
`) ? markdown : `${markdown}
|
|
17297
|
-
`);
|
|
17298
|
-
} catch (e) {
|
|
17299
|
-
log.warn(cwd, `could not write ${rel}; the plan stays on the card`, e);
|
|
17300
|
-
return null;
|
|
17301
|
-
}
|
|
17302
|
-
await excludeFromGit(cwd, `${PLANS_DIR}/`);
|
|
17303
|
-
return rel;
|
|
17304
|
-
}
|
|
17305
|
-
function planEdited(cwd, rel, proposed) {
|
|
17306
|
-
const file = join10(cwd, rel);
|
|
17307
|
-
if (!existsSync12(file))
|
|
17308
|
-
return false;
|
|
17309
|
-
try {
|
|
17310
|
-
return readFileSync10(file, "utf8").trim() !== proposed.trim();
|
|
17311
|
-
} catch (e) {
|
|
17312
|
-
log.warn(cwd, `could not read ${rel}`, e);
|
|
17313
|
-
return false;
|
|
17314
|
-
}
|
|
17315
|
-
}
|
|
17316
|
-
|
|
17317
17376
|
// ../daemon/src/runtime/forward.ts
|
|
17318
17377
|
var POLL_MS2 = 100;
|
|
17319
17378
|
var HOLD_MS = 60000;
|
|
@@ -18527,8 +18586,8 @@ function defaultAgent(wt, d, preview, onProcess) {
|
|
|
18527
18586
|
onEvent: (event, seq) => d.hub.emit("agent", wt.id, seq, event),
|
|
18528
18587
|
onStatus: (status) => d.hub.emit("agentStatus", wt.id, status),
|
|
18529
18588
|
beforeTurn: async () => d.turnStarting?.(wt.id),
|
|
18530
|
-
onAuth: (agentId, o) => d.
|
|
18531
|
-
onLimits: (agentId, limits) => d.
|
|
18589
|
+
onAuth: (agentId, o) => d.readiness?.observe(agentId, o),
|
|
18590
|
+
onLimits: (agentId, limits) => d.readiness?.observeLimits(agentId, limits),
|
|
18532
18591
|
seedCommands: () => d.state.cachedCommands(d.state.requireWorktree(wt.id).agent ?? "", wt.repoId),
|
|
18533
18592
|
onCommandsLearned: (commands) => d.state.setCachedCommands(d.state.requireWorktree(wt.id).agent ?? "", wt.repoId, commands),
|
|
18534
18593
|
commandOrigins: () => readCommandOrigins(commandFolders(spec(), wt.path)),
|
|
@@ -18537,13 +18596,14 @@ function defaultAgent(wt, d, preview, onProcess) {
|
|
|
18537
18596
|
if (d.state.setMode(wt.id, mode))
|
|
18538
18597
|
d.hub.emit("worktreesChanged");
|
|
18539
18598
|
},
|
|
18540
|
-
onPlan: async (markdown) => {
|
|
18599
|
+
onPlan: async (markdown, ownFile) => {
|
|
18541
18600
|
const w = d.state.worktree(wt.id);
|
|
18542
18601
|
if (w && !w.planned) {
|
|
18543
18602
|
w.planned = true;
|
|
18544
18603
|
d.state.save();
|
|
18545
18604
|
}
|
|
18546
|
-
const
|
|
18605
|
+
const own = ownFile ? ownPlanPath(wt.path, ownFile) : null;
|
|
18606
|
+
const path = own ?? (markdown.trim() ? await writePlanDoc(wt.path, markdown) : null);
|
|
18547
18607
|
const fresh = path ? d.state.worktree(wt.id) : undefined;
|
|
18548
18608
|
if (fresh && path && fresh.plan !== path) {
|
|
18549
18609
|
fresh.plan = path;
|
|
@@ -19836,10 +19896,12 @@ function makePlanner(agents) {
|
|
|
19836
19896
|
}
|
|
19837
19897
|
|
|
19838
19898
|
// ../daemon/src/agent/transcriptForward.ts
|
|
19839
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
19899
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
19840
19900
|
import { join as join12 } from "path";
|
|
19901
|
+
var FORWARD_STEP = "1";
|
|
19902
|
+
var MARK = '"page';
|
|
19841
19903
|
function forwardLine(line) {
|
|
19842
|
-
if (!line.includes(
|
|
19904
|
+
if (!line.includes(MARK))
|
|
19843
19905
|
return null;
|
|
19844
19906
|
let entry;
|
|
19845
19907
|
try {
|
|
@@ -19848,31 +19910,35 @@ function forwardLine(line) {
|
|
|
19848
19910
|
return null;
|
|
19849
19911
|
}
|
|
19850
19912
|
const e = entry.event;
|
|
19851
|
-
if (e?.type
|
|
19852
|
-
return
|
|
19853
|
-
|
|
19854
|
-
|
|
19913
|
+
if (e?.type === "page-error")
|
|
19914
|
+
return { drop: true };
|
|
19915
|
+
if (e?.type === "fix-asked" && e.kind === "page")
|
|
19916
|
+
return { line: JSON.stringify({ ...entry, event: { ...e, kind: "command" } }) };
|
|
19917
|
+
return null;
|
|
19855
19918
|
}
|
|
19856
19919
|
function forwardText(text) {
|
|
19857
19920
|
let changed = 0;
|
|
19858
|
-
const lines =
|
|
19859
|
-
|
|
19921
|
+
const lines = [];
|
|
19922
|
+
for (const line of text.split(`
|
|
19923
|
+
`)) {
|
|
19860
19924
|
const next = forwardLine(line);
|
|
19861
19925
|
if (next === null)
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19926
|
+
lines.push(line);
|
|
19927
|
+
else {
|
|
19928
|
+
changed++;
|
|
19929
|
+
if ("line" in next)
|
|
19930
|
+
lines.push(next.line);
|
|
19931
|
+
}
|
|
19932
|
+
}
|
|
19866
19933
|
return { text: changed ? lines.join(`
|
|
19867
19934
|
`) : text, changed };
|
|
19868
19935
|
}
|
|
19869
|
-
function forwardTranscripts(
|
|
19936
|
+
function forwardTranscripts(dirs, stamp) {
|
|
19937
|
+
if (readStamp(stamp) === FORWARD_STEP)
|
|
19938
|
+
return;
|
|
19870
19939
|
let files = 0;
|
|
19871
19940
|
let rows = 0;
|
|
19872
|
-
for (const
|
|
19873
|
-
if (!name.endsWith(".jsonl"))
|
|
19874
|
-
continue;
|
|
19875
|
-
const path = join12(dir, name);
|
|
19941
|
+
for (const path of dirs.flatMap(transcriptsUnder)) {
|
|
19876
19942
|
const { text, changed } = forwardText(readFileSync11(path, "utf8"));
|
|
19877
19943
|
if (!changed)
|
|
19878
19944
|
continue;
|
|
@@ -19882,15 +19948,36 @@ function forwardTranscripts(dir) {
|
|
|
19882
19948
|
files++;
|
|
19883
19949
|
rows += changed;
|
|
19884
19950
|
}
|
|
19951
|
+
writeFileSync6(stamp, FORWARD_STEP);
|
|
19885
19952
|
if (files)
|
|
19886
|
-
log.info("transcripts", `
|
|
19953
|
+
log.info("transcripts", `moved ${rows} row(s) in ${files} transcript(s) past the preview-threw row`);
|
|
19954
|
+
}
|
|
19955
|
+
function readStamp(stamp) {
|
|
19956
|
+
try {
|
|
19957
|
+
return readFileSync11(stamp, "utf8").trim();
|
|
19958
|
+
} catch {
|
|
19959
|
+
return null;
|
|
19960
|
+
}
|
|
19961
|
+
}
|
|
19962
|
+
function transcriptsUnder(dir) {
|
|
19963
|
+
const found = [];
|
|
19964
|
+
for (const entry of readdirSync5(dir, { withFileTypes: true })) {
|
|
19965
|
+
if (entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
19966
|
+
found.push(join12(dir, entry.name));
|
|
19967
|
+
else if (entry.isDirectory()) {
|
|
19968
|
+
const inner = join12(dir, entry.name, "transcript.jsonl");
|
|
19969
|
+
if (existsSync14(inner))
|
|
19970
|
+
found.push(inner);
|
|
19971
|
+
}
|
|
19972
|
+
}
|
|
19973
|
+
return found;
|
|
19887
19974
|
}
|
|
19888
19975
|
|
|
19889
19976
|
// ../daemon/src/core/assets.ts
|
|
19890
|
-
import { existsSync as
|
|
19977
|
+
import { existsSync as existsSync15, readdirSync as readdirSync6, rmSync as rmSync3, statSync as statSync5 } from "fs";
|
|
19891
19978
|
import { join as join13, resolve as resolve5 } from "path";
|
|
19892
19979
|
function locateAssets(here, env = process.env) {
|
|
19893
|
-
const packaged =
|
|
19980
|
+
const packaged = existsSync15(join13(here, "shell", "index.html"));
|
|
19894
19981
|
return {
|
|
19895
19982
|
shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join13(here, "shell") : join13(here, "../../shell/dist")),
|
|
19896
19983
|
bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join13(here, "bridge.js") : join13(here, "../../bridge/dist/bridge.js")),
|
|
@@ -20357,7 +20444,7 @@ class KeepAwake {
|
|
|
20357
20444
|
}
|
|
20358
20445
|
|
|
20359
20446
|
// ../daemon/src/core/dialog.ts
|
|
20360
|
-
import { existsSync as
|
|
20447
|
+
import { existsSync as existsSync16 } from "fs";
|
|
20361
20448
|
import { homedir as homedir6 } from "os";
|
|
20362
20449
|
var PROMPTS = {
|
|
20363
20450
|
location: "Choose where the new project goes",
|
|
@@ -20375,7 +20462,7 @@ function folderDialog() {
|
|
|
20375
20462
|
let cancelled = false;
|
|
20376
20463
|
const ask = async (start, purpose) => {
|
|
20377
20464
|
const typed = start === "~" || start.startsWith("~/") ? `${homedir6()}${start.slice(1)}` : start;
|
|
20378
|
-
const from = typed.startsWith("/") &&
|
|
20465
|
+
const from = typed.startsWith("/") && existsSync16(typed) ? typed : homedir6();
|
|
20379
20466
|
cancelled = false;
|
|
20380
20467
|
const p = Bun.spawn(["osascript", ...SCRIPT.flatMap((line) => ["-e", line]), PROMPTS[purpose], from], {
|
|
20381
20468
|
stdout: "pipe",
|
|
@@ -20452,9 +20539,9 @@ class GrantCodes {
|
|
|
20452
20539
|
|
|
20453
20540
|
// ../daemon/src/core/helper.ts
|
|
20454
20541
|
import { createHash as createHash3 } from "crypto";
|
|
20455
|
-
import { existsSync as
|
|
20542
|
+
import { existsSync as existsSync17 } from "fs";
|
|
20456
20543
|
import { chmod, copyFile as copyFile3, mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
20457
|
-
import { basename as
|
|
20544
|
+
import { basename as basename5, dirname as dirname5, join as join14 } from "path";
|
|
20458
20545
|
var EXECUTABLE = "Toyon";
|
|
20459
20546
|
function helperWanted(where) {
|
|
20460
20547
|
return where.platform === "darwin" && !where.cloud && where.port === DAEMON_DEFAULT_PORT && where.home === undefined;
|
|
@@ -20533,7 +20620,7 @@ class Helper {
|
|
|
20533
20620
|
}
|
|
20534
20621
|
async write() {
|
|
20535
20622
|
const { spec } = this;
|
|
20536
|
-
if (!
|
|
20623
|
+
if (!existsSync17(spec.stub))
|
|
20537
20624
|
return "skipped";
|
|
20538
20625
|
const stubHash = createHash3("sha256").update(await readFile2(spec.stub)).digest("hex").slice(0, 16);
|
|
20539
20626
|
const { plist, script } = helperFiles(spec, stubHash);
|
|
@@ -20541,7 +20628,7 @@ class Helper {
|
|
|
20541
20628
|
const resources = join14(contents, "Resources");
|
|
20542
20629
|
const stub = join14(contents, "MacOS", EXECUTABLE);
|
|
20543
20630
|
const scriptFile = join14(resources, "launch.sh");
|
|
20544
|
-
if (
|
|
20631
|
+
if (existsSync17(stub) && existsSync17(scriptFile) && await readFile2(scriptFile, "utf8") === script)
|
|
20545
20632
|
return "kept";
|
|
20546
20633
|
await mkdir2(join14(contents, "MacOS"), { recursive: true });
|
|
20547
20634
|
await mkdir2(resources, { recursive: true });
|
|
@@ -20549,7 +20636,7 @@ class Helper {
|
|
|
20549
20636
|
await writeFile2(scriptFile, script, { mode: 493 });
|
|
20550
20637
|
await copyFile3(spec.stub, stub);
|
|
20551
20638
|
await chmod(stub, 493);
|
|
20552
|
-
if (!
|
|
20639
|
+
if (!existsSync17(join14(resources, "AppIcon.icns")))
|
|
20553
20640
|
await this.drawIcon(resources);
|
|
20554
20641
|
await this.run(["xattr", "-cr", spec.dir]);
|
|
20555
20642
|
if (!await this.run(["codesign", "--force", "--deep", "-s", "-", spec.dir])) {
|
|
@@ -20564,8 +20651,8 @@ class Helper {
|
|
|
20564
20651
|
const set = join14(tmp, "AppIcon.iconset");
|
|
20565
20652
|
await mkdir2(set, { recursive: true });
|
|
20566
20653
|
await this.run(["qlmanage", "-t", "-s", "1024", "-o", tmp, svg]);
|
|
20567
|
-
const big = join14(tmp, `${
|
|
20568
|
-
if (
|
|
20654
|
+
const big = join14(tmp, `${basename5(svg)}.png`);
|
|
20655
|
+
if (existsSync17(big)) {
|
|
20569
20656
|
for (const size of [16, 32, 64, 128, 256, 512, 1024]) {
|
|
20570
20657
|
await this.run(["sips", "-z", String(size), String(size), big, "--out", join14(set, `icon_${size}x${size}.png`)]);
|
|
20571
20658
|
}
|
|
@@ -21008,7 +21095,7 @@ class SelfWatch {
|
|
|
21008
21095
|
|
|
21009
21096
|
// ../daemon/src/core/state.ts
|
|
21010
21097
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
21011
|
-
import { existsSync as
|
|
21098
|
+
import { existsSync as existsSync18, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
21012
21099
|
var TRUSTED_MAX = 16;
|
|
21013
21100
|
var DAEMON_GROUPS = "daemon";
|
|
21014
21101
|
var GROUPS_SAVE_MS = 250;
|
|
@@ -21017,7 +21104,7 @@ var empty = { repos: [], worktrees: [], sessions: {} };
|
|
|
21017
21104
|
function loadState(paths) {
|
|
21018
21105
|
ensureDirs(paths);
|
|
21019
21106
|
const STATE_FILE = paths.stateFile;
|
|
21020
|
-
if (!
|
|
21107
|
+
if (!existsSync18(STATE_FILE))
|
|
21021
21108
|
return structuredClone(empty);
|
|
21022
21109
|
let raw;
|
|
21023
21110
|
try {
|
|
@@ -21069,7 +21156,7 @@ function saveState(paths, state) {
|
|
|
21069
21156
|
function loadOrCreateToken(paths) {
|
|
21070
21157
|
ensureDirs(paths);
|
|
21071
21158
|
const TOKEN_FILE = paths.tokenFile;
|
|
21072
|
-
if (
|
|
21159
|
+
if (existsSync18(TOKEN_FILE))
|
|
21073
21160
|
return readFileSync12(TOKEN_FILE, "utf8").trim();
|
|
21074
21161
|
const token = randomBytes6(32).toString("hex");
|
|
21075
21162
|
writeFileSync8(TOKEN_FILE, token, { mode: 384 });
|
|
@@ -22104,7 +22191,7 @@ async function loadProjectTypescript(root) {
|
|
|
22104
22191
|
}
|
|
22105
22192
|
|
|
22106
22193
|
// ../daemon/src/drafts/store.ts
|
|
22107
|
-
import { existsSync as
|
|
22194
|
+
import { existsSync as existsSync19, readFileSync as readFileSync13, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
22108
22195
|
var SAVE_DELAY_MS = 1000;
|
|
22109
22196
|
|
|
22110
22197
|
class DraftStore {
|
|
@@ -22241,7 +22328,7 @@ class DraftStore {
|
|
|
22241
22328
|
}
|
|
22242
22329
|
}
|
|
22243
22330
|
function readRecord(file) {
|
|
22244
|
-
if (!
|
|
22331
|
+
if (!existsSync19(file))
|
|
22245
22332
|
return [];
|
|
22246
22333
|
try {
|
|
22247
22334
|
const raw = JSON.parse(readFileSync13(file, "utf8"));
|
|
@@ -22498,10 +22585,10 @@ class ExecService {
|
|
|
22498
22585
|
|
|
22499
22586
|
// ../daemon/src/files/open.ts
|
|
22500
22587
|
import { stat as stat3 } from "fs/promises";
|
|
22501
|
-
import { basename as
|
|
22588
|
+
import { basename as basename6, resolve as resolve7 } from "path";
|
|
22502
22589
|
|
|
22503
22590
|
// ../daemon/src/repos/browse.ts
|
|
22504
|
-
import { existsSync as
|
|
22591
|
+
import { existsSync as existsSync20, statSync as statSync6 } from "fs";
|
|
22505
22592
|
import { readdir } from "fs/promises";
|
|
22506
22593
|
import { homedir as homedir8 } from "os";
|
|
22507
22594
|
import { dirname as dirname6, join as join16 } from "path";
|
|
@@ -22518,7 +22605,7 @@ function holdsNothing(names) {
|
|
|
22518
22605
|
}
|
|
22519
22606
|
async function describeFolder(abs) {
|
|
22520
22607
|
const path = collapseTilde(abs);
|
|
22521
|
-
if (
|
|
22608
|
+
if (existsSync20(join16(abs, ".git")))
|
|
22522
22609
|
return { path, kind: "project" };
|
|
22523
22610
|
try {
|
|
22524
22611
|
return { path, kind: holdsNothing(await readdir(abs)) ? "empty" : "folder" };
|
|
@@ -22533,10 +22620,10 @@ function describe2(typed, parent) {
|
|
|
22533
22620
|
isDir = statSync6(typed).isDirectory();
|
|
22534
22621
|
} catch {}
|
|
22535
22622
|
return {
|
|
22536
|
-
exists:
|
|
22623
|
+
exists: existsSync20(typed),
|
|
22537
22624
|
isDir,
|
|
22538
|
-
isRepo: isDir &&
|
|
22539
|
-
parentExists:
|
|
22625
|
+
isRepo: isDir && existsSync20(join16(typed, ".git")),
|
|
22626
|
+
parentExists: existsSync20(parent)
|
|
22540
22627
|
};
|
|
22541
22628
|
}
|
|
22542
22629
|
async function browsePath(raw) {
|
|
@@ -22561,7 +22648,7 @@ async function browsePath(raw) {
|
|
|
22561
22648
|
if (!name.toLowerCase().startsWith(prefix))
|
|
22562
22649
|
continue;
|
|
22563
22650
|
const abs = join16(dir, name);
|
|
22564
|
-
entries.push({ path: collapseTilde(abs), name, isRepo:
|
|
22651
|
+
entries.push({ path: collapseTilde(abs), name, isRepo: existsSync20(join16(abs, ".git")) });
|
|
22565
22652
|
}
|
|
22566
22653
|
entries.sort((a, b) => a.isRepo !== b.isRepo ? a.isRepo ? -1 : 1 : a.name.localeCompare(b.name));
|
|
22567
22654
|
return { entries: entries.slice(0, MAX_ENTRIES), target };
|
|
@@ -23410,7 +23497,7 @@ class OpenService {
|
|
|
23410
23497
|
return hit ? { kind: "file", worktreeId: hit.id, path: path.slice(hit.root.length + 1) } : null;
|
|
23411
23498
|
}
|
|
23412
23499
|
async grant(path, size, mtimeMs) {
|
|
23413
|
-
const name =
|
|
23500
|
+
const name = basename6(path);
|
|
23414
23501
|
if (this.d.refused.some((r) => within2(path, canonical(resolve7(r))))) {
|
|
23415
23502
|
throw new UserError(`${name} is inside Toyon's own folder, which is not for editing`);
|
|
23416
23503
|
}
|
|
@@ -23630,7 +23717,6 @@ function prNumberOf(url) {
|
|
|
23630
23717
|
|
|
23631
23718
|
// ../daemon/src/preview/errors.ts
|
|
23632
23719
|
var KEPT = 5;
|
|
23633
|
-
var ROWS_MAX = 3;
|
|
23634
23720
|
var TWICE_MS = 1000;
|
|
23635
23721
|
|
|
23636
23722
|
class PageErrorService {
|
|
@@ -23638,75 +23724,35 @@ class PageErrorService {
|
|
|
23638
23724
|
held = new Map;
|
|
23639
23725
|
constructor(d) {
|
|
23640
23726
|
this.d = d;
|
|
23641
|
-
d.hub.on("agent", (worktreeId, _seq, event) => this.heard(worktreeId, event));
|
|
23642
23727
|
d.hub.on("worktreesChanged", () => {
|
|
23643
23728
|
for (const id of this.held.keys())
|
|
23644
23729
|
if (!d.known(id))
|
|
23645
23730
|
this.held.delete(id);
|
|
23646
23731
|
});
|
|
23647
23732
|
}
|
|
23648
|
-
of(worktreeId) {
|
|
23649
|
-
let h = this.held.get(worktreeId);
|
|
23650
|
-
if (!h) {
|
|
23651
|
-
h = { recent: [], running: false, noted: new Set, rows: 0 };
|
|
23652
|
-
this.held.set(worktreeId, h);
|
|
23653
|
-
}
|
|
23654
|
-
return h;
|
|
23655
|
-
}
|
|
23656
|
-
heard(worktreeId, event) {
|
|
23657
|
-
const h = this.of(worktreeId);
|
|
23658
|
-
switch (event.type) {
|
|
23659
|
-
case "user-message":
|
|
23660
|
-
case "fix-asked":
|
|
23661
|
-
h.noted = new Set;
|
|
23662
|
-
h.rows = 0;
|
|
23663
|
-
break;
|
|
23664
|
-
case "turn-start":
|
|
23665
|
-
h.running = true;
|
|
23666
|
-
break;
|
|
23667
|
-
case "turn-end":
|
|
23668
|
-
case "agent-error":
|
|
23669
|
-
case "agent-auth-required":
|
|
23670
|
-
h.running = false;
|
|
23671
|
-
this.flush(worktreeId, h, event.ts);
|
|
23672
|
-
break;
|
|
23673
|
-
}
|
|
23674
|
-
}
|
|
23675
23733
|
report(worktreeId, message) {
|
|
23676
23734
|
if (!this.d.known(worktreeId)) {
|
|
23677
23735
|
log.debug("pages", `an error from unknown worktree ${worktreeId} was dropped`);
|
|
23678
23736
|
return;
|
|
23679
23737
|
}
|
|
23680
|
-
|
|
23738
|
+
let h = this.held.get(worktreeId);
|
|
23739
|
+
if (!h) {
|
|
23740
|
+
h = [];
|
|
23741
|
+
this.held.set(worktreeId, h);
|
|
23742
|
+
}
|
|
23681
23743
|
const now = this.d.now?.() ?? Date.now();
|
|
23682
|
-
const last = h.
|
|
23744
|
+
const last = h.at(-1);
|
|
23683
23745
|
if (last && last.message === message && now - last.ts < TWICE_MS)
|
|
23684
23746
|
return;
|
|
23685
|
-
h.
|
|
23686
|
-
if (h.
|
|
23687
|
-
h.
|
|
23688
|
-
if (!h.running)
|
|
23689
|
-
this.flush(worktreeId, h, now);
|
|
23690
|
-
}
|
|
23691
|
-
flush(worktreeId, h, ts) {
|
|
23692
|
-
const fresh = [...new Set(h.recent.map((e) => e.message))].filter((m) => !h.noted.has(m));
|
|
23693
|
-
if (fresh.length === 0 || h.rows >= ROWS_MAX)
|
|
23694
|
-
return;
|
|
23695
|
-
const agent = this.d.runtime.agentFor(worktreeId);
|
|
23696
|
-
if (!agent)
|
|
23697
|
-
return;
|
|
23698
|
-
for (const m of fresh)
|
|
23699
|
-
h.noted.add(m);
|
|
23700
|
-
h.rows++;
|
|
23701
|
-
agent.note({ type: "page-error", errors: fresh, ts });
|
|
23747
|
+
h.push({ message, ts: now });
|
|
23748
|
+
if (h.length > KEPT)
|
|
23749
|
+
h.splice(0, h.length - KEPT);
|
|
23702
23750
|
}
|
|
23703
23751
|
loaded(worktreeId) {
|
|
23704
|
-
|
|
23705
|
-
if (h)
|
|
23706
|
-
h.recent = [];
|
|
23752
|
+
this.held.delete(worktreeId);
|
|
23707
23753
|
}
|
|
23708
23754
|
recent(worktreeId) {
|
|
23709
|
-
return this.held.get(worktreeId)?.
|
|
23755
|
+
return this.held.get(worktreeId)?.map((e) => e.message) ?? [];
|
|
23710
23756
|
}
|
|
23711
23757
|
ambient(worktreeId) {
|
|
23712
23758
|
const errors = this.recent(worktreeId);
|
|
@@ -23718,122 +23764,6 @@ ${errors.map((e) => `- ${e}`).join(`
|
|
|
23718
23764
|
}
|
|
23719
23765
|
}
|
|
23720
23766
|
|
|
23721
|
-
// ../daemon/src/preview/render.ts
|
|
23722
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
23723
|
-
var WAIT_MS2 = 1e4;
|
|
23724
|
-
var PATH_MAX = 2048;
|
|
23725
|
-
var refused = (text) => ({ text, isError: true });
|
|
23726
|
-
|
|
23727
|
-
class RenderService {
|
|
23728
|
-
d;
|
|
23729
|
-
courier = () => false;
|
|
23730
|
-
pending = new Map;
|
|
23731
|
-
out = new Set;
|
|
23732
|
-
constructor(d) {
|
|
23733
|
-
this.d = d;
|
|
23734
|
-
}
|
|
23735
|
-
tool() {
|
|
23736
|
-
return {
|
|
23737
|
-
name: PREVIEW_TOOL,
|
|
23738
|
-
description: "See whether a page of this worktree's preview renders without throwing. Pass `path` (for example `/about`): a browser the user has open loads it beside what they are looking at and reports the uncaught errors and unhandled rejections it threw in the moment after load. With no path, reports what the preview has thrown since it last loaded, without rendering. It does not describe what the page looks like, and it is not a substitute for the user's own checks.",
|
|
23739
|
-
inputSchema: {
|
|
23740
|
-
type: "object",
|
|
23741
|
-
properties: {
|
|
23742
|
-
path: {
|
|
23743
|
-
type: "string",
|
|
23744
|
-
description: "the route to render, starting with `/`; absent to report without rendering"
|
|
23745
|
-
}
|
|
23746
|
-
},
|
|
23747
|
-
additionalProperties: false
|
|
23748
|
-
}
|
|
23749
|
-
};
|
|
23750
|
-
}
|
|
23751
|
-
async call(worktreeId, args) {
|
|
23752
|
-
const a = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
23753
|
-
if (a.path !== undefined && typeof a.path !== "string")
|
|
23754
|
-
return refused("`path` is a string: a route, like `/about`.");
|
|
23755
|
-
const path = a.path?.trim();
|
|
23756
|
-
if (path !== undefined && (!/^(?:\/(?![/\\])|[?#])/.test(path) || path.length > PATH_MAX)) {
|
|
23757
|
-
return refused("`path` is a route of the preview, starting with `/`, like `/about`.");
|
|
23758
|
-
}
|
|
23759
|
-
const standing = this.d.runtime.previewStanding(worktreeId);
|
|
23760
|
-
if (!standing)
|
|
23761
|
-
return refused("This project has nothing to run, so there is no page to render.");
|
|
23762
|
-
if (standing.status !== "running")
|
|
23763
|
-
return refused(previewContext(standing) ?? "The preview is not running.");
|
|
23764
|
-
if (path === undefined)
|
|
23765
|
-
return { text: this.held(worktreeId) };
|
|
23766
|
-
if (this.out.has(worktreeId))
|
|
23767
|
-
return refused("A render is already under way; call again when it has returned.");
|
|
23768
|
-
const result = await this.render(worktreeId, path);
|
|
23769
|
-
if (!result)
|
|
23770
|
-
return refused(`No browser has this worktree open right now, so nothing can render it. ${this.held(worktreeId)}`);
|
|
23771
|
-
if (!result.ok)
|
|
23772
|
-
return refused(`Could not render ${path}: ${result.reason ?? "the page did not report"}.`);
|
|
23773
|
-
return { text: this.report(path, result) };
|
|
23774
|
-
}
|
|
23775
|
-
render(worktreeId, path) {
|
|
23776
|
-
const id = randomUUID2();
|
|
23777
|
-
return new Promise((resolve) => {
|
|
23778
|
-
const wait = this.d.waitMs ?? WAIT_MS2;
|
|
23779
|
-
const timer = setTimeout(() => settle({
|
|
23780
|
-
worktreeId,
|
|
23781
|
-
id,
|
|
23782
|
-
ok: false,
|
|
23783
|
-
reason: `the page did not report within ${Math.round(wait / 1000)}s`,
|
|
23784
|
-
errors: []
|
|
23785
|
-
}), wait);
|
|
23786
|
-
const settle = (r) => {
|
|
23787
|
-
if (!this.pending.delete(id))
|
|
23788
|
-
return;
|
|
23789
|
-
clearTimeout(timer);
|
|
23790
|
-
this.out.delete(worktreeId);
|
|
23791
|
-
resolve(r);
|
|
23792
|
-
};
|
|
23793
|
-
this.pending.set(id, { worktreeId, settle });
|
|
23794
|
-
this.out.add(worktreeId);
|
|
23795
|
-
if (!this.courier(worktreeId, { t: "render", worktreeId, id, path }))
|
|
23796
|
-
settle(null);
|
|
23797
|
-
});
|
|
23798
|
-
}
|
|
23799
|
-
rendered(msg) {
|
|
23800
|
-
const p = this.pending.get(msg.id);
|
|
23801
|
-
if (!p || p.worktreeId !== msg.worktreeId) {
|
|
23802
|
-
log.debug("render", `an answer for no render out on ${msg.worktreeId} was dropped`);
|
|
23803
|
-
return;
|
|
23804
|
-
}
|
|
23805
|
-
p.settle(msg);
|
|
23806
|
-
}
|
|
23807
|
-
held(worktreeId) {
|
|
23808
|
-
const errors = this.d.pageErrors.recent(worktreeId);
|
|
23809
|
-
if (errors.length === 0)
|
|
23810
|
-
return "The preview has thrown nothing since it last loaded.";
|
|
23811
|
-
return `The preview has thrown since it last loaded:
|
|
23812
|
-
${errors.map((e) => `- ${e}`).join(`
|
|
23813
|
-
`)}`;
|
|
23814
|
-
}
|
|
23815
|
-
report(path, r) {
|
|
23816
|
-
const title = r.title ? ` (title "${r.title}")` : "";
|
|
23817
|
-
const landed = r.url ? pathOf(r.url) : null;
|
|
23818
|
-
const elsewhere = landed && landed !== path ? ` It landed on ${landed}.` : "";
|
|
23819
|
-
const head = `Rendered ${path} in the user's browser${title}.${elsewhere}`;
|
|
23820
|
-
if (r.errors.length === 0)
|
|
23821
|
-
return `${head} No errors in the moment after load.`;
|
|
23822
|
-
const n = r.errors.length;
|
|
23823
|
-
return `${head} ${n} ${n === 1 ? "error" : "errors"} in the moment after load:
|
|
23824
|
-
${r.errors.map((e) => `- ${e}`).join(`
|
|
23825
|
-
`)}`;
|
|
23826
|
-
}
|
|
23827
|
-
}
|
|
23828
|
-
function pathOf(url) {
|
|
23829
|
-
try {
|
|
23830
|
-
const u = new URL(url);
|
|
23831
|
-
return `${u.pathname}${u.search}`;
|
|
23832
|
-
} catch {
|
|
23833
|
-
return null;
|
|
23834
|
-
}
|
|
23835
|
-
}
|
|
23836
|
-
|
|
23837
23767
|
// ../daemon/src/runtime/setup.ts
|
|
23838
23768
|
var SETUP_RING = 64 * 1024;
|
|
23839
23769
|
var SETUP_COLS = 120;
|
|
@@ -23962,9 +23892,9 @@ class AfterLand {
|
|
|
23962
23892
|
}
|
|
23963
23893
|
|
|
23964
23894
|
// ../daemon/src/repos/registry.ts
|
|
23965
|
-
import { existsSync as
|
|
23895
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
23966
23896
|
import { homedir as homedir10 } from "os";
|
|
23967
|
-
import { basename as
|
|
23897
|
+
import { basename as basename8, dirname as dirname8, join as join21 } from "path";
|
|
23968
23898
|
|
|
23969
23899
|
// ../daemon/src/git/base.ts
|
|
23970
23900
|
async function resolveBase(repoPath, defaultBranch, route) {
|
|
@@ -23975,7 +23905,7 @@ async function resolveBase(repoPath, defaultBranch, route) {
|
|
|
23975
23905
|
}
|
|
23976
23906
|
|
|
23977
23907
|
// ../daemon/src/repos/config.ts
|
|
23978
|
-
import { closeSync, existsSync as
|
|
23908
|
+
import { closeSync, existsSync as existsSync21, openSync as openSync2, readdirSync as readdirSync7, readFileSync as readFileSync14, readSync, statSync as statSync7 } from "fs";
|
|
23979
23909
|
import { join as join18 } from "path";
|
|
23980
23910
|
|
|
23981
23911
|
// ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
@@ -25340,7 +25270,7 @@ function applyEdits(text, edits) {
|
|
|
25340
25270
|
|
|
25341
25271
|
// ../daemon/src/repos/config.ts
|
|
25342
25272
|
var PLACES = [CONFIG_FILES.folder, CONFIG_FILES.root];
|
|
25343
|
-
var present = (repoPath, place) => [place.shared, place.local].filter((rel) =>
|
|
25273
|
+
var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync21(join18(repoPath, rel)));
|
|
25344
25274
|
var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
25345
25275
|
function parseSettings(text) {
|
|
25346
25276
|
const errors = [];
|
|
@@ -25425,7 +25355,7 @@ function configTarget(repoPath, made) {
|
|
|
25425
25355
|
function configBody(repoPath, rel, config) {
|
|
25426
25356
|
const place = PLACES.find((p) => p.local === rel);
|
|
25427
25357
|
const abs = place && join18(repoPath, place.shared);
|
|
25428
|
-
if (!abs || !
|
|
25358
|
+
if (!abs || !existsSync21(abs))
|
|
25429
25359
|
return config;
|
|
25430
25360
|
let shared;
|
|
25431
25361
|
try {
|
|
@@ -25473,7 +25403,7 @@ function detectConfig(repoPath) {
|
|
|
25473
25403
|
if (explicit)
|
|
25474
25404
|
log.warn("config", `${repoPath}: ${explicit.reason}; detecting instead`);
|
|
25475
25405
|
const pkgPath = join18(repoPath, "package.json");
|
|
25476
|
-
if (
|
|
25406
|
+
if (existsSync21(pkgPath)) {
|
|
25477
25407
|
const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
|
|
25478
25408
|
const scripts = pkg.scripts ?? {};
|
|
25479
25409
|
const runner = detectRunner(repoPath, pkg.packageManager);
|
|
@@ -25495,7 +25425,7 @@ function detectConfig(repoPath) {
|
|
|
25495
25425
|
};
|
|
25496
25426
|
}
|
|
25497
25427
|
}
|
|
25498
|
-
if (
|
|
25428
|
+
if (existsSync21(join18(repoPath, "start.sh"))) {
|
|
25499
25429
|
return { config: { run: { app: "./start.sh" } }, needsSetup: true, from: "start.sh" };
|
|
25500
25430
|
}
|
|
25501
25431
|
const assumed = assumeNothingToRun(repoPath);
|
|
@@ -25538,7 +25468,7 @@ function assumeNothingToRun(repoPath) {
|
|
|
25538
25468
|
return marker && !servesPage(repoPath) ? marker : undefined;
|
|
25539
25469
|
}
|
|
25540
25470
|
function markerIn(repoPath) {
|
|
25541
|
-
const named = NO_PAGE_MARKERS.find((f) =>
|
|
25471
|
+
const named = NO_PAGE_MARKERS.find((f) => existsSync21(join18(repoPath, f)));
|
|
25542
25472
|
if (named)
|
|
25543
25473
|
return named;
|
|
25544
25474
|
let entries;
|
|
@@ -25550,7 +25480,7 @@ function markerIn(repoPath) {
|
|
|
25550
25480
|
return entries.find((f) => NO_PAGE_EXTENSIONS.some((ext) => f.endsWith(ext)));
|
|
25551
25481
|
}
|
|
25552
25482
|
function servesPage(repoPath) {
|
|
25553
|
-
if (WEB_FILES.some((f) =>
|
|
25483
|
+
if (WEB_FILES.some((f) => existsSync21(join18(repoPath, f))))
|
|
25554
25484
|
return true;
|
|
25555
25485
|
if (/^web:/m.test(head(join18(repoPath, "Procfile"))))
|
|
25556
25486
|
return true;
|
|
@@ -25587,7 +25517,7 @@ function mentions(text, name) {
|
|
|
25587
25517
|
return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i").test(text);
|
|
25588
25518
|
}
|
|
25589
25519
|
function head(path) {
|
|
25590
|
-
if (!
|
|
25520
|
+
if (!existsSync21(path))
|
|
25591
25521
|
return "";
|
|
25592
25522
|
try {
|
|
25593
25523
|
const fd = openSync2(path, "r");
|
|
@@ -25628,7 +25558,7 @@ function isLocalDatabase(url) {
|
|
|
25628
25558
|
}
|
|
25629
25559
|
function detectServices(repoPath) {
|
|
25630
25560
|
const out = {};
|
|
25631
|
-
const compose = COMPOSE_FILES.find((f) =>
|
|
25561
|
+
const compose = COMPOSE_FILES.find((f) => existsSync21(join18(repoPath, f)));
|
|
25632
25562
|
if (compose)
|
|
25633
25563
|
out.compose = compose;
|
|
25634
25564
|
for (const file of ENV_FILES) {
|
|
@@ -25667,21 +25597,21 @@ function inferPaths(cmd, repoPath, depth = 1) {
|
|
|
25667
25597
|
const t = tokens[i];
|
|
25668
25598
|
if (t === "cd") {
|
|
25669
25599
|
const dir = tokens[i + 1];
|
|
25670
|
-
if (dir && !dir.startsWith("-") &&
|
|
25600
|
+
if (dir && !dir.startsWith("-") && existsSync21(join18(repoPath, dir)))
|
|
25671
25601
|
add(dir);
|
|
25672
25602
|
continue;
|
|
25673
25603
|
}
|
|
25674
25604
|
if (t.startsWith("-") || t.startsWith("$"))
|
|
25675
25605
|
continue;
|
|
25676
25606
|
const mod = /^([A-Za-z_]\w*)(?:\.\w+)*:\w+$/.exec(t);
|
|
25677
|
-
if (mod?.[1] &&
|
|
25607
|
+
if (mod?.[1] && existsSync21(join18(repoPath, mod[1]))) {
|
|
25678
25608
|
add(mod[1]);
|
|
25679
25609
|
continue;
|
|
25680
25610
|
}
|
|
25681
25611
|
const rel = t.replace(/^\.\//, "");
|
|
25682
25612
|
if (rel.startsWith("/") || rel.startsWith("..") || rel.includes("="))
|
|
25683
25613
|
continue;
|
|
25684
|
-
if (!
|
|
25614
|
+
if (!existsSync21(join18(repoPath, rel)))
|
|
25685
25615
|
continue;
|
|
25686
25616
|
add(rel);
|
|
25687
25617
|
if (depth > 0 && rel.endsWith(".sh")) {
|
|
@@ -25708,9 +25638,9 @@ function procCommand(runner, script, body) {
|
|
|
25708
25638
|
return `${base}${runner === "npm" ? " --" : ""} ${flag}`;
|
|
25709
25639
|
}
|
|
25710
25640
|
function detectPages(repoPath, scripts, runner) {
|
|
25711
|
-
if (!scripts.build || !
|
|
25641
|
+
if (!scripts.build || !existsSync21(join18(repoPath, "functions")))
|
|
25712
25642
|
return null;
|
|
25713
|
-
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join18(repoPath, f)).find((f) =>
|
|
25643
|
+
const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join18(repoPath, f)).find((f) => existsSync21(f));
|
|
25714
25644
|
if (!cfg)
|
|
25715
25645
|
return null;
|
|
25716
25646
|
const out = readFileSync14(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
|
|
@@ -25719,17 +25649,17 @@ function detectPages(repoPath, scripts, runner) {
|
|
|
25719
25649
|
return { web: `${runner} run build && npx wrangler pages dev ${out} --ip 127.0.0.1 --port $PORT` };
|
|
25720
25650
|
}
|
|
25721
25651
|
function detectRunner(repoPath, packageManager) {
|
|
25722
|
-
if (
|
|
25652
|
+
if (existsSync21(join18(repoPath, "bun.lock")) || existsSync21(join18(repoPath, "bun.lockb")))
|
|
25723
25653
|
return "bun";
|
|
25724
|
-
if (
|
|
25654
|
+
if (existsSync21(join18(repoPath, "pnpm-lock.yaml")))
|
|
25725
25655
|
return "pnpm";
|
|
25726
|
-
if (
|
|
25656
|
+
if (existsSync21(join18(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
|
|
25727
25657
|
return "yarn";
|
|
25728
25658
|
return "npm";
|
|
25729
25659
|
}
|
|
25730
25660
|
|
|
25731
25661
|
// ../daemon/src/repos/create.ts
|
|
25732
|
-
import { existsSync as
|
|
25662
|
+
import { existsSync as existsSync22, realpathSync as realpathSync2, statSync as statSync8 } from "fs";
|
|
25733
25663
|
import { appendFile as appendFile2, mkdir as mkdir3, rm as rm2 } from "fs/promises";
|
|
25734
25664
|
import { homedir as homedir9 } from "os";
|
|
25735
25665
|
import { dirname as dirname7, join as join19, resolve as resolve8 } from "path";
|
|
@@ -25744,13 +25674,13 @@ function planProject(opts, home = homedir9()) {
|
|
|
25744
25674
|
const first = firstProjectsFolder(opts.parent, home);
|
|
25745
25675
|
const parent = first ?? resolveParent(opts.parent);
|
|
25746
25676
|
const dir = join19(parent, opts.name.trim());
|
|
25747
|
-
if (
|
|
25677
|
+
if (existsSync22(dir))
|
|
25748
25678
|
throw new UserError(`${dir} already exists`);
|
|
25749
25679
|
return { parent, dir, makeParent: first !== null };
|
|
25750
25680
|
}
|
|
25751
25681
|
function firstProjectsFolder(raw, home) {
|
|
25752
25682
|
const typed = expandTilde(raw.trim()).replace(/\/+$/, "");
|
|
25753
|
-
if (typed !== join19(home, PROJECTS_FOLDER) ||
|
|
25683
|
+
if (typed !== join19(home, PROJECTS_FOLDER) || existsSync22(typed) || !existsSync22(home))
|
|
25754
25684
|
return null;
|
|
25755
25685
|
return join19(realpathSync2(home), PROJECTS_FOLDER);
|
|
25756
25686
|
}
|
|
@@ -25760,10 +25690,10 @@ function planInPlace(opts) {
|
|
|
25760
25690
|
throw new UserError(`${opts.name} is not a folder name`);
|
|
25761
25691
|
}
|
|
25762
25692
|
const typed = join19(resolveParent(opts.parent), name);
|
|
25763
|
-
if (!
|
|
25693
|
+
if (!existsSync22(typed) || !statSync8(typed).isDirectory())
|
|
25764
25694
|
throw new UserError(`${typed} is not a folder`);
|
|
25765
25695
|
const dir = realpathSync2(typed);
|
|
25766
|
-
if (
|
|
25696
|
+
if (existsSync22(join19(dir, ".git")))
|
|
25767
25697
|
throw new UserError(`${dir} is already a project; open it instead`);
|
|
25768
25698
|
return { parent: dirname7(dir), dir, makeParent: false };
|
|
25769
25699
|
}
|
|
@@ -25771,7 +25701,7 @@ function resolveParent(raw) {
|
|
|
25771
25701
|
const typed = expandTilde(raw.trim());
|
|
25772
25702
|
if (!typed.startsWith("/"))
|
|
25773
25703
|
throw new UserError(`${raw} is not a folder path`);
|
|
25774
|
-
if (!
|
|
25704
|
+
if (!existsSync22(typed))
|
|
25775
25705
|
throw new UserError(`${raw} does not exist; make it first`);
|
|
25776
25706
|
if (!statSync8(typed).isDirectory())
|
|
25777
25707
|
throw new UserError(`${raw} is not a folder`);
|
|
@@ -25888,8 +25818,8 @@ async function ok(p, what) {
|
|
|
25888
25818
|
}
|
|
25889
25819
|
|
|
25890
25820
|
// ../daemon/src/repos/watcher.ts
|
|
25891
|
-
import { existsSync as
|
|
25892
|
-
import { basename as
|
|
25821
|
+
import { existsSync as existsSync23, watch } from "fs";
|
|
25822
|
+
import { basename as basename7, join as join20 } from "path";
|
|
25893
25823
|
function watchDefaultBranch(repoPath, branch, onMove) {
|
|
25894
25824
|
const last = { local: null, upstream: null };
|
|
25895
25825
|
const read = async () => {
|
|
@@ -25953,11 +25883,11 @@ function watchDefaultBranch(repoPath, branch, onMove) {
|
|
|
25953
25883
|
let mid = null;
|
|
25954
25884
|
const armInner = () => {
|
|
25955
25885
|
inner?.close();
|
|
25956
|
-
inner =
|
|
25886
|
+
inner = existsSync23(mine) ? tryWatch(mine) : null;
|
|
25957
25887
|
};
|
|
25958
25888
|
const armMid = () => {
|
|
25959
25889
|
mid?.close();
|
|
25960
|
-
mid =
|
|
25890
|
+
mid = existsSync23(remotes) ? tryWatch(remotes, (_event, filename) => {
|
|
25961
25891
|
if (!filename || filename === remote.out)
|
|
25962
25892
|
armInner();
|
|
25963
25893
|
schedule();
|
|
@@ -26035,7 +25965,7 @@ function watchWorktreeDir(repoPath, onChange) {
|
|
|
26035
25965
|
}
|
|
26036
25966
|
function watchConfigFile(repoPath, onChange) {
|
|
26037
25967
|
const atRoot = new Set([CONFIG_FILES.root.shared, CONFIG_FILES.root.local, CONFIG_DIR]);
|
|
26038
|
-
const inDir = new Set([CONFIG_FILES.folder.shared, CONFIG_FILES.folder.local].map((rel) =>
|
|
25968
|
+
const inDir = new Set([CONFIG_FILES.folder.shared, CONFIG_FILES.folder.local].map((rel) => basename7(rel)));
|
|
26039
25969
|
const dir = join20(repoPath, CONFIG_DIR);
|
|
26040
25970
|
let timer = null;
|
|
26041
25971
|
let outer = null;
|
|
@@ -26051,7 +25981,7 @@ function watchConfigFile(repoPath, onChange) {
|
|
|
26051
25981
|
const armInner = () => {
|
|
26052
25982
|
inner?.close();
|
|
26053
25983
|
inner = null;
|
|
26054
|
-
if (!
|
|
25984
|
+
if (!existsSync23(dir))
|
|
26055
25985
|
return;
|
|
26056
25986
|
try {
|
|
26057
25987
|
inner = watch(dir, (_event, filename) => {
|
|
@@ -26105,7 +26035,7 @@ class RepoRegistry {
|
|
|
26105
26035
|
}
|
|
26106
26036
|
async boot() {
|
|
26107
26037
|
const { state } = this.d;
|
|
26108
|
-
const gone = state.worktrees.filter((wt) => !
|
|
26038
|
+
const gone = state.worktrees.filter((wt) => !existsSync24(wt.path) || !state.repos.some((r) => r.id === wt.repoId));
|
|
26109
26039
|
for (const wt of gone)
|
|
26110
26040
|
await this.d.worktrees.forgetGone(wt);
|
|
26111
26041
|
for (const repo of state.repos) {
|
|
@@ -26173,7 +26103,7 @@ class RepoRegistry {
|
|
|
26173
26103
|
return `${repo.name} is set up now`;
|
|
26174
26104
|
if (main.promptedAt || main.lastTurn)
|
|
26175
26105
|
return `${repo.name} has a chat now`;
|
|
26176
|
-
if (
|
|
26106
|
+
if (existsSync24(join21(repo.path, ".claude")))
|
|
26177
26107
|
return `an agent has already run in ${repo.name}`;
|
|
26178
26108
|
if ((await statusFiles(repo.path)).length > 0 || !await treeEmpty(repo.path)) {
|
|
26179
26109
|
return `${repo.name} has files in it now`;
|
|
@@ -26254,7 +26184,7 @@ class RepoRegistry {
|
|
|
26254
26184
|
}
|
|
26255
26185
|
async register(rawPath, made) {
|
|
26256
26186
|
const path = expandTilde(rawPath);
|
|
26257
|
-
if (!
|
|
26187
|
+
if (!existsSync24(path))
|
|
26258
26188
|
throw new UserError(`${path} does not exist`);
|
|
26259
26189
|
if (!await isGitRepo(path))
|
|
26260
26190
|
throw new UserError(`${path} is not a git repository`);
|
|
@@ -26268,7 +26198,7 @@ class RepoRegistry {
|
|
|
26268
26198
|
const repo = {
|
|
26269
26199
|
id: shortId(),
|
|
26270
26200
|
path: root,
|
|
26271
|
-
name:
|
|
26201
|
+
name: basename8(root),
|
|
26272
26202
|
defaultBranch: await defaultBranch(root),
|
|
26273
26203
|
config: detected.config,
|
|
26274
26204
|
configFile: configTarget(root, !!made),
|
|
@@ -26328,7 +26258,7 @@ class RepoRegistry {
|
|
|
26328
26258
|
throw new UserError(current.reason);
|
|
26329
26259
|
const was = configTarget(repo.path, !!repo.made);
|
|
26330
26260
|
const rel = configSibling(was, kind);
|
|
26331
|
-
const moved = rel !== was &&
|
|
26261
|
+
const moved = rel !== was && existsSync24(join21(repo.path, was));
|
|
26332
26262
|
if (moved && !(kind === "local" && await isTracked(repo.path, was))) {
|
|
26333
26263
|
try {
|
|
26334
26264
|
rmSync4(join21(repo.path, was));
|
|
@@ -26444,7 +26374,7 @@ class RepoRegistry {
|
|
|
26444
26374
|
}
|
|
26445
26375
|
placeConfig(repo) {
|
|
26446
26376
|
const rel = configTarget(repo.path, !!repo.made);
|
|
26447
|
-
if (isLocalConfigFile(rel) &&
|
|
26377
|
+
if (isLocalConfigFile(rel) && existsSync24(join21(repo.path, rel))) {
|
|
26448
26378
|
fireAndForget(repo.id, excludeFromGit(repo.path, rel), "exclude local settings");
|
|
26449
26379
|
}
|
|
26450
26380
|
if (repo.configFile === rel)
|
|
@@ -28023,7 +27953,7 @@ function isAlive(pid) {
|
|
|
28023
27953
|
}
|
|
28024
27954
|
|
|
28025
27955
|
// ../daemon/src/runtime/bridge-script.ts
|
|
28026
|
-
import { existsSync as
|
|
27956
|
+
import { existsSync as existsSync25, readFileSync as readFileSync15, statSync as statSync9 } from "fs";
|
|
28027
27957
|
var LEARNED_MAX = 8;
|
|
28028
27958
|
|
|
28029
27959
|
class BridgeScript {
|
|
@@ -28053,7 +27983,7 @@ class BridgeScript {
|
|
|
28053
27983
|
this.cache = null;
|
|
28054
27984
|
}
|
|
28055
27985
|
get() {
|
|
28056
|
-
if (!
|
|
27986
|
+
if (!existsSync25(this.path))
|
|
28057
27987
|
return "// toyon bridge not built";
|
|
28058
27988
|
const s = statSync9(this.path);
|
|
28059
27989
|
const stamp = `${s.mtimeMs}:${s.size}`;
|
|
@@ -28086,7 +28016,7 @@ import { homedir as homedir12, hostname as hostname2 } from "os";
|
|
|
28086
28016
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
28087
28017
|
|
|
28088
28018
|
// ../daemon/src/agent/config.ts
|
|
28089
|
-
import { existsSync as
|
|
28019
|
+
import { existsSync as existsSync26, readFileSync as readFileSync16 } from "fs";
|
|
28090
28020
|
import { homedir as homedir11 } from "os";
|
|
28091
28021
|
import { join as join23 } from "path";
|
|
28092
28022
|
function agentConfigFiles(spec, repoPath, home = homedir11()) {
|
|
@@ -28094,7 +28024,7 @@ function agentConfigFiles(spec, repoPath, home = homedir11()) {
|
|
|
28094
28024
|
id,
|
|
28095
28025
|
label,
|
|
28096
28026
|
path,
|
|
28097
|
-
exists:
|
|
28027
|
+
exists: existsSync26(path)
|
|
28098
28028
|
});
|
|
28099
28029
|
if (spec.id === "claude") {
|
|
28100
28030
|
return [
|
|
@@ -28126,7 +28056,7 @@ function describeAgentConfig(spec, repoPath, home = homedir11()) {
|
|
|
28126
28056
|
return { agent: spec.id, files, servers };
|
|
28127
28057
|
}
|
|
28128
28058
|
function readJson(path) {
|
|
28129
|
-
if (!
|
|
28059
|
+
if (!existsSync26(path))
|
|
28130
28060
|
return null;
|
|
28131
28061
|
try {
|
|
28132
28062
|
const v = JSON.parse(readFileSync16(path, "utf8"));
|
|
@@ -28147,7 +28077,7 @@ function serversOf(raw, scope) {
|
|
|
28147
28077
|
});
|
|
28148
28078
|
}
|
|
28149
28079
|
function codexServers(path) {
|
|
28150
|
-
if (!
|
|
28080
|
+
if (!existsSync26(path))
|
|
28151
28081
|
return [];
|
|
28152
28082
|
const out = [];
|
|
28153
28083
|
let current = null;
|
|
@@ -28780,12 +28710,6 @@ var handlers = {
|
|
|
28780
28710
|
"page-loaded"(msg, _ctx, s) {
|
|
28781
28711
|
s.pageErrors.loaded(msg.worktreeId);
|
|
28782
28712
|
},
|
|
28783
|
-
"fix-page"(msg, _ctx, s) {
|
|
28784
|
-
s.fix.pressPage(msg.worktreeId);
|
|
28785
|
-
},
|
|
28786
|
-
rendered(msg, _ctx, s) {
|
|
28787
|
-
s.render.rendered(msg);
|
|
28788
|
-
},
|
|
28789
28713
|
async chat(msg, ctx, s) {
|
|
28790
28714
|
await sending(s, ctx, msg, msg.attachments, () => s.worktrees.send(msg.worktreeId, {
|
|
28791
28715
|
text: msg.text,
|
|
@@ -29045,6 +28969,12 @@ var handlers = {
|
|
|
29045
28969
|
async "reveal-path"(msg, _ctx, s) {
|
|
29046
28970
|
s.files.revealPath(await s.opens.where(msg.path));
|
|
29047
28971
|
},
|
|
28972
|
+
async "reveal-attachment"(msg, _ctx, s) {
|
|
28973
|
+
const path = await s.attachments.locate(msg.worktreeId, msg.file, (file) => s.worktrees.archivedAttachment(msg.worktreeId, file));
|
|
28974
|
+
if (!path)
|
|
28975
|
+
throw new UserError("that attachment is no longer on this machine");
|
|
28976
|
+
s.files.revealPath(path);
|
|
28977
|
+
},
|
|
29048
28978
|
async "discard-files"(msg, _ctx, s) {
|
|
29049
28979
|
try {
|
|
29050
28980
|
for (const path of msg.paths)
|
|
@@ -29201,9 +29131,12 @@ var handlers = {
|
|
|
29201
29131
|
s.files.revealPath(file.path);
|
|
29202
29132
|
},
|
|
29203
29133
|
async "agent-logout"(msg, _ctx, s) {
|
|
29204
|
-
await s.
|
|
29134
|
+
await s.readiness.logout(msg.agent);
|
|
29205
29135
|
s.hub.emit("agentsChanged");
|
|
29206
29136
|
},
|
|
29137
|
+
"agent-check"(msg, _ctx, s) {
|
|
29138
|
+
fireAndForget("agents", s.readiness.check(msg.agent), "agent check");
|
|
29139
|
+
},
|
|
29207
29140
|
"set-default-agent"(msg, _ctx, s) {
|
|
29208
29141
|
s.agents.require(msg.agent);
|
|
29209
29142
|
s.state.setDefaultAgent(msg.agent);
|
|
@@ -29326,7 +29259,7 @@ async function dispatch(msg, ctx, s) {
|
|
|
29326
29259
|
}
|
|
29327
29260
|
|
|
29328
29261
|
// ../daemon/src/server/http.ts
|
|
29329
|
-
import { existsSync as
|
|
29262
|
+
import { existsSync as existsSync27 } from "fs";
|
|
29330
29263
|
import { hostname } from "os";
|
|
29331
29264
|
import { join as join24 } from "path";
|
|
29332
29265
|
|
|
@@ -29631,7 +29564,7 @@ function createFetch(opts) {
|
|
|
29631
29564
|
return new Response("unauthorized", { status: 401 });
|
|
29632
29565
|
const id = url.pathname.slice("/uploads/".length);
|
|
29633
29566
|
const path = opts.attachments.uploads.path(id);
|
|
29634
|
-
if (!path || !
|
|
29567
|
+
if (!path || !existsSync27(path))
|
|
29635
29568
|
return new Response("not found", { status: 404 });
|
|
29636
29569
|
return new Response(Bun.file(path), {
|
|
29637
29570
|
headers: { "cache-control": IMMUTABLE, ...attachedHeaders(opts.attachments.uploads.imageType(id)) }
|
|
@@ -29644,9 +29577,14 @@ function createFetch(opts) {
|
|
|
29644
29577
|
const places = worktreeId && file && !extra ? [opts.attachments.fileFor(worktreeId, file), opts.archivedAttachment(worktreeId, file)] : [];
|
|
29645
29578
|
if (worktreeId && file)
|
|
29646
29579
|
await opts.attachments.whenWritten(worktreeId, file);
|
|
29647
|
-
const path = places.find((p) => !!p &&
|
|
29580
|
+
const path = places.find((p) => !!p && existsSync27(p));
|
|
29648
29581
|
if (!path)
|
|
29649
29582
|
return new Response("not found", { status: 404 });
|
|
29583
|
+
if (url.searchParams.has("where")) {
|
|
29584
|
+
return new Response(path, {
|
|
29585
|
+
headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" }
|
|
29586
|
+
});
|
|
29587
|
+
}
|
|
29650
29588
|
return new Response(Bun.file(path), {
|
|
29651
29589
|
headers: { "cache-control": IMMUTABLE, ...attachedHeaders(drawnType(file ?? "")) }
|
|
29652
29590
|
});
|
|
@@ -29679,7 +29617,7 @@ function createFetch(opts) {
|
|
|
29679
29617
|
const rel = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
29680
29618
|
const file = join24(opts.shellDist, rel.replaceAll("..", ""));
|
|
29681
29619
|
const hashed = rel.startsWith("/assets/");
|
|
29682
|
-
if (
|
|
29620
|
+
if (existsSync27(file) && Bun.file(file).size > 0) {
|
|
29683
29621
|
if (rel === "/manifest.json") {
|
|
29684
29622
|
const { bar, ground } = opts.manifestColors();
|
|
29685
29623
|
const manifest = { ...await Bun.file(file).json(), theme_color: bar, background_color: ground };
|
|
@@ -29699,7 +29637,7 @@ function createFetch(opts) {
|
|
|
29699
29637
|
if (hashed)
|
|
29700
29638
|
return new Response("not found", { status: 404 });
|
|
29701
29639
|
const index = join24(opts.shellDist, "index.html");
|
|
29702
|
-
if (
|
|
29640
|
+
if (existsSync27(index))
|
|
29703
29641
|
return new Response(Bun.file(index), { headers: { "cache-control": NO_STORE2 } });
|
|
29704
29642
|
return new Response("Toyon daemon running; shell not built (run: bun run build)", { status: 200 });
|
|
29705
29643
|
}
|
|
@@ -29774,14 +29712,6 @@ function startServer(opts) {
|
|
|
29774
29712
|
}
|
|
29775
29713
|
};
|
|
29776
29714
|
const send = (ws, msg) => raw(ws, JSON.stringify(msg));
|
|
29777
|
-
s.render.courier = (worktreeId, msg) => {
|
|
29778
|
-
const shells = [...sockets].filter((ws) => ws.data.authed && !ws.data.preview);
|
|
29779
|
-
const ws = shells.find((w) => w.data.view === worktreeId) ?? shells.find((w) => w.data.subs.has(worktreeId));
|
|
29780
|
-
if (!ws)
|
|
29781
|
-
return false;
|
|
29782
|
-
send(ws, msg);
|
|
29783
|
-
return true;
|
|
29784
|
-
};
|
|
29785
29715
|
const broadcast = (msg) => {
|
|
29786
29716
|
const json = JSON.stringify(msg);
|
|
29787
29717
|
for (const ws of sockets)
|
|
@@ -29959,20 +29889,9 @@ function startServer(opts) {
|
|
|
29959
29889
|
};
|
|
29960
29890
|
s.hub.on("themesChanged", themesChanged);
|
|
29961
29891
|
themesChanged();
|
|
29962
|
-
const agentInfos = () => s.accounts.describe(s.agents.infos()).map((a) => {
|
|
29963
|
-
const models = s.state.cachedOptions(a.id, "model");
|
|
29964
|
-
const efforts = s.state.cachedOptions(a.id, "thought_level");
|
|
29965
|
-
const spec = s.agents.get(a.id);
|
|
29966
|
-
return {
|
|
29967
|
-
...a,
|
|
29968
|
-
...models.length > 0 ? { models } : {},
|
|
29969
|
-
...efforts.length > 0 ? { efforts } : {},
|
|
29970
|
-
...spec && !lacksQuickModel(spec, s.state) ? { quick: true } : {}
|
|
29971
|
-
};
|
|
29972
|
-
});
|
|
29973
29892
|
const agentsMsg = () => ({
|
|
29974
29893
|
t: "agents",
|
|
29975
|
-
agents:
|
|
29894
|
+
agents: s.readiness.describe(),
|
|
29976
29895
|
defaultAgent: s.agents.defaultId(s.state.defaultAgent),
|
|
29977
29896
|
agentChosen: s.state.defaultAgent !== undefined
|
|
29978
29897
|
});
|
|
@@ -30010,7 +29929,7 @@ function startServer(opts) {
|
|
|
30010
29929
|
trunks,
|
|
30011
29930
|
themes: s.themes.themes,
|
|
30012
29931
|
themePrefs: s.themes.prefs,
|
|
30013
|
-
agents:
|
|
29932
|
+
agents: s.readiness.describe(),
|
|
30014
29933
|
defaultAgent: s.agents.defaultId(s.state.defaultAgent),
|
|
30015
29934
|
agentChosen: s.state.defaultAgent !== undefined,
|
|
30016
29935
|
home: homedir12(),
|
|
@@ -30193,9 +30112,9 @@ function startServer(opts) {
|
|
|
30193
30112
|
}
|
|
30194
30113
|
|
|
30195
30114
|
// ../daemon/src/themes/store.ts
|
|
30196
|
-
import { existsSync as
|
|
30115
|
+
import { existsSync as existsSync28, readdirSync as readdirSync8, readFileSync as readFileSync17, statSync as statSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
30197
30116
|
import { homedir as homedir13 } from "os";
|
|
30198
|
-
import { basename as
|
|
30117
|
+
import { basename as basename9, dirname as dirname9, join as join25, resolve as resolve9 } from "path";
|
|
30199
30118
|
var home = homedir13();
|
|
30200
30119
|
var defaultExtensionDirs = [
|
|
30201
30120
|
join25(home, ".vscode", "extensions"),
|
|
@@ -30280,7 +30199,7 @@ class ThemeStore {
|
|
|
30280
30199
|
if (json && typeof json === "object" && typeof json.include === "string") {
|
|
30281
30200
|
throw new ThemeImportError("this theme file uses `include`; drop the whole extension folder's theme into ~/.toyon/themes or install it in VS Code and rescan");
|
|
30282
30201
|
}
|
|
30283
|
-
const base = slug(
|
|
30202
|
+
const base = slug(basename9(name).replace(/\.(json|jsonc)$/i, ""));
|
|
30284
30203
|
const theme = vscodeToTheme(json, {
|
|
30285
30204
|
id: `file:${base}`,
|
|
30286
30205
|
name: json?.name ?? base,
|
|
@@ -30315,7 +30234,7 @@ function readVscodeTheme(file, depth = 0) {
|
|
|
30315
30234
|
return t;
|
|
30316
30235
|
}
|
|
30317
30236
|
function loadDir(dir) {
|
|
30318
|
-
if (!
|
|
30237
|
+
if (!existsSync28(dir))
|
|
30319
30238
|
return [];
|
|
30320
30239
|
const out = [];
|
|
30321
30240
|
for (const f of readdirSync8(dir)) {
|
|
@@ -30337,7 +30256,7 @@ function loadDir(dir) {
|
|
|
30337
30256
|
return out;
|
|
30338
30257
|
}
|
|
30339
30258
|
function discover(dir) {
|
|
30340
|
-
if (!
|
|
30259
|
+
if (!existsSync28(dir))
|
|
30341
30260
|
return [];
|
|
30342
30261
|
const out = [];
|
|
30343
30262
|
let entries;
|
|
@@ -30350,7 +30269,7 @@ function discover(dir) {
|
|
|
30350
30269
|
const extDir = join25(dir, name);
|
|
30351
30270
|
const pkgFile = join25(extDir, "package.json");
|
|
30352
30271
|
try {
|
|
30353
|
-
if (!statSync10(extDir).isDirectory() || !
|
|
30272
|
+
if (!statSync10(extDir).isDirectory() || !existsSync28(pkgFile))
|
|
30354
30273
|
continue;
|
|
30355
30274
|
const pkg = JSON.parse(readFileSync17(pkgFile, "utf8"));
|
|
30356
30275
|
const themes = pkg.contributes?.themes;
|
|
@@ -30359,7 +30278,7 @@ function discover(dir) {
|
|
|
30359
30278
|
const ext = `${pkg.publisher ?? "local"}.${pkg.name ?? name}`;
|
|
30360
30279
|
let nls = {};
|
|
30361
30280
|
const nlsFile = join25(extDir, "package.nls.json");
|
|
30362
|
-
if (
|
|
30281
|
+
if (existsSync28(nlsFile)) {
|
|
30363
30282
|
try {
|
|
30364
30283
|
nls = JSON.parse(readFileSync17(nlsFile, "utf8"));
|
|
30365
30284
|
} catch {}
|
|
@@ -30773,7 +30692,7 @@ class BackendShare {
|
|
|
30773
30692
|
|
|
30774
30693
|
// ../daemon/src/worktrees/cache.ts
|
|
30775
30694
|
import { createHash as createHash4 } from "crypto";
|
|
30776
|
-
import { existsSync as
|
|
30695
|
+
import { existsSync as existsSync29, readFileSync as readFileSync18 } from "fs";
|
|
30777
30696
|
import { mkdir as mkdir5, readdir as readdir2, rename, rm as rm4, stat as stat4, utimes, writeFile as writeFile3 } from "fs/promises";
|
|
30778
30697
|
import { join as join26 } from "path";
|
|
30779
30698
|
|
|
@@ -30806,7 +30725,7 @@ var STALE_MS = 30 * 24 * 3600000;
|
|
|
30806
30725
|
var TMP = ".tmp-";
|
|
30807
30726
|
var MANIFEST = "manifest.json";
|
|
30808
30727
|
function defaultTools(dir) {
|
|
30809
|
-
const has = (...files) => files.some((f) =>
|
|
30728
|
+
const has = (...files) => files.some((f) => existsSync29(join26(dir, f)));
|
|
30810
30729
|
const out = [];
|
|
30811
30730
|
if (has("bun.lock", "bun.lockb"))
|
|
30812
30731
|
out.push("bun --version");
|
|
@@ -30819,7 +30738,7 @@ function defaultTools(dir) {
|
|
|
30819
30738
|
return out;
|
|
30820
30739
|
}
|
|
30821
30740
|
function withSidecars(root, rel) {
|
|
30822
|
-
return [rel, ...SIDECARS.map((s) => rel + s)].filter((n) =>
|
|
30741
|
+
return [rel, ...SIDECARS.map((s) => rel + s)].filter((n) => existsSync29(join26(root, n)));
|
|
30823
30742
|
}
|
|
30824
30743
|
var entryName = (base, at) => `${base.slice(0, 12)}-${at.toString(36)}`;
|
|
30825
30744
|
async function listEntries(familyDir) {
|
|
@@ -30835,7 +30754,7 @@ async function listEntries(familyDir) {
|
|
|
30835
30754
|
if (!m?.[1] || !m[2])
|
|
30836
30755
|
continue;
|
|
30837
30756
|
const dir = join26(familyDir, name);
|
|
30838
|
-
if (!
|
|
30757
|
+
if (!existsSync29(join26(dir, MANIFEST)))
|
|
30839
30758
|
continue;
|
|
30840
30759
|
out.push({ dir, base: m[1], at: Number.parseInt(m[2], 36) });
|
|
30841
30760
|
}
|
|
@@ -30873,7 +30792,7 @@ class ArtifactCache {
|
|
|
30873
30792
|
}
|
|
30874
30793
|
const restored = [];
|
|
30875
30794
|
for (const rel of policy.paths) {
|
|
30876
|
-
if (
|
|
30795
|
+
if (existsSync29(join26(wt.path, rel)))
|
|
30877
30796
|
continue;
|
|
30878
30797
|
const names = withSidecars(entry.dir, rel);
|
|
30879
30798
|
if (names.length === 0)
|
|
@@ -30914,7 +30833,7 @@ class ArtifactCache {
|
|
|
30914
30833
|
let tmp;
|
|
30915
30834
|
let slot;
|
|
30916
30835
|
try {
|
|
30917
|
-
const present = policy.paths.filter((rel) =>
|
|
30836
|
+
const present = policy.paths.filter((rel) => existsSync29(join26(wt.path, rel)));
|
|
30918
30837
|
if (present.length === 0)
|
|
30919
30838
|
return;
|
|
30920
30839
|
const family = await this.family(wt, repo, policy);
|
|
@@ -31198,8 +31117,7 @@ var RESPONSE = {
|
|
|
31198
31117
|
conflict: { does: "ask" },
|
|
31199
31118
|
check: { does: "ask", once: true },
|
|
31200
31119
|
command: { does: "offer" },
|
|
31201
|
-
preview: { does: "offer" }
|
|
31202
|
-
page: { does: "offer" }
|
|
31120
|
+
preview: { does: "offer" }
|
|
31203
31121
|
};
|
|
31204
31122
|
function lastAsked(entries) {
|
|
31205
31123
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
@@ -31217,7 +31135,7 @@ function askedFor(entries, toolId) {
|
|
|
31217
31135
|
function lastStop(entries) {
|
|
31218
31136
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
31219
31137
|
const e = entries[i].event;
|
|
31220
|
-
if (e.type === "usage" || e.type === "session-info"
|
|
31138
|
+
if (e.type === "usage" || e.type === "session-info")
|
|
31221
31139
|
continue;
|
|
31222
31140
|
if (e.type === "turn-end" && e.stopReason === "interrupted")
|
|
31223
31141
|
return { kind: "stopped" };
|
|
@@ -31227,21 +31145,6 @@ function lastStop(entries) {
|
|
|
31227
31145
|
}
|
|
31228
31146
|
return null;
|
|
31229
31147
|
}
|
|
31230
|
-
function lastPageErrors(entries) {
|
|
31231
|
-
const errors = [];
|
|
31232
|
-
for (let i = entries.length - 1;i >= 0; i--) {
|
|
31233
|
-
const e = entries[i].event;
|
|
31234
|
-
if (e.type === "page-error") {
|
|
31235
|
-
errors.unshift(...e.errors);
|
|
31236
|
-
continue;
|
|
31237
|
-
}
|
|
31238
|
-
if (e.type === "user-message" || e.type === "fix-asked")
|
|
31239
|
-
break;
|
|
31240
|
-
if (e.type === "tool-start" && isEditTool({ name: e.name, kind: e.kind }))
|
|
31241
|
-
break;
|
|
31242
|
-
}
|
|
31243
|
-
return errors;
|
|
31244
|
-
}
|
|
31245
31148
|
function failureOf(entries, toolId) {
|
|
31246
31149
|
let command;
|
|
31247
31150
|
for (const { event } of entries) {
|
|
@@ -31306,17 +31209,6 @@ class FixService {
|
|
|
31306
31209
|
if (!this.report(worktreeId, failure, { pressed: true }))
|
|
31307
31210
|
throw new UserError("nothing runs on main");
|
|
31308
31211
|
}
|
|
31309
|
-
pressPage(worktreeId) {
|
|
31310
|
-
this.d.state.requireWorktree(worktreeId);
|
|
31311
|
-
const agent = this.d.runtime.agentFor(worktreeId);
|
|
31312
|
-
if (!agent)
|
|
31313
|
-
throw new UserError("worktree still starting; try again in a moment");
|
|
31314
|
-
const errors = lastPageErrors(agent.transcript());
|
|
31315
|
-
if (errors.length === 0)
|
|
31316
|
-
throw new UserError("nothing the preview threw to fix");
|
|
31317
|
-
if (!this.report(worktreeId, { kind: "page", errors }, { pressed: true }))
|
|
31318
|
-
throw new UserError("nothing runs on main");
|
|
31319
|
-
}
|
|
31320
31212
|
goOn(worktreeId) {
|
|
31321
31213
|
const wt = this.d.state.requireWorktree(worktreeId);
|
|
31322
31214
|
if (isMain(wt))
|
|
@@ -31336,12 +31228,12 @@ class FixService {
|
|
|
31336
31228
|
}
|
|
31337
31229
|
|
|
31338
31230
|
// ../daemon/src/worktrees/handoff.ts
|
|
31339
|
-
import { randomUUID as
|
|
31231
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
31340
31232
|
var MESSAGE_MAX = 20000;
|
|
31341
31233
|
var TITLE_MAX2 = 200;
|
|
31342
31234
|
var PROJECT_MAX = 200;
|
|
31343
31235
|
var PER_TURN_MAX = 3;
|
|
31344
|
-
var
|
|
31236
|
+
var refused = (text) => ({ text, isError: true });
|
|
31345
31237
|
function openProposals(entries) {
|
|
31346
31238
|
const open = new Map;
|
|
31347
31239
|
for (const { event } of entries) {
|
|
@@ -31424,7 +31316,7 @@ class HandoffService {
|
|
|
31424
31316
|
const a = args && typeof args === "object" && !Array.isArray(args) ? args : null;
|
|
31425
31317
|
const shaped = a && typeof a.project === "string" && typeof a.message === "string" && (a.title === undefined || typeof a.title === "string");
|
|
31426
31318
|
if (!shaped)
|
|
31427
|
-
return
|
|
31319
|
+
return refused("handoff takes { project: string, message: string, title?: string }.");
|
|
31428
31320
|
return this.propose(worktreeId, { project: a.project, message: a.message, ...a.title ? { title: a.title } : {} }, "agent");
|
|
31429
31321
|
}
|
|
31430
31322
|
propose(worktreeId, p, by) {
|
|
@@ -31432,31 +31324,31 @@ class HandoffService {
|
|
|
31432
31324
|
if (!wt)
|
|
31433
31325
|
throw new UserError("that chat is gone");
|
|
31434
31326
|
if (p.project.length > PROJECT_MAX)
|
|
31435
|
-
return
|
|
31327
|
+
return refused(`The project name is over ${PROJECT_MAX} characters.`);
|
|
31436
31328
|
if ((p.title ?? "").length > TITLE_MAX2)
|
|
31437
|
-
return
|
|
31329
|
+
return refused(`The title is over ${TITLE_MAX2} characters; a few words.`);
|
|
31438
31330
|
if (p.message.length > MESSAGE_MAX) {
|
|
31439
|
-
return
|
|
31331
|
+
return refused(`The message is over ${MESSAGE_MAX} characters; cut it to what the other agent needs.`);
|
|
31440
31332
|
}
|
|
31441
31333
|
const target = this.resolve(worktreeId, p.project);
|
|
31442
31334
|
if ("refused" in target)
|
|
31443
|
-
return
|
|
31335
|
+
return refused(target.refused);
|
|
31444
31336
|
const message = p.message.trim();
|
|
31445
31337
|
if (!message)
|
|
31446
|
-
return
|
|
31338
|
+
return refused("Write the message the other project's agent should start from.");
|
|
31447
31339
|
const agent = this.d.runtime.ensureAgent(wt).agent;
|
|
31448
31340
|
const entries = agent.transcript();
|
|
31449
31341
|
if (openProposals(entries).length > 0) {
|
|
31450
|
-
return
|
|
31342
|
+
return refused("A handoff is already waiting for the person on this chat; finish your turn.");
|
|
31451
31343
|
}
|
|
31452
31344
|
if (by === "agent" && proposedThisTurn(entries) >= PER_TURN_MAX) {
|
|
31453
|
-
return
|
|
31345
|
+
return refused("Three handoffs were proposed this turn already; finish your turn and let the person answer.");
|
|
31454
31346
|
}
|
|
31455
31347
|
const title = p.title?.trim();
|
|
31456
31348
|
const { repo } = target;
|
|
31457
31349
|
agent.note({
|
|
31458
31350
|
type: "handoff-proposed",
|
|
31459
|
-
id:
|
|
31351
|
+
id: randomUUID2(),
|
|
31460
31352
|
repo: { id: repo.id, name: repo.name, path: repo.path },
|
|
31461
31353
|
message,
|
|
31462
31354
|
...title ? { title } : {},
|
|
@@ -32029,7 +31921,7 @@ async function mergedBranches(repoPath, defaultBr) {
|
|
|
32029
31921
|
|
|
32030
31922
|
// ../daemon/src/worktrees/discover.ts
|
|
32031
31923
|
import { createHash as createHash5 } from "crypto";
|
|
32032
|
-
import { basename as
|
|
31924
|
+
import { basename as basename10 } from "path";
|
|
32033
31925
|
|
|
32034
31926
|
// ../daemon/src/git/worktrees.ts
|
|
32035
31927
|
function parseWorktreeList(out) {
|
|
@@ -32118,7 +32010,7 @@ function subtractKnown(repoId, listed, known) {
|
|
|
32118
32010
|
rows.push({
|
|
32119
32011
|
id: discoveredId(g.path),
|
|
32120
32012
|
repoId,
|
|
32121
|
-
name: g.branch ??
|
|
32013
|
+
name: g.branch ?? basename10(g.path),
|
|
32122
32014
|
path: g.path,
|
|
32123
32015
|
...g.branch ? { branch: g.branch } : {},
|
|
32124
32016
|
...g.locked ? { locked: true } : {},
|
|
@@ -32280,7 +32172,7 @@ function rankRefs({ refs, merged, prs, query, defaultBranch, rowIdForPath }) {
|
|
|
32280
32172
|
}
|
|
32281
32173
|
|
|
32282
32174
|
// ../daemon/src/worktrees/service.ts
|
|
32283
|
-
import { cpSync, existsSync as
|
|
32175
|
+
import { cpSync, existsSync as existsSync34, readFileSync as readFileSync20, rmSync as rmSync8 } from "fs";
|
|
32284
32176
|
import { rm as rm5 } from "fs/promises";
|
|
32285
32177
|
import { dirname as dirname12, join as join30 } from "path";
|
|
32286
32178
|
|
|
@@ -32406,7 +32298,7 @@ async function dropLandRefs(repoPath, worktreeId) {
|
|
|
32406
32298
|
}
|
|
32407
32299
|
|
|
32408
32300
|
// ../daemon/src/git/land.ts
|
|
32409
|
-
import { existsSync as
|
|
32301
|
+
import { existsSync as existsSync30 } from "fs";
|
|
32410
32302
|
var STEP_TIMEOUT_MS = 10 * 60000;
|
|
32411
32303
|
function rowOf(r) {
|
|
32412
32304
|
return r.shown && r.toolId && r.command ? { toolId: r.toolId, command: r.command, text: r.text } : undefined;
|
|
@@ -32416,7 +32308,7 @@ var UNWATCHED = {
|
|
|
32416
32308
|
step() {},
|
|
32417
32309
|
git: (cwd, args) => stepRun(cwd, args)
|
|
32418
32310
|
};
|
|
32419
|
-
function
|
|
32311
|
+
function refused2(what, r, n = 200) {
|
|
32420
32312
|
if (r.exit === "timeout") {
|
|
32421
32313
|
const after = describeDuration(r.ceilingMs ?? STEP_TIMEOUT_MS);
|
|
32422
32314
|
return `${what} gave up after ${after}${r.shown ? "; what it printed is on the chat" : ""}`;
|
|
@@ -32427,7 +32319,7 @@ function stepRefused(what, r, n = 200) {
|
|
|
32427
32319
|
const row = typeof r.exit === "number" && r.hook ? rowOf(r) : undefined;
|
|
32428
32320
|
return {
|
|
32429
32321
|
ok: false,
|
|
32430
|
-
message:
|
|
32322
|
+
message: refused2(what, r, n),
|
|
32431
32323
|
...row && r.hook ? { hook: { ...row, name: r.hook } } : {}
|
|
32432
32324
|
};
|
|
32433
32325
|
}
|
|
@@ -32537,7 +32429,7 @@ async function midway(worktreePath) {
|
|
|
32537
32429
|
if (!r.ok)
|
|
32538
32430
|
return null;
|
|
32539
32431
|
const at = r.out.split(`
|
|
32540
|
-
`).findIndex((file) => file &&
|
|
32432
|
+
`).findIndex((file) => file && existsSync30(file));
|
|
32541
32433
|
return MIDWAY[at]?.[1] ?? null;
|
|
32542
32434
|
}
|
|
32543
32435
|
async function takeMainIn(worktreePath, defaultBr, own, w = UNWATCHED, carry = false) {
|
|
@@ -32791,7 +32683,7 @@ async function fetchBase(worktreePath, repo, w = UNWATCHED) {
|
|
|
32791
32683
|
w.step(`fetching ${base}`);
|
|
32792
32684
|
const f = await w.git(worktreePath, ["fetch", "--quiet", tracked.remote, tracked.merge]);
|
|
32793
32685
|
if (!f.ok)
|
|
32794
|
-
return { ok: false, message:
|
|
32686
|
+
return { ok: false, message: refused2("fetch failed", f) };
|
|
32795
32687
|
return { ok: true, message: `fetched ${base}` };
|
|
32796
32688
|
}
|
|
32797
32689
|
async function pushBranch(worktreePath, branch, w = UNWATCHED) {
|
|
@@ -32881,7 +32773,7 @@ function compareUrl(remoteUrl, base, branch) {
|
|
|
32881
32773
|
}
|
|
32882
32774
|
|
|
32883
32775
|
// ../daemon/src/git/turn.ts
|
|
32884
|
-
import { copyFileSync, existsSync as
|
|
32776
|
+
import { copyFileSync, existsSync as existsSync31, rmSync as rmSync6 } from "fs";
|
|
32885
32777
|
import { join as join27 } from "path";
|
|
32886
32778
|
var turnRef = (worktreeId, which) => `refs/toyon/turns/${worktreeId}/${which}`;
|
|
32887
32779
|
var SNAPSHOT_SUBJECT2 = "toyon: tree at a send";
|
|
@@ -32907,7 +32799,7 @@ async function treeNow(wtPath, head, dirty) {
|
|
|
32907
32799
|
const tree = await run(GIT, ["write-tree"], wtPath, env);
|
|
32908
32800
|
return tree.ok && tree.out ? tree.out : null;
|
|
32909
32801
|
};
|
|
32910
|
-
if (!
|
|
32802
|
+
if (!existsSync31(index))
|
|
32911
32803
|
was = undefined;
|
|
32912
32804
|
const paths = dirty && !dirty.some((f) => /[RC]/.test(f.xy)) ? dirty.map((f) => f.path) : null;
|
|
32913
32805
|
if (was && paths && was.head === head) {
|
|
@@ -32922,9 +32814,9 @@ async function treeNow(wtPath, head, dirty) {
|
|
|
32922
32814
|
}
|
|
32923
32815
|
}
|
|
32924
32816
|
synced.delete(wtPath);
|
|
32925
|
-
if (!
|
|
32817
|
+
if (!existsSync31(index)) {
|
|
32926
32818
|
const own = join27(index, "..", "index");
|
|
32927
|
-
if (
|
|
32819
|
+
if (existsSync31(own))
|
|
32928
32820
|
copyFileSync(own, index);
|
|
32929
32821
|
else if (!(await run(GIT, ["read-tree", "HEAD"], wtPath, env)).ok)
|
|
32930
32822
|
return null;
|
|
@@ -33005,8 +32897,8 @@ async function dropTurnRefs(repoPath, worktreeId) {
|
|
|
33005
32897
|
}
|
|
33006
32898
|
|
|
33007
32899
|
// ../daemon/src/worktrees/archive.ts
|
|
33008
|
-
import { existsSync as
|
|
33009
|
-
import { basename as
|
|
32900
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync8, readdirSync as readdirSync9, readFileSync as readFileSync19, renameSync as renameSync5, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
|
|
32901
|
+
import { basename as basename11, dirname as dirname11, join as join28 } from "path";
|
|
33010
32902
|
class WorktreeArchive {
|
|
33011
32903
|
dir;
|
|
33012
32904
|
records = new Map;
|
|
@@ -33015,7 +32907,7 @@ class WorktreeArchive {
|
|
|
33015
32907
|
mkdirSync8(dir, { recursive: true });
|
|
33016
32908
|
for (const id of readdirSync9(dir)) {
|
|
33017
32909
|
const file = this.filesOf(id).record;
|
|
33018
|
-
if (!
|
|
32910
|
+
if (!existsSync32(file))
|
|
33019
32911
|
continue;
|
|
33020
32912
|
try {
|
|
33021
32913
|
this.records.set(id, JSON.parse(readFileSync19(file, "utf8")));
|
|
@@ -33073,8 +32965,8 @@ class WorktreeArchive {
|
|
|
33073
32965
|
planFile(id, rel) {
|
|
33074
32966
|
if (!this.records.has(id) || !isPlanPath(rel))
|
|
33075
32967
|
return null;
|
|
33076
|
-
const file = join28(this.filesOf(id).plans,
|
|
33077
|
-
if (!
|
|
32968
|
+
const file = join28(this.filesOf(id).plans, basename11(rel));
|
|
32969
|
+
if (!existsSync32(file))
|
|
33078
32970
|
return null;
|
|
33079
32971
|
try {
|
|
33080
32972
|
return readFileSync19(file, "utf8");
|
|
@@ -33115,7 +33007,7 @@ function summarize(r, repoId, transcript) {
|
|
|
33115
33007
|
};
|
|
33116
33008
|
}
|
|
33117
33009
|
function lastUsage(transcript) {
|
|
33118
|
-
if (!
|
|
33010
|
+
if (!existsSync32(transcript))
|
|
33119
33011
|
return null;
|
|
33120
33012
|
const lines = readFileSync19(transcript, "utf8").split(`
|
|
33121
33013
|
`);
|
|
@@ -33131,7 +33023,7 @@ function lastUsage(transcript) {
|
|
|
33131
33023
|
return null;
|
|
33132
33024
|
}
|
|
33133
33025
|
function firstPrompt(transcript) {
|
|
33134
|
-
if (!
|
|
33026
|
+
if (!existsSync32(transcript))
|
|
33135
33027
|
return;
|
|
33136
33028
|
for (const line of readFileSync19(transcript, "utf8").split(`
|
|
33137
33029
|
`)) {
|
|
@@ -33146,7 +33038,7 @@ function firstPrompt(transcript) {
|
|
|
33146
33038
|
return;
|
|
33147
33039
|
}
|
|
33148
33040
|
function move(from, to) {
|
|
33149
|
-
if (!
|
|
33041
|
+
if (!existsSync32(from))
|
|
33150
33042
|
return;
|
|
33151
33043
|
mkdirSync8(dirname11(to), { recursive: true });
|
|
33152
33044
|
rmSync7(to, { recursive: true, force: true });
|
|
@@ -33317,8 +33209,8 @@ async function fillLandMarks(jobs, read, width = 8) {
|
|
|
33317
33209
|
}
|
|
33318
33210
|
|
|
33319
33211
|
// ../daemon/src/worktrees/spare.ts
|
|
33320
|
-
import { existsSync as
|
|
33321
|
-
import { basename as
|
|
33212
|
+
import { existsSync as existsSync33 } from "fs";
|
|
33213
|
+
import { basename as basename12, join as join29 } from "path";
|
|
33322
33214
|
class SparePool {
|
|
33323
33215
|
d;
|
|
33324
33216
|
inflight = new Map;
|
|
@@ -33337,7 +33229,7 @@ class SparePool {
|
|
|
33337
33229
|
if (!text.trim())
|
|
33338
33230
|
return;
|
|
33339
33231
|
const wt = this.d.state.worktree(boxId);
|
|
33340
|
-
if (wt?.kind !== "spare" || !
|
|
33232
|
+
if (wt?.kind !== "spare" || !existsSync33(wt.path))
|
|
33341
33233
|
return;
|
|
33342
33234
|
const agent = this.d.runtime.ensureAgent(wt).agent;
|
|
33343
33235
|
if (agent.runningAgent === null)
|
|
@@ -33401,7 +33293,7 @@ class SparePool {
|
|
|
33401
33293
|
const repoId = repo.id;
|
|
33402
33294
|
let cut = () => {};
|
|
33403
33295
|
const added = (async () => {
|
|
33404
|
-
if (
|
|
33296
|
+
if (existsSync33(wt.path))
|
|
33405
33297
|
return;
|
|
33406
33298
|
const run = this.d.runs.begin(wt.id, "setup", {
|
|
33407
33299
|
timeoutMs: timeoutFor(repo.config, "setup"),
|
|
@@ -33460,7 +33352,7 @@ class SparePool {
|
|
|
33460
33352
|
}
|
|
33461
33353
|
log.warn(wt.repoId, "spare warm-up failed; rolling back", e);
|
|
33462
33354
|
this.d.carryDraft?.(wt.id, wt.repoId);
|
|
33463
|
-
if (
|
|
33355
|
+
if (existsSync33(wt.path)) {
|
|
33464
33356
|
await this.d.discard(wt.id).catch((re) => log.warn(wt.repoId, "spare rollback failed", re));
|
|
33465
33357
|
return;
|
|
33466
33358
|
}
|
|
@@ -33519,7 +33411,7 @@ class SparePool {
|
|
|
33519
33411
|
wt.kind = "worktree";
|
|
33520
33412
|
delete wt.phase;
|
|
33521
33413
|
delete wt.lockfile;
|
|
33522
|
-
wt.branch = `toyon/${
|
|
33414
|
+
wt.branch = `toyon/${basename12(wt.path)}`;
|
|
33523
33415
|
wt.title = title;
|
|
33524
33416
|
wt.createdAt = Date.now();
|
|
33525
33417
|
wt.promptedAt = wt.createdAt;
|
|
@@ -33532,7 +33424,7 @@ class SparePool {
|
|
|
33532
33424
|
try {
|
|
33533
33425
|
await running?.added;
|
|
33534
33426
|
running?.cut();
|
|
33535
|
-
if (!
|
|
33427
|
+
if (!existsSync33(wt.path))
|
|
33536
33428
|
throw new Error("the worktree's directory is gone");
|
|
33537
33429
|
await this.refreshing.get(repoId);
|
|
33538
33430
|
await prepare(wt);
|
|
@@ -33560,7 +33452,7 @@ class SparePool {
|
|
|
33560
33452
|
for (const k of Object.keys(stamps))
|
|
33561
33453
|
delete wt[k];
|
|
33562
33454
|
delete wt.promptedAt;
|
|
33563
|
-
if (!
|
|
33455
|
+
if (!existsSync33(wt.path)) {
|
|
33564
33456
|
await this.rollBack(wt, e);
|
|
33565
33457
|
return;
|
|
33566
33458
|
}
|
|
@@ -33645,7 +33537,7 @@ class Trunk {
|
|
|
33645
33537
|
const f = await withFetchLock(repo.path, () => w.git(repo.path, ["fetch", "--quiet"]));
|
|
33646
33538
|
this.noteFetch(repo.id, f);
|
|
33647
33539
|
if (!f.ok)
|
|
33648
|
-
return { ok: false, message:
|
|
33540
|
+
return { ok: false, message: refused2("fetch failed", f) };
|
|
33649
33541
|
this.d.invalidateCounts();
|
|
33650
33542
|
return this.follow(main.id);
|
|
33651
33543
|
}
|
|
@@ -34040,7 +33932,7 @@ class WorktreeService {
|
|
|
34040
33932
|
};
|
|
34041
33933
|
const carried = await git(repo.path, "ls-tree", "-r", "--name-only", `${sha}^3`);
|
|
34042
33934
|
const restored = (carried.ok ? carried.out.split(`
|
|
34043
|
-
`).filter(Boolean) : []).filter((p) => !
|
|
33935
|
+
`).filter(Boolean) : []).filter((p) => !existsSync34(join30(wt.path, p)));
|
|
34044
33936
|
const applied = await git(wt.path, "stash", "apply", sha);
|
|
34045
33937
|
if (applied.ok) {
|
|
34046
33938
|
const ref = await entry();
|
|
@@ -34080,7 +33972,7 @@ class WorktreeService {
|
|
|
34080
33972
|
const branch = kind === "pr" ? `pr/${number}` : ref;
|
|
34081
33973
|
const title = kind === "pr" ? `pr-${number}` : cleanTitle2(ref) || "branch";
|
|
34082
33974
|
let slug = branchSlug(title) || "branch";
|
|
34083
|
-
if (
|
|
33975
|
+
if (existsSync34(join30(this.d.paths.worktreesDir, repo.name, slug)))
|
|
34084
33976
|
slug = `${slug}-${shortId().slice(0, 4)}`;
|
|
34085
33977
|
const wtPath = join30(this.d.paths.worktreesDir, repo.name, slug);
|
|
34086
33978
|
await withRepoLock(repo.path, async () => {
|
|
@@ -34348,7 +34240,7 @@ class WorktreeService {
|
|
|
34348
34240
|
}
|
|
34349
34241
|
async teardown(wt, repo) {
|
|
34350
34242
|
const cmds = repo.config.teardown ?? [];
|
|
34351
|
-
if (cmds.length === 0 || !
|
|
34243
|
+
if (cmds.length === 0 || !existsSync34(wt.path))
|
|
34352
34244
|
return;
|
|
34353
34245
|
for (const cmd of cmds) {
|
|
34354
34246
|
const code = await runSetup(cmd, wt.path, (line) => this.d.hub.emit("log", wt.id, "setup", line), worktreeEnv(wt, repo), { timeoutMs: timeoutFor(repo.config, "setup") });
|
|
@@ -34398,7 +34290,7 @@ class WorktreeService {
|
|
|
34398
34290
|
}
|
|
34399
34291
|
keepPlans(wt) {
|
|
34400
34292
|
const from = join30(wt.path, PLANS_DIR);
|
|
34401
|
-
if (!
|
|
34293
|
+
if (!existsSync34(from))
|
|
34402
34294
|
return;
|
|
34403
34295
|
try {
|
|
34404
34296
|
cpSync(from, this.plansScratch(wt.id), { recursive: true });
|
|
@@ -34592,7 +34484,7 @@ class WorktreeService {
|
|
|
34592
34484
|
throw new UserError(`${old.title} was archived without its commits, so there is nothing to restore`);
|
|
34593
34485
|
if (rec.auto)
|
|
34594
34486
|
log.info(old.id, `restoring a worktree that archived itself (${rec.auto})`);
|
|
34595
|
-
const path =
|
|
34487
|
+
const path = existsSync34(old.path) ? join30(dirname12(old.path), freeSlot(dirname12(old.path)).dir) : old.path;
|
|
34596
34488
|
const { variant: _variant, ...rest } = old;
|
|
34597
34489
|
const wt = {
|
|
34598
34490
|
...rest,
|
|
@@ -34706,12 +34598,12 @@ class WorktreeService {
|
|
|
34706
34598
|
return;
|
|
34707
34599
|
const repo = this.repoOf(rec);
|
|
34708
34600
|
const repoPath = repo?.path ?? rec.repoPath;
|
|
34709
|
-
if (rec.kept &&
|
|
34601
|
+
if (rec.kept && existsSync34(repoPath)) {
|
|
34710
34602
|
const r = await git(repoPath, "update-ref", "-d", archiveRef(archiveId));
|
|
34711
34603
|
if (!r.ok)
|
|
34712
34604
|
log.warn(archiveId, `could not drop its archive ref: ${r.err}`);
|
|
34713
34605
|
}
|
|
34714
|
-
if (rec.worktree.lands?.length &&
|
|
34606
|
+
if (rec.worktree.lands?.length && existsSync34(repoPath))
|
|
34715
34607
|
await dropLandRefs(repoPath, archiveId);
|
|
34716
34608
|
this.archive.delete(archiveId);
|
|
34717
34609
|
this.d.drafts?.drop(archiveId);
|
|
@@ -34820,8 +34712,8 @@ class WorktreeService {
|
|
|
34820
34712
|
}
|
|
34821
34713
|
async mirrorBaseIgnore(repo) {
|
|
34822
34714
|
const src = join30(repo.path, ".gitignore");
|
|
34823
|
-
const tracked =
|
|
34824
|
-
const mirror =
|
|
34715
|
+
const tracked = existsSync34(src) && (await git(repo.path, "ls-files", "--", ".gitignore")).out !== "";
|
|
34716
|
+
const mirror = existsSync34(src) && !tracked;
|
|
34825
34717
|
const lines = mirror ? readFileSync20(src, "utf8").split(`
|
|
34826
34718
|
`).filter((l) => l.trim() !== "") : [];
|
|
34827
34719
|
await excludeBlock(repo.path, BASE_IGNORE, lines);
|
|
@@ -34836,7 +34728,7 @@ class WorktreeService {
|
|
|
34836
34728
|
}
|
|
34837
34729
|
const srcNm = join30(depsSource, "node_modules");
|
|
34838
34730
|
const dstNm = join30(wt.path, "node_modules");
|
|
34839
|
-
if (
|
|
34731
|
+
if (existsSync34(srcNm) && !existsSync34(dstNm)) {
|
|
34840
34732
|
const run = this.runs.begin(wt.id, "setup", { timeoutMs: ceiling, stage: "copying dependencies" });
|
|
34841
34733
|
const r = await cloneTree(srcNm, dstNm);
|
|
34842
34734
|
run.finish(0);
|
|
@@ -34847,7 +34739,7 @@ class WorktreeService {
|
|
|
34847
34739
|
}
|
|
34848
34740
|
for (const f of LOCAL_CONFIG_FILES) {
|
|
34849
34741
|
const src = join30(depsSource, f);
|
|
34850
|
-
if (!
|
|
34742
|
+
if (!existsSync34(src) || existsSync34(join30(wt.path, f)))
|
|
34851
34743
|
continue;
|
|
34852
34744
|
const r = await run("cp", [src, join30(wt.path, f)], wt.path);
|
|
34853
34745
|
if (r.ok)
|
|
@@ -35322,7 +35214,8 @@ ${wt.landing.body}` : wt.landing.subject : undefined);
|
|
|
35322
35214
|
const r = await git(wt.path, "reset", "--keep", base);
|
|
35323
35215
|
if (!r.ok)
|
|
35324
35216
|
log.warn(wt.id, `could not restart ${wt.branch} from ${base}: ${r.err}`);
|
|
35325
|
-
|
|
35217
|
+
this.counts.stale(wt.id);
|
|
35218
|
+
await this.counts.refresh([wt.id]);
|
|
35326
35219
|
}
|
|
35327
35220
|
async fetchBase(wt, repo, w) {
|
|
35328
35221
|
if (!baseIsRemote(repo))
|
|
@@ -35915,7 +35808,7 @@ if (pruned > 0)
|
|
|
35915
35808
|
var managed = await loadManaged();
|
|
35916
35809
|
var paths = makePaths();
|
|
35917
35810
|
ensureDirs(paths);
|
|
35918
|
-
forwardTranscripts(paths.transcriptsDir);
|
|
35811
|
+
forwardTranscripts([paths.transcriptsDir, paths.archiveDir], join31(paths.home, "transcripts-forwarded"));
|
|
35919
35812
|
var token = loadOrCreateToken(paths);
|
|
35920
35813
|
var port = Number(process.env.TOYON_PORT ?? DAEMON_DEFAULT_PORT);
|
|
35921
35814
|
var helperApp = join31(paths.home, DAEMON_FILES.helper);
|
|
@@ -35956,43 +35849,39 @@ await linuxSandbox.refresh();
|
|
|
35956
35849
|
var agents = loadAgentRegistry(paths.agentsFile, paths.agentsDir, linuxSandbox, managed.policy);
|
|
35957
35850
|
if (state.defaultAgent !== undefined && !agents.get(state.defaultAgent))
|
|
35958
35851
|
state.clearDefaultAgent();
|
|
35959
|
-
var
|
|
35852
|
+
var readiness2 = new AgentReadiness({
|
|
35960
35853
|
infos: () => agents.infos(),
|
|
35961
35854
|
require: (id) => agents.require(id),
|
|
35962
|
-
connect: async (app, spec) => spawnAcp(app, agents.launch(spec, await prepareLaunch(paths.scratchDir, spec)), paths.scratchDir, `
|
|
35855
|
+
connect: async (app, spec) => spawnAcp(app, agents.launch(spec, await prepareLaunch(paths.scratchDir, spec)), paths.scratchDir, `check:${spec.id}`),
|
|
35963
35856
|
cwd: paths.scratchDir,
|
|
35964
|
-
|
|
35857
|
+
options: (id, category) => state.cachedOptions(id, category),
|
|
35965
35858
|
learned: (id, category, choices) => {
|
|
35966
35859
|
if (state.setCachedOptions(id, category, choices))
|
|
35967
35860
|
hub.emit("agentsChanged");
|
|
35861
|
+
},
|
|
35862
|
+
quick: (id) => {
|
|
35863
|
+
const spec = agents.get(id);
|
|
35864
|
+
return !!spec && !lacksQuickModel(spec, state);
|
|
35968
35865
|
}
|
|
35969
35866
|
});
|
|
35867
|
+
readiness2.onChange = () => hub.emit("agentsChanged");
|
|
35970
35868
|
agents.onChange = () => {
|
|
35971
35869
|
hub.emit("agentsChanged");
|
|
35972
|
-
fireAndForget("agents",
|
|
35870
|
+
fireAndForget("agents", readiness2.checkMissing(), "read agent models");
|
|
35973
35871
|
};
|
|
35974
|
-
var accounts = new AgentAccounts({
|
|
35975
|
-
require: (id) => agents.require(id),
|
|
35976
|
-
connect: async (app, spec) => spawnAcp(app, agents.launch(spec, await prepareLaunch(paths.scratchDir, spec)), paths.scratchDir, `auth:${spec.id}`)
|
|
35977
|
-
});
|
|
35978
|
-
accounts.onChange = () => hub.emit("agentsChanged");
|
|
35979
35872
|
var uploads = new UploadStore(paths.uploadsDir);
|
|
35980
35873
|
var attachments2 = new AttachmentStore(paths.attachmentsDir, uploads);
|
|
35981
35874
|
var mcpPort = port;
|
|
35982
35875
|
var tools = new ToolSet;
|
|
35983
35876
|
var mcp = new ToyonMcp({ url: (id) => `http://127.0.0.1:${mcpPort}/mcp/${id}`, version: package_default.version, tools });
|
|
35984
|
-
var pageErrors = new PageErrorService({
|
|
35985
|
-
hub,
|
|
35986
|
-
runtime: { agentFor: (id) => runtime.agentFor(id) },
|
|
35987
|
-
known: (id) => state.worktree(id) !== undefined
|
|
35988
|
-
});
|
|
35877
|
+
var pageErrors = new PageErrorService({ hub, known: (id) => state.worktree(id) !== undefined });
|
|
35989
35878
|
var runtime = new RuntimeRegistry({
|
|
35990
35879
|
hub,
|
|
35991
35880
|
state,
|
|
35992
35881
|
paths,
|
|
35993
35882
|
agents,
|
|
35994
35883
|
managed: managed.policy,
|
|
35995
|
-
|
|
35884
|
+
readiness: readiness2,
|
|
35996
35885
|
attachments: attachments2,
|
|
35997
35886
|
bridgeScript: () => bridge.get(),
|
|
35998
35887
|
pageErrors: (id) => pageErrors.ambient(id),
|
|
@@ -36035,8 +35924,6 @@ var handoff2 = new HandoffService({
|
|
|
36035
35924
|
create: (repoId, prompt, opts) => worktrees.create(repoId, prompt, opts)
|
|
36036
35925
|
});
|
|
36037
35926
|
tools.add(handoff2);
|
|
36038
|
-
var render = new RenderService({ runtime, pageErrors });
|
|
36039
|
-
tools.add(render);
|
|
36040
35927
|
var turns = new TurnService({
|
|
36041
35928
|
state,
|
|
36042
35929
|
hub,
|
|
@@ -36195,12 +36082,11 @@ var {
|
|
|
36195
36082
|
landing,
|
|
36196
36083
|
fix,
|
|
36197
36084
|
pageErrors,
|
|
36198
|
-
render,
|
|
36199
36085
|
handoff: handoff2,
|
|
36200
36086
|
mcp,
|
|
36201
36087
|
themes,
|
|
36202
36088
|
agents,
|
|
36203
|
-
|
|
36089
|
+
readiness: readiness2,
|
|
36204
36090
|
attachments: attachments2,
|
|
36205
36091
|
self,
|
|
36206
36092
|
afterLand,
|
|
@@ -36231,7 +36117,7 @@ idle.boot();
|
|
|
36231
36117
|
runs.boot();
|
|
36232
36118
|
landing.boot();
|
|
36233
36119
|
fireAndForget("cache", cache.sweep(), "cache sweep");
|
|
36234
|
-
fireAndForget("agents", agents.installMissing().then(() =>
|
|
36120
|
+
fireAndForget("agents", agents.installMissing().then(() => readiness2.checkMissing()), "agent adapter install");
|
|
36235
36121
|
var repoArg = process.argv[2];
|
|
36236
36122
|
if (repoArg) {
|
|
36237
36123
|
try {
|