syntaur 0.76.0 → 0.77.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/.claude-plugin/plugin.json +1 -1
- package/dashboard/dist/assets/{_basePickBy-BiwSunXO.js → _basePickBy-CmtR3VlC.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-DsjKPXxP.js → _baseUniq-d5yVqkuO.js} +1 -1
- package/dashboard/dist/assets/{arc-DlEs4wsF.js → arc-BTfvlUzk.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-DPvCrO57.js → architectureDiagram-2XIMDMQ5-ComKmfQn.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-CpNdCY-r.js → blockDiagram-WCTKOSBZ-69xtTOqC.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-CC29QJCs.js → c4Diagram-IC4MRINW-B5MsrIyR.js} +1 -1
- package/dashboard/dist/assets/channel-B55sx3Pw.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-DKyy-k2D.js → chunk-4BX2VUAB-BV-rQx2S.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-DjobngQk.js → chunk-55IACEB6-BHoZ0_2C.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-BUftm7dV.js → chunk-FMBD7UC4-BZppz6s9.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-Nv7rQv4t.js → chunk-JSJVCQXG-BKJIycjQ.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-Dg6UhNB9.js → chunk-KX2RTZJC-Drv6rL3f.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-D8neQrJ_.js → chunk-NQ4KR5QH-u1ws00gN.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-Uj9c1naA.js → chunk-QZHKN3VN-zCyNaZcO.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-DfeSsP3z.js → chunk-WL4C6EOR-Dz0hDBUx.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-Kgg-J05j.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-Kgg-J05j.js +1 -0
- package/dashboard/dist/assets/clone-DXxPBQmb.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-CLfZNKcO.js → cose-bilkent-S5V4N54A-CO0Ms-ys.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-DyJLICZx.js → dagre-KLK3FWXG-BDBGlQ5i.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-DhQc7Ta3.js → diagram-E7M64L7V-C1jhhA3a.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-C6kFO7hB.js → diagram-IFDJBPK2-DYA2G930.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-B6oCiOXk.js → diagram-P4PSJMXO-iox20fhC.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-BsMPaSwJ.js → erDiagram-INFDFZHY-CdrG4ZUW.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-DLxEqKtZ.js → flowDiagram-PKNHOUZH-CMuagI-6.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-D70pvqxG.js → ganttDiagram-A5KZAMGK-C8jgdfRO.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-D74VHtlE.js → gitGraphDiagram-K3NZZRJ6-Chu0FgBt.js} +1 -1
- package/dashboard/dist/assets/{graph-C8bz_IEO.js → graph-RWP7g4cL.js} +1 -1
- package/dashboard/dist/assets/{index-h-X_8om3.js → index-BFpKR1Z0.js} +163 -148
- package/dashboard/dist/assets/{index-BetqH_3H.css → index-C6KBRx3q.css} +1 -1
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-DivBd0Pz.js → infoDiagram-LFFYTUFH-bIlmxe7x.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BGihUeOE.js → ishikawaDiagram-PHBUUO56-CvvXAmzr.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-C7U4cWHQ.js → journeyDiagram-4ABVD52K-BlSVnlQk.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-CFp_hDTm.js → kanban-definition-K7BYSVSG-D6kAus7L.js} +1 -1
- package/dashboard/dist/assets/{layout-DMSDPqCs.js → layout-BIoVL4m2.js} +1 -1
- package/dashboard/dist/assets/{linear-CCC-meYc.js → linear-B4ySyXON.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-BDa2cc2K.js → mermaid.core-B_8IjgQO.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-DLKvONhO.js → mindmap-definition-YRQLILUH-B6RTS8xi.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-DAJoGZ93.js → pieDiagram-SKSYHLDU-EntB8Xmg.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-Bo7haX1W.js → quadrantDiagram-337W2JSQ-UTEbe4g6.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-uEvGz33v.js → requirementDiagram-Z7DCOOCP-qG2oV68i.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-cjYoCGCM.js → sankeyDiagram-WA2Y5GQK-C-EzJpn8.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CYXcNklt.js → sequenceDiagram-2WXFIKYE-rCjfcAr5.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-B5We7jAw.js → stateDiagram-RAJIS63D-C3hoQmLC.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-rHFeeHzp.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-CEqVmnVQ.js → timeline-definition-YZTLITO2-2gGWKHHW.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-CE1puPeN.js → treemap-KZPCXAKY-BV6TTFvC.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-CiiuRUjK.js → vennDiagram-LZ73GAT5-C1aeXQ29.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-B0OuxFm9.js → xychartDiagram-JWTSCODW-BDbba6Mg.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +845 -147
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +1290 -547
- package/dist/index.js.map +1 -1
- package/dist/launch/index.d.ts +41 -0
- package/dist/launch/index.js +200 -21
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
- package/platforms/codex/.codex-plugin/plugin.json +1 -1
- package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
- package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
- package/dashboard/dist/assets/channel-IutbH48W.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-8IywRj9I.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-8IywRj9I.js +0 -1
- package/dashboard/dist/assets/clone-C6saGiCd.js +0 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-CX9-EpBi.js +0 -1
package/dist/dashboard/server.js
CHANGED
|
@@ -2547,6 +2547,14 @@ function agentNameArgs(agent) {
|
|
|
2547
2547
|
const n = agent.agentName?.trim();
|
|
2548
2548
|
return n ? ["--agent", n] : [];
|
|
2549
2549
|
}
|
|
2550
|
+
function resolveRunner(agent) {
|
|
2551
|
+
if (agent.runner) return agent.runner;
|
|
2552
|
+
if (agent.agentName && agent.agentName.trim() !== "") return "claude";
|
|
2553
|
+
const cmd = agent.command ?? "";
|
|
2554
|
+
if (/claude/i.test(cmd) || agent.id === "claude") return "claude";
|
|
2555
|
+
if (/codex/i.test(cmd) || agent.id === "codex") return "codex";
|
|
2556
|
+
return "pi";
|
|
2557
|
+
}
|
|
2550
2558
|
function stripModelFlags(args) {
|
|
2551
2559
|
const out = [];
|
|
2552
2560
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -3817,6 +3825,7 @@ __export(config_exports, {
|
|
|
3817
3825
|
parseStatusConfig: () => parseStatusConfig,
|
|
3818
3826
|
parseTerminalConfig: () => parseTerminalConfig,
|
|
3819
3827
|
readConfig: () => readConfig,
|
|
3828
|
+
serializeAgentDiscoveryConfig: () => serializeAgentDiscoveryConfig,
|
|
3820
3829
|
serializeSearchConfig: () => serializeSearchConfig,
|
|
3821
3830
|
serializeStatusConfig: () => serializeStatusConfig,
|
|
3822
3831
|
toTitleCase: () => toTitleCase,
|
|
@@ -3830,6 +3839,7 @@ __export(config_exports, {
|
|
|
3830
3839
|
validateDeriveShape: () => validateDeriveShape,
|
|
3831
3840
|
validateFactDeclarations: () => validateFactDeclarations,
|
|
3832
3841
|
validateStalenessConfig: () => validateStalenessConfig,
|
|
3842
|
+
writeAgentDiscoveryConfig: () => writeAgentDiscoveryConfig,
|
|
3833
3843
|
writeAgentsConfig: () => writeAgentsConfig,
|
|
3834
3844
|
writeHotkeyBindingsConfig: () => writeHotkeyBindingsConfig,
|
|
3835
3845
|
writeSearchConfig: () => writeSearchConfig,
|
|
@@ -3922,6 +3932,50 @@ function validateAgentList(agents) {
|
|
|
3922
3932
|
`agent "${agent.id}" sets both agentName and model \u2014 the agent definition's own model is authoritative; remove the profile model`
|
|
3923
3933
|
);
|
|
3924
3934
|
}
|
|
3935
|
+
if (agent.runner !== void 0 && !RUNNER_KINDS.includes(agent.runner)) {
|
|
3936
|
+
throw new AgentConfigError(
|
|
3937
|
+
`agent "${agent.id}" has invalid runner "${agent.runner}" \u2014 must be one of ${RUNNER_KINDS.join(", ")}`
|
|
3938
|
+
);
|
|
3939
|
+
}
|
|
3940
|
+
if (agent.sourceKind !== void 0 && !AGENT_SOURCE_KINDS.includes(agent.sourceKind)) {
|
|
3941
|
+
throw new AgentConfigError(
|
|
3942
|
+
`agent "${agent.id}" has invalid sourceKind "${agent.sourceKind}" \u2014 must be one of ${AGENT_SOURCE_KINDS.join(", ")}`
|
|
3943
|
+
);
|
|
3944
|
+
}
|
|
3945
|
+
if (agent.sourcePath !== void 0 && /[\r\n]/.test(agent.sourcePath)) {
|
|
3946
|
+
throw new AgentConfigError(
|
|
3947
|
+
`agent "${agent.id}" has invalid sourcePath \u2014 must be a single line (no newlines)`
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3950
|
+
if (agent.sourceRepo !== void 0 && /[\r\n]/.test(agent.sourceRepo)) {
|
|
3951
|
+
throw new AgentConfigError(
|
|
3952
|
+
`agent "${agent.id}" has invalid sourceRepo \u2014 must be a single line (no newlines)`
|
|
3953
|
+
);
|
|
3954
|
+
}
|
|
3955
|
+
if (agent.runner === "claude" && agent.workdir !== void 0 && agent.workdir.trim() !== "") {
|
|
3956
|
+
throw new AgentConfigError(
|
|
3957
|
+
`agent "${agent.id}" has runner "claude" but sets workdir \u2014 a claude agent uses agentName, not workdir`
|
|
3958
|
+
);
|
|
3959
|
+
}
|
|
3960
|
+
if ((agent.runner === "pi" || agent.runner === "codex") && agent.agentName !== void 0 && agent.agentName.trim() !== "") {
|
|
3961
|
+
throw new AgentConfigError(
|
|
3962
|
+
`agent "${agent.id}" has runner "${agent.runner}" but sets agentName \u2014 a directory agent uses workdir, not agentName`
|
|
3963
|
+
);
|
|
3964
|
+
}
|
|
3965
|
+
if (agent.runner !== void 0 && agent.sourceKind !== void 0) {
|
|
3966
|
+
const directorySource = agent.sourceKind === "directory";
|
|
3967
|
+
const claudeRunner = agent.runner === "claude";
|
|
3968
|
+
if (directorySource && claudeRunner) {
|
|
3969
|
+
throw new AgentConfigError(
|
|
3970
|
+
`agent "${agent.id}" has sourceKind "directory" but runner "claude" \u2014 a directory source is a pi/codex agent`
|
|
3971
|
+
);
|
|
3972
|
+
}
|
|
3973
|
+
if (!directorySource && !claudeRunner) {
|
|
3974
|
+
throw new AgentConfigError(
|
|
3975
|
+
`agent "${agent.id}" has sourceKind "${agent.sourceKind}" but runner "${agent.runner}" \u2014 a ${agent.sourceKind} source is a claude agent`
|
|
3976
|
+
);
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3925
3979
|
validateSessionInvocation(agent, "resume", agent.resume);
|
|
3926
3980
|
validateSessionInvocation(agent, "fork", agent.fork);
|
|
3927
3981
|
if (agent.default) defaults++;
|
|
@@ -4024,6 +4078,21 @@ function parseInstalledAgents(fm) {
|
|
|
4024
4078
|
}
|
|
4025
4079
|
return Object.keys(installedAgents).length > 0 ? { installedAgents } : {};
|
|
4026
4080
|
}
|
|
4081
|
+
function parseAgentDiscoveryFromFm(fm) {
|
|
4082
|
+
const flag = (key, def) => {
|
|
4083
|
+
const v = fm[key];
|
|
4084
|
+
if (v === void 0) return def;
|
|
4085
|
+
return String(v).toLowerCase() === "true";
|
|
4086
|
+
};
|
|
4087
|
+
const rootsRaw = fm["agentDiscovery.roots"];
|
|
4088
|
+
const roots = rootsRaw !== void 0 && String(rootsRaw).trim() !== "" ? String(rootsRaw).split(":").map((r) => r.trim()).filter((r) => r.length > 0) : [];
|
|
4089
|
+
return {
|
|
4090
|
+
claudeGlobal: flag("agentDiscovery.claudeGlobal", true),
|
|
4091
|
+
claudeProject: flag("agentDiscovery.claudeProject", true),
|
|
4092
|
+
directory: flag("agentDiscovery.directory", true),
|
|
4093
|
+
roots: roots.length > 0 ? roots : DEFAULT_CONFIG.agentDiscovery.roots.slice()
|
|
4094
|
+
};
|
|
4095
|
+
}
|
|
4027
4096
|
function parseStatusConfig(content) {
|
|
4028
4097
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4029
4098
|
if (!match) return null;
|
|
@@ -4532,6 +4601,43 @@ ${cleanedFm}
|
|
|
4532
4601
|
---${afterFrontmatter}`;
|
|
4533
4602
|
await writeFileForce(configPath, newContent);
|
|
4534
4603
|
}
|
|
4604
|
+
function serializeAgentDiscoveryConfig(cfg) {
|
|
4605
|
+
return [
|
|
4606
|
+
"agentDiscovery:",
|
|
4607
|
+
` claudeGlobal: ${cfg.claudeGlobal}`,
|
|
4608
|
+
` claudeProject: ${cfg.claudeProject}`,
|
|
4609
|
+
` directory: ${cfg.directory}`,
|
|
4610
|
+
` roots: ${cfg.roots.join(":")}`
|
|
4611
|
+
].join("\n");
|
|
4612
|
+
}
|
|
4613
|
+
async function writeAgentDiscoveryConfig(cfg, standaloneDefaultCwd) {
|
|
4614
|
+
const configPath = resolve7(syntaurRoot(), "config.md");
|
|
4615
|
+
const block = serializeAgentDiscoveryConfig(cfg);
|
|
4616
|
+
const scalarLine = standaloneDefaultCwd ? `standaloneDefaultCwd: ${standaloneDefaultCwd}` : "";
|
|
4617
|
+
const additions = [block, scalarLine].filter(Boolean).join("\n");
|
|
4618
|
+
const existing = await fileExists(configPath) ? await readFile5(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
4619
|
+
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
4620
|
+
if (!fmMatch) {
|
|
4621
|
+
const content = `---
|
|
4622
|
+
version: "2.0"
|
|
4623
|
+
defaultProjectDir: ${defaultProjectDir()}
|
|
4624
|
+
${additions}
|
|
4625
|
+
---
|
|
4626
|
+
${existing}`;
|
|
4627
|
+
await writeFileForce(configPath, content);
|
|
4628
|
+
return;
|
|
4629
|
+
}
|
|
4630
|
+
const fmBlock = fmMatch[2];
|
|
4631
|
+
const afterFrontmatter = existing.slice(fmMatch[0].length);
|
|
4632
|
+
let cleanedFm = stripTopLevelBlock(fmBlock, "agentDiscovery");
|
|
4633
|
+
cleanedFm = stripTopLevelScalar(cleanedFm, "standaloneDefaultCwd");
|
|
4634
|
+
const newFm = `${cleanedFm}
|
|
4635
|
+
${additions}`.replace(/^\n+/, "").replace(/\n+$/, "");
|
|
4636
|
+
const newContent = `---
|
|
4637
|
+
${newFm}
|
|
4638
|
+
---${afterFrontmatter}`;
|
|
4639
|
+
await writeFileForce(configPath, newContent);
|
|
4640
|
+
}
|
|
4535
4641
|
function stripTopLevelScalar(fmBlock, key) {
|
|
4536
4642
|
const lines = fmBlock.split("\n");
|
|
4537
4643
|
const keyRegex = new RegExp(`^${key}:\\s*\\S`);
|
|
@@ -4744,6 +4850,10 @@ function parseAgentsConfig(content) {
|
|
|
4744
4850
|
...current.launchPrompt ? { launchPrompt: current.launchPrompt } : {},
|
|
4745
4851
|
...current.agentName ? { agentName: current.agentName } : {},
|
|
4746
4852
|
...current.workdir ? { workdir: current.workdir } : {},
|
|
4853
|
+
...current.runner ? { runner: current.runner } : {},
|
|
4854
|
+
...current.sourceKind ? { sourceKind: current.sourceKind } : {},
|
|
4855
|
+
...current.sourcePath ? { sourcePath: current.sourcePath } : {},
|
|
4856
|
+
...current.sourceRepo ? { sourceRepo: current.sourceRepo } : {},
|
|
4747
4857
|
...current.resume ? { resume: current.resume } : {},
|
|
4748
4858
|
...current.fork ? { fork: current.fork } : {}
|
|
4749
4859
|
});
|
|
@@ -4941,6 +5051,18 @@ function assignAgentField(target, key, rawValue) {
|
|
|
4941
5051
|
case "workdir":
|
|
4942
5052
|
target.workdir = value;
|
|
4943
5053
|
break;
|
|
5054
|
+
case "runner":
|
|
5055
|
+
target.runner = value;
|
|
5056
|
+
break;
|
|
5057
|
+
case "sourceKind":
|
|
5058
|
+
target.sourceKind = value;
|
|
5059
|
+
break;
|
|
5060
|
+
case "sourcePath":
|
|
5061
|
+
target.sourcePath = value;
|
|
5062
|
+
break;
|
|
5063
|
+
case "sourceRepo":
|
|
5064
|
+
target.sourceRepo = value;
|
|
5065
|
+
break;
|
|
4944
5066
|
}
|
|
4945
5067
|
}
|
|
4946
5068
|
function yamlQuoteScalar(value) {
|
|
@@ -4976,6 +5098,18 @@ function serializeAgentsConfig(agents) {
|
|
|
4976
5098
|
if (a.workdir) {
|
|
4977
5099
|
lines.push(` workdir: ${yamlQuoteScalar(a.workdir)}`);
|
|
4978
5100
|
}
|
|
5101
|
+
if (a.runner) {
|
|
5102
|
+
lines.push(` runner: ${yamlQuoteScalar(a.runner)}`);
|
|
5103
|
+
}
|
|
5104
|
+
if (a.sourceKind) {
|
|
5105
|
+
lines.push(` sourceKind: ${yamlQuoteScalar(a.sourceKind)}`);
|
|
5106
|
+
}
|
|
5107
|
+
if (a.sourcePath) {
|
|
5108
|
+
lines.push(` sourcePath: ${yamlQuoteScalar(a.sourcePath)}`);
|
|
5109
|
+
}
|
|
5110
|
+
if (a.sourceRepo) {
|
|
5111
|
+
lines.push(` sourceRepo: ${yamlQuoteScalar(a.sourceRepo)}`);
|
|
5112
|
+
}
|
|
4979
5113
|
if (a.args && a.args.length > 0) {
|
|
4980
5114
|
lines.push(` args:`);
|
|
4981
5115
|
for (const arg of a.args) {
|
|
@@ -5461,7 +5595,12 @@ async function readConfig() {
|
|
|
5461
5595
|
searchConfig: parseSearchConfig(content),
|
|
5462
5596
|
workspaceVisibility: parseWorkspaceVisibilityConfig(fmBlock),
|
|
5463
5597
|
staleness: parseStalenessConfig(content),
|
|
5464
|
-
stalenessWatchdog: String(fm["stalenessWatchdog"]).toLowerCase() === "true"
|
|
5598
|
+
stalenessWatchdog: String(fm["stalenessWatchdog"]).toLowerCase() === "true",
|
|
5599
|
+
standaloneDefaultCwd: parseOptionalAbsolutePath(
|
|
5600
|
+
fm["standaloneDefaultCwd"],
|
|
5601
|
+
"standaloneDefaultCwd"
|
|
5602
|
+
),
|
|
5603
|
+
agentDiscovery: parseAgentDiscoveryFromFm(fm)
|
|
5465
5604
|
};
|
|
5466
5605
|
}
|
|
5467
5606
|
function getAssignmentTypes(config) {
|
|
@@ -5524,7 +5663,7 @@ async function updateAgentsConfig(mutation, options = {}) {
|
|
|
5524
5663
|
await writeAgentsConfig(next);
|
|
5525
5664
|
return { previous, next, written: true };
|
|
5526
5665
|
}
|
|
5527
|
-
var STALENESS_KEY_TO_FIELD, DURATION_RE, DURATION_UNIT_MS, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
|
|
5666
|
+
var STALENESS_KEY_TO_FIELD, DURATION_RE, DURATION_UNIT_MS, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, RUNNER_KINDS, AGENT_SOURCE_KINDS, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
|
|
5528
5667
|
var init_config2 = __esm({
|
|
5529
5668
|
"src/utils/config.ts"() {
|
|
5530
5669
|
"use strict";
|
|
@@ -5600,10 +5739,23 @@ var init_config2 = __esm({
|
|
|
5600
5739
|
hidden: []
|
|
5601
5740
|
},
|
|
5602
5741
|
staleness: null,
|
|
5603
|
-
stalenessWatchdog: false
|
|
5742
|
+
stalenessWatchdog: false,
|
|
5743
|
+
standaloneDefaultCwd: null,
|
|
5744
|
+
agentDiscovery: {
|
|
5745
|
+
claudeGlobal: true,
|
|
5746
|
+
claudeProject: true,
|
|
5747
|
+
directory: true,
|
|
5748
|
+
roots: ["~"]
|
|
5749
|
+
}
|
|
5604
5750
|
};
|
|
5605
5751
|
AUTO_CREATE_WORKTREE_VALUES = ["skip", "ask", "always"];
|
|
5606
5752
|
SESSION_AUTO_TRACK_VALUES = ["all", "workspaces-only", "off"];
|
|
5753
|
+
RUNNER_KINDS = ["claude", "pi", "codex"];
|
|
5754
|
+
AGENT_SOURCE_KINDS = [
|
|
5755
|
+
"claude-global",
|
|
5756
|
+
"claude-project",
|
|
5757
|
+
"directory"
|
|
5758
|
+
];
|
|
5607
5759
|
AgentConfigError = class extends Error {
|
|
5608
5760
|
};
|
|
5609
5761
|
DEFAULT_STATUS_COLORS = {
|
|
@@ -5625,7 +5777,11 @@ var init_config2 = __esm({
|
|
|
5625
5777
|
"playbook",
|
|
5626
5778
|
"launchPrompt",
|
|
5627
5779
|
"agentName",
|
|
5628
|
-
"workdir"
|
|
5780
|
+
"workdir",
|
|
5781
|
+
"runner",
|
|
5782
|
+
"sourceKind",
|
|
5783
|
+
"sourcePath",
|
|
5784
|
+
"sourceRepo"
|
|
5629
5785
|
]);
|
|
5630
5786
|
migratedConfigPaths = /* @__PURE__ */ new Set();
|
|
5631
5787
|
TerminalConfigError = class extends Error {
|
|
@@ -6023,6 +6179,7 @@ var init_assignment_resolver = __esm({
|
|
|
6023
6179
|
|
|
6024
6180
|
// src/launch/cwd.ts
|
|
6025
6181
|
import { existsSync, statSync } from "fs";
|
|
6182
|
+
import { homedir as homedir2 } from "os";
|
|
6026
6183
|
import { isAbsolute as isAbsolute2 } from "path";
|
|
6027
6184
|
function isExistingDir(p) {
|
|
6028
6185
|
if (!p || !isAbsolute2(p)) return false;
|
|
@@ -6068,6 +6225,31 @@ function resolveLaunchCwd(agent, worktreeCwd) {
|
|
|
6068
6225
|
}
|
|
6069
6226
|
return { spawnCwd: worktreeCwd, worktreePath: worktreeCwd, invalidReason: null };
|
|
6070
6227
|
}
|
|
6228
|
+
function resolveStandaloneCwd(agent, standaloneDefaultCwd) {
|
|
6229
|
+
const wd = agent.workdir?.trim();
|
|
6230
|
+
if (wd) {
|
|
6231
|
+
const expanded = expandHome(wd);
|
|
6232
|
+
if (!isExistingDir(expanded)) {
|
|
6233
|
+
return {
|
|
6234
|
+
cwd: null,
|
|
6235
|
+
invalidReason: `agent "${agent.id}" workdir ${agent.workdir} (resolved ${expanded}) is not an existing directory \u2014 a standalone launch requires a valid workdir.`
|
|
6236
|
+
};
|
|
6237
|
+
}
|
|
6238
|
+
return { cwd: expanded, invalidReason: null };
|
|
6239
|
+
}
|
|
6240
|
+
if (resolveRunner(agent) === "claude") {
|
|
6241
|
+
const configured = standaloneDefaultCwd?.trim();
|
|
6242
|
+
if (configured) {
|
|
6243
|
+
const expanded = expandHome(configured);
|
|
6244
|
+
if (isExistingDir(expanded)) return { cwd: expanded, invalidReason: null };
|
|
6245
|
+
}
|
|
6246
|
+
return { cwd: homedir2(), invalidReason: null };
|
|
6247
|
+
}
|
|
6248
|
+
return {
|
|
6249
|
+
cwd: null,
|
|
6250
|
+
invalidReason: `agent "${agent.id}" has no workdir \u2014 a standalone launch of a ${resolveRunner(agent)} agent requires a valid workdir.`
|
|
6251
|
+
};
|
|
6252
|
+
}
|
|
6071
6253
|
function formatFallbackCwdWarning(opts) {
|
|
6072
6254
|
const missing = [];
|
|
6073
6255
|
if (!opts.worktreePath) missing.push("worktreePath");
|
|
@@ -6079,6 +6261,7 @@ function formatFallbackCwdWarning(opts) {
|
|
|
6079
6261
|
var init_cwd = __esm({
|
|
6080
6262
|
"src/launch/cwd.ts"() {
|
|
6081
6263
|
"use strict";
|
|
6264
|
+
init_agents_schema();
|
|
6082
6265
|
init_paths();
|
|
6083
6266
|
}
|
|
6084
6267
|
});
|
|
@@ -7913,8 +8096,8 @@ async function migrateFromMarkdown(projectsDir) {
|
|
|
7913
8096
|
return allSessions.length;
|
|
7914
8097
|
}
|
|
7915
8098
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
7916
|
-
const { readFile:
|
|
7917
|
-
const raw2 = await
|
|
8099
|
+
const { readFile: readFile34 } = await import("fs/promises");
|
|
8100
|
+
const raw2 = await readFile34(filePath, "utf-8");
|
|
7918
8101
|
const sessions = [];
|
|
7919
8102
|
const lines = raw2.split("\n");
|
|
7920
8103
|
let inTable = false;
|
|
@@ -8734,7 +8917,7 @@ function emptyScan() {
|
|
|
8734
8917
|
return { sessions: [], tmuxAvailable: false };
|
|
8735
8918
|
}
|
|
8736
8919
|
function scanKey(serversDir2, projectsDir, assignmentsDir2) {
|
|
8737
|
-
return
|
|
8920
|
+
return JSON.stringify([serversDir2, projectsDir, assignmentsDir2 ?? ""]);
|
|
8738
8921
|
}
|
|
8739
8922
|
function delay(ms) {
|
|
8740
8923
|
return new Promise((resolve49) => {
|
|
@@ -12157,12 +12340,12 @@ var init_transcript = __esm({
|
|
|
12157
12340
|
// src/usage/cwd-extractor.ts
|
|
12158
12341
|
import { open as open3, readdir as readdir13, stat as stat4 } from "fs/promises";
|
|
12159
12342
|
import { join as join3 } from "path";
|
|
12160
|
-
import { homedir as
|
|
12343
|
+
import { homedir as homedir3 } from "os";
|
|
12161
12344
|
async function extractClaudeSessionMeta(jsonlPath) {
|
|
12162
12345
|
const cwd = await derivePathFromTranscript(jsonlPath);
|
|
12163
12346
|
if (!cwd) return null;
|
|
12164
|
-
const
|
|
12165
|
-
const sessionId =
|
|
12347
|
+
const basename8 = jsonlPath.split("/").pop() ?? "";
|
|
12348
|
+
const sessionId = basename8.replace(/\.jsonl$/, "");
|
|
12166
12349
|
if (!sessionId) return null;
|
|
12167
12350
|
const startTs = await readFirstTimestamp(jsonlPath);
|
|
12168
12351
|
const endTs = await readLastTimestamp(jsonlPath);
|
|
@@ -12257,8 +12440,8 @@ async function* walkClaudeProjects(opts = {}) {
|
|
|
12257
12440
|
async function* walkCodexSessions(opts = {}) {
|
|
12258
12441
|
const root = resolveCodexSessionsRoot(opts.root);
|
|
12259
12442
|
for await (const filePath of walkJsonlRecursive(root)) {
|
|
12260
|
-
const
|
|
12261
|
-
if (!
|
|
12443
|
+
const basename8 = filePath.split("/").pop() ?? "";
|
|
12444
|
+
if (!basename8.endsWith(".jsonl")) continue;
|
|
12262
12445
|
if (opts.sinceMtimeMs !== void 0) {
|
|
12263
12446
|
const mtime = await mtimeMs(filePath);
|
|
12264
12447
|
if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
|
|
@@ -12273,13 +12456,13 @@ function resolveCodexSessionsRoot(override3) {
|
|
|
12273
12456
|
if (fromSessionsEnv && fromSessionsEnv.length > 0) return expandHome(fromSessionsEnv);
|
|
12274
12457
|
const fromHomeEnv = process.env.CODEX_HOME;
|
|
12275
12458
|
if (fromHomeEnv && fromHomeEnv.length > 0) return join3(expandHome(fromHomeEnv), "sessions");
|
|
12276
|
-
return join3(
|
|
12459
|
+
return join3(homedir3(), ".codex", "sessions");
|
|
12277
12460
|
}
|
|
12278
12461
|
async function extractPiSessionMeta(jsonlPath) {
|
|
12279
|
-
const
|
|
12280
|
-
const underscoreIdx =
|
|
12462
|
+
const basename8 = jsonlPath.split("/").pop() ?? "";
|
|
12463
|
+
const underscoreIdx = basename8.lastIndexOf("_");
|
|
12281
12464
|
if (underscoreIdx === -1) return null;
|
|
12282
|
-
const sessionId =
|
|
12465
|
+
const sessionId = basename8.slice(underscoreIdx + 1).replace(/\.jsonl$/, "");
|
|
12283
12466
|
if (!sessionId) return null;
|
|
12284
12467
|
let handle;
|
|
12285
12468
|
try {
|
|
@@ -12331,7 +12514,7 @@ function resolvePiSessionsRoot(override3) {
|
|
|
12331
12514
|
if (override3) return expandHome(override3);
|
|
12332
12515
|
const fromHomeEnv = process.env.PI_AGENT_DIR;
|
|
12333
12516
|
if (fromHomeEnv && fromHomeEnv.length > 0) return join3(expandHome(fromHomeEnv), "sessions");
|
|
12334
|
-
return join3(
|
|
12517
|
+
return join3(homedir3(), ".pi", "agent", "sessions");
|
|
12335
12518
|
}
|
|
12336
12519
|
async function* walkPiSessions(opts = {}) {
|
|
12337
12520
|
const root = resolvePiSessionsRoot(opts.root);
|
|
@@ -12495,7 +12678,7 @@ var init_cwd_extractor = __esm({
|
|
|
12495
12678
|
// src/utils/session-id.ts
|
|
12496
12679
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
12497
12680
|
import { mkdirSync, readFileSync as readFileSync2, statSync as statSync4, writeFileSync } from "fs";
|
|
12498
|
-
import { homedir as
|
|
12681
|
+
import { homedir as homedir4 } from "os";
|
|
12499
12682
|
import { dirname as dirname5, join as join4 } from "path";
|
|
12500
12683
|
function isSafeSessionId(value) {
|
|
12501
12684
|
return typeof value === "string" && value.length > 0 && value.length <= 256 && SAFE_SESSION_ID.test(value);
|
|
@@ -12562,10 +12745,10 @@ var init_user_descriptors = __esm({
|
|
|
12562
12745
|
});
|
|
12563
12746
|
|
|
12564
12747
|
// src/targets/registry.ts
|
|
12565
|
-
import { homedir as
|
|
12748
|
+
import { homedir as homedir5 } from "os";
|
|
12566
12749
|
import { join as join5, resolve as resolve27 } from "path";
|
|
12567
12750
|
function home(...segments) {
|
|
12568
|
-
return resolve27(
|
|
12751
|
+
return resolve27(homedir5(), ...segments);
|
|
12569
12752
|
}
|
|
12570
12753
|
function hermesHome() {
|
|
12571
12754
|
const env = process.env.HERMES_HOME;
|
|
@@ -13146,7 +13329,7 @@ init_agent_sessions();
|
|
|
13146
13329
|
import express from "express";
|
|
13147
13330
|
import { createServer } from "http";
|
|
13148
13331
|
import { resolve as resolve48 } from "path";
|
|
13149
|
-
import { writeFile as
|
|
13332
|
+
import { writeFile as writeFile9, unlink as unlink9 } from "fs/promises";
|
|
13150
13333
|
import { WebSocketServer, WebSocket } from "ws";
|
|
13151
13334
|
|
|
13152
13335
|
// src/dashboard/session-liveness.ts
|
|
@@ -18203,6 +18386,7 @@ function createAgentSessionsRouter(projectsDir, broadcast, assignmentsDir2) {
|
|
|
18203
18386
|
init_config2();
|
|
18204
18387
|
init_agents_schema();
|
|
18205
18388
|
import { Router as Router5 } from "express";
|
|
18389
|
+
import { access as access4 } from "fs/promises";
|
|
18206
18390
|
|
|
18207
18391
|
// src/targets/agent-definitions.ts
|
|
18208
18392
|
init_parser();
|
|
@@ -18251,7 +18435,293 @@ async function discoverClaudeAgents(root) {
|
|
|
18251
18435
|
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
18252
18436
|
}
|
|
18253
18437
|
|
|
18438
|
+
// src/targets/agent-discovery.ts
|
|
18439
|
+
init_paths();
|
|
18440
|
+
init_parser();
|
|
18441
|
+
import { access as access2, readdir as readdir16, readFile as readFile21 } from "fs/promises";
|
|
18442
|
+
import { join as join7 } from "path";
|
|
18443
|
+
async function pathExists(p) {
|
|
18444
|
+
try {
|
|
18445
|
+
await access2(p);
|
|
18446
|
+
return true;
|
|
18447
|
+
} catch {
|
|
18448
|
+
return false;
|
|
18449
|
+
}
|
|
18450
|
+
}
|
|
18451
|
+
function coerceRunner(raw2) {
|
|
18452
|
+
return raw2 === "claude" || raw2 === "pi" || raw2 === "codex" ? raw2 : void 0;
|
|
18453
|
+
}
|
|
18454
|
+
async function readSyntaurMeta(file) {
|
|
18455
|
+
let content;
|
|
18456
|
+
try {
|
|
18457
|
+
content = await readFile21(file, "utf-8");
|
|
18458
|
+
} catch {
|
|
18459
|
+
return null;
|
|
18460
|
+
}
|
|
18461
|
+
const [fm] = extractFrontmatter2(content);
|
|
18462
|
+
if (!fm) return null;
|
|
18463
|
+
if (!/^syntaur:[ \t]*$/m.test(fm)) return null;
|
|
18464
|
+
return {
|
|
18465
|
+
name: getNestedField(fm, "syntaur", "name")?.trim() || void 0,
|
|
18466
|
+
runner: coerceRunner(getNestedField(fm, "syntaur", "runner")?.trim() || void 0),
|
|
18467
|
+
description: getNestedField(fm, "syntaur", "description")?.trim() || void 0
|
|
18468
|
+
};
|
|
18469
|
+
}
|
|
18470
|
+
async function fileHasSyntaurBlock(file) {
|
|
18471
|
+
return await readSyntaurMeta(file) !== null;
|
|
18472
|
+
}
|
|
18473
|
+
function claudeAlreadyRegistered(agents, path, name) {
|
|
18474
|
+
return agents.some((a) => a.sourcePath === path || !!a.agentName && a.agentName === name);
|
|
18475
|
+
}
|
|
18476
|
+
function directoryAlreadyRegistered(agents, dir) {
|
|
18477
|
+
return agents.some((a) => a.sourcePath === dir || a.workdir === dir);
|
|
18478
|
+
}
|
|
18479
|
+
async function toClaudeCandidate(d, source, agents, sourceRepo) {
|
|
18480
|
+
return {
|
|
18481
|
+
name: d.name,
|
|
18482
|
+
runner: "claude",
|
|
18483
|
+
description: d.description,
|
|
18484
|
+
path: d.path,
|
|
18485
|
+
source,
|
|
18486
|
+
...sourceRepo ? { sourceRepo } : {},
|
|
18487
|
+
recommended: await fileHasSyntaurBlock(d.path),
|
|
18488
|
+
alreadyRegistered: claudeAlreadyRegistered(agents, d.path, d.name)
|
|
18489
|
+
};
|
|
18490
|
+
}
|
|
18491
|
+
async function inspectDirCandidate(dir, basename8, agents) {
|
|
18492
|
+
const hasPi = await pathExists(join7(dir, ".pi"));
|
|
18493
|
+
const hasMcp = await pathExists(join7(dir, ".mcp.json"));
|
|
18494
|
+
const hasSyntaurMd = await pathExists(join7(dir, "SYNTAUR.md"));
|
|
18495
|
+
const hasAgentsMd = await pathExists(join7(dir, "AGENTS.md"));
|
|
18496
|
+
let syntaur = null;
|
|
18497
|
+
if (hasSyntaurMd) syntaur = await readSyntaurMeta(join7(dir, "SYNTAUR.md"));
|
|
18498
|
+
if (!syntaur && hasAgentsMd) syntaur = await readSyntaurMeta(join7(dir, "AGENTS.md"));
|
|
18499
|
+
const strong = hasPi || hasMcp || syntaur !== null;
|
|
18500
|
+
if (!strong) return null;
|
|
18501
|
+
return {
|
|
18502
|
+
name: syntaur?.name ?? basename8,
|
|
18503
|
+
// A directory agent is pi/codex by definition; a contradictory
|
|
18504
|
+
// `syntaur.runner: claude` opt-in is clamped to pi so the candidate never
|
|
18505
|
+
// carries an impossible directory+claude combo.
|
|
18506
|
+
runner: syntaur?.runner === "codex" ? "codex" : "pi",
|
|
18507
|
+
description: syntaur?.description,
|
|
18508
|
+
path: dir,
|
|
18509
|
+
source: "directory",
|
|
18510
|
+
recommended: syntaur !== null,
|
|
18511
|
+
alreadyRegistered: directoryAlreadyRegistered(agents, dir)
|
|
18512
|
+
};
|
|
18513
|
+
}
|
|
18514
|
+
async function discoverAgents(input) {
|
|
18515
|
+
const out = [];
|
|
18516
|
+
if (input.claudeGlobal) {
|
|
18517
|
+
for (const d of await discoverClaudeAgents()) {
|
|
18518
|
+
out.push(await toClaudeCandidate(d, "claude-global", input.agents));
|
|
18519
|
+
}
|
|
18520
|
+
}
|
|
18521
|
+
if (input.claudeProject && input.repo) {
|
|
18522
|
+
const projRoot = join7(input.repo, ".claude", "agents");
|
|
18523
|
+
for (const d of await discoverClaudeAgents(projRoot)) {
|
|
18524
|
+
out.push(await toClaudeCandidate(d, "claude-project", input.agents, input.repo));
|
|
18525
|
+
}
|
|
18526
|
+
}
|
|
18527
|
+
if (input.directory || input.claudeProject) {
|
|
18528
|
+
for (const rawRoot of input.roots) {
|
|
18529
|
+
const root = expandHome(rawRoot);
|
|
18530
|
+
let entries;
|
|
18531
|
+
try {
|
|
18532
|
+
entries = await readdir16(root, { withFileTypes: true });
|
|
18533
|
+
} catch {
|
|
18534
|
+
continue;
|
|
18535
|
+
}
|
|
18536
|
+
for (const e of entries) {
|
|
18537
|
+
if (!e.isDirectory()) continue;
|
|
18538
|
+
const dir = join7(root, e.name);
|
|
18539
|
+
if (input.directory) {
|
|
18540
|
+
const dirCand = await inspectDirCandidate(dir, e.name, input.agents);
|
|
18541
|
+
if (dirCand) out.push(dirCand);
|
|
18542
|
+
}
|
|
18543
|
+
if (input.claudeProject && await pathExists(join7(dir, ".claude", "agents"))) {
|
|
18544
|
+
const projRoot = join7(dir, ".claude", "agents");
|
|
18545
|
+
for (const d of await discoverClaudeAgents(projRoot)) {
|
|
18546
|
+
out.push(await toClaudeCandidate(d, "claude-project", input.agents, dir));
|
|
18547
|
+
}
|
|
18548
|
+
}
|
|
18549
|
+
}
|
|
18550
|
+
}
|
|
18551
|
+
}
|
|
18552
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18553
|
+
const deduped = [];
|
|
18554
|
+
for (const c of out) {
|
|
18555
|
+
const key = JSON.stringify([c.source, c.path, c.name]);
|
|
18556
|
+
if (seen.has(key)) continue;
|
|
18557
|
+
seen.add(key);
|
|
18558
|
+
deduped.push(c);
|
|
18559
|
+
}
|
|
18560
|
+
deduped.sort(
|
|
18561
|
+
(a, b) => a.recommended === b.recommended ? a.name.localeCompare(b.name) : a.recommended ? -1 : 1
|
|
18562
|
+
);
|
|
18563
|
+
return deduped;
|
|
18564
|
+
}
|
|
18565
|
+
|
|
18566
|
+
// src/targets/agent-authoring.ts
|
|
18567
|
+
init_paths();
|
|
18568
|
+
init_parser();
|
|
18569
|
+
init_agents_schema();
|
|
18570
|
+
import { mkdir as mkdir2, writeFile as writeFile4, access as access3, readFile as readFile22, stat as stat5 } from "fs/promises";
|
|
18571
|
+
import { homedir as homedir6 } from "os";
|
|
18572
|
+
import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute5, join as join8 } from "path";
|
|
18573
|
+
var RUNNER_COMMAND = {
|
|
18574
|
+
claude: "claude",
|
|
18575
|
+
pi: "pi",
|
|
18576
|
+
codex: "codex"
|
|
18577
|
+
};
|
|
18578
|
+
function requireAbsolutePath(raw2, what = "path") {
|
|
18579
|
+
const p = expandHome(raw2.trim());
|
|
18580
|
+
if (!isAbsolute5(p)) {
|
|
18581
|
+
throw new Error(`${what} must be an absolute path (got ${JSON.stringify(raw2)})`);
|
|
18582
|
+
}
|
|
18583
|
+
return p;
|
|
18584
|
+
}
|
|
18585
|
+
function slugifyAgentId(name) {
|
|
18586
|
+
const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
18587
|
+
const safe = /^[a-z0-9]/.test(slug) ? slug : `agent-${slug}`;
|
|
18588
|
+
return AGENT_ID_PATTERN.test(safe) ? safe : "agent";
|
|
18589
|
+
}
|
|
18590
|
+
function uniqueAgentId(base, existingIds) {
|
|
18591
|
+
const taken = new Set(existingIds);
|
|
18592
|
+
if (!taken.has(base)) return base;
|
|
18593
|
+
for (let n = 2; ; n++) {
|
|
18594
|
+
const candidate = `${base}-${n}`;
|
|
18595
|
+
if (!taken.has(candidate)) return candidate;
|
|
18596
|
+
}
|
|
18597
|
+
}
|
|
18598
|
+
function buildRegisteredAgent(input) {
|
|
18599
|
+
const id = uniqueAgentId(slugifyAgentId(input.name), input.existingIds);
|
|
18600
|
+
const agent = {
|
|
18601
|
+
id,
|
|
18602
|
+
label: input.name,
|
|
18603
|
+
command: RUNNER_COMMAND[input.runner],
|
|
18604
|
+
runner: input.runner,
|
|
18605
|
+
sourceKind: input.sourceKind,
|
|
18606
|
+
sourcePath: input.sourcePath
|
|
18607
|
+
};
|
|
18608
|
+
if (input.sourceRepo) agent.sourceRepo = input.sourceRepo;
|
|
18609
|
+
if (input.runner === "claude") {
|
|
18610
|
+
agent.agentName = input.name;
|
|
18611
|
+
} else {
|
|
18612
|
+
agent.workdir = input.sourcePath;
|
|
18613
|
+
}
|
|
18614
|
+
return agent;
|
|
18615
|
+
}
|
|
18616
|
+
async function pathExists2(p) {
|
|
18617
|
+
try {
|
|
18618
|
+
await access3(p);
|
|
18619
|
+
return true;
|
|
18620
|
+
} catch {
|
|
18621
|
+
return false;
|
|
18622
|
+
}
|
|
18623
|
+
}
|
|
18624
|
+
function frontmatterLines(fields) {
|
|
18625
|
+
const lines = [];
|
|
18626
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
18627
|
+
if (v !== void 0 && v !== "") lines.push(`${k}: ${JSON.stringify(v)}`);
|
|
18628
|
+
}
|
|
18629
|
+
return lines;
|
|
18630
|
+
}
|
|
18631
|
+
function syntaurBlock(name, runner, description) {
|
|
18632
|
+
const lines = ["syntaur:", ` name: ${JSON.stringify(name)}`, ` runner: ${runner}`];
|
|
18633
|
+
if (description) lines.push(` description: ${JSON.stringify(description)}`);
|
|
18634
|
+
return lines;
|
|
18635
|
+
}
|
|
18636
|
+
async function authorAgentDef(input) {
|
|
18637
|
+
const slug = slugifyAgentId(input.name);
|
|
18638
|
+
if (input.runner === "claude") {
|
|
18639
|
+
const dir2 = input.location ? requireAbsolutePath(input.location, "location") : join8(homedir6(), ".claude", "agents");
|
|
18640
|
+
const path = join8(dir2, `${slug}.md`);
|
|
18641
|
+
if (await pathExists2(path)) {
|
|
18642
|
+
throw new Error(`refusing to overwrite existing def at ${path}`);
|
|
18643
|
+
}
|
|
18644
|
+
const fm2 = [
|
|
18645
|
+
"---",
|
|
18646
|
+
...frontmatterLines({
|
|
18647
|
+
name: input.name,
|
|
18648
|
+
description: input.description,
|
|
18649
|
+
model: input.model
|
|
18650
|
+
}),
|
|
18651
|
+
...syntaurBlock(input.name, "claude", input.description),
|
|
18652
|
+
"---"
|
|
18653
|
+
];
|
|
18654
|
+
await mkdir2(dir2, { recursive: true });
|
|
18655
|
+
await writeFile4(path, `${fm2.join("\n")}
|
|
18656
|
+
|
|
18657
|
+
${input.instructions.trim()}
|
|
18658
|
+
`);
|
|
18659
|
+
if (!input.location) {
|
|
18660
|
+
return { path, sourceKind: "claude-global" };
|
|
18661
|
+
}
|
|
18662
|
+
const sourceRepo = basename5(dir2) === "agents" && basename5(dirname6(dir2)) === ".claude" ? dirname6(dirname6(dir2)) : void 0;
|
|
18663
|
+
return { path, sourceKind: "claude-project", ...sourceRepo ? { sourceRepo } : {} };
|
|
18664
|
+
}
|
|
18665
|
+
const parent = input.location ? requireAbsolutePath(input.location, "location") : homedir6();
|
|
18666
|
+
const dir = join8(parent, slug);
|
|
18667
|
+
const agentsMd = join8(dir, "AGENTS.md");
|
|
18668
|
+
if (await pathExists2(agentsMd)) {
|
|
18669
|
+
throw new Error(`refusing to overwrite existing def at ${agentsMd}`);
|
|
18670
|
+
}
|
|
18671
|
+
const fm = ["---", ...syntaurBlock(input.name, input.runner, input.description), "---"];
|
|
18672
|
+
await mkdir2(dir, { recursive: true });
|
|
18673
|
+
await writeFile4(agentsMd, `${fm.join("\n")}
|
|
18674
|
+
|
|
18675
|
+
${input.instructions.trim()}
|
|
18676
|
+
`);
|
|
18677
|
+
return { path: dir, sourceKind: "directory" };
|
|
18678
|
+
}
|
|
18679
|
+
async function inferManualAdd(rawPath) {
|
|
18680
|
+
const path = requireAbsolutePath(rawPath);
|
|
18681
|
+
const info = await stat5(path);
|
|
18682
|
+
if (info.isFile()) {
|
|
18683
|
+
const content = await readFile22(path, "utf-8");
|
|
18684
|
+
const [fm] = extractFrontmatter2(content);
|
|
18685
|
+
const name = getNestedField(fm, "syntaur", "name")?.trim() || getField(fm, "name")?.trim() || basename5(path).replace(/\.md$/, "");
|
|
18686
|
+
const description = getNestedField(fm, "syntaur", "description")?.trim() || getField(fm, "description")?.trim() || void 0;
|
|
18687
|
+
return { name, runner: "claude", sourceKind: "claude-global", sourcePath: path, description };
|
|
18688
|
+
}
|
|
18689
|
+
let syntaurName;
|
|
18690
|
+
let syntaurRunner;
|
|
18691
|
+
let syntaurDesc;
|
|
18692
|
+
for (const f of ["SYNTAUR.md", "AGENTS.md"]) {
|
|
18693
|
+
const p = join8(path, f);
|
|
18694
|
+
if (await pathExists2(p)) {
|
|
18695
|
+
const [fm] = extractFrontmatter2(await readFile22(p, "utf-8"));
|
|
18696
|
+
syntaurName = getNestedField(fm, "syntaur", "name")?.trim() || syntaurName;
|
|
18697
|
+
const r = getNestedField(fm, "syntaur", "runner")?.trim();
|
|
18698
|
+
if (r === "claude" || r === "pi" || r === "codex") syntaurRunner = r;
|
|
18699
|
+
syntaurDesc = getNestedField(fm, "syntaur", "description")?.trim() || syntaurDesc;
|
|
18700
|
+
if (syntaurName || syntaurRunner) break;
|
|
18701
|
+
}
|
|
18702
|
+
}
|
|
18703
|
+
return {
|
|
18704
|
+
name: syntaurName ?? basename5(path),
|
|
18705
|
+
// A directory (pi/codex) agent is never claude — clamp a contradictory
|
|
18706
|
+
// `syntaur.runner: claude` opt-in to pi (parity with inspectDirCandidate).
|
|
18707
|
+
runner: syntaurRunner === "codex" ? "codex" : "pi",
|
|
18708
|
+
sourceKind: "directory",
|
|
18709
|
+
sourcePath: path,
|
|
18710
|
+
description: syntaurDesc
|
|
18711
|
+
};
|
|
18712
|
+
}
|
|
18713
|
+
|
|
18254
18714
|
// src/dashboard/api-agents.ts
|
|
18715
|
+
async function pathExists3(p) {
|
|
18716
|
+
try {
|
|
18717
|
+
await access4(p);
|
|
18718
|
+
return true;
|
|
18719
|
+
} catch {
|
|
18720
|
+
return false;
|
|
18721
|
+
}
|
|
18722
|
+
}
|
|
18723
|
+
var RUNNER_KINDS_SET = /* @__PURE__ */ new Set(["claude", "pi", "codex"]);
|
|
18724
|
+
var SOURCE_KINDS_SET = /* @__PURE__ */ new Set(["claude-global", "claude-project", "directory"]);
|
|
18255
18725
|
function mapAgentErrorToFieldErrors(err) {
|
|
18256
18726
|
const message = err.message;
|
|
18257
18727
|
let match = message.match(/^agent id "([^"]*)" is invalid/);
|
|
@@ -18546,6 +19016,22 @@ function coerceAgentRow(raw2, index) {
|
|
|
18546
19016
|
const workdir = entry.workdir.trim();
|
|
18547
19017
|
if (workdir) cleaned.workdir = workdir;
|
|
18548
19018
|
}
|
|
19019
|
+
for (const field of ["runner", "sourceKind", "sourcePath", "sourceRepo"]) {
|
|
19020
|
+
const raw3 = entry[field];
|
|
19021
|
+
if (raw3 === void 0) continue;
|
|
19022
|
+
if (typeof raw3 !== "string") {
|
|
19023
|
+
return {
|
|
19024
|
+
ok: false,
|
|
19025
|
+
status: 400,
|
|
19026
|
+
body: {
|
|
19027
|
+
error: `agents[${index}].${field} must be a string`,
|
|
19028
|
+
fieldErrors: [{ id, field, message: `${field} must be a string` }]
|
|
19029
|
+
}
|
|
19030
|
+
};
|
|
19031
|
+
}
|
|
19032
|
+
const v = raw3.trim();
|
|
19033
|
+
if (v) cleaned[field] = v;
|
|
19034
|
+
}
|
|
18549
19035
|
return { ok: true, value: cleaned };
|
|
18550
19036
|
}
|
|
18551
19037
|
function createAgentsRouter() {
|
|
@@ -18559,6 +19045,164 @@ function createAgentsRouter() {
|
|
|
18559
19045
|
res.status(500).json({ error: "Failed to discover Claude agents" });
|
|
18560
19046
|
}
|
|
18561
19047
|
});
|
|
19048
|
+
router.get("/discovered", async (req2, res) => {
|
|
19049
|
+
try {
|
|
19050
|
+
const config = await readConfig();
|
|
19051
|
+
const agents = config.agents ?? BUILTIN_AGENTS;
|
|
19052
|
+
const repo = typeof req2.query.repo === "string" && req2.query.repo ? req2.query.repo : null;
|
|
19053
|
+
const d = config.agentDiscovery;
|
|
19054
|
+
const candidates = await discoverAgents({
|
|
19055
|
+
claudeGlobal: d.claudeGlobal,
|
|
19056
|
+
claudeProject: d.claudeProject,
|
|
19057
|
+
directory: d.directory,
|
|
19058
|
+
roots: d.roots,
|
|
19059
|
+
repo,
|
|
19060
|
+
agents
|
|
19061
|
+
});
|
|
19062
|
+
res.json({ candidates });
|
|
19063
|
+
} catch (err) {
|
|
19064
|
+
console.error("Error discovering agents:", err);
|
|
19065
|
+
res.status(500).json({ error: "Failed to discover agents" });
|
|
19066
|
+
}
|
|
19067
|
+
});
|
|
19068
|
+
async function appendAndPersist(agent, res) {
|
|
19069
|
+
const config = await readConfig();
|
|
19070
|
+
const base = config.agents ?? BUILTIN_AGENTS;
|
|
19071
|
+
if (agent.sourcePath && base.some((a) => a.sourcePath === agent.sourcePath)) {
|
|
19072
|
+
res.status(409).json({ error: `already registered: ${agent.sourcePath}` });
|
|
19073
|
+
return null;
|
|
19074
|
+
}
|
|
19075
|
+
const next = [...base, agent];
|
|
19076
|
+
try {
|
|
19077
|
+
await writeAgentsConfig(next);
|
|
19078
|
+
} catch (err) {
|
|
19079
|
+
if (err instanceof AgentConfigError) {
|
|
19080
|
+
res.status(400).json(mapAgentErrorToFieldErrors(err));
|
|
19081
|
+
return null;
|
|
19082
|
+
}
|
|
19083
|
+
throw err;
|
|
19084
|
+
}
|
|
19085
|
+
return next;
|
|
19086
|
+
}
|
|
19087
|
+
router.post("/register", async (req2, res) => {
|
|
19088
|
+
try {
|
|
19089
|
+
const b = req2.body ?? {};
|
|
19090
|
+
if (!b.path || !b.name || !b.runner || !b.sourceKind) {
|
|
19091
|
+
res.status(400).json({ error: "register requires path, name, runner, sourceKind" });
|
|
19092
|
+
return;
|
|
19093
|
+
}
|
|
19094
|
+
if (!RUNNER_KINDS_SET.has(b.runner) || !SOURCE_KINDS_SET.has(b.sourceKind)) {
|
|
19095
|
+
res.status(400).json({ error: "invalid runner or sourceKind" });
|
|
19096
|
+
return;
|
|
19097
|
+
}
|
|
19098
|
+
let regPath;
|
|
19099
|
+
let sourceRepo;
|
|
19100
|
+
try {
|
|
19101
|
+
regPath = requireAbsolutePath(b.path);
|
|
19102
|
+
sourceRepo = b.sourceRepo ? requireAbsolutePath(b.sourceRepo, "sourceRepo") : void 0;
|
|
19103
|
+
} catch (err) {
|
|
19104
|
+
res.status(400).json({ error: err instanceof Error ? err.message : "invalid path" });
|
|
19105
|
+
return;
|
|
19106
|
+
}
|
|
19107
|
+
if (!await pathExists3(regPath)) {
|
|
19108
|
+
res.status(400).json({ error: `path not found: ${regPath}` });
|
|
19109
|
+
return;
|
|
19110
|
+
}
|
|
19111
|
+
const config = await readConfig();
|
|
19112
|
+
const base = config.agents ?? BUILTIN_AGENTS;
|
|
19113
|
+
const agent = buildRegisteredAgent({
|
|
19114
|
+
name: b.name,
|
|
19115
|
+
runner: b.runner,
|
|
19116
|
+
sourceKind: b.sourceKind,
|
|
19117
|
+
sourcePath: regPath,
|
|
19118
|
+
sourceRepo,
|
|
19119
|
+
description: b.description,
|
|
19120
|
+
existingIds: base.map((a) => a.id)
|
|
19121
|
+
});
|
|
19122
|
+
const next = await appendAndPersist(agent, res);
|
|
19123
|
+
if (!next) return;
|
|
19124
|
+
res.json({ agent, agents: next, custom: true });
|
|
19125
|
+
} catch (err) {
|
|
19126
|
+
console.error("Error registering agent:", err);
|
|
19127
|
+
res.status(500).json({ error: "Failed to register agent" });
|
|
19128
|
+
}
|
|
19129
|
+
});
|
|
19130
|
+
router.post("/manual-add", async (req2, res) => {
|
|
19131
|
+
try {
|
|
19132
|
+
const p = (req2.body ?? {}).path;
|
|
19133
|
+
if (typeof p !== "string" || !p) {
|
|
19134
|
+
res.status(400).json({ error: "manual-add requires a path" });
|
|
19135
|
+
return;
|
|
19136
|
+
}
|
|
19137
|
+
let inferred;
|
|
19138
|
+
try {
|
|
19139
|
+
inferred = await inferManualAdd(p);
|
|
19140
|
+
} catch {
|
|
19141
|
+
res.status(400).json({ error: `path not found or unreadable: ${p}` });
|
|
19142
|
+
return;
|
|
19143
|
+
}
|
|
19144
|
+
const config = await readConfig();
|
|
19145
|
+
const base = config.agents ?? BUILTIN_AGENTS;
|
|
19146
|
+
const agent = buildRegisteredAgent({
|
|
19147
|
+
name: inferred.name,
|
|
19148
|
+
runner: inferred.runner,
|
|
19149
|
+
sourceKind: inferred.sourceKind,
|
|
19150
|
+
sourcePath: inferred.sourcePath,
|
|
19151
|
+
description: inferred.description,
|
|
19152
|
+
existingIds: base.map((a) => a.id)
|
|
19153
|
+
});
|
|
19154
|
+
const next = await appendAndPersist(agent, res);
|
|
19155
|
+
if (!next) return;
|
|
19156
|
+
res.json({ agent, agents: next, custom: true });
|
|
19157
|
+
} catch (err) {
|
|
19158
|
+
console.error("Error manual-adding agent:", err);
|
|
19159
|
+
res.status(500).json({ error: "Failed to manual-add agent" });
|
|
19160
|
+
}
|
|
19161
|
+
});
|
|
19162
|
+
router.post("/create", async (req2, res) => {
|
|
19163
|
+
try {
|
|
19164
|
+
const b = req2.body ?? {};
|
|
19165
|
+
if (!b.name || !b.runner || typeof b.instructions !== "string") {
|
|
19166
|
+
res.status(400).json({ error: "create requires name, runner, instructions" });
|
|
19167
|
+
return;
|
|
19168
|
+
}
|
|
19169
|
+
if (!RUNNER_KINDS_SET.has(b.runner)) {
|
|
19170
|
+
res.status(400).json({ error: "invalid runner" });
|
|
19171
|
+
return;
|
|
19172
|
+
}
|
|
19173
|
+
let authored;
|
|
19174
|
+
try {
|
|
19175
|
+
authored = await authorAgentDef({
|
|
19176
|
+
name: b.name,
|
|
19177
|
+
runner: b.runner,
|
|
19178
|
+
model: b.model,
|
|
19179
|
+
description: b.description,
|
|
19180
|
+
instructions: b.instructions,
|
|
19181
|
+
location: b.location
|
|
19182
|
+
});
|
|
19183
|
+
} catch (err) {
|
|
19184
|
+
res.status(400).json({ error: err instanceof Error ? err.message : "failed to author def" });
|
|
19185
|
+
return;
|
|
19186
|
+
}
|
|
19187
|
+
const config = await readConfig();
|
|
19188
|
+
const base = config.agents ?? BUILTIN_AGENTS;
|
|
19189
|
+
const agent = buildRegisteredAgent({
|
|
19190
|
+
name: b.name,
|
|
19191
|
+
runner: b.runner,
|
|
19192
|
+
sourceKind: authored.sourceKind,
|
|
19193
|
+
sourcePath: authored.path,
|
|
19194
|
+
sourceRepo: authored.sourceRepo,
|
|
19195
|
+
description: b.description,
|
|
19196
|
+
existingIds: base.map((a) => a.id)
|
|
19197
|
+
});
|
|
19198
|
+
const next = await appendAndPersist(agent, res);
|
|
19199
|
+
if (!next) return;
|
|
19200
|
+
res.json({ agent, path: authored.path, agents: next, custom: true });
|
|
19201
|
+
} catch (err) {
|
|
19202
|
+
console.error("Error creating agent:", err);
|
|
19203
|
+
res.status(500).json({ error: "Failed to create agent" });
|
|
19204
|
+
}
|
|
19205
|
+
});
|
|
18562
19206
|
router.get("/", async (_req, res) => {
|
|
18563
19207
|
try {
|
|
18564
19208
|
const config = await readConfig();
|
|
@@ -18621,8 +19265,8 @@ import { Router as Router6 } from "express";
|
|
|
18621
19265
|
// src/utils/terminal-probe.ts
|
|
18622
19266
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
18623
19267
|
import { existsSync as existsSync2 } from "fs";
|
|
18624
|
-
import { homedir as
|
|
18625
|
-
import { join as
|
|
19268
|
+
import { homedir as homedir7 } from "os";
|
|
19269
|
+
import { join as join9 } from "path";
|
|
18626
19270
|
var APP_BUNDLE_IDS = {
|
|
18627
19271
|
"terminal-app": "com.apple.Terminal",
|
|
18628
19272
|
iterm: "com.googlecode.iterm2",
|
|
@@ -18639,7 +19283,7 @@ var APP_FIXED_PATHS = {
|
|
|
18639
19283
|
"terminal-app": "/System/Applications/Utilities/Terminal.app"
|
|
18640
19284
|
};
|
|
18641
19285
|
function defaultApplicationsDirs() {
|
|
18642
|
-
return ["/Applications",
|
|
19286
|
+
return ["/Applications", join9(homedir7(), "Applications")];
|
|
18643
19287
|
}
|
|
18644
19288
|
function findAppBundle(terminal, dirs = defaultApplicationsDirs()) {
|
|
18645
19289
|
const fixed = APP_FIXED_PATHS[terminal];
|
|
@@ -18647,7 +19291,7 @@ function findAppBundle(terminal, dirs = defaultApplicationsDirs()) {
|
|
|
18647
19291
|
const bundleName = APP_BUNDLE_NAMES[terminal];
|
|
18648
19292
|
if (bundleName) {
|
|
18649
19293
|
for (const dir of dirs) {
|
|
18650
|
-
const candidate =
|
|
19294
|
+
const candidate = join9(dir, bundleName);
|
|
18651
19295
|
if (existsSync2(candidate)) return candidate;
|
|
18652
19296
|
}
|
|
18653
19297
|
}
|
|
@@ -18677,11 +19321,11 @@ var defaultCmuxLsappinfoRunner = () => {
|
|
|
18677
19321
|
function resolveCmuxCli(applicationsDirsOverride, cliDirsOverride, lsappinfoRunnerOverride) {
|
|
18678
19322
|
const bundle = findAppBundle("cmux", applicationsDirsOverride);
|
|
18679
19323
|
if (bundle) {
|
|
18680
|
-
const cli =
|
|
19324
|
+
const cli = join9(bundle, "Contents/Resources/bin/cmux");
|
|
18681
19325
|
if (existsSync2(cli)) return cli;
|
|
18682
19326
|
}
|
|
18683
19327
|
for (const dir of cliDirsOverride ?? CMUX_CLI_DIRS) {
|
|
18684
|
-
const cli =
|
|
19328
|
+
const cli = join9(dir, "cmux");
|
|
18685
19329
|
if (existsSync2(cli)) return cli;
|
|
18686
19330
|
}
|
|
18687
19331
|
if (process.platform === "darwin") {
|
|
@@ -18690,7 +19334,7 @@ function resolveCmuxCli(applicationsDirsOverride, cliDirsOverride, lsappinfoRunn
|
|
|
18690
19334
|
if (stdout) {
|
|
18691
19335
|
const match = stdout.match(/"LSBundlePath"="([^"]+)"/);
|
|
18692
19336
|
if (match) {
|
|
18693
|
-
const cli =
|
|
19337
|
+
const cli = join9(match[1], "Contents/Resources/bin/cmux");
|
|
18694
19338
|
if (existsSync2(cli)) return cli;
|
|
18695
19339
|
}
|
|
18696
19340
|
}
|
|
@@ -18861,15 +19505,15 @@ init_agent_sessions();
|
|
|
18861
19505
|
|
|
18862
19506
|
// src/launch/argv.ts
|
|
18863
19507
|
init_agents_schema();
|
|
18864
|
-
import { isAbsolute as
|
|
19508
|
+
import { isAbsolute as isAbsolute7 } from "path";
|
|
18865
19509
|
|
|
18866
19510
|
// src/tui/launch.ts
|
|
18867
19511
|
init_api();
|
|
18868
19512
|
init_agents_schema();
|
|
18869
19513
|
init_cwd();
|
|
18870
19514
|
import { spawn as spawn2 } from "child_process";
|
|
18871
|
-
import { mkdir as
|
|
18872
|
-
import { isAbsolute as
|
|
19515
|
+
import { mkdir as mkdir3, writeFile as writeFile5 } from "fs/promises";
|
|
19516
|
+
import { isAbsolute as isAbsolute6, resolve as resolve28 } from "path";
|
|
18873
19517
|
init_paths();
|
|
18874
19518
|
init_playbooks();
|
|
18875
19519
|
init_cwd();
|
|
@@ -18887,7 +19531,7 @@ function buildAgentArgv(agent, prompt, env = process.env) {
|
|
|
18887
19531
|
const requested = env.SHELL;
|
|
18888
19532
|
let shell = requested;
|
|
18889
19533
|
let warning = null;
|
|
18890
|
-
if (!shell || !
|
|
19534
|
+
if (!shell || !isAbsolute6(shell)) {
|
|
18891
19535
|
warning = `syntaur: $SHELL ${requested ? `("${requested}") is not absolute` : "is unset"} \u2014 falling back to /bin/sh for shell-alias resolution`;
|
|
18892
19536
|
shell = "/bin/sh";
|
|
18893
19537
|
}
|
|
@@ -18922,7 +19566,7 @@ function buildSessionArgv(agent, sessionId, mode, env = process.env) {
|
|
|
18922
19566
|
const requested = env.SHELL;
|
|
18923
19567
|
let shell = requested;
|
|
18924
19568
|
let warning = null;
|
|
18925
|
-
if (!shell || !
|
|
19569
|
+
if (!shell || !isAbsolute7(shell)) {
|
|
18926
19570
|
warning = `syntaur: $SHELL ${requested ? `("${requested}") is not absolute` : "is unset"} \u2014 falling back to /bin/sh for shell-alias resolution`;
|
|
18927
19571
|
shell = "/bin/sh";
|
|
18928
19572
|
}
|
|
@@ -18982,18 +19626,14 @@ async function resolveStandalonePlan(input, terminal) {
|
|
|
18982
19626
|
`Agent "${agentId}" requested for a standalone launch is not in your agents list.`
|
|
18983
19627
|
);
|
|
18984
19628
|
}
|
|
18985
|
-
const
|
|
18986
|
-
|
|
18987
|
-
|
|
18988
|
-
|
|
18989
|
-
|
|
18990
|
-
);
|
|
18991
|
-
}
|
|
18992
|
-
const expanded = expandHome(wd);
|
|
18993
|
-
if (!isExistingDir(expanded)) {
|
|
19629
|
+
const { cwd: spawnCwd, invalidReason } = resolveStandaloneCwd(
|
|
19630
|
+
agent,
|
|
19631
|
+
input.config.standaloneDefaultCwd
|
|
19632
|
+
);
|
|
19633
|
+
if (spawnCwd === null) {
|
|
18994
19634
|
throw new LaunchError(
|
|
18995
19635
|
"workspace-path-invalid",
|
|
18996
|
-
`Agent "${agent.id}"
|
|
19636
|
+
invalidReason ?? `Agent "${agent.id}" has no valid standalone launch directory.`
|
|
18997
19637
|
);
|
|
18998
19638
|
}
|
|
18999
19639
|
const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
|
|
@@ -19007,7 +19647,7 @@ async function resolveStandalonePlan(input, terminal) {
|
|
|
19007
19647
|
const { argv, shellFallbackWarning } = buildFreshArgv(agent, prompt);
|
|
19008
19648
|
return {
|
|
19009
19649
|
terminal,
|
|
19010
|
-
cwd:
|
|
19650
|
+
cwd: spawnCwd,
|
|
19011
19651
|
argv,
|
|
19012
19652
|
env: process.env,
|
|
19013
19653
|
agentId: agent.id,
|
|
@@ -19174,8 +19814,8 @@ async function resolveSessionPlan(input, terminal) {
|
|
|
19174
19814
|
|
|
19175
19815
|
// src/launch/execute.ts
|
|
19176
19816
|
import { spawn as spawn3 } from "child_process";
|
|
19177
|
-
import { homedir as
|
|
19178
|
-
import { basename as
|
|
19817
|
+
import { homedir as homedir8 } from "os";
|
|
19818
|
+
import { basename as basename6, join as join10, resolve as resolve29 } from "path";
|
|
19179
19819
|
init_fs();
|
|
19180
19820
|
init_config2();
|
|
19181
19821
|
init_session_id();
|
|
@@ -19195,7 +19835,7 @@ var TerminalNotFoundError = class extends Error {
|
|
|
19195
19835
|
var realSpawn = (command, args, options) => spawn3(command, args, options);
|
|
19196
19836
|
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["osascript", "open", "sh"]);
|
|
19197
19837
|
function isWrapperCommand(command) {
|
|
19198
|
-
return WRAPPER_COMMANDS.has(
|
|
19838
|
+
return WRAPPER_COMMANDS.has(basename6(command));
|
|
19199
19839
|
}
|
|
19200
19840
|
var WRAPPER_EXIT_TIMEOUT_MS = 1500;
|
|
19201
19841
|
async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
@@ -19281,7 +19921,7 @@ async function registerLaunchAtBirth(plan, child) {
|
|
|
19281
19921
|
const sessionId = plan.session?.sessionId ?? null;
|
|
19282
19922
|
const procStart = captureProcessStartedAt(pid);
|
|
19283
19923
|
const envDir = process.env.SYNTAUR_RUNTIME_SESSIONS_DIR;
|
|
19284
|
-
const markerDir = envDir && envDir.length > 0 ? envDir :
|
|
19924
|
+
const markerDir = envDir && envDir.length > 0 ? envDir : join10(homedir8(), ".syntaur", "runtime", "sessions");
|
|
19285
19925
|
writeRuntimeMarker(
|
|
19286
19926
|
pid,
|
|
19287
19927
|
{
|
|
@@ -19470,7 +20110,7 @@ function appleScriptString(value) {
|
|
|
19470
20110
|
init_paths();
|
|
19471
20111
|
init_fs();
|
|
19472
20112
|
import { fileURLToPath } from "url";
|
|
19473
|
-
import { dirname as
|
|
20113
|
+
import { dirname as dirname7, resolve as resolve30, join as join11 } from "path";
|
|
19474
20114
|
import { realpathSync, readFileSync as readFileSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
19475
20115
|
|
|
19476
20116
|
// src/dashboard/api-launch-preflight.ts
|
|
@@ -19863,21 +20503,78 @@ function createWorkspaceVisibilityConfigRouter() {
|
|
|
19863
20503
|
return router;
|
|
19864
20504
|
}
|
|
19865
20505
|
|
|
20506
|
+
// src/dashboard/api-agent-discovery-config.ts
|
|
20507
|
+
init_config2();
|
|
20508
|
+
import { Router as Router11 } from "express";
|
|
20509
|
+
function createAgentDiscoveryConfigRouter() {
|
|
20510
|
+
const router = Router11();
|
|
20511
|
+
router.get("/", async (_req, res) => {
|
|
20512
|
+
try {
|
|
20513
|
+
const config = await readConfig();
|
|
20514
|
+
res.json({
|
|
20515
|
+
agentDiscovery: config.agentDiscovery,
|
|
20516
|
+
standaloneDefaultCwd: config.standaloneDefaultCwd
|
|
20517
|
+
});
|
|
20518
|
+
} catch (error) {
|
|
20519
|
+
console.error("Error getting agent-discovery config:", error);
|
|
20520
|
+
res.status(500).json({ error: "Failed to get agent-discovery config" });
|
|
20521
|
+
}
|
|
20522
|
+
});
|
|
20523
|
+
router.post("/", async (req2, res) => {
|
|
20524
|
+
try {
|
|
20525
|
+
const b = req2.body ?? {};
|
|
20526
|
+
const d = b.agentDiscovery ?? {};
|
|
20527
|
+
let roots;
|
|
20528
|
+
try {
|
|
20529
|
+
roots = (Array.isArray(d.roots) ? d.roots : []).filter((r) => typeof r === "string" && r.trim() !== "").map((r) => {
|
|
20530
|
+
requireAbsolutePath(r, "root");
|
|
20531
|
+
return r.trim();
|
|
20532
|
+
});
|
|
20533
|
+
} catch (err) {
|
|
20534
|
+
res.status(400).json({ error: err instanceof Error ? err.message : "invalid root" });
|
|
20535
|
+
return;
|
|
20536
|
+
}
|
|
20537
|
+
const cfg = {
|
|
20538
|
+
// Default-true semantics: only an explicit `false` turns a source off.
|
|
20539
|
+
claudeGlobal: d.claudeGlobal !== false,
|
|
20540
|
+
claudeProject: d.claudeProject !== false,
|
|
20541
|
+
directory: d.directory !== false,
|
|
20542
|
+
roots: roots.length > 0 ? roots : ["~"]
|
|
20543
|
+
};
|
|
20544
|
+
let cwd = null;
|
|
20545
|
+
if (typeof b.standaloneDefaultCwd === "string" && b.standaloneDefaultCwd.trim()) {
|
|
20546
|
+
try {
|
|
20547
|
+
cwd = requireAbsolutePath(b.standaloneDefaultCwd, "standaloneDefaultCwd");
|
|
20548
|
+
} catch (err) {
|
|
20549
|
+
res.status(400).json({ error: err instanceof Error ? err.message : "invalid cwd" });
|
|
20550
|
+
return;
|
|
20551
|
+
}
|
|
20552
|
+
}
|
|
20553
|
+
await writeAgentDiscoveryConfig(cfg, cwd);
|
|
20554
|
+
res.json({ agentDiscovery: cfg, standaloneDefaultCwd: cwd });
|
|
20555
|
+
} catch (error) {
|
|
20556
|
+
console.error("Error saving agent-discovery config:", error);
|
|
20557
|
+
res.status(500).json({ error: "Failed to save agent-discovery config" });
|
|
20558
|
+
}
|
|
20559
|
+
});
|
|
20560
|
+
return router;
|
|
20561
|
+
}
|
|
20562
|
+
|
|
19866
20563
|
// src/dashboard/api-status-config.ts
|
|
19867
20564
|
init_config2();
|
|
19868
20565
|
init_fact_registry();
|
|
19869
20566
|
init_state_machine();
|
|
19870
20567
|
init_api();
|
|
19871
20568
|
init_derive();
|
|
19872
|
-
import { Router as
|
|
20569
|
+
import { Router as Router12 } from "express";
|
|
19873
20570
|
|
|
19874
20571
|
// src/utils/status-config-resolution.ts
|
|
19875
20572
|
init_frontmatter();
|
|
19876
20573
|
init_event_emit();
|
|
19877
20574
|
init_assignment_walk();
|
|
19878
20575
|
init_timestamp();
|
|
19879
|
-
import { readFile as
|
|
19880
|
-
import { dirname as
|
|
20576
|
+
import { readFile as readFile23, writeFile as writeFile6, rm as rm2 } from "fs/promises";
|
|
20577
|
+
import { dirname as dirname8 } from "path";
|
|
19881
20578
|
var StatusResolutionError = class extends Error {
|
|
19882
20579
|
constructor(message, code) {
|
|
19883
20580
|
super(message);
|
|
@@ -19895,7 +20592,7 @@ async function scanAssignmentsByStatus(projectsDir, standaloneDir, ids) {
|
|
|
19895
20592
|
const assignmentPath = `${entry.assignmentDir}/assignment.md`;
|
|
19896
20593
|
let content;
|
|
19897
20594
|
try {
|
|
19898
|
-
content = await
|
|
20595
|
+
content = await readFile23(assignmentPath, "utf-8");
|
|
19899
20596
|
} catch (err) {
|
|
19900
20597
|
const code = err?.code;
|
|
19901
20598
|
if (code === "ENOENT") {
|
|
@@ -19958,7 +20655,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
19958
20655
|
const list = affected.get(r.id) ?? [];
|
|
19959
20656
|
for (const a of list) {
|
|
19960
20657
|
try {
|
|
19961
|
-
const content = await
|
|
20658
|
+
const content = await readFile23(a.path, "utf-8");
|
|
19962
20659
|
buffer.set(a.path, content);
|
|
19963
20660
|
} catch (err) {
|
|
19964
20661
|
const code = err?.code;
|
|
@@ -19987,7 +20684,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
19987
20684
|
for (const a of list) {
|
|
19988
20685
|
let current;
|
|
19989
20686
|
try {
|
|
19990
|
-
current = await
|
|
20687
|
+
current = await readFile23(a.path, "utf-8");
|
|
19991
20688
|
} catch (err) {
|
|
19992
20689
|
const code = err?.code;
|
|
19993
20690
|
if (code === "ENOENT") {
|
|
@@ -20010,7 +20707,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
20010
20707
|
}),
|
|
20011
20708
|
{ at: now, from: r.id, to: r.target, command: "remap", by: null }
|
|
20012
20709
|
);
|
|
20013
|
-
await
|
|
20710
|
+
await writeFile6(a.path, next, "utf-8");
|
|
20014
20711
|
pendingRemapEvents.push({
|
|
20015
20712
|
assignmentId: fm.id,
|
|
20016
20713
|
projectSlug: a.projectSlug,
|
|
@@ -20028,7 +20725,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
20028
20725
|
for (const p of writtenPaths) {
|
|
20029
20726
|
const original = buffer.get(p);
|
|
20030
20727
|
if (original !== void 0) {
|
|
20031
|
-
await
|
|
20728
|
+
await writeFile6(p, original, "utf-8").catch((rollbackErr) => {
|
|
20032
20729
|
console.error(
|
|
20033
20730
|
`status-config-resolution: rollback write failed for ${p}:`,
|
|
20034
20731
|
rollbackErr
|
|
@@ -20058,7 +20755,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
20058
20755
|
const list = affected.get(r.id) ?? [];
|
|
20059
20756
|
for (const a of list) {
|
|
20060
20757
|
try {
|
|
20061
|
-
const current = await
|
|
20758
|
+
const current = await readFile23(a.path, "utf-8");
|
|
20062
20759
|
const fm = parseAssignmentFrontmatter(current);
|
|
20063
20760
|
if (fm.status !== r.id) {
|
|
20064
20761
|
console.warn(
|
|
@@ -20069,7 +20766,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
20069
20766
|
} catch {
|
|
20070
20767
|
continue;
|
|
20071
20768
|
}
|
|
20072
|
-
const assignmentDir =
|
|
20769
|
+
const assignmentDir = dirname8(a.path);
|
|
20073
20770
|
try {
|
|
20074
20771
|
await rm2(assignmentDir, { recursive: true, force: true });
|
|
20075
20772
|
deleted++;
|
|
@@ -20212,7 +20909,7 @@ function mapResolutionErrorToHttp(err, applied) {
|
|
|
20212
20909
|
}
|
|
20213
20910
|
}
|
|
20214
20911
|
function createStatusConfigRouter(projectsDir, assignmentsDir2) {
|
|
20215
|
-
const router =
|
|
20912
|
+
const router = Router12();
|
|
20216
20913
|
installRecordsInvalidation(router);
|
|
20217
20914
|
router.get("/", async (_req, res) => {
|
|
20218
20915
|
try {
|
|
@@ -20532,7 +21229,7 @@ function createStatusConfigRouter(projectsDir, assignmentsDir2) {
|
|
|
20532
21229
|
}
|
|
20533
21230
|
|
|
20534
21231
|
// src/dashboard/api-leases.ts
|
|
20535
|
-
import { Router as
|
|
21232
|
+
import { Router as Router13 } from "express";
|
|
20536
21233
|
|
|
20537
21234
|
// src/db/leases-db.ts
|
|
20538
21235
|
init_paths();
|
|
@@ -20714,7 +21411,7 @@ function forceReleaseLease(lease_id) {
|
|
|
20714
21411
|
|
|
20715
21412
|
// src/dashboard/api-leases.ts
|
|
20716
21413
|
function createLeasesRouter(broadcast) {
|
|
20717
|
-
const router =
|
|
21414
|
+
const router = Router13();
|
|
20718
21415
|
router.get("/", (_req, res) => {
|
|
20719
21416
|
try {
|
|
20720
21417
|
initLeasesDb();
|
|
@@ -20770,14 +21467,14 @@ function createLeasesRouter(broadcast) {
|
|
|
20770
21467
|
|
|
20771
21468
|
// src/dashboard/api-schedules.ts
|
|
20772
21469
|
init_timestamp();
|
|
20773
|
-
import { Router as
|
|
21470
|
+
import { Router as Router14 } from "express";
|
|
20774
21471
|
|
|
20775
21472
|
// src/schedules/store.ts
|
|
20776
21473
|
init_fs();
|
|
20777
21474
|
init_paths();
|
|
20778
21475
|
init_timestamp();
|
|
20779
21476
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
20780
|
-
import { readFile as
|
|
21477
|
+
import { readFile as readFile24, readdir as readdir17, rm as rm3 } from "fs/promises";
|
|
20781
21478
|
import { resolve as resolve32 } from "path";
|
|
20782
21479
|
function schedulesDir() {
|
|
20783
21480
|
const override3 = process.env.SYNTAUR_SCHEDULES_DIR;
|
|
@@ -20910,7 +21607,7 @@ function validateJob(raw2) {
|
|
|
20910
21607
|
async function readJob(id) {
|
|
20911
21608
|
const path = jobPath(id);
|
|
20912
21609
|
if (!await fileExists(path)) return null;
|
|
20913
|
-
return parseJobFile(await
|
|
21610
|
+
return parseJobFile(await readFile24(path, "utf-8"));
|
|
20914
21611
|
}
|
|
20915
21612
|
async function writeJob(job) {
|
|
20916
21613
|
const next = { ...job, updatedAt: nowTimestamp() };
|
|
@@ -20920,7 +21617,7 @@ async function writeJob(job) {
|
|
|
20920
21617
|
async function listJobs() {
|
|
20921
21618
|
let names;
|
|
20922
21619
|
try {
|
|
20923
|
-
names = await
|
|
21620
|
+
names = await readdir17(schedulesDir());
|
|
20924
21621
|
} catch {
|
|
20925
21622
|
return [];
|
|
20926
21623
|
}
|
|
@@ -20928,7 +21625,7 @@ async function listJobs() {
|
|
|
20928
21625
|
for (const name of names) {
|
|
20929
21626
|
if (!name.endsWith(".md")) continue;
|
|
20930
21627
|
try {
|
|
20931
|
-
jobs.push(parseJobFile(await
|
|
21628
|
+
jobs.push(parseJobFile(await readFile24(resolve32(schedulesDir(), name), "utf-8")));
|
|
20932
21629
|
} catch {
|
|
20933
21630
|
}
|
|
20934
21631
|
}
|
|
@@ -20938,8 +21635,8 @@ async function listJobs() {
|
|
|
20938
21635
|
|
|
20939
21636
|
// src/schedules/event-log.ts
|
|
20940
21637
|
init_timestamp();
|
|
20941
|
-
import { appendFile, readFile as
|
|
20942
|
-
import { dirname as
|
|
21638
|
+
import { appendFile, readFile as readFile25, mkdir as mkdir4 } from "fs/promises";
|
|
21639
|
+
import { dirname as dirname9, resolve as resolve33 } from "path";
|
|
20943
21640
|
function eventLogPath(jobId) {
|
|
20944
21641
|
if (!/^[A-Za-z0-9_-]+$/.test(jobId)) {
|
|
20945
21642
|
throw new Error(`Unsafe schedule id: ${JSON.stringify(jobId)}`);
|
|
@@ -20949,7 +21646,7 @@ function eventLogPath(jobId) {
|
|
|
20949
21646
|
async function appendEvent(jobId, type, data) {
|
|
20950
21647
|
const event = { type, at: nowTimestamp(), ...data ? { data } : {} };
|
|
20951
21648
|
const path = eventLogPath(jobId);
|
|
20952
|
-
await
|
|
21649
|
+
await mkdir4(dirname9(path), { recursive: true });
|
|
20953
21650
|
await appendFile(path, `${JSON.stringify(event)}
|
|
20954
21651
|
`, "utf-8");
|
|
20955
21652
|
return event;
|
|
@@ -20957,7 +21654,7 @@ async function appendEvent(jobId, type, data) {
|
|
|
20957
21654
|
async function readEvents(jobId) {
|
|
20958
21655
|
let content;
|
|
20959
21656
|
try {
|
|
20960
|
-
content = await
|
|
21657
|
+
content = await readFile25(eventLogPath(jobId), "utf-8");
|
|
20961
21658
|
} catch {
|
|
20962
21659
|
return [];
|
|
20963
21660
|
}
|
|
@@ -20976,7 +21673,7 @@ async function readEvents(jobId) {
|
|
|
20976
21673
|
|
|
20977
21674
|
// src/schedules/attempt.ts
|
|
20978
21675
|
import { createHash as createHash3 } from "crypto";
|
|
20979
|
-
import { open as open4, readFile as
|
|
21676
|
+
import { open as open4, readFile as readFile26, stat as stat6, unlink as unlink6 } from "fs/promises";
|
|
20980
21677
|
import { resolve as resolve34 } from "path";
|
|
20981
21678
|
|
|
20982
21679
|
// src/schedules/triggers.ts
|
|
@@ -21165,7 +21862,7 @@ async function acquireJobLock(id) {
|
|
|
21165
21862
|
await handle.close();
|
|
21166
21863
|
return async () => {
|
|
21167
21864
|
try {
|
|
21168
|
-
const current = await
|
|
21865
|
+
const current = await readFile26(lockPath, "utf-8");
|
|
21169
21866
|
if (current === token) await unlink6(lockPath);
|
|
21170
21867
|
} catch {
|
|
21171
21868
|
}
|
|
@@ -21174,7 +21871,7 @@ async function acquireJobLock(id) {
|
|
|
21174
21871
|
const code = err.code;
|
|
21175
21872
|
if (code !== "EEXIST") throw err;
|
|
21176
21873
|
try {
|
|
21177
|
-
const info = await
|
|
21874
|
+
const info = await stat6(lockPath);
|
|
21178
21875
|
if (Date.now() - info.mtimeMs > LOCK_STALE_MS2) {
|
|
21179
21876
|
await unlink6(lockPath).catch(() => {
|
|
21180
21877
|
});
|
|
@@ -21513,7 +22210,7 @@ function canFire(job, deps) {
|
|
|
21513
22210
|
init_agent_sessions();
|
|
21514
22211
|
init_session_db();
|
|
21515
22212
|
function createSchedulesRouter(broadcast) {
|
|
21516
|
-
const router =
|
|
22213
|
+
const router = Router14();
|
|
21517
22214
|
const notify = () => broadcast?.({ type: "schedules-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
21518
22215
|
router.get("/", async (_req, res) => {
|
|
21519
22216
|
try {
|
|
@@ -21638,20 +22335,20 @@ init_config2();
|
|
|
21638
22335
|
init_paths();
|
|
21639
22336
|
init_assignment_resolver();
|
|
21640
22337
|
init_frontmatter();
|
|
21641
|
-
import { readFile as
|
|
22338
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
21642
22339
|
import { resolve as resolve36 } from "path";
|
|
21643
22340
|
|
|
21644
22341
|
// src/schedules/launch-ack.ts
|
|
21645
22342
|
init_session_id();
|
|
21646
22343
|
import { readdirSync as readdirSync2 } from "fs";
|
|
21647
|
-
import { homedir as
|
|
21648
|
-
import { join as
|
|
22344
|
+
import { homedir as homedir9 } from "os";
|
|
22345
|
+
import { join as join12 } from "path";
|
|
21649
22346
|
function defaultSleep(ms) {
|
|
21650
22347
|
return new Promise((r) => setTimeout(r, ms));
|
|
21651
22348
|
}
|
|
21652
22349
|
function runtimeSessionsDir() {
|
|
21653
22350
|
const override3 = process.env.SYNTAUR_RUNTIME_SESSIONS_DIR;
|
|
21654
|
-
return override3 && override3.length > 0 ? override3 :
|
|
22351
|
+
return override3 && override3.length > 0 ? override3 : join12(homedir9(), ".syntaur", "runtime", "sessions");
|
|
21655
22352
|
}
|
|
21656
22353
|
function defaultAckProbe(handle) {
|
|
21657
22354
|
const dir = runtimeSessionsDir();
|
|
@@ -21699,7 +22396,7 @@ async function defaultReadAssignment(assignmentId) {
|
|
|
21699
22396
|
const resolved = await resolveAssignmentById(projectsDir, assignmentsDir(), assignmentId);
|
|
21700
22397
|
if (!resolved) return null;
|
|
21701
22398
|
try {
|
|
21702
|
-
const content = await
|
|
22399
|
+
const content = await readFile27(resolve36(resolved.assignmentDir, "assignment.md"), "utf-8");
|
|
21703
22400
|
return parseAssignmentFrontmatter(content);
|
|
21704
22401
|
} catch {
|
|
21705
22402
|
return null;
|
|
@@ -21786,7 +22483,7 @@ async function runTick(deps = {}) {
|
|
|
21786
22483
|
}
|
|
21787
22484
|
|
|
21788
22485
|
// src/dashboard/api-usage.ts
|
|
21789
|
-
import { Router as
|
|
22486
|
+
import { Router as Router15 } from "express";
|
|
21790
22487
|
|
|
21791
22488
|
// src/db/usage-db.ts
|
|
21792
22489
|
init_paths();
|
|
@@ -22236,7 +22933,7 @@ function projectWindowCosts(opts) {
|
|
|
22236
22933
|
|
|
22237
22934
|
// src/dashboard/api-usage.ts
|
|
22238
22935
|
function createUsageRouter(projectsDir, assignmentsDir2) {
|
|
22239
|
-
const router =
|
|
22936
|
+
const router = Router15();
|
|
22240
22937
|
router.get("/facets", (_req, res) => {
|
|
22241
22938
|
try {
|
|
22242
22939
|
initUsageDb();
|
|
@@ -22477,11 +23174,11 @@ function projectAssignmentRollup(projectSlug, rows, common) {
|
|
|
22477
23174
|
init_fs();
|
|
22478
23175
|
init_parser();
|
|
22479
23176
|
init_events_db();
|
|
22480
|
-
import { Router as
|
|
23177
|
+
import { Router as Router16 } from "express";
|
|
22481
23178
|
import { resolve as resolve38 } from "path";
|
|
22482
|
-
import { readFile as
|
|
23179
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
22483
23180
|
function createEventsRouter(projectsDir, assignmentsDir2) {
|
|
22484
|
-
const router =
|
|
23181
|
+
const router = Router16();
|
|
22485
23182
|
router.get("/projects/:slug/assignments/:aslug/events", async (req2, res) => {
|
|
22486
23183
|
try {
|
|
22487
23184
|
const { slug, aslug } = req2.params;
|
|
@@ -22518,7 +23215,7 @@ function createEventsRouter(projectsDir, assignmentsDir2) {
|
|
|
22518
23215
|
}
|
|
22519
23216
|
async function readAssignmentId2(assignmentMdPath) {
|
|
22520
23217
|
if (!await fileExists(assignmentMdPath)) return null;
|
|
22521
|
-
const content = await
|
|
23218
|
+
const content = await readFile28(assignmentMdPath, "utf-8");
|
|
22522
23219
|
const [fm] = extractFrontmatter2(content);
|
|
22523
23220
|
const id = getField(fm, "id");
|
|
22524
23221
|
return id && id.length > 0 ? id : null;
|
|
@@ -22555,7 +23252,7 @@ function parseFilters(query) {
|
|
|
22555
23252
|
}
|
|
22556
23253
|
|
|
22557
23254
|
// src/dashboard/api-inbox.ts
|
|
22558
|
-
import { Router as
|
|
23255
|
+
import { Router as Router17 } from "express";
|
|
22559
23256
|
|
|
22560
23257
|
// src/inbox/index.ts
|
|
22561
23258
|
init_fs();
|
|
@@ -22564,7 +23261,7 @@ init_parser();
|
|
|
22564
23261
|
init_facts();
|
|
22565
23262
|
init_state_machine();
|
|
22566
23263
|
import { resolve as resolve39 } from "path";
|
|
22567
|
-
import { readFile as
|
|
23264
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
22568
23265
|
|
|
22569
23266
|
// src/inbox/types.ts
|
|
22570
23267
|
var INBOX_CATEGORIES = [
|
|
@@ -22744,7 +23441,7 @@ async function computeInbox(opts) {
|
|
|
22744
23441
|
if (opts.project !== void 0 && entry.projectSlug !== opts.project) continue;
|
|
22745
23442
|
let parsed;
|
|
22746
23443
|
try {
|
|
22747
|
-
const content = await
|
|
23444
|
+
const content = await readFile29(resolve39(entry.assignmentDir, "assignment.md"), "utf-8");
|
|
22748
23445
|
parsed = parseAssignmentFull(content);
|
|
22749
23446
|
} catch {
|
|
22750
23447
|
continue;
|
|
@@ -22790,7 +23487,7 @@ async function computeInbox(opts) {
|
|
|
22790
23487
|
const commentsPath = resolve39(entry.assignmentDir, "comments.md");
|
|
22791
23488
|
if (await fileExists(commentsPath)) {
|
|
22792
23489
|
try {
|
|
22793
|
-
const content = await
|
|
23490
|
+
const content = await readFile29(commentsPath, "utf-8");
|
|
22794
23491
|
const parsedComments = parseComments(content);
|
|
22795
23492
|
for (const c of unresolvedQuestions(parsedComments.entries)) {
|
|
22796
23493
|
const since = resolveSince("question", parsed, now, c);
|
|
@@ -22846,7 +23543,7 @@ async function computeInbox(opts) {
|
|
|
22846
23543
|
init_api();
|
|
22847
23544
|
init_config2();
|
|
22848
23545
|
function createInboxRouter(projectsDir, assignmentsDir2) {
|
|
22849
|
-
const router =
|
|
23546
|
+
const router = Router17();
|
|
22850
23547
|
router.get("/inbox", async (req2, res) => {
|
|
22851
23548
|
try {
|
|
22852
23549
|
const project = typeof req2.query.project === "string" && req2.query.project.length > 0 ? req2.query.project : void 0;
|
|
@@ -22900,9 +23597,9 @@ init_parser();
|
|
|
22900
23597
|
init_slug();
|
|
22901
23598
|
init_timestamp();
|
|
22902
23599
|
init_fs();
|
|
22903
|
-
import { Router as
|
|
23600
|
+
import { Router as Router18 } from "express";
|
|
22904
23601
|
import { resolve as resolve40 } from "path";
|
|
22905
|
-
import { readFile as
|
|
23602
|
+
import { readFile as readFile30 } from "fs/promises";
|
|
22906
23603
|
init_playbooks();
|
|
22907
23604
|
function statusForPlaybookError(code) {
|
|
22908
23605
|
switch (code) {
|
|
@@ -22917,7 +23614,7 @@ function statusForPlaybookError(code) {
|
|
|
22917
23614
|
}
|
|
22918
23615
|
}
|
|
22919
23616
|
function createPlaybooksRouter(playbooksDir2) {
|
|
22920
|
-
const router =
|
|
23617
|
+
const router = Router18();
|
|
22921
23618
|
router.get("/", async (_req, res) => {
|
|
22922
23619
|
try {
|
|
22923
23620
|
const playbooks = await listPlaybooks(playbooksDir2);
|
|
@@ -22985,7 +23682,7 @@ function createPlaybooksRouter(playbooksDir2) {
|
|
|
22985
23682
|
return;
|
|
22986
23683
|
}
|
|
22987
23684
|
const filePath = resolve40(playbooksDir2, resolved.filename);
|
|
22988
|
-
const content = await
|
|
23685
|
+
const content = await readFile30(filePath, "utf-8");
|
|
22989
23686
|
res.json({
|
|
22990
23687
|
documentType: "playbook",
|
|
22991
23688
|
title: `Edit Playbook: ${resolved.slug}`,
|
|
@@ -23081,10 +23778,10 @@ init_fs_migration();
|
|
|
23081
23778
|
init_parser2();
|
|
23082
23779
|
init_fs();
|
|
23083
23780
|
init_paths();
|
|
23084
|
-
import { Router as
|
|
23085
|
-
import { readdir as
|
|
23086
|
-
import { resolve as resolvePath, dirname as
|
|
23087
|
-
import { rename as rename6, mkdir as
|
|
23781
|
+
import { Router as Router20 } from "express";
|
|
23782
|
+
import { readdir as readdir19 } from "fs/promises";
|
|
23783
|
+
import { resolve as resolvePath, dirname as dirname11 } from "path";
|
|
23784
|
+
import { rename as rename6, mkdir as mkdir6 } from "fs/promises";
|
|
23088
23785
|
init_slug();
|
|
23089
23786
|
|
|
23090
23787
|
// src/utils/promote-todos.ts
|
|
@@ -23429,8 +24126,8 @@ init_api();
|
|
|
23429
24126
|
import { raw } from "express";
|
|
23430
24127
|
|
|
23431
24128
|
// src/todos/attachments.ts
|
|
23432
|
-
import { mkdir as
|
|
23433
|
-
import { resolve as resolve42, basename as
|
|
24129
|
+
import { mkdir as mkdir5, readdir as readdir18, stat as stat7, rename as rename5, rm as rm4, unlink as unlink7, writeFile as writeFile7, cp } from "fs/promises";
|
|
24130
|
+
import { resolve as resolve42, basename as basename7, dirname as dirname10, extname } from "path";
|
|
23434
24131
|
|
|
23435
24132
|
// src/utils/proof-artifact-id.ts
|
|
23436
24133
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -23503,7 +24200,7 @@ function isSafeInlineMime(mime) {
|
|
|
23503
24200
|
return SAFE_INLINE_MIME.has(mime);
|
|
23504
24201
|
}
|
|
23505
24202
|
function sanitizeAttachmentName(name) {
|
|
23506
|
-
let n =
|
|
24203
|
+
let n = basename7(name || "").replace(/["'\\/]/g, "_");
|
|
23507
24204
|
n = Array.from(n, (ch) => {
|
|
23508
24205
|
const code = ch.charCodeAt(0);
|
|
23509
24206
|
return code < 32 || code === 127 ? "_" : ch;
|
|
@@ -23526,17 +24223,17 @@ function attachmentDirFor(todosDir2, scopeId, todoId) {
|
|
|
23526
24223
|
}
|
|
23527
24224
|
async function dirExists(p) {
|
|
23528
24225
|
try {
|
|
23529
|
-
return (await
|
|
24226
|
+
return (await stat7(p)).isDirectory();
|
|
23530
24227
|
} catch {
|
|
23531
24228
|
return false;
|
|
23532
24229
|
}
|
|
23533
24230
|
}
|
|
23534
24231
|
async function writeAttachment(todosDir2, scopeId, todoId, originalName, bytes) {
|
|
23535
24232
|
const dir = attachmentDirFor(todosDir2, scopeId, todoId);
|
|
23536
|
-
await
|
|
24233
|
+
await mkdir5(dir, { recursive: true });
|
|
23537
24234
|
const id = generateArtifactId();
|
|
23538
24235
|
const filename = sanitizeAttachmentName(originalName);
|
|
23539
|
-
await
|
|
24236
|
+
await writeFile7(resolve42(dir, `${id}__${filename}`), bytes);
|
|
23540
24237
|
return {
|
|
23541
24238
|
id,
|
|
23542
24239
|
filename,
|
|
@@ -23549,7 +24246,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
23549
24246
|
const dir = attachmentDirFor(todosDir2, scopeId, todoId);
|
|
23550
24247
|
let names;
|
|
23551
24248
|
try {
|
|
23552
|
-
names = await
|
|
24249
|
+
names = await readdir18(dir);
|
|
23553
24250
|
} catch {
|
|
23554
24251
|
return [];
|
|
23555
24252
|
}
|
|
@@ -23561,7 +24258,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
23561
24258
|
if (!ATTACHMENT_ID_RE.test(id)) continue;
|
|
23562
24259
|
const filename = stored.slice(sep2 + 2);
|
|
23563
24260
|
try {
|
|
23564
|
-
const st = await
|
|
24261
|
+
const st = await stat7(resolve42(dir, stored));
|
|
23565
24262
|
if (!st.isFile()) continue;
|
|
23566
24263
|
out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
|
|
23567
24264
|
} catch {
|
|
@@ -23575,7 +24272,7 @@ async function readScopeAttachments(todosDir2, scopeId) {
|
|
|
23575
24272
|
const scopeDir = resolve42(attachmentsRootDir(todosDir2), scopeId);
|
|
23576
24273
|
let todoIds;
|
|
23577
24274
|
try {
|
|
23578
|
-
todoIds = await
|
|
24275
|
+
todoIds = await readdir18(scopeDir);
|
|
23579
24276
|
} catch {
|
|
23580
24277
|
return {};
|
|
23581
24278
|
}
|
|
@@ -23592,7 +24289,7 @@ async function resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId) {
|
|
|
23592
24289
|
const dir = attachmentDirFor(todosDir2, scopeId, todoId);
|
|
23593
24290
|
let names;
|
|
23594
24291
|
try {
|
|
23595
|
-
names = await
|
|
24292
|
+
names = await readdir18(dir);
|
|
23596
24293
|
} catch {
|
|
23597
24294
|
return null;
|
|
23598
24295
|
}
|
|
@@ -23620,7 +24317,7 @@ async function moveAttachments(srcTodosDir, srcScopeId, dstTodosDir, dstScopeId,
|
|
|
23620
24317
|
const src = attachmentDirFor(srcTodosDir, srcScopeId, todoId);
|
|
23621
24318
|
if (!await dirExists(src)) return;
|
|
23622
24319
|
const dst = attachmentDirFor(dstTodosDir, dstScopeId, todoId);
|
|
23623
|
-
await
|
|
24320
|
+
await mkdir5(dirname10(dst), { recursive: true });
|
|
23624
24321
|
try {
|
|
23625
24322
|
await rename5(src, dst);
|
|
23626
24323
|
} catch (err) {
|
|
@@ -23762,7 +24459,7 @@ function touchItem3(item) {
|
|
|
23762
24459
|
item.updatedAt = now;
|
|
23763
24460
|
}
|
|
23764
24461
|
function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
23765
|
-
const router =
|
|
24462
|
+
const router = Router20();
|
|
23766
24463
|
installRecordsInvalidation(router);
|
|
23767
24464
|
function broadcastUpdate() {
|
|
23768
24465
|
broadcast({ type: "todos-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -23896,7 +24593,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
23896
24593
|
router.get("/", async (_req, res) => {
|
|
23897
24594
|
try {
|
|
23898
24595
|
await ensureDir(todosDir2);
|
|
23899
|
-
const files = await
|
|
24596
|
+
const files = await readdir19(todosDir2).catch(() => []);
|
|
23900
24597
|
const workspaces = [];
|
|
23901
24598
|
for (const file of files) {
|
|
23902
24599
|
if (typeof file !== "string") continue;
|
|
@@ -24017,7 +24714,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
24017
24714
|
try {
|
|
24018
24715
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
24019
24716
|
const { resolve: resolve49 } = await import("path");
|
|
24020
|
-
const { readFile:
|
|
24717
|
+
const { readFile: readFile34 } = await import("fs/promises");
|
|
24021
24718
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
24022
24719
|
const workspace = getWorkspaceParam(req2.params.workspace);
|
|
24023
24720
|
const outcome = await wsLock(workspace, async () => {
|
|
@@ -24036,7 +24733,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
24036
24733
|
await ensureDir(resolve49(todosDir2, "archive"));
|
|
24037
24734
|
let archContent = "";
|
|
24038
24735
|
if (await fileExists(archFile)) {
|
|
24039
|
-
archContent = await
|
|
24736
|
+
archContent = await readFile34(archFile, "utf-8");
|
|
24040
24737
|
archContent = archContent.trimEnd() + "\n\n";
|
|
24041
24738
|
} else {
|
|
24042
24739
|
archContent = `---
|
|
@@ -24337,7 +25034,7 @@ workspace: ${workspace}
|
|
|
24337
25034
|
const { readConfig: readConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
24338
25035
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
24339
25036
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
24340
|
-
const { readFile:
|
|
25037
|
+
const { readFile: readFile34 } = await import("fs/promises");
|
|
24341
25038
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
24342
25039
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
24343
25040
|
let assignmentRef;
|
|
@@ -24358,7 +25055,7 @@ workspace: ${workspace}
|
|
|
24358
25055
|
}
|
|
24359
25056
|
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
24360
25057
|
if (!await fileExists2(assignmentMdPath)) return { error: `Target assignment not found: ${assignmentMdPath}` };
|
|
24361
|
-
let content = await
|
|
25058
|
+
let content = await readFile34(assignmentMdPath, "utf-8");
|
|
24362
25059
|
content = appendTodosToAssignmentBody2(
|
|
24363
25060
|
content,
|
|
24364
25061
|
items.map((it) => ({
|
|
@@ -24475,7 +25172,7 @@ workspace: ${workspace}
|
|
|
24475
25172
|
return { status: 409, error: "attachments already exist in target" };
|
|
24476
25173
|
}
|
|
24477
25174
|
if (item.planDir && newPlanDir) {
|
|
24478
|
-
await
|
|
25175
|
+
await mkdir6(dirname11(newPlanDir), { recursive: true });
|
|
24479
25176
|
await rename6(item.planDir, newPlanDir);
|
|
24480
25177
|
item.planDir = newPlanDir;
|
|
24481
25178
|
}
|
|
@@ -24552,9 +25249,9 @@ init_parser2();
|
|
|
24552
25249
|
init_fs();
|
|
24553
25250
|
init_paths();
|
|
24554
25251
|
init_slug();
|
|
24555
|
-
import { Router as
|
|
24556
|
-
import { mkdir as
|
|
24557
|
-
import { resolve as resolve43, dirname as
|
|
25252
|
+
import { Router as Router21 } from "express";
|
|
25253
|
+
import { mkdir as mkdir7, readFile as readFile31, rename as rename7 } from "fs/promises";
|
|
25254
|
+
import { resolve as resolve43, dirname as dirname12 } from "path";
|
|
24558
25255
|
init_api();
|
|
24559
25256
|
var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
24560
25257
|
function touchItem4(item) {
|
|
@@ -24575,7 +25272,7 @@ async function projectExists(projectsDir, slug) {
|
|
|
24575
25272
|
async function ensureProjectTodosDir(projectsDir, slug) {
|
|
24576
25273
|
const todosDir2 = projectTodosDir(projectsDir, slug);
|
|
24577
25274
|
try {
|
|
24578
|
-
await
|
|
25275
|
+
await mkdir7(todosDir2, { recursive: false });
|
|
24579
25276
|
} catch (err) {
|
|
24580
25277
|
const code = err.code;
|
|
24581
25278
|
if (code === "EEXIST") return;
|
|
@@ -24587,7 +25284,7 @@ async function ensureProjectTodosDir(projectsDir, slug) {
|
|
|
24587
25284
|
throw err;
|
|
24588
25285
|
}
|
|
24589
25286
|
try {
|
|
24590
|
-
await
|
|
25287
|
+
await mkdir7(resolve43(todosDir2, "archive"), { recursive: false });
|
|
24591
25288
|
} catch (err) {
|
|
24592
25289
|
const code = err.code;
|
|
24593
25290
|
if (code === "EEXIST") return;
|
|
@@ -24603,7 +25300,7 @@ function notFound(res, slug) {
|
|
|
24603
25300
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
24604
25301
|
}
|
|
24605
25302
|
function createProjectTodosRouter(projectsDir, broadcast, workspaceTodosDir) {
|
|
24606
|
-
const router =
|
|
25303
|
+
const router = Router21({ mergeParams: true });
|
|
24607
25304
|
installRecordsInvalidation(router);
|
|
24608
25305
|
function broadcastUpdate(projectSlug) {
|
|
24609
25306
|
broadcast({ type: "todos-updated", projectSlug, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -24795,7 +25492,7 @@ function createProjectTodosRouter(projectsDir, broadcast, workspaceTodosDir) {
|
|
|
24795
25492
|
const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
|
|
24796
25493
|
let archContent = "";
|
|
24797
25494
|
if (await fileExists(archFile)) {
|
|
24798
|
-
archContent = await
|
|
25495
|
+
archContent = await readFile31(archFile, "utf-8");
|
|
24799
25496
|
archContent = archContent.trimEnd() + "\n\n";
|
|
24800
25497
|
} else {
|
|
24801
25498
|
archContent = `---
|
|
@@ -25276,7 +25973,7 @@ workspace: ${slug}
|
|
|
25276
25973
|
}
|
|
25277
25974
|
const assignmentMdPath = resolve43(assignmentDir, "assignment.md");
|
|
25278
25975
|
if (!await fileExists(assignmentMdPath)) return { error: `Target assignment not found: ${assignmentMdPath}` };
|
|
25279
|
-
let content = await
|
|
25976
|
+
let content = await readFile31(assignmentMdPath, "utf-8");
|
|
25280
25977
|
content = appendTodosToAssignmentBody2(
|
|
25281
25978
|
content,
|
|
25282
25979
|
items.map((it) => ({
|
|
@@ -25415,7 +26112,7 @@ workspace: ${slug}
|
|
|
25415
26112
|
return { status: 409, error: "attachments already exist in target" };
|
|
25416
26113
|
}
|
|
25417
26114
|
if (item.planDir && newPlanDir) {
|
|
25418
|
-
await
|
|
26115
|
+
await mkdir7(dirname12(newPlanDir), { recursive: true });
|
|
25419
26116
|
await rename7(item.planDir, newPlanDir);
|
|
25420
26117
|
item.planDir = newPlanDir;
|
|
25421
26118
|
}
|
|
@@ -25478,8 +26175,8 @@ workspace: ${slug}
|
|
|
25478
26175
|
}
|
|
25479
26176
|
|
|
25480
26177
|
// src/dashboard/api-bundles.ts
|
|
25481
|
-
import { Router as
|
|
25482
|
-
import { readdir as
|
|
26178
|
+
import { Router as Router22 } from "express";
|
|
26179
|
+
import { readdir as readdir20 } from "fs/promises";
|
|
25483
26180
|
|
|
25484
26181
|
// src/todos/bundle-parser.ts
|
|
25485
26182
|
init_parser();
|
|
@@ -25487,7 +26184,7 @@ init_fs();
|
|
|
25487
26184
|
init_paths();
|
|
25488
26185
|
init_parser2();
|
|
25489
26186
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
25490
|
-
import { readFile as
|
|
26187
|
+
import { readFile as readFile32 } from "fs/promises";
|
|
25491
26188
|
var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
|
|
25492
26189
|
var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
|
|
25493
26190
|
var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
|
|
@@ -25554,7 +26251,7 @@ function parseBundles(content) {
|
|
|
25554
26251
|
async function readBundles(todosDir2) {
|
|
25555
26252
|
const path = bundlesPath(todosDir2);
|
|
25556
26253
|
if (!await fileExists(path)) return [];
|
|
25557
|
-
const content = await
|
|
26254
|
+
const content = await readFile32(path, "utf-8");
|
|
25558
26255
|
return parseBundles(content).bundles;
|
|
25559
26256
|
}
|
|
25560
26257
|
|
|
@@ -25589,7 +26286,7 @@ function annotate(bundle, items) {
|
|
|
25589
26286
|
}
|
|
25590
26287
|
function createBundlesRouter(todosDir2, broadcast) {
|
|
25591
26288
|
void broadcast;
|
|
25592
|
-
const router =
|
|
26289
|
+
const router = Router22();
|
|
25593
26290
|
function validateWorkspace(req2, res, next) {
|
|
25594
26291
|
const workspace = getWorkspaceParam2(req2.params.workspace);
|
|
25595
26292
|
if (workspace && !WORKSPACE_REGEX3.test(workspace)) {
|
|
@@ -25603,7 +26300,7 @@ function createBundlesRouter(todosDir2, broadcast) {
|
|
|
25603
26300
|
try {
|
|
25604
26301
|
await ensureDir(todosDir2);
|
|
25605
26302
|
const bundles = await readBundles(todosDir2);
|
|
25606
|
-
const workspaceFiles = await
|
|
26303
|
+
const workspaceFiles = await readdir20(todosDir2).catch(() => []);
|
|
25607
26304
|
const itemsByKey = /* @__PURE__ */ new Map();
|
|
25608
26305
|
for (const f of workspaceFiles) {
|
|
25609
26306
|
if (typeof f !== "string") continue;
|
|
@@ -25655,7 +26352,7 @@ function createBundlesRouter(todosDir2, broadcast) {
|
|
|
25655
26352
|
init_fs();
|
|
25656
26353
|
init_paths();
|
|
25657
26354
|
init_slug();
|
|
25658
|
-
import { Router as
|
|
26355
|
+
import { Router as Router23 } from "express";
|
|
25659
26356
|
import { resolve as resolve44 } from "path";
|
|
25660
26357
|
init_parser2();
|
|
25661
26358
|
function deriveStatus2(bundle, items) {
|
|
@@ -25685,7 +26382,7 @@ function notFound2(res, slug) {
|
|
|
25685
26382
|
}
|
|
25686
26383
|
function createProjectBundlesRouter(projectsDir, broadcast) {
|
|
25687
26384
|
void broadcast;
|
|
25688
|
-
const router =
|
|
26385
|
+
const router = Router23({ mergeParams: true });
|
|
25689
26386
|
function validateProjectId(req2, res, next) {
|
|
25690
26387
|
const slug = getProjectIdParam2(req2.params.projectId);
|
|
25691
26388
|
if (!slug || !isValidSlug(slug)) {
|
|
@@ -25719,7 +26416,7 @@ function createProjectBundlesRouter(projectsDir, broadcast) {
|
|
|
25719
26416
|
init_config2();
|
|
25720
26417
|
init_api();
|
|
25721
26418
|
init_scanner();
|
|
25722
|
-
import { Router as
|
|
26419
|
+
import { Router as Router24 } from "express";
|
|
25723
26420
|
|
|
25724
26421
|
// src/utils/github-backup.ts
|
|
25725
26422
|
init_paths();
|
|
@@ -25727,8 +26424,8 @@ init_fs();
|
|
|
25727
26424
|
init_config2();
|
|
25728
26425
|
import { execFile as execFile2 } from "child_process";
|
|
25729
26426
|
import { promisify as promisify2 } from "util";
|
|
25730
|
-
import { cp as cp2, mkdtemp, rm as rm5, readFile as
|
|
25731
|
-
import { resolve as resolve45, join as
|
|
26427
|
+
import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile33, writeFile as writeFile8, unlink as unlink8, stat as stat8, open as open5, rename as rename8 } from "fs/promises";
|
|
26428
|
+
import { resolve as resolve45, join as join13 } from "path";
|
|
25732
26429
|
import { tmpdir } from "os";
|
|
25733
26430
|
var exec2 = promisify2(execFile2);
|
|
25734
26431
|
var VALID_CATEGORIES = ["projects", "playbooks", "todos", "servers", "config"];
|
|
@@ -25788,7 +26485,7 @@ async function acquireLock2() {
|
|
|
25788
26485
|
return lockPath;
|
|
25789
26486
|
} catch (err) {
|
|
25790
26487
|
if (err.code === "EEXIST") {
|
|
25791
|
-
const pid = await
|
|
26488
|
+
const pid = await readFile33(lockPath, "utf-8").catch(() => "");
|
|
25792
26489
|
throw new Error(
|
|
25793
26490
|
`Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
|
|
25794
26491
|
);
|
|
@@ -25821,7 +26518,7 @@ async function cloneOrInit(repoUrl, destDir) {
|
|
|
25821
26518
|
}
|
|
25822
26519
|
async function copyRecursive(src, dest) {
|
|
25823
26520
|
if (!await fileExists(src)) return;
|
|
25824
|
-
const s = await
|
|
26521
|
+
const s = await stat8(src);
|
|
25825
26522
|
if (s.isDirectory()) {
|
|
25826
26523
|
await ensureDir(dest);
|
|
25827
26524
|
await cp2(src, dest, { recursive: true, force: true });
|
|
@@ -25835,7 +26532,7 @@ function resolveCategoriesStrict(csv) {
|
|
|
25835
26532
|
return parseCategoriesStrict(parts);
|
|
25836
26533
|
}
|
|
25837
26534
|
async function readSanitizedConfig(configPath) {
|
|
25838
|
-
const content = await
|
|
26535
|
+
const content = await readFile33(configPath, "utf-8");
|
|
25839
26536
|
return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
|
|
25840
26537
|
}
|
|
25841
26538
|
async function backupToGithub(overrides) {
|
|
@@ -25858,11 +26555,11 @@ async function backupToGithub(overrides) {
|
|
|
25858
26555
|
let tmpDir = null;
|
|
25859
26556
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
25860
26557
|
try {
|
|
25861
|
-
tmpDir = await mkdtemp(
|
|
26558
|
+
tmpDir = await mkdtemp(join13(tmpdir(), "syntaur-backup-"));
|
|
25862
26559
|
await cloneOrInit(repo, tmpDir);
|
|
25863
26560
|
for (const category of categories) {
|
|
25864
26561
|
const { sourcePath, repoPath, isFile } = await resolveCategoryPath(category);
|
|
25865
|
-
const destPath =
|
|
26562
|
+
const destPath = join13(tmpDir, repoPath);
|
|
25866
26563
|
if (isFile) {
|
|
25867
26564
|
await rm5(destPath, { force: true });
|
|
25868
26565
|
} else {
|
|
@@ -25875,7 +26572,7 @@ async function backupToGithub(overrides) {
|
|
|
25875
26572
|
if (category === "config") {
|
|
25876
26573
|
const sanitized = await readSanitizedConfig(sourcePath);
|
|
25877
26574
|
await ensureDir(resolve45(destPath, ".."));
|
|
25878
|
-
await
|
|
26575
|
+
await writeFile8(destPath, sanitized, "utf-8");
|
|
25879
26576
|
} else {
|
|
25880
26577
|
await copyRecursive(sourcePath, destPath);
|
|
25881
26578
|
}
|
|
@@ -25990,7 +26687,7 @@ async function restoreFromGithub(overrides) {
|
|
|
25990
26687
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
25991
26688
|
try {
|
|
25992
26689
|
await updateBackupConfig({ lastRestore: timestamp });
|
|
25993
|
-
tmpDir = await mkdtemp(
|
|
26690
|
+
tmpDir = await mkdtemp(join13(tmpdir(), "syntaur-restore-"));
|
|
25994
26691
|
await cloneOrInit(repo, tmpDir);
|
|
25995
26692
|
for (const category of categories) {
|
|
25996
26693
|
if (category === "config") {
|
|
@@ -25999,7 +26696,7 @@ async function restoreFromGithub(overrides) {
|
|
|
25999
26696
|
}
|
|
26000
26697
|
try {
|
|
26001
26698
|
const { sourcePath: localPath, repoPath, isFile } = await resolveCategoryPath(category);
|
|
26002
|
-
const repoSrcPath =
|
|
26699
|
+
const repoSrcPath = join13(tmpDir, repoPath);
|
|
26003
26700
|
if (!await fileExists(repoSrcPath)) {
|
|
26004
26701
|
console.warn(`Category "${category}" not found in backup repo, skipping.`);
|
|
26005
26702
|
continue;
|
|
@@ -26042,7 +26739,7 @@ async function getBackupStatus() {
|
|
|
26042
26739
|
|
|
26043
26740
|
// src/dashboard/api-backup.ts
|
|
26044
26741
|
function createBackupRouter() {
|
|
26045
|
-
const router =
|
|
26742
|
+
const router = Router24();
|
|
26046
26743
|
router.get("/", async (_req, res) => {
|
|
26047
26744
|
try {
|
|
26048
26745
|
const status = await getBackupStatus();
|
|
@@ -27410,6 +28107,7 @@ function createDashboardServer(options) {
|
|
|
27410
28107
|
app.use("/api", createInboxRouter(projectsDir, assignmentsDir2));
|
|
27411
28108
|
app.use("/api/agent-sessions", createAgentSessionsRouter(projectsDir, broadcast, assignmentsDir2));
|
|
27412
28109
|
app.use("/api/config/agents", createAgentsRouter());
|
|
28110
|
+
app.use("/api/config/agent-discovery", createAgentDiscoveryConfigRouter());
|
|
27413
28111
|
app.use(
|
|
27414
28112
|
"/api/launch",
|
|
27415
28113
|
createLaunchPreflightRouter(projectsDir, assignmentsDir2)
|
|
@@ -27607,7 +28305,7 @@ function createDashboardServer(options) {
|
|
|
27607
28305
|
});
|
|
27608
28306
|
server.listen(port, () => {
|
|
27609
28307
|
const portFile = resolve48(syntaurRoot(), "dashboard-port");
|
|
27610
|
-
|
|
28308
|
+
writeFile9(portFile, String(port), "utf-8").catch(() => {
|
|
27611
28309
|
});
|
|
27612
28310
|
resolvePromise();
|
|
27613
28311
|
});
|