syntaur 0.74.0 → 0.76.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-CBNQaqzq.js → _basePickBy-BiwSunXO.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-Bqwz8hnN.js → _baseUniq-DsjKPXxP.js} +1 -1
- package/dashboard/dist/assets/{arc-B6lVW0Fc.js → arc-DlEs4wsF.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-eKpHkGki.js → architectureDiagram-2XIMDMQ5-DPvCrO57.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-D3sd7ccQ.js → blockDiagram-WCTKOSBZ-CpNdCY-r.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-0qW21hVQ.js → c4Diagram-IC4MRINW-CC29QJCs.js} +1 -1
- package/dashboard/dist/assets/channel-IutbH48W.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-tDdoFSYw.js → chunk-4BX2VUAB-DKyy-k2D.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-BU45eS64.js → chunk-55IACEB6-DjobngQk.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-C0iT8QWM.js → chunk-FMBD7UC4-BUftm7dV.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-CELv9Y6Y.js → chunk-JSJVCQXG-Nv7rQv4t.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-CQA_hH8g.js → chunk-KX2RTZJC-Dg6UhNB9.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-BDv-d6qz.js → chunk-NQ4KR5QH-D8neQrJ_.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-BUOeJd34.js → chunk-QZHKN3VN-Uj9c1naA.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-DBc-Hsx0.js → chunk-WL4C6EOR-DfeSsP3z.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-8IywRj9I.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-8IywRj9I.js +1 -0
- package/dashboard/dist/assets/clone-C6saGiCd.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-C_Q4c3xo.js → cose-bilkent-S5V4N54A-CLfZNKcO.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-DdI10bBF.js → dagre-KLK3FWXG-DyJLICZx.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-DtCF4Ea9.js → diagram-E7M64L7V-DhQc7Ta3.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-BVmpBmIK.js → diagram-IFDJBPK2-C6kFO7hB.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-BH4XFc0W.js → diagram-P4PSJMXO-B6oCiOXk.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-CCjG3Ewp.js → erDiagram-INFDFZHY-BsMPaSwJ.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-V0PT07-Y.js → flowDiagram-PKNHOUZH-DLxEqKtZ.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-y_85pJZf.js → ganttDiagram-A5KZAMGK-D70pvqxG.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-B2maPPcL.js → gitGraphDiagram-K3NZZRJ6-D74VHtlE.js} +1 -1
- package/dashboard/dist/assets/{graph-DO1oCTrt.js → graph-C8bz_IEO.js} +1 -1
- package/dashboard/dist/assets/{index-BZwPAi8K.css → index-BetqH_3H.css} +1 -1
- package/dashboard/dist/assets/index-h-X_8om3.js +675 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-BIaqpTlK.js → infoDiagram-LFFYTUFH-DivBd0Pz.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BaBqTUzg.js → ishikawaDiagram-PHBUUO56-BGihUeOE.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-CS2l7gmE.js → journeyDiagram-4ABVD52K-C7U4cWHQ.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-B3Viag1o.js → kanban-definition-K7BYSVSG-CFp_hDTm.js} +1 -1
- package/dashboard/dist/assets/{layout-BQu5nxr5.js → layout-DMSDPqCs.js} +1 -1
- package/dashboard/dist/assets/{linear-DliLQN5_.js → linear-CCC-meYc.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-y_G2N_4o.js → mermaid.core-BDa2cc2K.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-EZz-bEA_.js → mindmap-definition-YRQLILUH-DLKvONhO.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-E67ZAs-E.js → pieDiagram-SKSYHLDU-DAJoGZ93.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-GU25jLyr.js → quadrantDiagram-337W2JSQ-Bo7haX1W.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-4Jfq0mB9.js → requirementDiagram-Z7DCOOCP-uEvGz33v.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-C22SY4ul.js → sankeyDiagram-WA2Y5GQK-cjYoCGCM.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CkBh7V9t.js → sequenceDiagram-2WXFIKYE-CYXcNklt.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-n4DRlbfF.js → stateDiagram-RAJIS63D-B5We7jAw.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-CX9-EpBi.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-CGD5ojK7.js → timeline-definition-YZTLITO2-CEqVmnVQ.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-EZfHxNz6.js → treemap-KZPCXAKY-CE1puPeN.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-B9x6fx-l.js → vennDiagram-LZ73GAT5-CiiuRUjK.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-ufzpkH1I.js → xychartDiagram-JWTSCODW-B0OuxFm9.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +509 -221
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +1210 -823
- package/dist/index.js.map +1 -1
- package/dist/launch/index.d.ts +69 -7
- package/dist/launch/index.js +229 -24
- 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-BDsImlpM.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-BhdNnCQ2.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BhdNnCQ2.js +0 -1
- package/dashboard/dist/assets/clone-B5RwQsX7.js +0 -1
- package/dashboard/dist/assets/index-CG8rQF5o.js +0 -670
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-RpTmT3-9.js +0 -1
package/dist/dashboard/server.js
CHANGED
|
@@ -2543,6 +2543,10 @@ function modelFlagArgs(agent) {
|
|
|
2543
2543
|
const m = agent.model?.trim();
|
|
2544
2544
|
return m ? ["--model", m] : [];
|
|
2545
2545
|
}
|
|
2546
|
+
function agentNameArgs(agent) {
|
|
2547
|
+
const n = agent.agentName?.trim();
|
|
2548
|
+
return n ? ["--agent", n] : [];
|
|
2549
|
+
}
|
|
2546
2550
|
function stripModelFlags(args) {
|
|
2547
2551
|
const out = [];
|
|
2548
2552
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -3898,6 +3902,26 @@ function validateAgentList(agents) {
|
|
|
3898
3902
|
`agent "${agent.id}" has invalid launchPrompt \u2014 must be a single line (no newlines)`
|
|
3899
3903
|
);
|
|
3900
3904
|
}
|
|
3905
|
+
if (agent.agentName !== void 0 && /[\r\n]/.test(agent.agentName)) {
|
|
3906
|
+
throw new AgentConfigError(
|
|
3907
|
+
`agent "${agent.id}" has invalid agentName \u2014 must be a single line (no newlines)`
|
|
3908
|
+
);
|
|
3909
|
+
}
|
|
3910
|
+
if (agent.workdir !== void 0 && /[\r\n]/.test(agent.workdir)) {
|
|
3911
|
+
throw new AgentConfigError(
|
|
3912
|
+
`agent "${agent.id}" has invalid workdir \u2014 must be a single line (no newlines)`
|
|
3913
|
+
);
|
|
3914
|
+
}
|
|
3915
|
+
if (agent.agentName !== void 0 && agent.agentName.trim() !== "" && agent.workdir !== void 0 && agent.workdir.trim() !== "") {
|
|
3916
|
+
throw new AgentConfigError(
|
|
3917
|
+
`agent "${agent.id}" sets both agentName and workdir \u2014 these are mutually exclusive`
|
|
3918
|
+
);
|
|
3919
|
+
}
|
|
3920
|
+
if (agent.agentName !== void 0 && agent.agentName.trim() !== "" && agent.model !== void 0 && agent.model.trim() !== "") {
|
|
3921
|
+
throw new AgentConfigError(
|
|
3922
|
+
`agent "${agent.id}" sets both agentName and model \u2014 the agent definition's own model is authoritative; remove the profile model`
|
|
3923
|
+
);
|
|
3924
|
+
}
|
|
3901
3925
|
validateSessionInvocation(agent, "resume", agent.resume);
|
|
3902
3926
|
validateSessionInvocation(agent, "fork", agent.fork);
|
|
3903
3927
|
if (agent.default) defaults++;
|
|
@@ -4718,6 +4742,8 @@ function parseAgentsConfig(content) {
|
|
|
4718
4742
|
...current.model ? { model: current.model } : {},
|
|
4719
4743
|
...current.playbook ? { playbook: current.playbook } : {},
|
|
4720
4744
|
...current.launchPrompt ? { launchPrompt: current.launchPrompt } : {},
|
|
4745
|
+
...current.agentName ? { agentName: current.agentName } : {},
|
|
4746
|
+
...current.workdir ? { workdir: current.workdir } : {},
|
|
4721
4747
|
...current.resume ? { resume: current.resume } : {},
|
|
4722
4748
|
...current.fork ? { fork: current.fork } : {}
|
|
4723
4749
|
});
|
|
@@ -4909,6 +4935,12 @@ function assignAgentField(target, key, rawValue) {
|
|
|
4909
4935
|
case "launchPrompt":
|
|
4910
4936
|
target.launchPrompt = value;
|
|
4911
4937
|
break;
|
|
4938
|
+
case "agentName":
|
|
4939
|
+
target.agentName = value;
|
|
4940
|
+
break;
|
|
4941
|
+
case "workdir":
|
|
4942
|
+
target.workdir = value;
|
|
4943
|
+
break;
|
|
4912
4944
|
}
|
|
4913
4945
|
}
|
|
4914
4946
|
function yamlQuoteScalar(value) {
|
|
@@ -4938,6 +4970,12 @@ function serializeAgentsConfig(agents) {
|
|
|
4938
4970
|
if (a.launchPrompt) {
|
|
4939
4971
|
lines.push(` launchPrompt: ${yamlQuoteScalar(a.launchPrompt)}`);
|
|
4940
4972
|
}
|
|
4973
|
+
if (a.agentName) {
|
|
4974
|
+
lines.push(` agentName: ${yamlQuoteScalar(a.agentName)}`);
|
|
4975
|
+
}
|
|
4976
|
+
if (a.workdir) {
|
|
4977
|
+
lines.push(` workdir: ${yamlQuoteScalar(a.workdir)}`);
|
|
4978
|
+
}
|
|
4941
4979
|
if (a.args && a.args.length > 0) {
|
|
4942
4980
|
lines.push(` args:`);
|
|
4943
4981
|
for (const arg of a.args) {
|
|
@@ -5585,7 +5623,9 @@ var init_config2 = __esm({
|
|
|
5585
5623
|
"resolveFromShellAliases",
|
|
5586
5624
|
"model",
|
|
5587
5625
|
"playbook",
|
|
5588
|
-
"launchPrompt"
|
|
5626
|
+
"launchPrompt",
|
|
5627
|
+
"agentName",
|
|
5628
|
+
"workdir"
|
|
5589
5629
|
]);
|
|
5590
5630
|
migratedConfigPaths = /* @__PURE__ */ new Set();
|
|
5591
5631
|
TerminalConfigError = class extends Error {
|
|
@@ -6013,6 +6053,21 @@ function resolveWorkspaceCwd(input) {
|
|
|
6013
6053
|
invalidReason: `workspace path invalid for ${assignmentSlug}: tried worktreePath ${shown(worktreePath)} and repository ${shown(repository)} \u2014 neither is an existing directory`
|
|
6014
6054
|
};
|
|
6015
6055
|
}
|
|
6056
|
+
function resolveLaunchCwd(agent, worktreeCwd) {
|
|
6057
|
+
const wd = agent.workdir?.trim();
|
|
6058
|
+
if (wd) {
|
|
6059
|
+
const expanded = expandHome(wd);
|
|
6060
|
+
if (!isExistingDir(expanded)) {
|
|
6061
|
+
return {
|
|
6062
|
+
spawnCwd: worktreeCwd,
|
|
6063
|
+
worktreePath: worktreeCwd,
|
|
6064
|
+
invalidReason: `agent "${agent.id}" workdir ${agent.workdir} (resolved ${expanded}) is not an existing directory`
|
|
6065
|
+
};
|
|
6066
|
+
}
|
|
6067
|
+
return { spawnCwd: expanded, worktreePath: worktreeCwd, invalidReason: null };
|
|
6068
|
+
}
|
|
6069
|
+
return { spawnCwd: worktreeCwd, worktreePath: worktreeCwd, invalidReason: null };
|
|
6070
|
+
}
|
|
6016
6071
|
function formatFallbackCwdWarning(opts) {
|
|
6017
6072
|
const missing = [];
|
|
6018
6073
|
if (!opts.worktreePath) missing.push("worktreePath");
|
|
@@ -6024,6 +6079,7 @@ function formatFallbackCwdWarning(opts) {
|
|
|
6024
6079
|
var init_cwd = __esm({
|
|
6025
6080
|
"src/launch/cwd.ts"() {
|
|
6026
6081
|
"use strict";
|
|
6082
|
+
init_paths();
|
|
6027
6083
|
}
|
|
6028
6084
|
});
|
|
6029
6085
|
|
|
@@ -7857,8 +7913,8 @@ async function migrateFromMarkdown(projectsDir) {
|
|
|
7857
7913
|
return allSessions.length;
|
|
7858
7914
|
}
|
|
7859
7915
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
7860
|
-
const { readFile:
|
|
7861
|
-
const raw2 = await
|
|
7916
|
+
const { readFile: readFile32 } = await import("fs/promises");
|
|
7917
|
+
const raw2 = await readFile32(filePath, "utf-8");
|
|
7862
7918
|
const sessions = [];
|
|
7863
7919
|
const lines = raw2.split("\n");
|
|
7864
7920
|
let inTable = false;
|
|
@@ -12472,69 +12528,6 @@ var init_session_id = __esm({
|
|
|
12472
12528
|
}
|
|
12473
12529
|
});
|
|
12474
12530
|
|
|
12475
|
-
// src/utils/assignment-todos.ts
|
|
12476
|
-
var assignment_todos_exports = {};
|
|
12477
|
-
__export(assignment_todos_exports, {
|
|
12478
|
-
appendTodosToAssignmentBody: () => appendTodosToAssignmentBody,
|
|
12479
|
-
touchAssignmentUpdated: () => touchAssignmentUpdated
|
|
12480
|
-
});
|
|
12481
|
-
function setTopLevelField2(content, key, value) {
|
|
12482
|
-
const fieldRegex = new RegExp(`^(${key}:)\\s*.*$`, "m");
|
|
12483
|
-
if (fieldRegex.test(content)) {
|
|
12484
|
-
return content.replace(fieldRegex, `$1 ${value}`);
|
|
12485
|
-
}
|
|
12486
|
-
return content;
|
|
12487
|
-
}
|
|
12488
|
-
function appendTodosToAssignmentBody(body, todos) {
|
|
12489
|
-
if (todos.length === 0) return body;
|
|
12490
|
-
const lines = todos.map((t) => {
|
|
12491
|
-
const base = `- [ ] ${t.description.trim()}`;
|
|
12492
|
-
return t.trace ? `${base} <!-- ${t.trace} -->` : base;
|
|
12493
|
-
});
|
|
12494
|
-
const block = lines.join("\n");
|
|
12495
|
-
const todosHeading = /^## Todos\s*$/m;
|
|
12496
|
-
if (todosHeading.test(body)) {
|
|
12497
|
-
return body.replace(
|
|
12498
|
-
/(^## Todos[\s\S]*?)(\n## |\n*$)/m,
|
|
12499
|
-
(_m, section, nextHeading) => {
|
|
12500
|
-
return `${section.trimEnd()}
|
|
12501
|
-
${block}
|
|
12502
|
-
${nextHeading}`;
|
|
12503
|
-
}
|
|
12504
|
-
);
|
|
12505
|
-
}
|
|
12506
|
-
return `${body.trimEnd()}
|
|
12507
|
-
|
|
12508
|
-
## Todos
|
|
12509
|
-
|
|
12510
|
-
${block}
|
|
12511
|
-
`;
|
|
12512
|
-
}
|
|
12513
|
-
function touchAssignmentUpdated(content, timestamp) {
|
|
12514
|
-
return setTopLevelField2(content, "updated", `"${timestamp}"`);
|
|
12515
|
-
}
|
|
12516
|
-
var init_assignment_todos = __esm({
|
|
12517
|
-
"src/utils/assignment-todos.ts"() {
|
|
12518
|
-
"use strict";
|
|
12519
|
-
}
|
|
12520
|
-
});
|
|
12521
|
-
|
|
12522
|
-
// src/utils/path-canon.ts
|
|
12523
|
-
import { realpathSync as realpathSync2 } from "fs";
|
|
12524
|
-
import { resolve as resolve44 } from "path";
|
|
12525
|
-
function canonicalPath(p) {
|
|
12526
|
-
try {
|
|
12527
|
-
return realpathSync2(resolve44(p));
|
|
12528
|
-
} catch {
|
|
12529
|
-
return resolve44(p).replace(/\/+$/, "");
|
|
12530
|
-
}
|
|
12531
|
-
}
|
|
12532
|
-
var init_path_canon = __esm({
|
|
12533
|
-
"src/utils/path-canon.ts"() {
|
|
12534
|
-
"use strict";
|
|
12535
|
-
}
|
|
12536
|
-
});
|
|
12537
|
-
|
|
12538
12531
|
// src/targets/renderers.ts
|
|
12539
12532
|
var RENDERERS;
|
|
12540
12533
|
var init_renderers = __esm({
|
|
@@ -12555,8 +12548,8 @@ var init_renderers = __esm({
|
|
|
12555
12548
|
});
|
|
12556
12549
|
|
|
12557
12550
|
// src/targets/user-descriptors.ts
|
|
12558
|
-
import { resolve as
|
|
12559
|
-
import { readFile as
|
|
12551
|
+
import { resolve as resolve26 } from "path";
|
|
12552
|
+
import { readFile as readFile19, readdir as readdir14 } from "fs/promises";
|
|
12560
12553
|
var VALID_RENDERER_KEYS;
|
|
12561
12554
|
var init_user_descriptors = __esm({
|
|
12562
12555
|
"src/targets/user-descriptors.ts"() {
|
|
@@ -12569,21 +12562,21 @@ var init_user_descriptors = __esm({
|
|
|
12569
12562
|
});
|
|
12570
12563
|
|
|
12571
12564
|
// src/targets/registry.ts
|
|
12572
|
-
import { homedir as
|
|
12573
|
-
import { join as
|
|
12565
|
+
import { homedir as homedir4 } from "os";
|
|
12566
|
+
import { join as join5, resolve as resolve27 } from "path";
|
|
12574
12567
|
function home(...segments) {
|
|
12575
|
-
return
|
|
12568
|
+
return resolve27(homedir4(), ...segments);
|
|
12576
12569
|
}
|
|
12577
12570
|
function hermesHome() {
|
|
12578
12571
|
const env = process.env.HERMES_HOME;
|
|
12579
|
-
return env && env.length > 0 ?
|
|
12572
|
+
return env && env.length > 0 ? resolve27(env) : home(".hermes");
|
|
12580
12573
|
}
|
|
12581
12574
|
function hermesSkillsDir() {
|
|
12582
|
-
return
|
|
12575
|
+
return resolve27(hermesHome(), "skills");
|
|
12583
12576
|
}
|
|
12584
12577
|
function codexHome() {
|
|
12585
12578
|
const env = process.env.CODEX_HOME;
|
|
12586
|
-
return env && env.length > 0 ?
|
|
12579
|
+
return env && env.length > 0 ? resolve27(env) : home(".codex");
|
|
12587
12580
|
}
|
|
12588
12581
|
function toDiscovered(meta) {
|
|
12589
12582
|
if (!meta) return null;
|
|
@@ -12595,6 +12588,9 @@ function toDiscovered(meta) {
|
|
|
12595
12588
|
transcriptPath: meta.path
|
|
12596
12589
|
};
|
|
12597
12590
|
}
|
|
12591
|
+
function getAgentTarget(id) {
|
|
12592
|
+
return AGENT_TARGETS_BY_ID[id];
|
|
12593
|
+
}
|
|
12598
12594
|
var detectDir, claudeSessions, codexSessions, piSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
|
|
12599
12595
|
var init_registry = __esm({
|
|
12600
12596
|
"src/targets/registry.ts"() {
|
|
@@ -12604,7 +12600,7 @@ var init_registry = __esm({
|
|
|
12604
12600
|
init_user_descriptors();
|
|
12605
12601
|
detectDir = (dir) => () => fileExists(dir);
|
|
12606
12602
|
claudeSessions = {
|
|
12607
|
-
globs: (root) => [
|
|
12603
|
+
globs: (root) => [join5(root ?? home(".claude", "projects"), "*", "*.jsonl")],
|
|
12608
12604
|
parse: async (file) => toDiscovered(await extractClaudeSessionMeta(file)),
|
|
12609
12605
|
walk: async function* (opts = {}) {
|
|
12610
12606
|
for await (const meta of walkClaudeProjects({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
|
|
@@ -12614,7 +12610,7 @@ var init_registry = __esm({
|
|
|
12614
12610
|
}
|
|
12615
12611
|
};
|
|
12616
12612
|
codexSessions = {
|
|
12617
|
-
globs: (root) => [
|
|
12613
|
+
globs: (root) => [join5(root ?? resolveCodexSessionsRoot(), "**", "*.jsonl")],
|
|
12618
12614
|
parse: async (file) => toDiscovered(await extractCodexSessionMeta(file)),
|
|
12619
12615
|
walk: async function* (opts = {}) {
|
|
12620
12616
|
for await (const meta of walkCodexSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
|
|
@@ -12624,7 +12620,7 @@ var init_registry = __esm({
|
|
|
12624
12620
|
}
|
|
12625
12621
|
};
|
|
12626
12622
|
piSessions = {
|
|
12627
|
-
globs: (root) => [
|
|
12623
|
+
globs: (root) => [join5(root ?? resolvePiSessionsRoot(), "*", "*.jsonl")],
|
|
12628
12624
|
parse: async (file) => toDiscovered(await extractPiSessionMeta(file)),
|
|
12629
12625
|
walk: async function* (opts = {}) {
|
|
12630
12626
|
for await (const meta of walkPiSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
|
|
@@ -12654,7 +12650,7 @@ var init_registry = __esm({
|
|
|
12654
12650
|
skillsShAgentId: "codex",
|
|
12655
12651
|
nativePlugin: "codex",
|
|
12656
12652
|
detect: detectDir(codexHome()),
|
|
12657
|
-
skillsDir: { global:
|
|
12653
|
+
skillsDir: { global: resolve27(codexHome(), "skills") },
|
|
12658
12654
|
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
|
|
12659
12655
|
sessions: codexSessions
|
|
12660
12656
|
},
|
|
@@ -12680,7 +12676,8 @@ var init_registry = __esm({
|
|
|
12680
12676
|
nativePlugin: "claude",
|
|
12681
12677
|
detect: detectDir(home(".claude")),
|
|
12682
12678
|
skillsDir: { global: home(".claude", "skills") },
|
|
12683
|
-
sessions: claudeSessions
|
|
12679
|
+
sessions: claudeSessions,
|
|
12680
|
+
agentsDir: home(".claude", "agents")
|
|
12684
12681
|
},
|
|
12685
12682
|
{
|
|
12686
12683
|
id: "pi",
|
|
@@ -12723,7 +12720,7 @@ var init_registry = __esm({
|
|
|
12723
12720
|
tier3: {
|
|
12724
12721
|
kind: "hermes-plugin",
|
|
12725
12722
|
source: "platforms/hermes/plugins/syntaur",
|
|
12726
|
-
installDir: () =>
|
|
12723
|
+
installDir: () => resolve27(hermesHome(), "plugins", "syntaur"),
|
|
12727
12724
|
entry: "plugin.yaml"
|
|
12728
12725
|
}
|
|
12729
12726
|
}
|
|
@@ -12734,6 +12731,69 @@ var init_registry = __esm({
|
|
|
12734
12731
|
}
|
|
12735
12732
|
});
|
|
12736
12733
|
|
|
12734
|
+
// src/utils/assignment-todos.ts
|
|
12735
|
+
var assignment_todos_exports = {};
|
|
12736
|
+
__export(assignment_todos_exports, {
|
|
12737
|
+
appendTodosToAssignmentBody: () => appendTodosToAssignmentBody,
|
|
12738
|
+
touchAssignmentUpdated: () => touchAssignmentUpdated
|
|
12739
|
+
});
|
|
12740
|
+
function setTopLevelField2(content, key, value) {
|
|
12741
|
+
const fieldRegex = new RegExp(`^(${key}:)\\s*.*$`, "m");
|
|
12742
|
+
if (fieldRegex.test(content)) {
|
|
12743
|
+
return content.replace(fieldRegex, `$1 ${value}`);
|
|
12744
|
+
}
|
|
12745
|
+
return content;
|
|
12746
|
+
}
|
|
12747
|
+
function appendTodosToAssignmentBody(body, todos) {
|
|
12748
|
+
if (todos.length === 0) return body;
|
|
12749
|
+
const lines = todos.map((t) => {
|
|
12750
|
+
const base = `- [ ] ${t.description.trim()}`;
|
|
12751
|
+
return t.trace ? `${base} <!-- ${t.trace} -->` : base;
|
|
12752
|
+
});
|
|
12753
|
+
const block = lines.join("\n");
|
|
12754
|
+
const todosHeading = /^## Todos\s*$/m;
|
|
12755
|
+
if (todosHeading.test(body)) {
|
|
12756
|
+
return body.replace(
|
|
12757
|
+
/(^## Todos[\s\S]*?)(\n## |\n*$)/m,
|
|
12758
|
+
(_m, section, nextHeading) => {
|
|
12759
|
+
return `${section.trimEnd()}
|
|
12760
|
+
${block}
|
|
12761
|
+
${nextHeading}`;
|
|
12762
|
+
}
|
|
12763
|
+
);
|
|
12764
|
+
}
|
|
12765
|
+
return `${body.trimEnd()}
|
|
12766
|
+
|
|
12767
|
+
## Todos
|
|
12768
|
+
|
|
12769
|
+
${block}
|
|
12770
|
+
`;
|
|
12771
|
+
}
|
|
12772
|
+
function touchAssignmentUpdated(content, timestamp) {
|
|
12773
|
+
return setTopLevelField2(content, "updated", `"${timestamp}"`);
|
|
12774
|
+
}
|
|
12775
|
+
var init_assignment_todos = __esm({
|
|
12776
|
+
"src/utils/assignment-todos.ts"() {
|
|
12777
|
+
"use strict";
|
|
12778
|
+
}
|
|
12779
|
+
});
|
|
12780
|
+
|
|
12781
|
+
// src/utils/path-canon.ts
|
|
12782
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
12783
|
+
import { resolve as resolve46 } from "path";
|
|
12784
|
+
function canonicalPath(p) {
|
|
12785
|
+
try {
|
|
12786
|
+
return realpathSync2(resolve46(p));
|
|
12787
|
+
} catch {
|
|
12788
|
+
return resolve46(p).replace(/\/+$/, "");
|
|
12789
|
+
}
|
|
12790
|
+
}
|
|
12791
|
+
var init_path_canon = __esm({
|
|
12792
|
+
"src/utils/path-canon.ts"() {
|
|
12793
|
+
"use strict";
|
|
12794
|
+
}
|
|
12795
|
+
});
|
|
12796
|
+
|
|
12737
12797
|
// src/sessions/agent-view.ts
|
|
12738
12798
|
import { execFile as execFile3 } from "child_process";
|
|
12739
12799
|
import { promisify as promisify3 } from "util";
|
|
@@ -18143,6 +18203,55 @@ function createAgentSessionsRouter(projectsDir, broadcast, assignmentsDir2) {
|
|
|
18143
18203
|
init_config2();
|
|
18144
18204
|
init_agents_schema();
|
|
18145
18205
|
import { Router as Router5 } from "express";
|
|
18206
|
+
|
|
18207
|
+
// src/targets/agent-definitions.ts
|
|
18208
|
+
init_parser();
|
|
18209
|
+
init_registry();
|
|
18210
|
+
import { readdir as readdir15, readFile as readFile20 } from "fs/promises";
|
|
18211
|
+
import { join as join6 } from "path";
|
|
18212
|
+
async function collectMarkdown(dir, out) {
|
|
18213
|
+
let entries;
|
|
18214
|
+
try {
|
|
18215
|
+
entries = await readdir15(dir, { withFileTypes: true });
|
|
18216
|
+
} catch {
|
|
18217
|
+
return;
|
|
18218
|
+
}
|
|
18219
|
+
for (const entry of entries) {
|
|
18220
|
+
const full = join6(dir, entry.name);
|
|
18221
|
+
if (entry.isDirectory()) {
|
|
18222
|
+
await collectMarkdown(full, out);
|
|
18223
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
18224
|
+
out.push(full);
|
|
18225
|
+
}
|
|
18226
|
+
}
|
|
18227
|
+
}
|
|
18228
|
+
async function discoverClaudeAgents(root) {
|
|
18229
|
+
const dir = root ?? getAgentTarget("claude")?.agentsDir;
|
|
18230
|
+
if (!dir) return [];
|
|
18231
|
+
const files = [];
|
|
18232
|
+
await collectMarkdown(dir, files);
|
|
18233
|
+
files.sort();
|
|
18234
|
+
const byName = /* @__PURE__ */ new Map();
|
|
18235
|
+
for (const file of files) {
|
|
18236
|
+
let content;
|
|
18237
|
+
try {
|
|
18238
|
+
content = await readFile20(file, "utf-8");
|
|
18239
|
+
} catch {
|
|
18240
|
+
continue;
|
|
18241
|
+
}
|
|
18242
|
+
const [frontmatter] = extractFrontmatter2(content);
|
|
18243
|
+
if (!frontmatter) continue;
|
|
18244
|
+
const name = getField(frontmatter, "name")?.trim();
|
|
18245
|
+
if (!name) continue;
|
|
18246
|
+
if (byName.has(name)) continue;
|
|
18247
|
+
const description = getField(frontmatter, "description")?.trim() || void 0;
|
|
18248
|
+
const model = getField(frontmatter, "model")?.trim() || void 0;
|
|
18249
|
+
byName.set(name, { name, description, model, path: file });
|
|
18250
|
+
}
|
|
18251
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
18252
|
+
}
|
|
18253
|
+
|
|
18254
|
+
// src/dashboard/api-agents.ts
|
|
18146
18255
|
function mapAgentErrorToFieldErrors(err) {
|
|
18147
18256
|
const message = err.message;
|
|
18148
18257
|
let match = message.match(/^agent id "([^"]*)" is invalid/);
|
|
@@ -18230,6 +18339,38 @@ function mapAgentErrorToFieldErrors(err) {
|
|
|
18230
18339
|
]
|
|
18231
18340
|
};
|
|
18232
18341
|
}
|
|
18342
|
+
match = message.match(/^agent "([^"]+)" has invalid agentName/);
|
|
18343
|
+
if (match) {
|
|
18344
|
+
return {
|
|
18345
|
+
error: message,
|
|
18346
|
+
fieldErrors: [{ id: match[1], field: "agentName", message: "must be a single line" }]
|
|
18347
|
+
};
|
|
18348
|
+
}
|
|
18349
|
+
match = message.match(/^agent "([^"]+)" has invalid workdir/);
|
|
18350
|
+
if (match) {
|
|
18351
|
+
return {
|
|
18352
|
+
error: message,
|
|
18353
|
+
fieldErrors: [{ id: match[1], field: "workdir", message: "must be a single line" }]
|
|
18354
|
+
};
|
|
18355
|
+
}
|
|
18356
|
+
match = message.match(/^agent "([^"]+)" sets both agentName and workdir/);
|
|
18357
|
+
if (match) {
|
|
18358
|
+
return {
|
|
18359
|
+
error: message,
|
|
18360
|
+
fieldErrors: [
|
|
18361
|
+
{ id: match[1], field: "workdir", message: "agentName and workdir are mutually exclusive" }
|
|
18362
|
+
]
|
|
18363
|
+
};
|
|
18364
|
+
}
|
|
18365
|
+
match = message.match(/^agent "([^"]+)" sets both agentName and model/);
|
|
18366
|
+
if (match) {
|
|
18367
|
+
return {
|
|
18368
|
+
error: message,
|
|
18369
|
+
fieldErrors: [
|
|
18370
|
+
{ id: match[1], field: "model", message: "remove the profile model; the agent defines its own" }
|
|
18371
|
+
]
|
|
18372
|
+
};
|
|
18373
|
+
}
|
|
18233
18374
|
return { error: message };
|
|
18234
18375
|
}
|
|
18235
18376
|
function coerceAgentRow(raw2, index) {
|
|
@@ -18377,10 +18518,47 @@ function coerceAgentRow(raw2, index) {
|
|
|
18377
18518
|
}
|
|
18378
18519
|
if (entry.launchPrompt.trim()) cleaned.launchPrompt = entry.launchPrompt;
|
|
18379
18520
|
}
|
|
18521
|
+
if (entry.agentName !== void 0) {
|
|
18522
|
+
if (typeof entry.agentName !== "string") {
|
|
18523
|
+
return {
|
|
18524
|
+
ok: false,
|
|
18525
|
+
status: 400,
|
|
18526
|
+
body: {
|
|
18527
|
+
error: `agents[${index}].agentName must be a string`,
|
|
18528
|
+
fieldErrors: [{ id, field: "agentName", message: "agentName must be a string" }]
|
|
18529
|
+
}
|
|
18530
|
+
};
|
|
18531
|
+
}
|
|
18532
|
+
const agentName = entry.agentName.trim();
|
|
18533
|
+
if (agentName) cleaned.agentName = agentName;
|
|
18534
|
+
}
|
|
18535
|
+
if (entry.workdir !== void 0) {
|
|
18536
|
+
if (typeof entry.workdir !== "string") {
|
|
18537
|
+
return {
|
|
18538
|
+
ok: false,
|
|
18539
|
+
status: 400,
|
|
18540
|
+
body: {
|
|
18541
|
+
error: `agents[${index}].workdir must be a string`,
|
|
18542
|
+
fieldErrors: [{ id, field: "workdir", message: "workdir must be a string" }]
|
|
18543
|
+
}
|
|
18544
|
+
};
|
|
18545
|
+
}
|
|
18546
|
+
const workdir = entry.workdir.trim();
|
|
18547
|
+
if (workdir) cleaned.workdir = workdir;
|
|
18548
|
+
}
|
|
18380
18549
|
return { ok: true, value: cleaned };
|
|
18381
18550
|
}
|
|
18382
18551
|
function createAgentsRouter() {
|
|
18383
18552
|
const router = Router5();
|
|
18553
|
+
router.get("/claude-discovered", async (_req, res) => {
|
|
18554
|
+
try {
|
|
18555
|
+
const agents = await discoverClaudeAgents();
|
|
18556
|
+
res.json({ agents });
|
|
18557
|
+
} catch (err) {
|
|
18558
|
+
console.error("Error discovering Claude agents:", err);
|
|
18559
|
+
res.status(500).json({ error: "Failed to discover Claude agents" });
|
|
18560
|
+
}
|
|
18561
|
+
});
|
|
18384
18562
|
router.get("/", async (_req, res) => {
|
|
18385
18563
|
try {
|
|
18386
18564
|
const config = await readConfig();
|
|
@@ -18443,8 +18621,8 @@ import { Router as Router6 } from "express";
|
|
|
18443
18621
|
// src/utils/terminal-probe.ts
|
|
18444
18622
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
18445
18623
|
import { existsSync as existsSync2 } from "fs";
|
|
18446
|
-
import { homedir as
|
|
18447
|
-
import { join as
|
|
18624
|
+
import { homedir as homedir5 } from "os";
|
|
18625
|
+
import { join as join7 } from "path";
|
|
18448
18626
|
var APP_BUNDLE_IDS = {
|
|
18449
18627
|
"terminal-app": "com.apple.Terminal",
|
|
18450
18628
|
iterm: "com.googlecode.iterm2",
|
|
@@ -18461,7 +18639,7 @@ var APP_FIXED_PATHS = {
|
|
|
18461
18639
|
"terminal-app": "/System/Applications/Utilities/Terminal.app"
|
|
18462
18640
|
};
|
|
18463
18641
|
function defaultApplicationsDirs() {
|
|
18464
|
-
return ["/Applications",
|
|
18642
|
+
return ["/Applications", join7(homedir5(), "Applications")];
|
|
18465
18643
|
}
|
|
18466
18644
|
function findAppBundle(terminal, dirs = defaultApplicationsDirs()) {
|
|
18467
18645
|
const fixed = APP_FIXED_PATHS[terminal];
|
|
@@ -18469,7 +18647,7 @@ function findAppBundle(terminal, dirs = defaultApplicationsDirs()) {
|
|
|
18469
18647
|
const bundleName = APP_BUNDLE_NAMES[terminal];
|
|
18470
18648
|
if (bundleName) {
|
|
18471
18649
|
for (const dir of dirs) {
|
|
18472
|
-
const candidate =
|
|
18650
|
+
const candidate = join7(dir, bundleName);
|
|
18473
18651
|
if (existsSync2(candidate)) return candidate;
|
|
18474
18652
|
}
|
|
18475
18653
|
}
|
|
@@ -18499,11 +18677,11 @@ var defaultCmuxLsappinfoRunner = () => {
|
|
|
18499
18677
|
function resolveCmuxCli(applicationsDirsOverride, cliDirsOverride, lsappinfoRunnerOverride) {
|
|
18500
18678
|
const bundle = findAppBundle("cmux", applicationsDirsOverride);
|
|
18501
18679
|
if (bundle) {
|
|
18502
|
-
const cli =
|
|
18680
|
+
const cli = join7(bundle, "Contents/Resources/bin/cmux");
|
|
18503
18681
|
if (existsSync2(cli)) return cli;
|
|
18504
18682
|
}
|
|
18505
18683
|
for (const dir of cliDirsOverride ?? CMUX_CLI_DIRS) {
|
|
18506
|
-
const cli =
|
|
18684
|
+
const cli = join7(dir, "cmux");
|
|
18507
18685
|
if (existsSync2(cli)) return cli;
|
|
18508
18686
|
}
|
|
18509
18687
|
if (process.platform === "darwin") {
|
|
@@ -18512,7 +18690,7 @@ function resolveCmuxCli(applicationsDirsOverride, cliDirsOverride, lsappinfoRunn
|
|
|
18512
18690
|
if (stdout) {
|
|
18513
18691
|
const match = stdout.match(/"LSBundlePath"="([^"]+)"/);
|
|
18514
18692
|
if (match) {
|
|
18515
|
-
const cli =
|
|
18693
|
+
const cli = join7(match[1], "Contents/Resources/bin/cmux");
|
|
18516
18694
|
if (existsSync2(cli)) return cli;
|
|
18517
18695
|
}
|
|
18518
18696
|
}
|
|
@@ -18594,8 +18772,19 @@ function resolveTemplate(template, ctx) {
|
|
|
18594
18772
|
const warnings = [];
|
|
18595
18773
|
const prompt = template.replace(TOKEN_RE, (_match, boundary, token) => {
|
|
18596
18774
|
if (token === "assignment") {
|
|
18775
|
+
if (!ctx.assignmentDir) {
|
|
18776
|
+
warnings.push(`launchPrompt: "@assignment" has no assignment context here \u2014 left as literal text.`);
|
|
18777
|
+
return boundary + "@assignment";
|
|
18778
|
+
}
|
|
18597
18779
|
return boundary + assignmentPointer(ctx.id, ctx.assignmentDir);
|
|
18598
18780
|
}
|
|
18781
|
+
if (token === "worktree") {
|
|
18782
|
+
if (!ctx.worktreePath) {
|
|
18783
|
+
warnings.push(`launchPrompt: "@worktree" has no worktree path here \u2014 left as literal text.`);
|
|
18784
|
+
return boundary + "@worktree";
|
|
18785
|
+
}
|
|
18786
|
+
return boundary + ctx.worktreePath;
|
|
18787
|
+
}
|
|
18599
18788
|
if (!isValidSlug(token)) {
|
|
18600
18789
|
warnings.push(`launchPrompt: "@${token}" is not a valid playbook token \u2014 left as literal text.`);
|
|
18601
18790
|
return boundary + "@" + token;
|
|
@@ -18609,16 +18798,37 @@ function resolveTemplate(template, ctx) {
|
|
|
18609
18798
|
return { prompt, warnings };
|
|
18610
18799
|
}
|
|
18611
18800
|
function resolveLaunchPrompt(input) {
|
|
18612
|
-
const {
|
|
18801
|
+
const {
|
|
18802
|
+
template,
|
|
18803
|
+
playbook,
|
|
18804
|
+
id,
|
|
18805
|
+
assignmentDir,
|
|
18806
|
+
projectSlug,
|
|
18807
|
+
assignmentSlug,
|
|
18808
|
+
worktreePath,
|
|
18809
|
+
spawnCwd,
|
|
18810
|
+
knownPlaybookSlugs
|
|
18811
|
+
} = input;
|
|
18613
18812
|
if (template && template.trim()) {
|
|
18614
|
-
return resolveTemplate(template, { id, assignmentDir, knownPlaybookSlugs });
|
|
18813
|
+
return resolveTemplate(template, { id, assignmentDir, worktreePath, knownPlaybookSlugs });
|
|
18615
18814
|
}
|
|
18616
18815
|
const pb = playbook?.trim();
|
|
18617
18816
|
if (pb) {
|
|
18618
|
-
const
|
|
18619
|
-
|
|
18817
|
+
const clause = runPlaybookClause(pb);
|
|
18818
|
+
const prompt = assignmentDir ? `${assignmentPointer(id, assignmentDir)} Run ${clause} end-to-end.` : `Run ${clause} end-to-end.`;
|
|
18819
|
+
return { prompt, warnings: [] };
|
|
18820
|
+
}
|
|
18821
|
+
if (worktreePath && spawnCwd && worktreePath !== spawnCwd) {
|
|
18822
|
+
const grab = bareGrabSeed({ projectSlug, assignmentSlug: assignmentSlug ?? "", id });
|
|
18823
|
+
return {
|
|
18824
|
+
prompt: `The assignment's worktree is at ${worktreePath}. cd there, then grab the assignment with ${grab} and work it end-to-end.`,
|
|
18825
|
+
warnings: []
|
|
18826
|
+
};
|
|
18827
|
+
}
|
|
18828
|
+
if (!assignmentSlug && !id) {
|
|
18829
|
+
return { prompt: "", warnings: [] };
|
|
18620
18830
|
}
|
|
18621
|
-
return { prompt: bareGrabSeed({ projectSlug, assignmentSlug, id }), warnings: [] };
|
|
18831
|
+
return { prompt: bareGrabSeed({ projectSlug, assignmentSlug: assignmentSlug ?? "", id }), warnings: [] };
|
|
18622
18832
|
}
|
|
18623
18833
|
function effectiveLaunchTemplate(input) {
|
|
18624
18834
|
if (input.launchPrompt && input.launchPrompt.trim()) {
|
|
@@ -18628,6 +18838,14 @@ function effectiveLaunchTemplate(input) {
|
|
|
18628
18838
|
if (pb) {
|
|
18629
18839
|
return `@assignment Run ${runPlaybookClause(pb)} end-to-end.`;
|
|
18630
18840
|
}
|
|
18841
|
+
if (input.workdir && input.workdir.trim()) {
|
|
18842
|
+
const grab = bareGrabSeed({
|
|
18843
|
+
projectSlug: input.projectSlug,
|
|
18844
|
+
assignmentSlug: input.assignmentSlug,
|
|
18845
|
+
id: input.id
|
|
18846
|
+
});
|
|
18847
|
+
return `The assignment's worktree is at @worktree. cd there, then grab the assignment with ${grab} and work it end-to-end.`;
|
|
18848
|
+
}
|
|
18631
18849
|
return bareGrabSeed({
|
|
18632
18850
|
projectSlug: input.projectSlug,
|
|
18633
18851
|
assignmentSlug: input.assignmentSlug,
|
|
@@ -18651,7 +18869,7 @@ init_agents_schema();
|
|
|
18651
18869
|
init_cwd();
|
|
18652
18870
|
import { spawn as spawn2 } from "child_process";
|
|
18653
18871
|
import { mkdir as mkdir2, writeFile as writeFile4 } from "fs/promises";
|
|
18654
|
-
import { isAbsolute as isAbsolute5, resolve as
|
|
18872
|
+
import { isAbsolute as isAbsolute5, resolve as resolve28 } from "path";
|
|
18655
18873
|
init_paths();
|
|
18656
18874
|
init_playbooks();
|
|
18657
18875
|
init_cwd();
|
|
@@ -18661,8 +18879,10 @@ function shellQuote(arg) {
|
|
|
18661
18879
|
}
|
|
18662
18880
|
function buildAgentArgv(agent, prompt, env = process.env) {
|
|
18663
18881
|
const position = agent.promptArgPosition ?? "first";
|
|
18664
|
-
const
|
|
18665
|
-
const
|
|
18882
|
+
const prefix = agentNameArgs(agent);
|
|
18883
|
+
const baseArgs = agent.agentName ? [...agent.args ?? []] : applyModelFlag(agent, [...agent.args ?? []]);
|
|
18884
|
+
const positioned = position === "first" ? [prompt, ...baseArgs] : position === "last" ? [...baseArgs, prompt] : baseArgs;
|
|
18885
|
+
const agentArgs = [...prefix, ...positioned];
|
|
18666
18886
|
if (agent.resolveFromShellAliases) {
|
|
18667
18887
|
const requested = env.SHELL;
|
|
18668
18888
|
let shell = requested;
|
|
@@ -18742,8 +18962,60 @@ async function resolveLaunchPlan(input) {
|
|
|
18742
18962
|
if (input.kind === "assignment") {
|
|
18743
18963
|
return resolveAssignmentPlan(input, terminal);
|
|
18744
18964
|
}
|
|
18965
|
+
if (input.kind === "standalone") {
|
|
18966
|
+
return resolveStandalonePlan(input, terminal);
|
|
18967
|
+
}
|
|
18745
18968
|
return resolveSessionPlan(input, terminal);
|
|
18746
18969
|
}
|
|
18970
|
+
async function resolveStandalonePlan(input, terminal) {
|
|
18971
|
+
const agentId = input.agentId ?? input.id;
|
|
18972
|
+
if (!agentId) {
|
|
18973
|
+
throw new LaunchError(
|
|
18974
|
+
"agent-not-configured",
|
|
18975
|
+
"A standalone launch requires an agent id."
|
|
18976
|
+
);
|
|
18977
|
+
}
|
|
18978
|
+
const agent = getAgents(input.config).find((a) => a.id === agentId);
|
|
18979
|
+
if (!agent) {
|
|
18980
|
+
throw new LaunchError(
|
|
18981
|
+
"agent-not-configured",
|
|
18982
|
+
`Agent "${agentId}" requested for a standalone launch is not in your agents list.`
|
|
18983
|
+
);
|
|
18984
|
+
}
|
|
18985
|
+
const wd = agent.workdir?.trim();
|
|
18986
|
+
if (!wd) {
|
|
18987
|
+
throw new LaunchError(
|
|
18988
|
+
"workspace-path-invalid",
|
|
18989
|
+
`Agent "${agent.id}" has no workdir \u2014 a standalone launch requires a valid workdir.`
|
|
18990
|
+
);
|
|
18991
|
+
}
|
|
18992
|
+
const expanded = expandHome(wd);
|
|
18993
|
+
if (!isExistingDir(expanded)) {
|
|
18994
|
+
throw new LaunchError(
|
|
18995
|
+
"workspace-path-invalid",
|
|
18996
|
+
`Agent "${agent.id}" workdir ${agent.workdir} (resolved ${expanded}) is not an existing directory \u2014 a standalone launch requires a valid workdir.`
|
|
18997
|
+
);
|
|
18998
|
+
}
|
|
18999
|
+
const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
|
|
19000
|
+
const template = input.promptOverride !== void 0 ? input.promptOverride : agent.launchPrompt;
|
|
19001
|
+
const { prompt, warnings: promptWarnings } = resolveLaunchPrompt({
|
|
19002
|
+
template,
|
|
19003
|
+
playbook: agent.playbook,
|
|
19004
|
+
projectSlug: null,
|
|
19005
|
+
knownPlaybookSlugs
|
|
19006
|
+
});
|
|
19007
|
+
const { argv, shellFallbackWarning } = buildFreshArgv(agent, prompt);
|
|
19008
|
+
return {
|
|
19009
|
+
terminal,
|
|
19010
|
+
cwd: expanded,
|
|
19011
|
+
argv,
|
|
19012
|
+
env: process.env,
|
|
19013
|
+
agentId: agent.id,
|
|
19014
|
+
fallbackWarning: null,
|
|
19015
|
+
shellFallbackWarning,
|
|
19016
|
+
promptWarnings
|
|
19017
|
+
};
|
|
19018
|
+
}
|
|
18747
19019
|
async function resolveAssignmentPlan(input, terminal) {
|
|
18748
19020
|
const resolved = await resolveAssignmentById(
|
|
18749
19021
|
input.projectsDir,
|
|
@@ -18767,17 +19039,6 @@ async function resolveAssignmentPlan(input, terminal) {
|
|
|
18767
19039
|
`Assignment ${input.id} resolver returned a directory but detail could not be loaded`
|
|
18768
19040
|
);
|
|
18769
19041
|
}
|
|
18770
|
-
const picked = resolveWorkspaceCwd({
|
|
18771
|
-
worktreePath: detail.workspace.worktreePath,
|
|
18772
|
-
repository: detail.workspace.repository,
|
|
18773
|
-
branch: detail.workspace.branch,
|
|
18774
|
-
assignmentSlug: resolved.assignmentSlug
|
|
18775
|
-
});
|
|
18776
|
-
if (picked.cwd === null) {
|
|
18777
|
-
throw new LaunchError("workspace-path-invalid", picked.invalidReason);
|
|
18778
|
-
}
|
|
18779
|
-
const cwd = picked.cwd;
|
|
18780
|
-
const fallbackWarning = picked.fallbackWarning;
|
|
18781
19042
|
let agent;
|
|
18782
19043
|
if (input.agentId) {
|
|
18783
19044
|
const found = getAgents(input.config).find((a) => a.id === input.agentId);
|
|
@@ -18791,6 +19052,30 @@ async function resolveAssignmentPlan(input, terminal) {
|
|
|
18791
19052
|
} else {
|
|
18792
19053
|
agent = pickAgent(input.config);
|
|
18793
19054
|
}
|
|
19055
|
+
if (input.agentName !== void 0) {
|
|
19056
|
+
agent = { ...agent, agentName: input.agentName };
|
|
19057
|
+
}
|
|
19058
|
+
const picked = resolveWorkspaceCwd({
|
|
19059
|
+
worktreePath: detail.workspace.worktreePath,
|
|
19060
|
+
repository: detail.workspace.repository,
|
|
19061
|
+
branch: detail.workspace.branch,
|
|
19062
|
+
assignmentSlug: resolved.assignmentSlug
|
|
19063
|
+
});
|
|
19064
|
+
if (picked.cwd === null) {
|
|
19065
|
+
throw new LaunchError("workspace-path-invalid", picked.invalidReason);
|
|
19066
|
+
}
|
|
19067
|
+
const launchCwd = resolveLaunchCwd(agent, picked.cwd);
|
|
19068
|
+
if (launchCwd.invalidReason) {
|
|
19069
|
+
throw new LaunchError("workspace-path-invalid", launchCwd.invalidReason);
|
|
19070
|
+
}
|
|
19071
|
+
const cwd = launchCwd.spawnCwd;
|
|
19072
|
+
const fallbackWarning = picked.fallbackWarning;
|
|
19073
|
+
const promptWarningsExtra = [];
|
|
19074
|
+
if (input.agentName !== void 0 && input.agentName.trim() && agent.model?.trim()) {
|
|
19075
|
+
promptWarningsExtra.push(
|
|
19076
|
+
`profile model "${agent.model.trim()}" suppressed: agent "${input.agentName.trim()}" defines its own model`
|
|
19077
|
+
);
|
|
19078
|
+
}
|
|
18794
19079
|
const knownPlaybookSlugs = await listPlaybookSlugs(playbooksDir());
|
|
18795
19080
|
const template = input.promptOverride !== void 0 ? input.promptOverride : agent.launchPrompt;
|
|
18796
19081
|
const { prompt, warnings: promptWarnings } = resolveLaunchPrompt({
|
|
@@ -18800,6 +19085,8 @@ async function resolveAssignmentPlan(input, terminal) {
|
|
|
18800
19085
|
assignmentDir: resolved.assignmentDir,
|
|
18801
19086
|
projectSlug: resolved.projectSlug,
|
|
18802
19087
|
assignmentSlug: resolved.assignmentSlug,
|
|
19088
|
+
worktreePath: launchCwd.worktreePath,
|
|
19089
|
+
spawnCwd: launchCwd.spawnCwd,
|
|
18803
19090
|
knownPlaybookSlugs
|
|
18804
19091
|
});
|
|
18805
19092
|
const { argv, shellFallbackWarning } = buildFreshArgv(agent, prompt);
|
|
@@ -18811,7 +19098,7 @@ async function resolveAssignmentPlan(input, terminal) {
|
|
|
18811
19098
|
agentId: agent.id,
|
|
18812
19099
|
fallbackWarning,
|
|
18813
19100
|
shellFallbackWarning,
|
|
18814
|
-
promptWarnings
|
|
19101
|
+
promptWarnings: [...promptWarningsExtra, ...promptWarnings]
|
|
18815
19102
|
};
|
|
18816
19103
|
}
|
|
18817
19104
|
async function resolveSessionPlan(input, terminal) {
|
|
@@ -18887,8 +19174,8 @@ async function resolveSessionPlan(input, terminal) {
|
|
|
18887
19174
|
|
|
18888
19175
|
// src/launch/execute.ts
|
|
18889
19176
|
import { spawn as spawn3 } from "child_process";
|
|
18890
|
-
import { homedir as
|
|
18891
|
-
import { basename as basename5, join as
|
|
19177
|
+
import { homedir as homedir6 } from "os";
|
|
19178
|
+
import { basename as basename5, join as join8, resolve as resolve29 } from "path";
|
|
18892
19179
|
init_fs();
|
|
18893
19180
|
init_config2();
|
|
18894
19181
|
init_session_id();
|
|
@@ -18994,7 +19281,7 @@ async function registerLaunchAtBirth(plan, child) {
|
|
|
18994
19281
|
const sessionId = plan.session?.sessionId ?? null;
|
|
18995
19282
|
const procStart = captureProcessStartedAt(pid);
|
|
18996
19283
|
const envDir = process.env.SYNTAUR_RUNTIME_SESSIONS_DIR;
|
|
18997
|
-
const markerDir = envDir && envDir.length > 0 ? envDir :
|
|
19284
|
+
const markerDir = envDir && envDir.length > 0 ? envDir : join8(homedir6(), ".syntaur", "runtime", "sessions");
|
|
18998
19285
|
writeRuntimeMarker(
|
|
18999
19286
|
pid,
|
|
19000
19287
|
{
|
|
@@ -19007,7 +19294,7 @@ async function registerLaunchAtBirth(plan, child) {
|
|
|
19007
19294
|
markerDir
|
|
19008
19295
|
);
|
|
19009
19296
|
if (!sessionId) return;
|
|
19010
|
-
if (autoTrack === "workspaces-only" && !await fileExists(
|
|
19297
|
+
if (autoTrack === "workspaces-only" && !await fileExists(resolve29(plan.cwd, ".syntaur", "context.json"))) {
|
|
19011
19298
|
return;
|
|
19012
19299
|
}
|
|
19013
19300
|
const { initSessionDb: initSessionDb2 } = await Promise.resolve().then(() => (init_session_db(), session_db_exports));
|
|
@@ -19183,7 +19470,7 @@ function appleScriptString(value) {
|
|
|
19183
19470
|
init_paths();
|
|
19184
19471
|
init_fs();
|
|
19185
19472
|
import { fileURLToPath } from "url";
|
|
19186
|
-
import { dirname as dirname6, resolve as
|
|
19473
|
+
import { dirname as dirname6, resolve as resolve30, join as join9 } from "path";
|
|
19187
19474
|
import { realpathSync, readFileSync as readFileSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
19188
19475
|
|
|
19189
19476
|
// src/dashboard/api-launch-preflight.ts
|
|
@@ -19344,7 +19631,8 @@ function createLaunchPreflightRouter(projectsDir, assignmentsDir2) {
|
|
|
19344
19631
|
playbook: agent.playbook,
|
|
19345
19632
|
projectSlug: resolved.projectSlug,
|
|
19346
19633
|
assignmentSlug: resolved.assignmentSlug,
|
|
19347
|
-
id: resolved.id
|
|
19634
|
+
id: resolved.id,
|
|
19635
|
+
workdir: agent.workdir
|
|
19348
19636
|
});
|
|
19349
19637
|
res.json({ template, knownPlaybookSlugs });
|
|
19350
19638
|
} catch (error) {
|
|
@@ -19588,7 +19876,7 @@ init_frontmatter();
|
|
|
19588
19876
|
init_event_emit();
|
|
19589
19877
|
init_assignment_walk();
|
|
19590
19878
|
init_timestamp();
|
|
19591
|
-
import { readFile as
|
|
19879
|
+
import { readFile as readFile21, writeFile as writeFile5, rm as rm2 } from "fs/promises";
|
|
19592
19880
|
import { dirname as dirname7 } from "path";
|
|
19593
19881
|
var StatusResolutionError = class extends Error {
|
|
19594
19882
|
constructor(message, code) {
|
|
@@ -19607,7 +19895,7 @@ async function scanAssignmentsByStatus(projectsDir, standaloneDir, ids) {
|
|
|
19607
19895
|
const assignmentPath = `${entry.assignmentDir}/assignment.md`;
|
|
19608
19896
|
let content;
|
|
19609
19897
|
try {
|
|
19610
|
-
content = await
|
|
19898
|
+
content = await readFile21(assignmentPath, "utf-8");
|
|
19611
19899
|
} catch (err) {
|
|
19612
19900
|
const code = err?.code;
|
|
19613
19901
|
if (code === "ENOENT") {
|
|
@@ -19670,7 +19958,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
19670
19958
|
const list = affected.get(r.id) ?? [];
|
|
19671
19959
|
for (const a of list) {
|
|
19672
19960
|
try {
|
|
19673
|
-
const content = await
|
|
19961
|
+
const content = await readFile21(a.path, "utf-8");
|
|
19674
19962
|
buffer.set(a.path, content);
|
|
19675
19963
|
} catch (err) {
|
|
19676
19964
|
const code = err?.code;
|
|
@@ -19699,7 +19987,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
19699
19987
|
for (const a of list) {
|
|
19700
19988
|
let current;
|
|
19701
19989
|
try {
|
|
19702
|
-
current = await
|
|
19990
|
+
current = await readFile21(a.path, "utf-8");
|
|
19703
19991
|
} catch (err) {
|
|
19704
19992
|
const code = err?.code;
|
|
19705
19993
|
if (code === "ENOENT") {
|
|
@@ -19770,7 +20058,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
19770
20058
|
const list = affected.get(r.id) ?? [];
|
|
19771
20059
|
for (const a of list) {
|
|
19772
20060
|
try {
|
|
19773
|
-
const current = await
|
|
20061
|
+
const current = await readFile21(a.path, "utf-8");
|
|
19774
20062
|
const fm = parseAssignmentFrontmatter(current);
|
|
19775
20063
|
if (fm.status !== r.id) {
|
|
19776
20064
|
console.warn(
|
|
@@ -20250,7 +20538,7 @@ import { Router as Router12 } from "express";
|
|
|
20250
20538
|
init_paths();
|
|
20251
20539
|
import Database3 from "better-sqlite3";
|
|
20252
20540
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
20253
|
-
import { resolve as
|
|
20541
|
+
import { resolve as resolve31 } from "path";
|
|
20254
20542
|
var db3 = null;
|
|
20255
20543
|
var LEASE_SCHEMA_VERSION = "1";
|
|
20256
20544
|
var SCHEMA_SQL3 = `
|
|
@@ -20337,7 +20625,7 @@ function isBusyError(err) {
|
|
|
20337
20625
|
}
|
|
20338
20626
|
function initLeasesDb(dbPath) {
|
|
20339
20627
|
if (db3) return db3;
|
|
20340
|
-
const finalPath = dbPath ??
|
|
20628
|
+
const finalPath = dbPath ?? resolve31(syntaurRoot(), "syntaur.db");
|
|
20341
20629
|
db3 = new Database3(finalPath);
|
|
20342
20630
|
db3.pragma("journal_mode = WAL");
|
|
20343
20631
|
db3.pragma("busy_timeout = 5000");
|
|
@@ -20489,15 +20777,15 @@ init_fs();
|
|
|
20489
20777
|
init_paths();
|
|
20490
20778
|
init_timestamp();
|
|
20491
20779
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
20492
|
-
import { readFile as
|
|
20493
|
-
import { resolve as
|
|
20780
|
+
import { readFile as readFile22, readdir as readdir16, rm as rm3 } from "fs/promises";
|
|
20781
|
+
import { resolve as resolve32 } from "path";
|
|
20494
20782
|
function schedulesDir() {
|
|
20495
20783
|
const override3 = process.env.SYNTAUR_SCHEDULES_DIR;
|
|
20496
|
-
if (override3 && override3.length > 0) return
|
|
20497
|
-
return
|
|
20784
|
+
if (override3 && override3.length > 0) return resolve32(override3);
|
|
20785
|
+
return resolve32(syntaurRoot(), "schedules");
|
|
20498
20786
|
}
|
|
20499
20787
|
function jobPath(id) {
|
|
20500
|
-
return
|
|
20788
|
+
return resolve32(schedulesDir(), `${jobFileSafe(id)}.md`);
|
|
20501
20789
|
}
|
|
20502
20790
|
function jobFileSafe(id) {
|
|
20503
20791
|
if (!/^[A-Za-z0-9_-]+$/.test(id)) {
|
|
@@ -20622,7 +20910,7 @@ function validateJob(raw2) {
|
|
|
20622
20910
|
async function readJob(id) {
|
|
20623
20911
|
const path = jobPath(id);
|
|
20624
20912
|
if (!await fileExists(path)) return null;
|
|
20625
|
-
return parseJobFile(await
|
|
20913
|
+
return parseJobFile(await readFile22(path, "utf-8"));
|
|
20626
20914
|
}
|
|
20627
20915
|
async function writeJob(job) {
|
|
20628
20916
|
const next = { ...job, updatedAt: nowTimestamp() };
|
|
@@ -20632,7 +20920,7 @@ async function writeJob(job) {
|
|
|
20632
20920
|
async function listJobs() {
|
|
20633
20921
|
let names;
|
|
20634
20922
|
try {
|
|
20635
|
-
names = await
|
|
20923
|
+
names = await readdir16(schedulesDir());
|
|
20636
20924
|
} catch {
|
|
20637
20925
|
return [];
|
|
20638
20926
|
}
|
|
@@ -20640,7 +20928,7 @@ async function listJobs() {
|
|
|
20640
20928
|
for (const name of names) {
|
|
20641
20929
|
if (!name.endsWith(".md")) continue;
|
|
20642
20930
|
try {
|
|
20643
|
-
jobs.push(parseJobFile(await
|
|
20931
|
+
jobs.push(parseJobFile(await readFile22(resolve32(schedulesDir(), name), "utf-8")));
|
|
20644
20932
|
} catch {
|
|
20645
20933
|
}
|
|
20646
20934
|
}
|
|
@@ -20650,13 +20938,13 @@ async function listJobs() {
|
|
|
20650
20938
|
|
|
20651
20939
|
// src/schedules/event-log.ts
|
|
20652
20940
|
init_timestamp();
|
|
20653
|
-
import { appendFile, readFile as
|
|
20654
|
-
import { dirname as dirname8, resolve as
|
|
20941
|
+
import { appendFile, readFile as readFile23, mkdir as mkdir3 } from "fs/promises";
|
|
20942
|
+
import { dirname as dirname8, resolve as resolve33 } from "path";
|
|
20655
20943
|
function eventLogPath(jobId) {
|
|
20656
20944
|
if (!/^[A-Za-z0-9_-]+$/.test(jobId)) {
|
|
20657
20945
|
throw new Error(`Unsafe schedule id: ${JSON.stringify(jobId)}`);
|
|
20658
20946
|
}
|
|
20659
|
-
return
|
|
20947
|
+
return resolve33(schedulesDir(), `${jobId}.jsonl`);
|
|
20660
20948
|
}
|
|
20661
20949
|
async function appendEvent(jobId, type, data) {
|
|
20662
20950
|
const event = { type, at: nowTimestamp(), ...data ? { data } : {} };
|
|
@@ -20669,7 +20957,7 @@ async function appendEvent(jobId, type, data) {
|
|
|
20669
20957
|
async function readEvents(jobId) {
|
|
20670
20958
|
let content;
|
|
20671
20959
|
try {
|
|
20672
|
-
content = await
|
|
20960
|
+
content = await readFile23(eventLogPath(jobId), "utf-8");
|
|
20673
20961
|
} catch {
|
|
20674
20962
|
return [];
|
|
20675
20963
|
}
|
|
@@ -20688,8 +20976,8 @@ async function readEvents(jobId) {
|
|
|
20688
20976
|
|
|
20689
20977
|
// src/schedules/attempt.ts
|
|
20690
20978
|
import { createHash as createHash3 } from "crypto";
|
|
20691
|
-
import { open as open4, readFile as
|
|
20692
|
-
import { resolve as
|
|
20979
|
+
import { open as open4, readFile as readFile24, stat as stat5, unlink as unlink6 } from "fs/promises";
|
|
20980
|
+
import { resolve as resolve34 } from "path";
|
|
20693
20981
|
|
|
20694
20982
|
// src/schedules/triggers.ts
|
|
20695
20983
|
import { Cron } from "croner";
|
|
@@ -20868,7 +21156,7 @@ var LOCK_WAIT_MS2 = 50;
|
|
|
20868
21156
|
var LOCK_MAX_WAITS2 = 100;
|
|
20869
21157
|
var MAX_CONSUMED_EDGES = 50;
|
|
20870
21158
|
async function acquireJobLock(id) {
|
|
20871
|
-
const lockPath =
|
|
21159
|
+
const lockPath = resolve34(schedulesDir(), `${id}.lock`);
|
|
20872
21160
|
const token = `${process.pid}:${createHash3("sha256").update(`${Math.random()}${Date.now()}`).digest("hex").slice(0, 12)}`;
|
|
20873
21161
|
for (let attempt = 0; attempt <= LOCK_MAX_WAITS2; attempt++) {
|
|
20874
21162
|
try {
|
|
@@ -20877,7 +21165,7 @@ async function acquireJobLock(id) {
|
|
|
20877
21165
|
await handle.close();
|
|
20878
21166
|
return async () => {
|
|
20879
21167
|
try {
|
|
20880
|
-
const current = await
|
|
21168
|
+
const current = await readFile24(lockPath, "utf-8");
|
|
20881
21169
|
if (current === token) await unlink6(lockPath);
|
|
20882
21170
|
} catch {
|
|
20883
21171
|
}
|
|
@@ -21183,7 +21471,7 @@ async function reapStale(jobs, deps) {
|
|
|
21183
21471
|
|
|
21184
21472
|
// src/schedules/unattended.ts
|
|
21185
21473
|
import { existsSync as existsSync3 } from "fs";
|
|
21186
|
-
import { resolve as
|
|
21474
|
+
import { resolve as resolve35 } from "path";
|
|
21187
21475
|
var UnattendedRefusalError = class extends Error {
|
|
21188
21476
|
constructor(message) {
|
|
21189
21477
|
super(message);
|
|
@@ -21199,7 +21487,7 @@ function assertUnattendedTerminalSupported(terminal) {
|
|
|
21199
21487
|
}
|
|
21200
21488
|
function isKillSwitchEngaged() {
|
|
21201
21489
|
if (process.env.SYNTAUR_SCHEDULES_DISABLED === "1") return true;
|
|
21202
|
-
return existsSync3(
|
|
21490
|
+
return existsSync3(resolve35(schedulesDir(), "KILL"));
|
|
21203
21491
|
}
|
|
21204
21492
|
function canFire(job, deps) {
|
|
21205
21493
|
const killSwitch = deps.killSwitch ?? isKillSwitchEngaged;
|
|
@@ -21350,20 +21638,20 @@ init_config2();
|
|
|
21350
21638
|
init_paths();
|
|
21351
21639
|
init_assignment_resolver();
|
|
21352
21640
|
init_frontmatter();
|
|
21353
|
-
import { readFile as
|
|
21354
|
-
import { resolve as
|
|
21641
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
21642
|
+
import { resolve as resolve36 } from "path";
|
|
21355
21643
|
|
|
21356
21644
|
// src/schedules/launch-ack.ts
|
|
21357
21645
|
init_session_id();
|
|
21358
21646
|
import { readdirSync as readdirSync2 } from "fs";
|
|
21359
|
-
import { homedir as
|
|
21360
|
-
import { join as
|
|
21647
|
+
import { homedir as homedir7 } from "os";
|
|
21648
|
+
import { join as join10 } from "path";
|
|
21361
21649
|
function defaultSleep(ms) {
|
|
21362
21650
|
return new Promise((r) => setTimeout(r, ms));
|
|
21363
21651
|
}
|
|
21364
21652
|
function runtimeSessionsDir() {
|
|
21365
21653
|
const override3 = process.env.SYNTAUR_RUNTIME_SESSIONS_DIR;
|
|
21366
|
-
return override3 && override3.length > 0 ? override3 :
|
|
21654
|
+
return override3 && override3.length > 0 ? override3 : join10(homedir7(), ".syntaur", "runtime", "sessions");
|
|
21367
21655
|
}
|
|
21368
21656
|
function defaultAckProbe(handle) {
|
|
21369
21657
|
const dir = runtimeSessionsDir();
|
|
@@ -21411,7 +21699,7 @@ async function defaultReadAssignment(assignmentId) {
|
|
|
21411
21699
|
const resolved = await resolveAssignmentById(projectsDir, assignmentsDir(), assignmentId);
|
|
21412
21700
|
if (!resolved) return null;
|
|
21413
21701
|
try {
|
|
21414
|
-
const content = await
|
|
21702
|
+
const content = await readFile25(resolve36(resolved.assignmentDir, "assignment.md"), "utf-8");
|
|
21415
21703
|
return parseAssignmentFrontmatter(content);
|
|
21416
21704
|
} catch {
|
|
21417
21705
|
return null;
|
|
@@ -21503,7 +21791,7 @@ import { Router as Router14 } from "express";
|
|
|
21503
21791
|
// src/db/usage-db.ts
|
|
21504
21792
|
init_paths();
|
|
21505
21793
|
import Database4 from "better-sqlite3";
|
|
21506
|
-
import { resolve as
|
|
21794
|
+
import { resolve as resolve37 } from "path";
|
|
21507
21795
|
var db4 = null;
|
|
21508
21796
|
var USAGE_SCHEMA_VERSION = "1";
|
|
21509
21797
|
var SCHEMA_SQL4 = `
|
|
@@ -21577,7 +21865,7 @@ function nowIso2() {
|
|
|
21577
21865
|
}
|
|
21578
21866
|
function initUsageDb(dbPath) {
|
|
21579
21867
|
if (db4) return db4;
|
|
21580
|
-
const finalPath = dbPath ??
|
|
21868
|
+
const finalPath = dbPath ?? resolve37(syntaurRoot(), "syntaur.db");
|
|
21581
21869
|
db4 = new Database4(finalPath);
|
|
21582
21870
|
db4.pragma("journal_mode = WAL");
|
|
21583
21871
|
db4.pragma("busy_timeout = 5000");
|
|
@@ -22190,14 +22478,14 @@ init_fs();
|
|
|
22190
22478
|
init_parser();
|
|
22191
22479
|
init_events_db();
|
|
22192
22480
|
import { Router as Router15 } from "express";
|
|
22193
|
-
import { resolve as
|
|
22194
|
-
import { readFile as
|
|
22481
|
+
import { resolve as resolve38 } from "path";
|
|
22482
|
+
import { readFile as readFile26 } from "fs/promises";
|
|
22195
22483
|
function createEventsRouter(projectsDir, assignmentsDir2) {
|
|
22196
22484
|
const router = Router15();
|
|
22197
22485
|
router.get("/projects/:slug/assignments/:aslug/events", async (req2, res) => {
|
|
22198
22486
|
try {
|
|
22199
22487
|
const { slug, aslug } = req2.params;
|
|
22200
|
-
const assignmentMdPath =
|
|
22488
|
+
const assignmentMdPath = resolve38(
|
|
22201
22489
|
projectsDir,
|
|
22202
22490
|
slug,
|
|
22203
22491
|
"assignments",
|
|
@@ -22218,7 +22506,7 @@ function createEventsRouter(projectsDir, assignmentsDir2) {
|
|
|
22218
22506
|
router.get("/standalone/assignments/:id/events", async (req2, res) => {
|
|
22219
22507
|
try {
|
|
22220
22508
|
const { id: dirId } = req2.params;
|
|
22221
|
-
const assignmentMdPath =
|
|
22509
|
+
const assignmentMdPath = resolve38(assignmentsDir2, dirId, "assignment.md");
|
|
22222
22510
|
const id = await readAssignmentId2(assignmentMdPath) ?? dirId;
|
|
22223
22511
|
res.json({ events: loadEvents(id, req2.query) });
|
|
22224
22512
|
} catch (error) {
|
|
@@ -22230,7 +22518,7 @@ function createEventsRouter(projectsDir, assignmentsDir2) {
|
|
|
22230
22518
|
}
|
|
22231
22519
|
async function readAssignmentId2(assignmentMdPath) {
|
|
22232
22520
|
if (!await fileExists(assignmentMdPath)) return null;
|
|
22233
|
-
const content = await
|
|
22521
|
+
const content = await readFile26(assignmentMdPath, "utf-8");
|
|
22234
22522
|
const [fm] = extractFrontmatter2(content);
|
|
22235
22523
|
const id = getField(fm, "id");
|
|
22236
22524
|
return id && id.length > 0 ? id : null;
|
|
@@ -22275,8 +22563,8 @@ init_assignment_walk();
|
|
|
22275
22563
|
init_parser();
|
|
22276
22564
|
init_facts();
|
|
22277
22565
|
init_state_machine();
|
|
22278
|
-
import { resolve as
|
|
22279
|
-
import { readFile as
|
|
22566
|
+
import { resolve as resolve39 } from "path";
|
|
22567
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
22280
22568
|
|
|
22281
22569
|
// src/inbox/types.ts
|
|
22282
22570
|
var INBOX_CATEGORIES = [
|
|
@@ -22456,7 +22744,7 @@ async function computeInbox(opts) {
|
|
|
22456
22744
|
if (opts.project !== void 0 && entry.projectSlug !== opts.project) continue;
|
|
22457
22745
|
let parsed;
|
|
22458
22746
|
try {
|
|
22459
|
-
const content = await
|
|
22747
|
+
const content = await readFile27(resolve39(entry.assignmentDir, "assignment.md"), "utf-8");
|
|
22460
22748
|
parsed = parseAssignmentFull(content);
|
|
22461
22749
|
} catch {
|
|
22462
22750
|
continue;
|
|
@@ -22499,10 +22787,10 @@ async function computeInbox(opts) {
|
|
|
22499
22787
|
});
|
|
22500
22788
|
}
|
|
22501
22789
|
if (!typeFilter || typeFilter.has("question")) {
|
|
22502
|
-
const commentsPath =
|
|
22790
|
+
const commentsPath = resolve39(entry.assignmentDir, "comments.md");
|
|
22503
22791
|
if (await fileExists(commentsPath)) {
|
|
22504
22792
|
try {
|
|
22505
|
-
const content = await
|
|
22793
|
+
const content = await readFile27(commentsPath, "utf-8");
|
|
22506
22794
|
const parsedComments = parseComments(content);
|
|
22507
22795
|
for (const c of unresolvedQuestions(parsedComments.entries)) {
|
|
22508
22796
|
const since = resolveSince("question", parsed, now, c);
|
|
@@ -22613,8 +22901,8 @@ init_slug();
|
|
|
22613
22901
|
init_timestamp();
|
|
22614
22902
|
init_fs();
|
|
22615
22903
|
import { Router as Router17 } from "express";
|
|
22616
|
-
import { resolve as
|
|
22617
|
-
import { readFile as
|
|
22904
|
+
import { resolve as resolve40 } from "path";
|
|
22905
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
22618
22906
|
init_playbooks();
|
|
22619
22907
|
function statusForPlaybookError(code) {
|
|
22620
22908
|
switch (code) {
|
|
@@ -22696,8 +22984,8 @@ function createPlaybooksRouter(playbooksDir2) {
|
|
|
22696
22984
|
res.status(404).json({ error: `Playbook "${req2.params.slug}" not found` });
|
|
22697
22985
|
return;
|
|
22698
22986
|
}
|
|
22699
|
-
const filePath =
|
|
22700
|
-
const content = await
|
|
22987
|
+
const filePath = resolve40(playbooksDir2, resolved.filename);
|
|
22988
|
+
const content = await readFile28(filePath, "utf-8");
|
|
22701
22989
|
res.json({
|
|
22702
22990
|
documentType: "playbook",
|
|
22703
22991
|
title: `Edit Playbook: ${resolved.slug}`,
|
|
@@ -22722,7 +23010,7 @@ function createPlaybooksRouter(playbooksDir2) {
|
|
|
22722
23010
|
return;
|
|
22723
23011
|
}
|
|
22724
23012
|
await ensureDir(playbooksDir2);
|
|
22725
|
-
const filePath =
|
|
23013
|
+
const filePath = resolve40(playbooksDir2, `${slug}.md`);
|
|
22726
23014
|
if (await fileExists(filePath)) {
|
|
22727
23015
|
res.status(409).json({ error: `Playbook "${slug}" already exists` });
|
|
22728
23016
|
return;
|
|
@@ -22746,7 +23034,7 @@ function createPlaybooksRouter(playbooksDir2) {
|
|
|
22746
23034
|
res.status(404).json({ error: `Playbook "${req2.params.slug}" not found` });
|
|
22747
23035
|
return;
|
|
22748
23036
|
}
|
|
22749
|
-
const filePath =
|
|
23037
|
+
const filePath = resolve40(playbooksDir2, resolved.filename);
|
|
22750
23038
|
await writeFileForce(filePath, content);
|
|
22751
23039
|
await rebuildPlaybookManifest(playbooksDir2);
|
|
22752
23040
|
res.json({ slug: resolved.slug, path: filePath });
|
|
@@ -22794,7 +23082,7 @@ init_parser2();
|
|
|
22794
23082
|
init_fs();
|
|
22795
23083
|
init_paths();
|
|
22796
23084
|
import { Router as Router19 } from "express";
|
|
22797
|
-
import { readdir as
|
|
23085
|
+
import { readdir as readdir18 } from "fs/promises";
|
|
22798
23086
|
import { resolve as resolvePath, dirname as dirname10 } from "path";
|
|
22799
23087
|
import { rename as rename6, mkdir as mkdir5 } from "fs/promises";
|
|
22800
23088
|
init_slug();
|
|
@@ -22809,7 +23097,7 @@ init_uuid();
|
|
|
22809
23097
|
init_paths();
|
|
22810
23098
|
init_fs();
|
|
22811
23099
|
init_config2();
|
|
22812
|
-
import { resolve as
|
|
23100
|
+
import { resolve as resolve41 } from "path";
|
|
22813
23101
|
async function createAssignmentCommand(title, options) {
|
|
22814
23102
|
if (!title.trim()) {
|
|
22815
23103
|
throw new Error("Assignment title cannot be empty.");
|
|
@@ -22884,14 +23172,14 @@ async function createAssignmentCommand(title, options) {
|
|
|
22884
23172
|
if (options.oneOff) {
|
|
22885
23173
|
const standaloneRoot = assignmentsDir();
|
|
22886
23174
|
folderName = id;
|
|
22887
|
-
assignmentDir =
|
|
23175
|
+
assignmentDir = resolve41(standaloneRoot, folderName);
|
|
22888
23176
|
projectSlug = null;
|
|
22889
23177
|
await ensureDir(standaloneRoot);
|
|
22890
23178
|
} else {
|
|
22891
23179
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
22892
23180
|
projectSlug = options.project;
|
|
22893
|
-
const projectDir =
|
|
22894
|
-
const projectMdPath =
|
|
23181
|
+
const projectDir = resolve41(baseDir, projectSlug);
|
|
23182
|
+
const projectMdPath = resolve41(projectDir, "project.md");
|
|
22895
23183
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
22896
23184
|
throw new Error(
|
|
22897
23185
|
`Project "${projectSlug}" not found at ${projectDir}.
|
|
@@ -22899,9 +23187,9 @@ Run 'syntaur create-project' first or use --one-off.`
|
|
|
22899
23187
|
);
|
|
22900
23188
|
}
|
|
22901
23189
|
if (dependsOn.length > 0) {
|
|
22902
|
-
const depDirBase =
|
|
23190
|
+
const depDirBase = resolve41(projectDir, "assignments");
|
|
22903
23191
|
for (const dep of dependsOn) {
|
|
22904
|
-
const depDir =
|
|
23192
|
+
const depDir = resolve41(depDirBase, dep);
|
|
22905
23193
|
if (!await fileExists(depDir)) {
|
|
22906
23194
|
console.warn(
|
|
22907
23195
|
`Warning: dependency "${dep}" does not exist in project "${projectSlug}" yet.`
|
|
@@ -22910,7 +23198,7 @@ Run 'syntaur create-project' first or use --one-off.`
|
|
|
22910
23198
|
}
|
|
22911
23199
|
}
|
|
22912
23200
|
folderName = assignmentSlug;
|
|
22913
|
-
assignmentDir =
|
|
23201
|
+
assignmentDir = resolve41(projectDir, "assignments", folderName);
|
|
22914
23202
|
}
|
|
22915
23203
|
if (await fileExists(assignmentDir)) {
|
|
22916
23204
|
throw new Error(
|
|
@@ -22922,7 +23210,7 @@ Use --slug to specify a different slug.`
|
|
|
22922
23210
|
const companionAssignmentRef = projectSlug === null ? id : assignmentSlug;
|
|
22923
23211
|
const files = [
|
|
22924
23212
|
[
|
|
22925
|
-
|
|
23213
|
+
resolve41(assignmentDir, "assignment.md"),
|
|
22926
23214
|
renderAssignment({
|
|
22927
23215
|
id,
|
|
22928
23216
|
slug: assignmentSlug,
|
|
@@ -22940,35 +23228,35 @@ Use --slug to specify a different slug.`
|
|
|
22940
23228
|
})
|
|
22941
23229
|
],
|
|
22942
23230
|
[
|
|
22943
|
-
|
|
23231
|
+
resolve41(assignmentDir, "scratchpad.md"),
|
|
22944
23232
|
renderScratchpad({
|
|
22945
23233
|
assignmentSlug: companionAssignmentRef,
|
|
22946
23234
|
timestamp
|
|
22947
23235
|
})
|
|
22948
23236
|
],
|
|
22949
23237
|
[
|
|
22950
|
-
|
|
23238
|
+
resolve41(assignmentDir, "handoff.md"),
|
|
22951
23239
|
renderHandoff({
|
|
22952
23240
|
assignmentSlug: companionAssignmentRef,
|
|
22953
23241
|
timestamp
|
|
22954
23242
|
})
|
|
22955
23243
|
],
|
|
22956
23244
|
[
|
|
22957
|
-
|
|
23245
|
+
resolve41(assignmentDir, "decision-record.md"),
|
|
22958
23246
|
renderDecisionRecord({
|
|
22959
23247
|
assignmentSlug: companionAssignmentRef,
|
|
22960
23248
|
timestamp
|
|
22961
23249
|
})
|
|
22962
23250
|
],
|
|
22963
23251
|
[
|
|
22964
|
-
|
|
23252
|
+
resolve41(assignmentDir, "progress.md"),
|
|
22965
23253
|
renderProgress({
|
|
22966
23254
|
assignment: companionAssignmentRef,
|
|
22967
23255
|
timestamp
|
|
22968
23256
|
})
|
|
22969
23257
|
],
|
|
22970
23258
|
[
|
|
22971
|
-
|
|
23259
|
+
resolve41(assignmentDir, "comments.md"),
|
|
22972
23260
|
renderComments({
|
|
22973
23261
|
assignment: companionAssignmentRef,
|
|
22974
23262
|
timestamp
|
|
@@ -23141,8 +23429,8 @@ init_api();
|
|
|
23141
23429
|
import { raw } from "express";
|
|
23142
23430
|
|
|
23143
23431
|
// src/todos/attachments.ts
|
|
23144
|
-
import { mkdir as mkdir4, readdir as
|
|
23145
|
-
import { resolve as
|
|
23432
|
+
import { mkdir as mkdir4, readdir as readdir17, stat as stat6, rename as rename5, rm as rm4, unlink as unlink7, writeFile as writeFile6, cp } from "fs/promises";
|
|
23433
|
+
import { resolve as resolve42, basename as basename6, dirname as dirname9, extname } from "path";
|
|
23146
23434
|
|
|
23147
23435
|
// src/utils/proof-artifact-id.ts
|
|
23148
23436
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -23229,12 +23517,12 @@ function sanitizeAttachmentName(name) {
|
|
|
23229
23517
|
return n;
|
|
23230
23518
|
}
|
|
23231
23519
|
function attachmentsRootDir(todosDir2) {
|
|
23232
|
-
return
|
|
23520
|
+
return resolve42(todosDir2, "attachments");
|
|
23233
23521
|
}
|
|
23234
23522
|
function attachmentDirFor(todosDir2, scopeId, todoId) {
|
|
23235
23523
|
assertScope(scopeId);
|
|
23236
23524
|
assertTodoId(todoId);
|
|
23237
|
-
return
|
|
23525
|
+
return resolve42(attachmentsRootDir(todosDir2), scopeId, todoId);
|
|
23238
23526
|
}
|
|
23239
23527
|
async function dirExists(p) {
|
|
23240
23528
|
try {
|
|
@@ -23248,7 +23536,7 @@ async function writeAttachment(todosDir2, scopeId, todoId, originalName, bytes)
|
|
|
23248
23536
|
await mkdir4(dir, { recursive: true });
|
|
23249
23537
|
const id = generateArtifactId();
|
|
23250
23538
|
const filename = sanitizeAttachmentName(originalName);
|
|
23251
|
-
await writeFile6(
|
|
23539
|
+
await writeFile6(resolve42(dir, `${id}__${filename}`), bytes);
|
|
23252
23540
|
return {
|
|
23253
23541
|
id,
|
|
23254
23542
|
filename,
|
|
@@ -23261,7 +23549,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
23261
23549
|
const dir = attachmentDirFor(todosDir2, scopeId, todoId);
|
|
23262
23550
|
let names;
|
|
23263
23551
|
try {
|
|
23264
|
-
names = await
|
|
23552
|
+
names = await readdir17(dir);
|
|
23265
23553
|
} catch {
|
|
23266
23554
|
return [];
|
|
23267
23555
|
}
|
|
@@ -23273,7 +23561,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
23273
23561
|
if (!ATTACHMENT_ID_RE.test(id)) continue;
|
|
23274
23562
|
const filename = stored.slice(sep2 + 2);
|
|
23275
23563
|
try {
|
|
23276
|
-
const st = await stat6(
|
|
23564
|
+
const st = await stat6(resolve42(dir, stored));
|
|
23277
23565
|
if (!st.isFile()) continue;
|
|
23278
23566
|
out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
|
|
23279
23567
|
} catch {
|
|
@@ -23284,10 +23572,10 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
23284
23572
|
}
|
|
23285
23573
|
async function readScopeAttachments(todosDir2, scopeId) {
|
|
23286
23574
|
assertScope(scopeId);
|
|
23287
|
-
const scopeDir =
|
|
23575
|
+
const scopeDir = resolve42(attachmentsRootDir(todosDir2), scopeId);
|
|
23288
23576
|
let todoIds;
|
|
23289
23577
|
try {
|
|
23290
|
-
todoIds = await
|
|
23578
|
+
todoIds = await readdir17(scopeDir);
|
|
23291
23579
|
} catch {
|
|
23292
23580
|
return {};
|
|
23293
23581
|
}
|
|
@@ -23304,7 +23592,7 @@ async function resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId) {
|
|
|
23304
23592
|
const dir = attachmentDirFor(todosDir2, scopeId, todoId);
|
|
23305
23593
|
let names;
|
|
23306
23594
|
try {
|
|
23307
|
-
names = await
|
|
23595
|
+
names = await readdir17(dir);
|
|
23308
23596
|
} catch {
|
|
23309
23597
|
return null;
|
|
23310
23598
|
}
|
|
@@ -23312,7 +23600,7 @@ async function resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId) {
|
|
|
23312
23600
|
const stored = names.find((n) => n.startsWith(prefix));
|
|
23313
23601
|
if (!stored) return null;
|
|
23314
23602
|
const filename = stored.slice(prefix.length);
|
|
23315
|
-
return { path:
|
|
23603
|
+
return { path: resolve42(dir, stored), filename, mime: mimeForName(filename) };
|
|
23316
23604
|
}
|
|
23317
23605
|
async function deleteAttachment(todosDir2, scopeId, todoId, attachmentId) {
|
|
23318
23606
|
const resolved = await resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId);
|
|
@@ -23608,7 +23896,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
23608
23896
|
router.get("/", async (_req, res) => {
|
|
23609
23897
|
try {
|
|
23610
23898
|
await ensureDir(todosDir2);
|
|
23611
|
-
const files = await
|
|
23899
|
+
const files = await readdir18(todosDir2).catch(() => []);
|
|
23612
23900
|
const workspaces = [];
|
|
23613
23901
|
for (const file of files) {
|
|
23614
23902
|
if (typeof file !== "string") continue;
|
|
@@ -23729,7 +24017,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
23729
24017
|
try {
|
|
23730
24018
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
23731
24019
|
const { resolve: resolve49 } = await import("path");
|
|
23732
|
-
const { readFile:
|
|
24020
|
+
const { readFile: readFile32 } = await import("fs/promises");
|
|
23733
24021
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
23734
24022
|
const workspace = getWorkspaceParam(req2.params.workspace);
|
|
23735
24023
|
const outcome = await wsLock(workspace, async () => {
|
|
@@ -23748,7 +24036,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
23748
24036
|
await ensureDir(resolve49(todosDir2, "archive"));
|
|
23749
24037
|
let archContent = "";
|
|
23750
24038
|
if (await fileExists(archFile)) {
|
|
23751
|
-
archContent = await
|
|
24039
|
+
archContent = await readFile32(archFile, "utf-8");
|
|
23752
24040
|
archContent = archContent.trimEnd() + "\n\n";
|
|
23753
24041
|
} else {
|
|
23754
24042
|
archContent = `---
|
|
@@ -24049,7 +24337,7 @@ workspace: ${workspace}
|
|
|
24049
24337
|
const { readConfig: readConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
24050
24338
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
24051
24339
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
24052
|
-
const { readFile:
|
|
24340
|
+
const { readFile: readFile32 } = await import("fs/promises");
|
|
24053
24341
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
24054
24342
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
24055
24343
|
let assignmentRef;
|
|
@@ -24070,7 +24358,7 @@ workspace: ${workspace}
|
|
|
24070
24358
|
}
|
|
24071
24359
|
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
24072
24360
|
if (!await fileExists2(assignmentMdPath)) return { error: `Target assignment not found: ${assignmentMdPath}` };
|
|
24073
|
-
let content = await
|
|
24361
|
+
let content = await readFile32(assignmentMdPath, "utf-8");
|
|
24074
24362
|
content = appendTodosToAssignmentBody2(
|
|
24075
24363
|
content,
|
|
24076
24364
|
items.map((it) => ({
|
|
@@ -24265,8 +24553,8 @@ init_fs();
|
|
|
24265
24553
|
init_paths();
|
|
24266
24554
|
init_slug();
|
|
24267
24555
|
import { Router as Router20 } from "express";
|
|
24268
|
-
import { mkdir as mkdir6, readFile as
|
|
24269
|
-
import { resolve as
|
|
24556
|
+
import { mkdir as mkdir6, readFile as readFile29, rename as rename7 } from "fs/promises";
|
|
24557
|
+
import { resolve as resolve43, dirname as dirname11 } from "path";
|
|
24270
24558
|
init_api();
|
|
24271
24559
|
var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
24272
24560
|
function touchItem4(item) {
|
|
@@ -24282,7 +24570,7 @@ function params(req2) {
|
|
|
24282
24570
|
return req2.params;
|
|
24283
24571
|
}
|
|
24284
24572
|
async function projectExists(projectsDir, slug) {
|
|
24285
|
-
return fileExists(
|
|
24573
|
+
return fileExists(resolve43(projectsDir, slug, "project.md"));
|
|
24286
24574
|
}
|
|
24287
24575
|
async function ensureProjectTodosDir(projectsDir, slug) {
|
|
24288
24576
|
const todosDir2 = projectTodosDir(projectsDir, slug);
|
|
@@ -24299,7 +24587,7 @@ async function ensureProjectTodosDir(projectsDir, slug) {
|
|
|
24299
24587
|
throw err;
|
|
24300
24588
|
}
|
|
24301
24589
|
try {
|
|
24302
|
-
await mkdir6(
|
|
24590
|
+
await mkdir6(resolve43(todosDir2, "archive"), { recursive: false });
|
|
24303
24591
|
} catch (err) {
|
|
24304
24592
|
const code = err.code;
|
|
24305
24593
|
if (code === "EEXIST") return;
|
|
@@ -24507,7 +24795,7 @@ function createProjectTodosRouter(projectsDir, broadcast, workspaceTodosDir) {
|
|
|
24507
24795
|
const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
|
|
24508
24796
|
let archContent = "";
|
|
24509
24797
|
if (await fileExists(archFile)) {
|
|
24510
|
-
archContent = await
|
|
24798
|
+
archContent = await readFile29(archFile, "utf-8");
|
|
24511
24799
|
archContent = archContent.trimEnd() + "\n\n";
|
|
24512
24800
|
} else {
|
|
24513
24801
|
archContent = `---
|
|
@@ -24978,17 +25266,17 @@ workspace: ${slug}
|
|
|
24978
25266
|
if (tg.includes("/")) {
|
|
24979
25267
|
const parts = tg.split("/");
|
|
24980
25268
|
if (parts.length !== 2) return { error: `Invalid target.assignment "${tg}"` };
|
|
24981
|
-
assignmentDir =
|
|
25269
|
+
assignmentDir = resolve43(projectsDir, parts[0], "assignments", parts[1]);
|
|
24982
25270
|
assignmentRef = `${parts[0]}/${parts[1]}`;
|
|
24983
25271
|
} else if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tg)) {
|
|
24984
|
-
assignmentDir =
|
|
25272
|
+
assignmentDir = resolve43(assignmentsDirFn(), tg);
|
|
24985
25273
|
assignmentRef = tg;
|
|
24986
25274
|
} else {
|
|
24987
25275
|
return { error: `Invalid target.assignment "${tg}"` };
|
|
24988
25276
|
}
|
|
24989
|
-
const assignmentMdPath =
|
|
25277
|
+
const assignmentMdPath = resolve43(assignmentDir, "assignment.md");
|
|
24990
25278
|
if (!await fileExists(assignmentMdPath)) return { error: `Target assignment not found: ${assignmentMdPath}` };
|
|
24991
|
-
let content = await
|
|
25279
|
+
let content = await readFile29(assignmentMdPath, "utf-8");
|
|
24992
25280
|
content = appendTodosToAssignmentBody2(
|
|
24993
25281
|
content,
|
|
24994
25282
|
items.map((it) => ({
|
|
@@ -25191,7 +25479,7 @@ workspace: ${slug}
|
|
|
25191
25479
|
|
|
25192
25480
|
// src/dashboard/api-bundles.ts
|
|
25193
25481
|
import { Router as Router21 } from "express";
|
|
25194
|
-
import { readdir as
|
|
25482
|
+
import { readdir as readdir19 } from "fs/promises";
|
|
25195
25483
|
|
|
25196
25484
|
// src/todos/bundle-parser.ts
|
|
25197
25485
|
init_parser();
|
|
@@ -25199,7 +25487,7 @@ init_fs();
|
|
|
25199
25487
|
init_paths();
|
|
25200
25488
|
init_parser2();
|
|
25201
25489
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
25202
|
-
import { readFile as
|
|
25490
|
+
import { readFile as readFile30 } from "fs/promises";
|
|
25203
25491
|
var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
|
|
25204
25492
|
var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
|
|
25205
25493
|
var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
|
|
@@ -25266,7 +25554,7 @@ function parseBundles(content) {
|
|
|
25266
25554
|
async function readBundles(todosDir2) {
|
|
25267
25555
|
const path = bundlesPath(todosDir2);
|
|
25268
25556
|
if (!await fileExists(path)) return [];
|
|
25269
|
-
const content = await
|
|
25557
|
+
const content = await readFile30(path, "utf-8");
|
|
25270
25558
|
return parseBundles(content).bundles;
|
|
25271
25559
|
}
|
|
25272
25560
|
|
|
@@ -25315,7 +25603,7 @@ function createBundlesRouter(todosDir2, broadcast) {
|
|
|
25315
25603
|
try {
|
|
25316
25604
|
await ensureDir(todosDir2);
|
|
25317
25605
|
const bundles = await readBundles(todosDir2);
|
|
25318
|
-
const workspaceFiles = await
|
|
25606
|
+
const workspaceFiles = await readdir19(todosDir2).catch(() => []);
|
|
25319
25607
|
const itemsByKey = /* @__PURE__ */ new Map();
|
|
25320
25608
|
for (const f of workspaceFiles) {
|
|
25321
25609
|
if (typeof f !== "string") continue;
|
|
@@ -25368,7 +25656,7 @@ init_fs();
|
|
|
25368
25656
|
init_paths();
|
|
25369
25657
|
init_slug();
|
|
25370
25658
|
import { Router as Router22 } from "express";
|
|
25371
|
-
import { resolve as
|
|
25659
|
+
import { resolve as resolve44 } from "path";
|
|
25372
25660
|
init_parser2();
|
|
25373
25661
|
function deriveStatus2(bundle, items) {
|
|
25374
25662
|
const members = bundle.todoIds.map((id) => items.find((i) => i.id === id)).filter((i) => i !== void 0);
|
|
@@ -25410,7 +25698,7 @@ function createProjectBundlesRouter(projectsDir, broadcast) {
|
|
|
25410
25698
|
router.get("/", async (req2, res) => {
|
|
25411
25699
|
try {
|
|
25412
25700
|
const slug = getProjectIdParam2(req2.params.projectId);
|
|
25413
|
-
const projectMd =
|
|
25701
|
+
const projectMd = resolve44(projectsDir, slug, "project.md");
|
|
25414
25702
|
if (!await fileExists(projectMd)) {
|
|
25415
25703
|
notFound2(res, slug);
|
|
25416
25704
|
return;
|
|
@@ -25439,8 +25727,8 @@ init_fs();
|
|
|
25439
25727
|
init_config2();
|
|
25440
25728
|
import { execFile as execFile2 } from "child_process";
|
|
25441
25729
|
import { promisify as promisify2 } from "util";
|
|
25442
|
-
import { cp as cp2, mkdtemp, rm as rm5, readFile as
|
|
25443
|
-
import { resolve as
|
|
25730
|
+
import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile31, writeFile as writeFile7, unlink as unlink8, stat as stat7, open as open5, rename as rename8 } from "fs/promises";
|
|
25731
|
+
import { resolve as resolve45, join as join11 } from "path";
|
|
25444
25732
|
import { tmpdir } from "os";
|
|
25445
25733
|
var exec2 = promisify2(execFile2);
|
|
25446
25734
|
var VALID_CATEGORIES = ["projects", "playbooks", "todos", "servers", "config"];
|
|
@@ -25480,7 +25768,7 @@ async function resolveCategoryPath(category) {
|
|
|
25480
25768
|
case "servers":
|
|
25481
25769
|
return { sourcePath: serversDir(), repoPath: "servers", isFile: false };
|
|
25482
25770
|
case "config":
|
|
25483
|
-
return { sourcePath:
|
|
25771
|
+
return { sourcePath: resolve45(syntaurRoot(), "config.md"), repoPath: "config.md", isFile: true };
|
|
25484
25772
|
}
|
|
25485
25773
|
}
|
|
25486
25774
|
async function checkGitInstalled() {
|
|
@@ -25491,7 +25779,7 @@ async function checkGitInstalled() {
|
|
|
25491
25779
|
}
|
|
25492
25780
|
}
|
|
25493
25781
|
async function acquireLock2() {
|
|
25494
|
-
const lockPath =
|
|
25782
|
+
const lockPath = resolve45(syntaurRoot(), LOCK_FILE_NAME);
|
|
25495
25783
|
await ensureDir(syntaurRoot());
|
|
25496
25784
|
try {
|
|
25497
25785
|
const handle = await open5(lockPath, "wx");
|
|
@@ -25500,7 +25788,7 @@ async function acquireLock2() {
|
|
|
25500
25788
|
return lockPath;
|
|
25501
25789
|
} catch (err) {
|
|
25502
25790
|
if (err.code === "EEXIST") {
|
|
25503
|
-
const pid = await
|
|
25791
|
+
const pid = await readFile31(lockPath, "utf-8").catch(() => "");
|
|
25504
25792
|
throw new Error(
|
|
25505
25793
|
`Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
|
|
25506
25794
|
);
|
|
@@ -25538,7 +25826,7 @@ async function copyRecursive(src, dest) {
|
|
|
25538
25826
|
await ensureDir(dest);
|
|
25539
25827
|
await cp2(src, dest, { recursive: true, force: true });
|
|
25540
25828
|
} else {
|
|
25541
|
-
await ensureDir(
|
|
25829
|
+
await ensureDir(resolve45(dest, ".."));
|
|
25542
25830
|
await cp2(src, dest, { force: true });
|
|
25543
25831
|
}
|
|
25544
25832
|
}
|
|
@@ -25547,7 +25835,7 @@ function resolveCategoriesStrict(csv) {
|
|
|
25547
25835
|
return parseCategoriesStrict(parts);
|
|
25548
25836
|
}
|
|
25549
25837
|
async function readSanitizedConfig(configPath) {
|
|
25550
|
-
const content = await
|
|
25838
|
+
const content = await readFile31(configPath, "utf-8");
|
|
25551
25839
|
return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
|
|
25552
25840
|
}
|
|
25553
25841
|
async function backupToGithub(overrides) {
|
|
@@ -25570,11 +25858,11 @@ async function backupToGithub(overrides) {
|
|
|
25570
25858
|
let tmpDir = null;
|
|
25571
25859
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
25572
25860
|
try {
|
|
25573
|
-
tmpDir = await mkdtemp(
|
|
25861
|
+
tmpDir = await mkdtemp(join11(tmpdir(), "syntaur-backup-"));
|
|
25574
25862
|
await cloneOrInit(repo, tmpDir);
|
|
25575
25863
|
for (const category of categories) {
|
|
25576
25864
|
const { sourcePath, repoPath, isFile } = await resolveCategoryPath(category);
|
|
25577
|
-
const destPath =
|
|
25865
|
+
const destPath = join11(tmpDir, repoPath);
|
|
25578
25866
|
if (isFile) {
|
|
25579
25867
|
await rm5(destPath, { force: true });
|
|
25580
25868
|
} else {
|
|
@@ -25586,7 +25874,7 @@ async function backupToGithub(overrides) {
|
|
|
25586
25874
|
}
|
|
25587
25875
|
if (category === "config") {
|
|
25588
25876
|
const sanitized = await readSanitizedConfig(sourcePath);
|
|
25589
|
-
await ensureDir(
|
|
25877
|
+
await ensureDir(resolve45(destPath, ".."));
|
|
25590
25878
|
await writeFile7(destPath, sanitized, "utf-8");
|
|
25591
25879
|
} else {
|
|
25592
25880
|
await copyRecursive(sourcePath, destPath);
|
|
@@ -25640,7 +25928,7 @@ async function backupToGithub(overrides) {
|
|
|
25640
25928
|
}
|
|
25641
25929
|
async function safeRestoreCategory(localPath, repoSrcPath, isFile) {
|
|
25642
25930
|
if (isFile) {
|
|
25643
|
-
await ensureDir(
|
|
25931
|
+
await ensureDir(resolve45(localPath, ".."));
|
|
25644
25932
|
await cp2(repoSrcPath, localPath, { force: true });
|
|
25645
25933
|
return;
|
|
25646
25934
|
}
|
|
@@ -25702,7 +25990,7 @@ async function restoreFromGithub(overrides) {
|
|
|
25702
25990
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
25703
25991
|
try {
|
|
25704
25992
|
await updateBackupConfig({ lastRestore: timestamp });
|
|
25705
|
-
tmpDir = await mkdtemp(
|
|
25993
|
+
tmpDir = await mkdtemp(join11(tmpdir(), "syntaur-restore-"));
|
|
25706
25994
|
await cloneOrInit(repo, tmpDir);
|
|
25707
25995
|
for (const category of categories) {
|
|
25708
25996
|
if (category === "config") {
|
|
@@ -25711,7 +25999,7 @@ async function restoreFromGithub(overrides) {
|
|
|
25711
25999
|
}
|
|
25712
26000
|
try {
|
|
25713
26001
|
const { sourcePath: localPath, repoPath, isFile } = await resolveCategoryPath(category);
|
|
25714
|
-
const repoSrcPath =
|
|
26002
|
+
const repoSrcPath = join11(tmpDir, repoPath);
|
|
25715
26003
|
if (!await fileExists(repoSrcPath)) {
|
|
25716
26004
|
console.warn(`Category "${category}" not found in backup repo, skipping.`);
|
|
25717
26005
|
continue;
|
|
@@ -25741,7 +26029,7 @@ async function restoreFromGithub(overrides) {
|
|
|
25741
26029
|
}
|
|
25742
26030
|
async function getBackupStatus() {
|
|
25743
26031
|
const config = await readConfig();
|
|
25744
|
-
const lockPath =
|
|
26032
|
+
const lockPath = resolve45(syntaurRoot(), LOCK_FILE_NAME);
|
|
25745
26033
|
const locked = await fileExists(lockPath);
|
|
25746
26034
|
return {
|
|
25747
26035
|
repo: config.backup?.repo ?? null,
|