syntaur 0.62.0 → 0.64.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/README.md +20 -0
- package/dashboard/dist/assets/{_basePickBy-C0DmX-Lf.js → _basePickBy-CwwFEBj8.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-B656N5Fp.js → _baseUniq-BoxAvlar.js} +1 -1
- package/dashboard/dist/assets/{arc-DWrED_i3.js → arc-DohD31yM.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-B7gQ_B6F.js → architectureDiagram-2XIMDMQ5-J4SdvLJs.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-CrqgprVY.js → blockDiagram-WCTKOSBZ-BHuFRK4q.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-B6JBjUpY.js → c4Diagram-IC4MRINW-C5jiXpiB.js} +1 -1
- package/dashboard/dist/assets/channel-3-4_gf6X.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-Co4Kcyxc.js → chunk-4BX2VUAB-CcShTIhb.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-D9r_hMAn.js → chunk-55IACEB6-BJDZp3Ih.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-Dxxe9AQy.js → chunk-FMBD7UC4-DgX8V3qh.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-CXn2IalW.js → chunk-JSJVCQXG-DnYWrOFd.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-C0lpRUjK.js → chunk-KX2RTZJC-CvsjbfkS.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-CE2rv8cX.js → chunk-NQ4KR5QH-DVLwGTNn.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-Drs_1Xrc.js → chunk-QZHKN3VN-CFNsM5VV.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-IlTQx43D.js → chunk-WL4C6EOR-BgvnUwmv.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-CrATshpz.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-CrATshpz.js +1 -0
- package/dashboard/dist/assets/clone-CfV5MG52.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-C4Xv3sEQ.js → cose-bilkent-S5V4N54A-C4aLmHe0.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-BzfEk7Lk.js → dagre-KLK3FWXG-BN-9NtEU.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-CPwBFGGc.js → diagram-E7M64L7V-CeYAeYg6.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-DSbGA26W.js → diagram-IFDJBPK2-DwKlGh9_.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-XGhn9Qv-.js → diagram-P4PSJMXO-Brl0MDaZ.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-XeTWZkc6.js → erDiagram-INFDFZHY-DUyLXkUI.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-7vHLCNpk.js → flowDiagram-PKNHOUZH-DBgZTa3b.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-C9ifnMpV.js → ganttDiagram-A5KZAMGK-CTMXhGer.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-2o_Myer6.js → gitGraphDiagram-K3NZZRJ6-CMeLom6N.js} +1 -1
- package/dashboard/dist/assets/{graph-D9VPbOXW.js → graph-BQZrNogB.js} +1 -1
- package/dashboard/dist/assets/{index-BLRRdPLK.css → index-DlUgV5eO.css} +1 -1
- package/dashboard/dist/assets/index-tUNHiaW4.js +659 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-BlCEEtf8.js → infoDiagram-LFFYTUFH-CfiYCGUc.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BGYOHhaE.js → ishikawaDiagram-PHBUUO56-WSdBFOoI.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-Cn3nH440.js → journeyDiagram-4ABVD52K-u-S5-YRq.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-Beagbum1.js → kanban-definition-K7BYSVSG-B5sU56Sm.js} +1 -1
- package/dashboard/dist/assets/{layout-DXJOlege.js → layout-CH9z9Vzx.js} +1 -1
- package/dashboard/dist/assets/{linear-gb5BSwpC.js → linear-C-SiDLxL.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-leU3TCkv.js → mermaid.core-CGgij72_.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-DFGnCvnf.js → mindmap-definition-YRQLILUH-yu7UCSjb.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-HmtQWxMR.js → pieDiagram-SKSYHLDU-BGSUgFeI.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-BTDN-lUs.js → quadrantDiagram-337W2JSQ-XnJqntVQ.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-Da0Yc9Cc.js → requirementDiagram-Z7DCOOCP-CZ5tl3qr.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-DgfucBhp.js → sankeyDiagram-WA2Y5GQK-CgcGiKDB.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CFIS7rpy.js → sequenceDiagram-2WXFIKYE--GDQSqiF.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-CYPPhI5Y.js → stateDiagram-RAJIS63D-sbaNsQ3O.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-Cr-Lm_NG.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-BidLs2_X.js → timeline-definition-YZTLITO2-DieNq8qp.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-cEHQHmiX.js → treemap-KZPCXAKY-CND0AGzG.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-B78ndeUP.js → vennDiagram-LZ73GAT5-C32CbTtv.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-DhNlfMcT.js → xychartDiagram-JWTSCODW-I-8Tu4ki.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +441 -100
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +1011 -561
- package/dist/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-CDkiQtSs.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-C8nQGnrI.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-C8nQGnrI.js +0 -1
- package/dashboard/dist/assets/clone-BYliW2bC.js +0 -1
- package/dashboard/dist/assets/index-n4_qf3_i.js +0 -644
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DC2NHdS4.js +0 -1
package/dist/dashboard/server.js
CHANGED
|
@@ -6215,7 +6215,7 @@ async function computeFactsDetailed(input) {
|
|
|
6215
6215
|
const ac = countRealAcceptanceCriteria(body);
|
|
6216
6216
|
const needsPlanDigest = frontmatter.planApproval !== null || declarations.some((d) => d.type === "attestation" && d.binds === "plan");
|
|
6217
6217
|
const planFile = await latestPlanFile(assignmentDir);
|
|
6218
|
-
const [planFileContent,
|
|
6218
|
+
const [planFileContent, unresolvedQuestions2, depsSatisfied] = await Promise.all([
|
|
6219
6219
|
needsPlanDigest && planFile ? readFile9(resolve10(assignmentDir, planFile), "utf-8").catch(() => null) : Promise.resolve(null),
|
|
6220
6220
|
countUnresolvedQuestions(assignmentDir),
|
|
6221
6221
|
areDependenciesSatisfied(projectDir, frontmatter.dependsOn, terminalStatuses)
|
|
@@ -6233,7 +6233,7 @@ async function computeFactsDetailed(input) {
|
|
|
6233
6233
|
workspaceSet: frontmatter.workspace.repository !== null && frontmatter.workspace.branch !== null,
|
|
6234
6234
|
implementationStarted: frontmatter.implementationStarted,
|
|
6235
6235
|
depsSatisfied,
|
|
6236
|
-
unresolvedQuestions,
|
|
6236
|
+
unresolvedQuestions: unresolvedQuestions2,
|
|
6237
6237
|
blocked: frontmatter.blockedReason !== null,
|
|
6238
6238
|
parked: frontmatter.parked,
|
|
6239
6239
|
reviewRequested: frontmatter.reviewRequested,
|
|
@@ -7494,8 +7494,8 @@ async function migrateFromMarkdown(projectsDir) {
|
|
|
7494
7494
|
return allSessions.length;
|
|
7495
7495
|
}
|
|
7496
7496
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
7497
|
-
const { readFile:
|
|
7498
|
-
const raw2 = await
|
|
7497
|
+
const { readFile: readFile32 } = await import("fs/promises");
|
|
7498
|
+
const raw2 = await readFile32(filePath, "utf-8");
|
|
7499
7499
|
const sessions = [];
|
|
7500
7500
|
const lines = raw2.split("\n");
|
|
7501
7501
|
let inTable = false;
|
|
@@ -7954,8 +7954,8 @@ function scanKey(serversDir2, projectsDir, assignmentsDir2) {
|
|
|
7954
7954
|
return `${serversDir2}\0${projectsDir}\0${assignmentsDir2 ?? ""}`;
|
|
7955
7955
|
}
|
|
7956
7956
|
function delay(ms) {
|
|
7957
|
-
return new Promise((
|
|
7958
|
-
const timer3 = setTimeout(
|
|
7957
|
+
return new Promise((resolve47) => {
|
|
7958
|
+
const timer3 = setTimeout(resolve47, ms);
|
|
7959
7959
|
if (typeof timer3.unref === "function") {
|
|
7960
7960
|
timer3.unref();
|
|
7961
7961
|
}
|
|
@@ -11662,8 +11662,8 @@ var init_renderers = __esm({
|
|
|
11662
11662
|
});
|
|
11663
11663
|
|
|
11664
11664
|
// src/targets/user-descriptors.ts
|
|
11665
|
-
import { resolve as
|
|
11666
|
-
import { readFile as
|
|
11665
|
+
import { resolve as resolve43 } from "path";
|
|
11666
|
+
import { readFile as readFile30, readdir as readdir18 } from "fs/promises";
|
|
11667
11667
|
var VALID_RENDERER_KEYS;
|
|
11668
11668
|
var init_user_descriptors = __esm({
|
|
11669
11669
|
"src/targets/user-descriptors.ts"() {
|
|
@@ -11677,20 +11677,20 @@ var init_user_descriptors = __esm({
|
|
|
11677
11677
|
|
|
11678
11678
|
// src/targets/registry.ts
|
|
11679
11679
|
import { homedir as homedir7 } from "os";
|
|
11680
|
-
import { join as join10, resolve as
|
|
11680
|
+
import { join as join10, resolve as resolve44 } from "path";
|
|
11681
11681
|
function home(...segments) {
|
|
11682
|
-
return
|
|
11682
|
+
return resolve44(homedir7(), ...segments);
|
|
11683
11683
|
}
|
|
11684
11684
|
function hermesHome() {
|
|
11685
11685
|
const env = process.env.HERMES_HOME;
|
|
11686
|
-
return env && env.length > 0 ?
|
|
11686
|
+
return env && env.length > 0 ? resolve44(env) : home(".hermes");
|
|
11687
11687
|
}
|
|
11688
11688
|
function hermesSkillsDir() {
|
|
11689
|
-
return
|
|
11689
|
+
return resolve44(hermesHome(), "skills");
|
|
11690
11690
|
}
|
|
11691
11691
|
function codexHome() {
|
|
11692
11692
|
const env = process.env.CODEX_HOME;
|
|
11693
|
-
return env && env.length > 0 ?
|
|
11693
|
+
return env && env.length > 0 ? resolve44(env) : home(".codex");
|
|
11694
11694
|
}
|
|
11695
11695
|
function toDiscovered(meta) {
|
|
11696
11696
|
if (!meta) return null;
|
|
@@ -11751,7 +11751,7 @@ var init_registry = __esm({
|
|
|
11751
11751
|
skillsShAgentId: "codex",
|
|
11752
11752
|
nativePlugin: "codex",
|
|
11753
11753
|
detect: detectDir(codexHome()),
|
|
11754
|
-
skillsDir: { global:
|
|
11754
|
+
skillsDir: { global: resolve44(codexHome(), "skills") },
|
|
11755
11755
|
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
|
|
11756
11756
|
sessions: codexSessions
|
|
11757
11757
|
},
|
|
@@ -11819,7 +11819,7 @@ var init_registry = __esm({
|
|
|
11819
11819
|
tier3: {
|
|
11820
11820
|
kind: "hermes-plugin",
|
|
11821
11821
|
source: "platforms/hermes/plugins/syntaur",
|
|
11822
|
-
installDir: () =>
|
|
11822
|
+
installDir: () => resolve44(hermesHome(), "plugins", "syntaur"),
|
|
11823
11823
|
entry: "plugin.yaml"
|
|
11824
11824
|
}
|
|
11825
11825
|
}
|
|
@@ -11838,8 +11838,8 @@ __export(scanner_exports2, {
|
|
|
11838
11838
|
import { execFile as execFile3, execFileSync as execFileSync4 } from "child_process";
|
|
11839
11839
|
import { promisify as promisify3 } from "util";
|
|
11840
11840
|
import { statSync as statSync4 } from "fs";
|
|
11841
|
-
import { readFile as
|
|
11842
|
-
import { resolve as
|
|
11841
|
+
import { readFile as readFile31 } from "fs/promises";
|
|
11842
|
+
import { resolve as resolve45 } from "path";
|
|
11843
11843
|
function emptySummary() {
|
|
11844
11844
|
return { discovered: 0, inserted: 0, revived: 0, swept: 0, skipped: 0, changed: false };
|
|
11845
11845
|
}
|
|
@@ -11895,10 +11895,10 @@ async function defaultOpenFiles(files) {
|
|
|
11895
11895
|
async function readContextLink(cwd, cache3) {
|
|
11896
11896
|
if (cache3.has(cwd)) return cache3.get(cwd);
|
|
11897
11897
|
let link = null;
|
|
11898
|
-
const path =
|
|
11898
|
+
const path = resolve45(cwd, ".syntaur", "context.json");
|
|
11899
11899
|
if (await fileExists(path)) {
|
|
11900
11900
|
try {
|
|
11901
|
-
const parsed = JSON.parse(await
|
|
11901
|
+
const parsed = JSON.parse(await readFile31(path, "utf-8"));
|
|
11902
11902
|
link = {
|
|
11903
11903
|
projectSlug: typeof parsed.projectSlug === "string" ? parsed.projectSlug : null,
|
|
11904
11904
|
assignmentSlug: typeof parsed.assignmentSlug === "string" ? parsed.assignmentSlug : null
|
|
@@ -12063,7 +12063,7 @@ init_assignment_resolver();
|
|
|
12063
12063
|
init_agent_sessions();
|
|
12064
12064
|
import express from "express";
|
|
12065
12065
|
import { createServer } from "http";
|
|
12066
|
-
import { resolve as
|
|
12066
|
+
import { resolve as resolve46 } from "path";
|
|
12067
12067
|
import { writeFile as writeFile8, unlink as unlink9 } from "fs/promises";
|
|
12068
12068
|
import { WebSocketServer, WebSocket } from "ws";
|
|
12069
12069
|
|
|
@@ -17857,7 +17857,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
17857
17857
|
`Spawn failed: ${msg}. Verify the terminal is installed and on PATH.`
|
|
17858
17858
|
);
|
|
17859
17859
|
}
|
|
17860
|
-
await new Promise((
|
|
17860
|
+
await new Promise((resolve47, reject) => {
|
|
17861
17861
|
let settled = false;
|
|
17862
17862
|
let stderr = "";
|
|
17863
17863
|
const finishOk = () => {
|
|
@@ -17867,7 +17867,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
17867
17867
|
child.unref();
|
|
17868
17868
|
} catch {
|
|
17869
17869
|
}
|
|
17870
|
-
|
|
17870
|
+
resolve47();
|
|
17871
17871
|
};
|
|
17872
17872
|
const finishErr = (remediation) => {
|
|
17873
17873
|
if (settled) return;
|
|
@@ -20934,15 +20934,355 @@ function parseFilters(query) {
|
|
|
20934
20934
|
return out;
|
|
20935
20935
|
}
|
|
20936
20936
|
|
|
20937
|
+
// src/dashboard/api-inbox.ts
|
|
20938
|
+
import { Router as Router16 } from "express";
|
|
20939
|
+
|
|
20940
|
+
// src/inbox/index.ts
|
|
20941
|
+
init_fs();
|
|
20942
|
+
init_assignment_walk();
|
|
20943
|
+
init_parser();
|
|
20944
|
+
init_facts();
|
|
20945
|
+
init_state_machine();
|
|
20946
|
+
import { resolve as resolve36 } from "path";
|
|
20947
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
20948
|
+
|
|
20949
|
+
// src/inbox/types.ts
|
|
20950
|
+
var INBOX_CATEGORIES = [
|
|
20951
|
+
"review",
|
|
20952
|
+
"blocked",
|
|
20953
|
+
"question",
|
|
20954
|
+
"plan-approval"
|
|
20955
|
+
];
|
|
20956
|
+
|
|
20957
|
+
// src/inbox/index.ts
|
|
20958
|
+
function isReview(a) {
|
|
20959
|
+
return a.status === "review";
|
|
20960
|
+
}
|
|
20961
|
+
function isBlocked(a) {
|
|
20962
|
+
return a.status === "blocked";
|
|
20963
|
+
}
|
|
20964
|
+
function isUnresolvedQuestion(c) {
|
|
20965
|
+
return c.type === "question" && c.resolved !== true;
|
|
20966
|
+
}
|
|
20967
|
+
function unresolvedQuestions(comments) {
|
|
20968
|
+
return comments.filter(isUnresolvedQuestion);
|
|
20969
|
+
}
|
|
20970
|
+
async function isPlanAwaitingApproval(a, assignmentDir) {
|
|
20971
|
+
if (a.status !== "ready_for_planning") return false;
|
|
20972
|
+
const latest = await latestPlanFile(assignmentDir);
|
|
20973
|
+
if (latest === null) return false;
|
|
20974
|
+
const approved = await isPlanApproved(assignmentDir, { planApproval: a.planApproval });
|
|
20975
|
+
return !approved;
|
|
20976
|
+
}
|
|
20977
|
+
function canonicalRfc3339(ms) {
|
|
20978
|
+
return new Date(ms).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
20979
|
+
}
|
|
20980
|
+
function validTimestamp(value) {
|
|
20981
|
+
if (typeof value !== "string") return null;
|
|
20982
|
+
const t = value.trim();
|
|
20983
|
+
if (t.length === 0) return null;
|
|
20984
|
+
const ms = Date.parse(t);
|
|
20985
|
+
return Number.isNaN(ms) ? null : canonicalRfc3339(ms);
|
|
20986
|
+
}
|
|
20987
|
+
function latestStatusHistoryAt(a) {
|
|
20988
|
+
let best = null;
|
|
20989
|
+
for (const e of a.statusHistory) {
|
|
20990
|
+
const at = validTimestamp(e.at);
|
|
20991
|
+
if (at === null) continue;
|
|
20992
|
+
const ms = Date.parse(at);
|
|
20993
|
+
if (best === null || ms >= best.ms) best = { at, ms };
|
|
20994
|
+
}
|
|
20995
|
+
return best?.at ?? null;
|
|
20996
|
+
}
|
|
20997
|
+
function latestStatusHistoryAtWhere(a, pred) {
|
|
20998
|
+
let best = null;
|
|
20999
|
+
for (const e of a.statusHistory) {
|
|
21000
|
+
if (!pred(e)) continue;
|
|
21001
|
+
const at = validTimestamp(e.at);
|
|
21002
|
+
if (at === null) continue;
|
|
21003
|
+
const ms = Date.parse(at);
|
|
21004
|
+
if (best === null || ms >= best.ms) best = { at, ms };
|
|
21005
|
+
}
|
|
21006
|
+
return best?.at ?? null;
|
|
21007
|
+
}
|
|
21008
|
+
function resolveSince(category, a, now, comment) {
|
|
21009
|
+
let primary = null;
|
|
21010
|
+
switch (category) {
|
|
21011
|
+
case "review":
|
|
21012
|
+
primary = latestStatusHistoryAtWhere(a, (e) => e.to === "review");
|
|
21013
|
+
break;
|
|
21014
|
+
case "blocked":
|
|
21015
|
+
primary = latestStatusHistoryAtWhere(a, (e) => e.dispositionTo === "blocked");
|
|
21016
|
+
break;
|
|
21017
|
+
case "question":
|
|
21018
|
+
primary = validTimestamp(comment?.timestamp);
|
|
21019
|
+
break;
|
|
21020
|
+
case "plan-approval":
|
|
21021
|
+
primary = latestStatusHistoryAt(a);
|
|
21022
|
+
break;
|
|
21023
|
+
}
|
|
21024
|
+
return primary ?? latestStatusHistoryAt(a) ?? validTimestamp(a.updated) ?? validTimestamp(a.created) ?? canonicalRfc3339(now);
|
|
21025
|
+
}
|
|
21026
|
+
function computeAgeMs(since, now) {
|
|
21027
|
+
const ms = Date.parse(since);
|
|
21028
|
+
if (Number.isNaN(ms)) return 0;
|
|
21029
|
+
return Math.max(0, now - ms);
|
|
21030
|
+
}
|
|
21031
|
+
var KNOWN_TRANSITION_CLI_VERBS = /* @__PURE__ */ new Set([
|
|
21032
|
+
"start",
|
|
21033
|
+
"complete",
|
|
21034
|
+
"fail",
|
|
21035
|
+
"reopen",
|
|
21036
|
+
"block",
|
|
21037
|
+
"unblock",
|
|
21038
|
+
"review"
|
|
21039
|
+
]);
|
|
21040
|
+
function deriveReviewVerbs(config) {
|
|
21041
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
21042
|
+
for (const t of config.transitions) {
|
|
21043
|
+
if (t.from === "review") candidates.add(t.command);
|
|
21044
|
+
}
|
|
21045
|
+
for (const key of config.transitionTable.keys()) {
|
|
21046
|
+
if (key.startsWith("review:")) candidates.add(key.slice("review:".length));
|
|
21047
|
+
}
|
|
21048
|
+
const blockedParked = config.blockedParkedStatuses ?? /* @__PURE__ */ new Set();
|
|
21049
|
+
const terminalAccept = [];
|
|
21050
|
+
const activeReopen = [];
|
|
21051
|
+
for (const command of candidates) {
|
|
21052
|
+
const target = getTargetStatus("review", command, config.transitionTable);
|
|
21053
|
+
if (target === null) continue;
|
|
21054
|
+
const isTerminal = config.terminalStatuses.has(target);
|
|
21055
|
+
if (isTerminal && command !== "fail" && KNOWN_TRANSITION_CLI_VERBS.has(command)) {
|
|
21056
|
+
terminalAccept.push(command);
|
|
21057
|
+
}
|
|
21058
|
+
if (!isTerminal && !blockedParked.has(target) && (command === "start" || command === "reopen")) {
|
|
21059
|
+
activeReopen.push(command);
|
|
21060
|
+
}
|
|
21061
|
+
}
|
|
21062
|
+
const accept = terminalAccept.find((c) => c === "complete") ?? terminalAccept[0] ?? null;
|
|
21063
|
+
const reopen = activeReopen.find((c) => c === "start") ?? activeReopen.find((c) => c === "reopen") ?? null;
|
|
21064
|
+
return { accept, reopen };
|
|
21065
|
+
}
|
|
21066
|
+
function targetAndProject(item) {
|
|
21067
|
+
if (item.project === null) {
|
|
21068
|
+
return { target: item.assignmentId, projectFlag: "" };
|
|
21069
|
+
}
|
|
21070
|
+
return { target: item.assignmentSlug, projectFlag: ` --project ${item.project}` };
|
|
21071
|
+
}
|
|
21072
|
+
function buildAction(category, item, ctx) {
|
|
21073
|
+
const { target, projectFlag } = targetAndProject(item);
|
|
21074
|
+
switch (category) {
|
|
21075
|
+
case "review":
|
|
21076
|
+
if (ctx.acceptCommand) {
|
|
21077
|
+
return {
|
|
21078
|
+
verb: "Accept",
|
|
21079
|
+
command: `syntaur ${ctx.acceptCommand} ${target}${projectFlag}`
|
|
21080
|
+
};
|
|
21081
|
+
}
|
|
21082
|
+
if (ctx.reopenCommand) {
|
|
21083
|
+
return {
|
|
21084
|
+
verb: "Reopen",
|
|
21085
|
+
command: `syntaur ${ctx.reopenCommand} ${target}${projectFlag}`
|
|
21086
|
+
};
|
|
21087
|
+
}
|
|
21088
|
+
return {
|
|
21089
|
+
verb: "Review",
|
|
21090
|
+
command: `syntaur timeline ${target}${projectFlag}`
|
|
21091
|
+
};
|
|
21092
|
+
case "blocked":
|
|
21093
|
+
return {
|
|
21094
|
+
verb: "Unblock",
|
|
21095
|
+
command: `syntaur unblock ${target}${projectFlag}`
|
|
21096
|
+
};
|
|
21097
|
+
case "question":
|
|
21098
|
+
return {
|
|
21099
|
+
verb: "Answer",
|
|
21100
|
+
command: `syntaur comment ${target} "<answer>" --reply-to ${ctx.commentId ?? ""}${projectFlag}`
|
|
21101
|
+
};
|
|
21102
|
+
case "plan-approval":
|
|
21103
|
+
return {
|
|
21104
|
+
verb: "Approve plan",
|
|
21105
|
+
command: `syntaur plan approve ${target}${projectFlag}`
|
|
21106
|
+
};
|
|
21107
|
+
}
|
|
21108
|
+
}
|
|
21109
|
+
function orderByUrgency(items) {
|
|
21110
|
+
return [...items].sort((x, y) => y.ageMs - x.ageMs);
|
|
21111
|
+
}
|
|
21112
|
+
function summarizeQuestion(c) {
|
|
21113
|
+
const body = c.body.replace(/\s+/g, " ").trim();
|
|
21114
|
+
const clipped = body.length > 140 ? `${body.slice(0, 137)}...` : body;
|
|
21115
|
+
return clipped.length > 0 ? clipped : "(empty question)";
|
|
21116
|
+
}
|
|
21117
|
+
async function computeInbox(opts) {
|
|
21118
|
+
const now = opts.now ?? Date.now();
|
|
21119
|
+
const typeFilter = opts.types && opts.types.length > 0 ? new Set(opts.types) : null;
|
|
21120
|
+
const reviewVerbs = deriveReviewVerbs(opts.statusConfig);
|
|
21121
|
+
const walk = await listAssignmentsByProject(opts.projectsDir, opts.assignmentsDir);
|
|
21122
|
+
const matched = [];
|
|
21123
|
+
for (const entry of walk.withAssignmentMd) {
|
|
21124
|
+
if (opts.project !== void 0 && entry.projectSlug !== opts.project) continue;
|
|
21125
|
+
let parsed;
|
|
21126
|
+
try {
|
|
21127
|
+
const content = await readFile25(resolve36(entry.assignmentDir, "assignment.md"), "utf-8");
|
|
21128
|
+
parsed = parseAssignmentFull(content);
|
|
21129
|
+
} catch {
|
|
21130
|
+
continue;
|
|
21131
|
+
}
|
|
21132
|
+
if (parsed.archived) continue;
|
|
21133
|
+
if (parsed.disposition === "parked" || parsed.disposition === "terminal") continue;
|
|
21134
|
+
if (opts.statusConfig.terminalStatuses.has(parsed.status)) continue;
|
|
21135
|
+
const project = entry.projectSlug;
|
|
21136
|
+
const assignmentSlug = entry.assignmentSlug;
|
|
21137
|
+
const assignmentId = parsed.id;
|
|
21138
|
+
const title = parsed.title;
|
|
21139
|
+
const baseItem = { project, assignmentSlug, assignmentId };
|
|
21140
|
+
if ((!typeFilter || typeFilter.has("review")) && isReview(parsed)) {
|
|
21141
|
+
const since = resolveSince("review", parsed, now);
|
|
21142
|
+
matched.push({
|
|
21143
|
+
...baseItem,
|
|
21144
|
+
title,
|
|
21145
|
+
category: "review",
|
|
21146
|
+
since,
|
|
21147
|
+
ageMs: computeAgeMs(since, now),
|
|
21148
|
+
summary: parsed.reviewRequested ? "Review requested \u2014 awaiting accept or reopen." : "Awaiting review \u2014 accept or reopen.",
|
|
21149
|
+
acceptCommand: reviewVerbs.accept,
|
|
21150
|
+
reopenCommand: reviewVerbs.reopen,
|
|
21151
|
+
action: buildAction("review", baseItem, {
|
|
21152
|
+
acceptCommand: reviewVerbs.accept,
|
|
21153
|
+
reopenCommand: reviewVerbs.reopen
|
|
21154
|
+
})
|
|
21155
|
+
});
|
|
21156
|
+
}
|
|
21157
|
+
if ((!typeFilter || typeFilter.has("blocked")) && isBlocked(parsed)) {
|
|
21158
|
+
const since = resolveSince("blocked", parsed, now);
|
|
21159
|
+
matched.push({
|
|
21160
|
+
...baseItem,
|
|
21161
|
+
title,
|
|
21162
|
+
category: "blocked",
|
|
21163
|
+
since,
|
|
21164
|
+
ageMs: computeAgeMs(since, now),
|
|
21165
|
+
summary: parsed.blockedReason ? `Blocked: ${parsed.blockedReason}` : "Blocked \u2014 awaiting unblock.",
|
|
21166
|
+
action: buildAction("blocked", baseItem, {})
|
|
21167
|
+
});
|
|
21168
|
+
}
|
|
21169
|
+
if (!typeFilter || typeFilter.has("question")) {
|
|
21170
|
+
const commentsPath = resolve36(entry.assignmentDir, "comments.md");
|
|
21171
|
+
if (await fileExists(commentsPath)) {
|
|
21172
|
+
try {
|
|
21173
|
+
const content = await readFile25(commentsPath, "utf-8");
|
|
21174
|
+
const parsedComments = parseComments(content);
|
|
21175
|
+
for (const c of unresolvedQuestions(parsedComments.entries)) {
|
|
21176
|
+
const since = resolveSince("question", parsed, now, c);
|
|
21177
|
+
matched.push({
|
|
21178
|
+
...baseItem,
|
|
21179
|
+
title,
|
|
21180
|
+
category: "question",
|
|
21181
|
+
since,
|
|
21182
|
+
ageMs: computeAgeMs(since, now),
|
|
21183
|
+
summary: summarizeQuestion(c),
|
|
21184
|
+
commentId: c.id,
|
|
21185
|
+
action: buildAction("question", baseItem, {
|
|
21186
|
+
commentId: c.id
|
|
21187
|
+
})
|
|
21188
|
+
});
|
|
21189
|
+
}
|
|
21190
|
+
} catch {
|
|
21191
|
+
}
|
|
21192
|
+
}
|
|
21193
|
+
}
|
|
21194
|
+
if (!typeFilter || typeFilter.has("plan-approval")) {
|
|
21195
|
+
if (await isPlanAwaitingApproval(parsed, entry.assignmentDir)) {
|
|
21196
|
+
const since = resolveSince("plan-approval", parsed, now);
|
|
21197
|
+
matched.push({
|
|
21198
|
+
...baseItem,
|
|
21199
|
+
title,
|
|
21200
|
+
category: "plan-approval",
|
|
21201
|
+
since,
|
|
21202
|
+
ageMs: computeAgeMs(since, now),
|
|
21203
|
+
summary: "Plan awaiting approval.",
|
|
21204
|
+
action: buildAction("plan-approval", baseItem, {})
|
|
21205
|
+
});
|
|
21206
|
+
}
|
|
21207
|
+
}
|
|
21208
|
+
}
|
|
21209
|
+
const counts = {
|
|
21210
|
+
review: 0,
|
|
21211
|
+
blocked: 0,
|
|
21212
|
+
question: 0,
|
|
21213
|
+
"plan-approval": 0
|
|
21214
|
+
};
|
|
21215
|
+
for (const item of matched) counts[item.category]++;
|
|
21216
|
+
const total = matched.length;
|
|
21217
|
+
const ordered = [];
|
|
21218
|
+
for (const category of INBOX_CATEGORIES) {
|
|
21219
|
+
ordered.push(...orderByUrgency(matched.filter((i) => i.category === category)));
|
|
21220
|
+
}
|
|
21221
|
+
const items = opts.limit !== void 0 && opts.limit >= 0 ? ordered.slice(0, opts.limit) : ordered;
|
|
21222
|
+
return { items, counts, total };
|
|
21223
|
+
}
|
|
21224
|
+
|
|
21225
|
+
// src/dashboard/api-inbox.ts
|
|
21226
|
+
init_api();
|
|
21227
|
+
init_config2();
|
|
21228
|
+
function createInboxRouter(projectsDir, assignmentsDir2) {
|
|
21229
|
+
const router = Router16();
|
|
21230
|
+
router.get("/inbox", async (req2, res) => {
|
|
21231
|
+
try {
|
|
21232
|
+
const project = typeof req2.query.project === "string" && req2.query.project.length > 0 ? req2.query.project : void 0;
|
|
21233
|
+
let types;
|
|
21234
|
+
if (typeof req2.query.type === "string" && req2.query.type.length > 0) {
|
|
21235
|
+
const raw2 = req2.query.type.split(",").map((t) => t.trim()).filter(Boolean);
|
|
21236
|
+
const unknown = raw2.filter((t) => !INBOX_CATEGORIES.includes(t));
|
|
21237
|
+
if (unknown.length > 0) {
|
|
21238
|
+
res.status(400).json({
|
|
21239
|
+
error: `Unknown inbox type(s): ${unknown.map((u) => JSON.stringify(u)).join(", ")}. Valid types: ${INBOX_CATEGORIES.join(", ")}.`
|
|
21240
|
+
});
|
|
21241
|
+
return;
|
|
21242
|
+
}
|
|
21243
|
+
types = raw2;
|
|
21244
|
+
}
|
|
21245
|
+
let limit;
|
|
21246
|
+
if (typeof req2.query.limit === "string") {
|
|
21247
|
+
const n = Number(req2.query.limit);
|
|
21248
|
+
if (Number.isInteger(n) && n > 0) limit = n;
|
|
21249
|
+
}
|
|
21250
|
+
const resolved = await getStatusConfig();
|
|
21251
|
+
const headline = (resolved.derive ?? DEFAULT_DERIVE_CONFIG).headline;
|
|
21252
|
+
const blockedParkedStatuses = new Set(
|
|
21253
|
+
[headline.blocked, headline.parked].filter(Boolean)
|
|
21254
|
+
);
|
|
21255
|
+
const statusConfig = { ...resolved, blockedParkedStatuses };
|
|
21256
|
+
const result = await computeInbox({
|
|
21257
|
+
projectsDir,
|
|
21258
|
+
assignmentsDir: assignmentsDir2,
|
|
21259
|
+
project,
|
|
21260
|
+
types,
|
|
21261
|
+
limit,
|
|
21262
|
+
statusConfig
|
|
21263
|
+
});
|
|
21264
|
+
res.json(result);
|
|
21265
|
+
} catch (error) {
|
|
21266
|
+
console.warn("[inbox] failed to compute inbox:", error);
|
|
21267
|
+
res.json({
|
|
21268
|
+
items: [],
|
|
21269
|
+
counts: { review: 0, blocked: 0, question: 0, "plan-approval": 0 },
|
|
21270
|
+
total: 0
|
|
21271
|
+
});
|
|
21272
|
+
}
|
|
21273
|
+
});
|
|
21274
|
+
return router;
|
|
21275
|
+
}
|
|
21276
|
+
|
|
20937
21277
|
// src/dashboard/api-playbooks.ts
|
|
20938
21278
|
init_api();
|
|
20939
21279
|
init_parser();
|
|
20940
21280
|
init_slug();
|
|
20941
21281
|
init_timestamp();
|
|
20942
21282
|
init_fs();
|
|
20943
|
-
import { Router as
|
|
20944
|
-
import { resolve as
|
|
20945
|
-
import { readFile as
|
|
21283
|
+
import { Router as Router17 } from "express";
|
|
21284
|
+
import { resolve as resolve37 } from "path";
|
|
21285
|
+
import { readFile as readFile26 } from "fs/promises";
|
|
20946
21286
|
init_playbooks();
|
|
20947
21287
|
function statusForPlaybookError(code) {
|
|
20948
21288
|
switch (code) {
|
|
@@ -20957,7 +21297,7 @@ function statusForPlaybookError(code) {
|
|
|
20957
21297
|
}
|
|
20958
21298
|
}
|
|
20959
21299
|
function createPlaybooksRouter(playbooksDir2) {
|
|
20960
|
-
const router =
|
|
21300
|
+
const router = Router17();
|
|
20961
21301
|
router.get("/", async (_req, res) => {
|
|
20962
21302
|
try {
|
|
20963
21303
|
const playbooks = await listPlaybooks(playbooksDir2);
|
|
@@ -21024,8 +21364,8 @@ function createPlaybooksRouter(playbooksDir2) {
|
|
|
21024
21364
|
res.status(404).json({ error: `Playbook "${req2.params.slug}" not found` });
|
|
21025
21365
|
return;
|
|
21026
21366
|
}
|
|
21027
|
-
const filePath =
|
|
21028
|
-
const content = await
|
|
21367
|
+
const filePath = resolve37(playbooksDir2, resolved.filename);
|
|
21368
|
+
const content = await readFile26(filePath, "utf-8");
|
|
21029
21369
|
res.json({
|
|
21030
21370
|
documentType: "playbook",
|
|
21031
21371
|
title: `Edit Playbook: ${resolved.slug}`,
|
|
@@ -21050,7 +21390,7 @@ function createPlaybooksRouter(playbooksDir2) {
|
|
|
21050
21390
|
return;
|
|
21051
21391
|
}
|
|
21052
21392
|
await ensureDir(playbooksDir2);
|
|
21053
|
-
const filePath =
|
|
21393
|
+
const filePath = resolve37(playbooksDir2, `${slug}.md`);
|
|
21054
21394
|
if (await fileExists(filePath)) {
|
|
21055
21395
|
res.status(409).json({ error: `Playbook "${slug}" already exists` });
|
|
21056
21396
|
return;
|
|
@@ -21074,7 +21414,7 @@ function createPlaybooksRouter(playbooksDir2) {
|
|
|
21074
21414
|
res.status(404).json({ error: `Playbook "${req2.params.slug}" not found` });
|
|
21075
21415
|
return;
|
|
21076
21416
|
}
|
|
21077
|
-
const filePath =
|
|
21417
|
+
const filePath = resolve37(playbooksDir2, resolved.filename);
|
|
21078
21418
|
await writeFileForce(filePath, content);
|
|
21079
21419
|
await rebuildPlaybookManifest(playbooksDir2);
|
|
21080
21420
|
res.json({ slug: resolved.slug, path: filePath });
|
|
@@ -21121,7 +21461,7 @@ init_fs_migration();
|
|
|
21121
21461
|
init_parser2();
|
|
21122
21462
|
init_fs();
|
|
21123
21463
|
init_paths();
|
|
21124
|
-
import { Router as
|
|
21464
|
+
import { Router as Router19 } from "express";
|
|
21125
21465
|
import { readdir as readdir16 } from "fs/promises";
|
|
21126
21466
|
import { resolve as resolvePath, dirname as dirname10 } from "path";
|
|
21127
21467
|
import { rename as rename6, mkdir as mkdir5 } from "fs/promises";
|
|
@@ -21137,7 +21477,7 @@ init_uuid();
|
|
|
21137
21477
|
init_paths();
|
|
21138
21478
|
init_fs();
|
|
21139
21479
|
init_config2();
|
|
21140
|
-
import { resolve as
|
|
21480
|
+
import { resolve as resolve38 } from "path";
|
|
21141
21481
|
async function createAssignmentCommand(title, options) {
|
|
21142
21482
|
if (!title.trim()) {
|
|
21143
21483
|
throw new Error("Assignment title cannot be empty.");
|
|
@@ -21212,14 +21552,14 @@ async function createAssignmentCommand(title, options) {
|
|
|
21212
21552
|
if (options.oneOff) {
|
|
21213
21553
|
const standaloneRoot = assignmentsDir();
|
|
21214
21554
|
folderName = id;
|
|
21215
|
-
assignmentDir =
|
|
21555
|
+
assignmentDir = resolve38(standaloneRoot, folderName);
|
|
21216
21556
|
projectSlug = null;
|
|
21217
21557
|
await ensureDir(standaloneRoot);
|
|
21218
21558
|
} else {
|
|
21219
21559
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
21220
21560
|
projectSlug = options.project;
|
|
21221
|
-
const projectDir =
|
|
21222
|
-
const projectMdPath =
|
|
21561
|
+
const projectDir = resolve38(baseDir, projectSlug);
|
|
21562
|
+
const projectMdPath = resolve38(projectDir, "project.md");
|
|
21223
21563
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
21224
21564
|
throw new Error(
|
|
21225
21565
|
`Project "${projectSlug}" not found at ${projectDir}.
|
|
@@ -21227,9 +21567,9 @@ Run 'syntaur create-project' first or use --one-off.`
|
|
|
21227
21567
|
);
|
|
21228
21568
|
}
|
|
21229
21569
|
if (dependsOn.length > 0) {
|
|
21230
|
-
const depDirBase =
|
|
21570
|
+
const depDirBase = resolve38(projectDir, "assignments");
|
|
21231
21571
|
for (const dep of dependsOn) {
|
|
21232
|
-
const depDir =
|
|
21572
|
+
const depDir = resolve38(depDirBase, dep);
|
|
21233
21573
|
if (!await fileExists(depDir)) {
|
|
21234
21574
|
console.warn(
|
|
21235
21575
|
`Warning: dependency "${dep}" does not exist in project "${projectSlug}" yet.`
|
|
@@ -21238,7 +21578,7 @@ Run 'syntaur create-project' first or use --one-off.`
|
|
|
21238
21578
|
}
|
|
21239
21579
|
}
|
|
21240
21580
|
folderName = assignmentSlug;
|
|
21241
|
-
assignmentDir =
|
|
21581
|
+
assignmentDir = resolve38(projectDir, "assignments", folderName);
|
|
21242
21582
|
}
|
|
21243
21583
|
if (await fileExists(assignmentDir)) {
|
|
21244
21584
|
throw new Error(
|
|
@@ -21250,7 +21590,7 @@ Use --slug to specify a different slug.`
|
|
|
21250
21590
|
const companionAssignmentRef = projectSlug === null ? id : assignmentSlug;
|
|
21251
21591
|
const files = [
|
|
21252
21592
|
[
|
|
21253
|
-
|
|
21593
|
+
resolve38(assignmentDir, "assignment.md"),
|
|
21254
21594
|
renderAssignment({
|
|
21255
21595
|
id,
|
|
21256
21596
|
slug: assignmentSlug,
|
|
@@ -21268,35 +21608,35 @@ Use --slug to specify a different slug.`
|
|
|
21268
21608
|
})
|
|
21269
21609
|
],
|
|
21270
21610
|
[
|
|
21271
|
-
|
|
21611
|
+
resolve38(assignmentDir, "scratchpad.md"),
|
|
21272
21612
|
renderScratchpad({
|
|
21273
21613
|
assignmentSlug: companionAssignmentRef,
|
|
21274
21614
|
timestamp
|
|
21275
21615
|
})
|
|
21276
21616
|
],
|
|
21277
21617
|
[
|
|
21278
|
-
|
|
21618
|
+
resolve38(assignmentDir, "handoff.md"),
|
|
21279
21619
|
renderHandoff({
|
|
21280
21620
|
assignmentSlug: companionAssignmentRef,
|
|
21281
21621
|
timestamp
|
|
21282
21622
|
})
|
|
21283
21623
|
],
|
|
21284
21624
|
[
|
|
21285
|
-
|
|
21625
|
+
resolve38(assignmentDir, "decision-record.md"),
|
|
21286
21626
|
renderDecisionRecord({
|
|
21287
21627
|
assignmentSlug: companionAssignmentRef,
|
|
21288
21628
|
timestamp
|
|
21289
21629
|
})
|
|
21290
21630
|
],
|
|
21291
21631
|
[
|
|
21292
|
-
|
|
21632
|
+
resolve38(assignmentDir, "progress.md"),
|
|
21293
21633
|
renderProgress({
|
|
21294
21634
|
assignment: companionAssignmentRef,
|
|
21295
21635
|
timestamp
|
|
21296
21636
|
})
|
|
21297
21637
|
],
|
|
21298
21638
|
[
|
|
21299
|
-
|
|
21639
|
+
resolve38(assignmentDir, "comments.md"),
|
|
21300
21640
|
renderComments({
|
|
21301
21641
|
assignment: companionAssignmentRef,
|
|
21302
21642
|
timestamp
|
|
@@ -21470,7 +21810,7 @@ import { raw } from "express";
|
|
|
21470
21810
|
|
|
21471
21811
|
// src/todos/attachments.ts
|
|
21472
21812
|
import { mkdir as mkdir4, readdir as readdir15, stat as stat5, rename as rename5, rm as rm4, unlink as unlink7, writeFile as writeFile6, cp } from "fs/promises";
|
|
21473
|
-
import { resolve as
|
|
21813
|
+
import { resolve as resolve39, basename as basename5, dirname as dirname9, extname } from "path";
|
|
21474
21814
|
|
|
21475
21815
|
// src/utils/proof-artifact-id.ts
|
|
21476
21816
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -21557,12 +21897,12 @@ function sanitizeAttachmentName(name) {
|
|
|
21557
21897
|
return n;
|
|
21558
21898
|
}
|
|
21559
21899
|
function attachmentsRootDir(todosDir2) {
|
|
21560
|
-
return
|
|
21900
|
+
return resolve39(todosDir2, "attachments");
|
|
21561
21901
|
}
|
|
21562
21902
|
function attachmentDirFor(todosDir2, scopeId, todoId) {
|
|
21563
21903
|
assertScope(scopeId);
|
|
21564
21904
|
assertTodoId(todoId);
|
|
21565
|
-
return
|
|
21905
|
+
return resolve39(attachmentsRootDir(todosDir2), scopeId, todoId);
|
|
21566
21906
|
}
|
|
21567
21907
|
async function dirExists(p) {
|
|
21568
21908
|
try {
|
|
@@ -21576,7 +21916,7 @@ async function writeAttachment(todosDir2, scopeId, todoId, originalName, bytes)
|
|
|
21576
21916
|
await mkdir4(dir, { recursive: true });
|
|
21577
21917
|
const id = generateArtifactId();
|
|
21578
21918
|
const filename = sanitizeAttachmentName(originalName);
|
|
21579
|
-
await writeFile6(
|
|
21919
|
+
await writeFile6(resolve39(dir, `${id}__${filename}`), bytes);
|
|
21580
21920
|
return {
|
|
21581
21921
|
id,
|
|
21582
21922
|
filename,
|
|
@@ -21601,7 +21941,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
21601
21941
|
if (!ATTACHMENT_ID_RE.test(id)) continue;
|
|
21602
21942
|
const filename = stored.slice(sep2 + 2);
|
|
21603
21943
|
try {
|
|
21604
|
-
const st = await stat5(
|
|
21944
|
+
const st = await stat5(resolve39(dir, stored));
|
|
21605
21945
|
if (!st.isFile()) continue;
|
|
21606
21946
|
out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
|
|
21607
21947
|
} catch {
|
|
@@ -21612,7 +21952,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
21612
21952
|
}
|
|
21613
21953
|
async function readScopeAttachments(todosDir2, scopeId) {
|
|
21614
21954
|
assertScope(scopeId);
|
|
21615
|
-
const scopeDir =
|
|
21955
|
+
const scopeDir = resolve39(attachmentsRootDir(todosDir2), scopeId);
|
|
21616
21956
|
let todoIds;
|
|
21617
21957
|
try {
|
|
21618
21958
|
todoIds = await readdir15(scopeDir);
|
|
@@ -21640,7 +21980,7 @@ async function resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId) {
|
|
|
21640
21980
|
const stored = names.find((n) => n.startsWith(prefix));
|
|
21641
21981
|
if (!stored) return null;
|
|
21642
21982
|
const filename = stored.slice(prefix.length);
|
|
21643
|
-
return { path:
|
|
21983
|
+
return { path: resolve39(dir, stored), filename, mime: mimeForName(filename) };
|
|
21644
21984
|
}
|
|
21645
21985
|
async function deleteAttachment(todosDir2, scopeId, todoId, attachmentId) {
|
|
21646
21986
|
const resolved = await resolveAttachmentFile(todosDir2, scopeId, todoId, attachmentId);
|
|
@@ -21802,7 +22142,7 @@ function touchItem3(item) {
|
|
|
21802
22142
|
item.updatedAt = now;
|
|
21803
22143
|
}
|
|
21804
22144
|
function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
21805
|
-
const router =
|
|
22145
|
+
const router = Router19();
|
|
21806
22146
|
installRecordsInvalidation(router);
|
|
21807
22147
|
function broadcastUpdate() {
|
|
21808
22148
|
broadcast({ type: "todos-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -22052,8 +22392,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
22052
22392
|
router.post("/:workspace/archive", async (req2, res) => {
|
|
22053
22393
|
try {
|
|
22054
22394
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
22055
|
-
const { resolve:
|
|
22056
|
-
const { readFile:
|
|
22395
|
+
const { resolve: resolve47 } = await import("path");
|
|
22396
|
+
const { readFile: readFile32 } = await import("fs/promises");
|
|
22057
22397
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
22058
22398
|
const workspace = getWorkspaceParam(req2.params.workspace);
|
|
22059
22399
|
const outcome = await wsLock(workspace, async () => {
|
|
@@ -22069,10 +22409,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
22069
22409
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
22070
22410
|
);
|
|
22071
22411
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
22072
|
-
await ensureDir(
|
|
22412
|
+
await ensureDir(resolve47(todosDir2, "archive"));
|
|
22073
22413
|
let archContent = "";
|
|
22074
22414
|
if (await fileExists(archFile)) {
|
|
22075
|
-
archContent = await
|
|
22415
|
+
archContent = await readFile32(archFile, "utf-8");
|
|
22076
22416
|
archContent = archContent.trimEnd() + "\n\n";
|
|
22077
22417
|
} else {
|
|
22078
22418
|
archContent = `---
|
|
@@ -22364,7 +22704,7 @@ workspace: ${workspace}
|
|
|
22364
22704
|
const { readConfig: readConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
22365
22705
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
22366
22706
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
22367
|
-
const { readFile:
|
|
22707
|
+
const { readFile: readFile32 } = await import("fs/promises");
|
|
22368
22708
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
22369
22709
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
22370
22710
|
let assignmentRef;
|
|
@@ -22385,7 +22725,7 @@ workspace: ${workspace}
|
|
|
22385
22725
|
}
|
|
22386
22726
|
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
22387
22727
|
if (!await fileExists2(assignmentMdPath)) return { error: `Target assignment not found: ${assignmentMdPath}` };
|
|
22388
|
-
let content = await
|
|
22728
|
+
let content = await readFile32(assignmentMdPath, "utf-8");
|
|
22389
22729
|
content = appendTodosToAssignmentBody2(
|
|
22390
22730
|
content,
|
|
22391
22731
|
items.map((it) => ({
|
|
@@ -22579,9 +22919,9 @@ init_parser2();
|
|
|
22579
22919
|
init_fs();
|
|
22580
22920
|
init_paths();
|
|
22581
22921
|
init_slug();
|
|
22582
|
-
import { Router as
|
|
22583
|
-
import { mkdir as mkdir6, readFile as
|
|
22584
|
-
import { resolve as
|
|
22922
|
+
import { Router as Router20 } from "express";
|
|
22923
|
+
import { mkdir as mkdir6, readFile as readFile27, rename as rename7 } from "fs/promises";
|
|
22924
|
+
import { resolve as resolve40, dirname as dirname11 } from "path";
|
|
22585
22925
|
init_api();
|
|
22586
22926
|
var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
22587
22927
|
function touchItem4(item) {
|
|
@@ -22597,7 +22937,7 @@ function params(req2) {
|
|
|
22597
22937
|
return req2.params;
|
|
22598
22938
|
}
|
|
22599
22939
|
async function projectExists(projectsDir, slug) {
|
|
22600
|
-
return fileExists(
|
|
22940
|
+
return fileExists(resolve40(projectsDir, slug, "project.md"));
|
|
22601
22941
|
}
|
|
22602
22942
|
async function ensureProjectTodosDir(projectsDir, slug) {
|
|
22603
22943
|
const todosDir2 = projectTodosDir(projectsDir, slug);
|
|
@@ -22614,7 +22954,7 @@ async function ensureProjectTodosDir(projectsDir, slug) {
|
|
|
22614
22954
|
throw err;
|
|
22615
22955
|
}
|
|
22616
22956
|
try {
|
|
22617
|
-
await mkdir6(
|
|
22957
|
+
await mkdir6(resolve40(todosDir2, "archive"), { recursive: false });
|
|
22618
22958
|
} catch (err) {
|
|
22619
22959
|
const code = err.code;
|
|
22620
22960
|
if (code === "EEXIST") return;
|
|
@@ -22630,7 +22970,7 @@ function notFound(res, slug) {
|
|
|
22630
22970
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
22631
22971
|
}
|
|
22632
22972
|
function createProjectTodosRouter(projectsDir, broadcast, workspaceTodosDir) {
|
|
22633
|
-
const router =
|
|
22973
|
+
const router = Router20({ mergeParams: true });
|
|
22634
22974
|
installRecordsInvalidation(router);
|
|
22635
22975
|
function broadcastUpdate(projectSlug) {
|
|
22636
22976
|
broadcast({ type: "todos-updated", projectSlug, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -22818,7 +23158,7 @@ function createProjectTodosRouter(projectsDir, broadcast, workspaceTodosDir) {
|
|
|
22818
23158
|
const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
|
|
22819
23159
|
let archContent = "";
|
|
22820
23160
|
if (await fileExists(archFile)) {
|
|
22821
|
-
archContent = await
|
|
23161
|
+
archContent = await readFile27(archFile, "utf-8");
|
|
22822
23162
|
archContent = archContent.trimEnd() + "\n\n";
|
|
22823
23163
|
} else {
|
|
22824
23164
|
archContent = `---
|
|
@@ -23280,17 +23620,17 @@ workspace: ${slug}
|
|
|
23280
23620
|
if (tg.includes("/")) {
|
|
23281
23621
|
const parts = tg.split("/");
|
|
23282
23622
|
if (parts.length !== 2) return { error: `Invalid target.assignment "${tg}"` };
|
|
23283
|
-
assignmentDir =
|
|
23623
|
+
assignmentDir = resolve40(projectsDir, parts[0], "assignments", parts[1]);
|
|
23284
23624
|
assignmentRef = `${parts[0]}/${parts[1]}`;
|
|
23285
23625
|
} 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)) {
|
|
23286
|
-
assignmentDir =
|
|
23626
|
+
assignmentDir = resolve40(assignmentsDirFn(), tg);
|
|
23287
23627
|
assignmentRef = tg;
|
|
23288
23628
|
} else {
|
|
23289
23629
|
return { error: `Invalid target.assignment "${tg}"` };
|
|
23290
23630
|
}
|
|
23291
|
-
const assignmentMdPath =
|
|
23631
|
+
const assignmentMdPath = resolve40(assignmentDir, "assignment.md");
|
|
23292
23632
|
if (!await fileExists(assignmentMdPath)) return { error: `Target assignment not found: ${assignmentMdPath}` };
|
|
23293
|
-
let content = await
|
|
23633
|
+
let content = await readFile27(assignmentMdPath, "utf-8");
|
|
23294
23634
|
content = appendTodosToAssignmentBody2(
|
|
23295
23635
|
content,
|
|
23296
23636
|
items.map((it) => ({
|
|
@@ -23492,7 +23832,7 @@ workspace: ${slug}
|
|
|
23492
23832
|
}
|
|
23493
23833
|
|
|
23494
23834
|
// src/dashboard/api-bundles.ts
|
|
23495
|
-
import { Router as
|
|
23835
|
+
import { Router as Router21 } from "express";
|
|
23496
23836
|
import { readdir as readdir17 } from "fs/promises";
|
|
23497
23837
|
|
|
23498
23838
|
// src/todos/bundle-parser.ts
|
|
@@ -23501,7 +23841,7 @@ init_fs();
|
|
|
23501
23841
|
init_paths();
|
|
23502
23842
|
init_parser2();
|
|
23503
23843
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
23504
|
-
import { readFile as
|
|
23844
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
23505
23845
|
var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
|
|
23506
23846
|
var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
|
|
23507
23847
|
var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
|
|
@@ -23568,7 +23908,7 @@ function parseBundles(content) {
|
|
|
23568
23908
|
async function readBundles(todosDir2) {
|
|
23569
23909
|
const path = bundlesPath(todosDir2);
|
|
23570
23910
|
if (!await fileExists(path)) return [];
|
|
23571
|
-
const content = await
|
|
23911
|
+
const content = await readFile28(path, "utf-8");
|
|
23572
23912
|
return parseBundles(content).bundles;
|
|
23573
23913
|
}
|
|
23574
23914
|
|
|
@@ -23603,7 +23943,7 @@ function annotate(bundle, items) {
|
|
|
23603
23943
|
}
|
|
23604
23944
|
function createBundlesRouter(todosDir2, broadcast) {
|
|
23605
23945
|
void broadcast;
|
|
23606
|
-
const router =
|
|
23946
|
+
const router = Router21();
|
|
23607
23947
|
function validateWorkspace(req2, res, next) {
|
|
23608
23948
|
const workspace = getWorkspaceParam2(req2.params.workspace);
|
|
23609
23949
|
if (workspace && !WORKSPACE_REGEX3.test(workspace)) {
|
|
@@ -23669,8 +24009,8 @@ function createBundlesRouter(todosDir2, broadcast) {
|
|
|
23669
24009
|
init_fs();
|
|
23670
24010
|
init_paths();
|
|
23671
24011
|
init_slug();
|
|
23672
|
-
import { Router as
|
|
23673
|
-
import { resolve as
|
|
24012
|
+
import { Router as Router22 } from "express";
|
|
24013
|
+
import { resolve as resolve41 } from "path";
|
|
23674
24014
|
init_parser2();
|
|
23675
24015
|
function deriveStatus2(bundle, items) {
|
|
23676
24016
|
const members = bundle.todoIds.map((id) => items.find((i) => i.id === id)).filter((i) => i !== void 0);
|
|
@@ -23699,7 +24039,7 @@ function notFound2(res, slug) {
|
|
|
23699
24039
|
}
|
|
23700
24040
|
function createProjectBundlesRouter(projectsDir, broadcast) {
|
|
23701
24041
|
void broadcast;
|
|
23702
|
-
const router =
|
|
24042
|
+
const router = Router22({ mergeParams: true });
|
|
23703
24043
|
function validateProjectId(req2, res, next) {
|
|
23704
24044
|
const slug = getProjectIdParam2(req2.params.projectId);
|
|
23705
24045
|
if (!slug || !isValidSlug(slug)) {
|
|
@@ -23712,7 +24052,7 @@ function createProjectBundlesRouter(projectsDir, broadcast) {
|
|
|
23712
24052
|
router.get("/", async (req2, res) => {
|
|
23713
24053
|
try {
|
|
23714
24054
|
const slug = getProjectIdParam2(req2.params.projectId);
|
|
23715
|
-
const projectMd =
|
|
24055
|
+
const projectMd = resolve41(projectsDir, slug, "project.md");
|
|
23716
24056
|
if (!await fileExists(projectMd)) {
|
|
23717
24057
|
notFound2(res, slug);
|
|
23718
24058
|
return;
|
|
@@ -23733,7 +24073,7 @@ function createProjectBundlesRouter(projectsDir, broadcast) {
|
|
|
23733
24073
|
init_config2();
|
|
23734
24074
|
init_api();
|
|
23735
24075
|
init_scanner();
|
|
23736
|
-
import { Router as
|
|
24076
|
+
import { Router as Router23 } from "express";
|
|
23737
24077
|
|
|
23738
24078
|
// src/utils/github-backup.ts
|
|
23739
24079
|
init_paths();
|
|
@@ -23741,8 +24081,8 @@ init_fs();
|
|
|
23741
24081
|
init_config2();
|
|
23742
24082
|
import { execFile as execFile2 } from "child_process";
|
|
23743
24083
|
import { promisify as promisify2 } from "util";
|
|
23744
|
-
import { cp as cp2, mkdtemp, rm as rm5, readFile as
|
|
23745
|
-
import { resolve as
|
|
24084
|
+
import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile29, writeFile as writeFile7, unlink as unlink8, stat as stat6, open as open5, rename as rename8 } from "fs/promises";
|
|
24085
|
+
import { resolve as resolve42, join as join9 } from "path";
|
|
23746
24086
|
import { tmpdir } from "os";
|
|
23747
24087
|
var exec2 = promisify2(execFile2);
|
|
23748
24088
|
var VALID_CATEGORIES = ["projects", "playbooks", "todos", "servers", "config"];
|
|
@@ -23782,7 +24122,7 @@ async function resolveCategoryPath(category) {
|
|
|
23782
24122
|
case "servers":
|
|
23783
24123
|
return { sourcePath: serversDir(), repoPath: "servers", isFile: false };
|
|
23784
24124
|
case "config":
|
|
23785
|
-
return { sourcePath:
|
|
24125
|
+
return { sourcePath: resolve42(syntaurRoot(), "config.md"), repoPath: "config.md", isFile: true };
|
|
23786
24126
|
}
|
|
23787
24127
|
}
|
|
23788
24128
|
async function checkGitInstalled() {
|
|
@@ -23793,7 +24133,7 @@ async function checkGitInstalled() {
|
|
|
23793
24133
|
}
|
|
23794
24134
|
}
|
|
23795
24135
|
async function acquireLock2() {
|
|
23796
|
-
const lockPath =
|
|
24136
|
+
const lockPath = resolve42(syntaurRoot(), LOCK_FILE_NAME);
|
|
23797
24137
|
await ensureDir(syntaurRoot());
|
|
23798
24138
|
try {
|
|
23799
24139
|
const handle = await open5(lockPath, "wx");
|
|
@@ -23802,7 +24142,7 @@ async function acquireLock2() {
|
|
|
23802
24142
|
return lockPath;
|
|
23803
24143
|
} catch (err) {
|
|
23804
24144
|
if (err.code === "EEXIST") {
|
|
23805
|
-
const pid = await
|
|
24145
|
+
const pid = await readFile29(lockPath, "utf-8").catch(() => "");
|
|
23806
24146
|
throw new Error(
|
|
23807
24147
|
`Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
|
|
23808
24148
|
);
|
|
@@ -23840,7 +24180,7 @@ async function copyRecursive(src, dest) {
|
|
|
23840
24180
|
await ensureDir(dest);
|
|
23841
24181
|
await cp2(src, dest, { recursive: true, force: true });
|
|
23842
24182
|
} else {
|
|
23843
|
-
await ensureDir(
|
|
24183
|
+
await ensureDir(resolve42(dest, ".."));
|
|
23844
24184
|
await cp2(src, dest, { force: true });
|
|
23845
24185
|
}
|
|
23846
24186
|
}
|
|
@@ -23849,7 +24189,7 @@ function resolveCategoriesStrict(csv) {
|
|
|
23849
24189
|
return parseCategoriesStrict(parts);
|
|
23850
24190
|
}
|
|
23851
24191
|
async function readSanitizedConfig(configPath) {
|
|
23852
|
-
const content = await
|
|
24192
|
+
const content = await readFile29(configPath, "utf-8");
|
|
23853
24193
|
return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
|
|
23854
24194
|
}
|
|
23855
24195
|
async function backupToGithub(overrides) {
|
|
@@ -23888,7 +24228,7 @@ async function backupToGithub(overrides) {
|
|
|
23888
24228
|
}
|
|
23889
24229
|
if (category === "config") {
|
|
23890
24230
|
const sanitized = await readSanitizedConfig(sourcePath);
|
|
23891
|
-
await ensureDir(
|
|
24231
|
+
await ensureDir(resolve42(destPath, ".."));
|
|
23892
24232
|
await writeFile7(destPath, sanitized, "utf-8");
|
|
23893
24233
|
} else {
|
|
23894
24234
|
await copyRecursive(sourcePath, destPath);
|
|
@@ -23942,7 +24282,7 @@ async function backupToGithub(overrides) {
|
|
|
23942
24282
|
}
|
|
23943
24283
|
async function safeRestoreCategory(localPath, repoSrcPath, isFile) {
|
|
23944
24284
|
if (isFile) {
|
|
23945
|
-
await ensureDir(
|
|
24285
|
+
await ensureDir(resolve42(localPath, ".."));
|
|
23946
24286
|
await cp2(repoSrcPath, localPath, { force: true });
|
|
23947
24287
|
return;
|
|
23948
24288
|
}
|
|
@@ -24043,7 +24383,7 @@ async function restoreFromGithub(overrides) {
|
|
|
24043
24383
|
}
|
|
24044
24384
|
async function getBackupStatus() {
|
|
24045
24385
|
const config = await readConfig();
|
|
24046
|
-
const lockPath =
|
|
24386
|
+
const lockPath = resolve42(syntaurRoot(), LOCK_FILE_NAME);
|
|
24047
24387
|
const locked = await fileExists(lockPath);
|
|
24048
24388
|
return {
|
|
24049
24389
|
repo: config.backup?.repo ?? null,
|
|
@@ -24056,7 +24396,7 @@ async function getBackupStatus() {
|
|
|
24056
24396
|
|
|
24057
24397
|
// src/dashboard/api-backup.ts
|
|
24058
24398
|
function createBackupRouter() {
|
|
24059
|
-
const router =
|
|
24399
|
+
const router = Router23();
|
|
24060
24400
|
router.get("/", async (_req, res) => {
|
|
24061
24401
|
try {
|
|
24062
24402
|
const status = await getBackupStatus();
|
|
@@ -24526,7 +24866,7 @@ async function runCcusage(opts = {}) {
|
|
|
24526
24866
|
};
|
|
24527
24867
|
}
|
|
24528
24868
|
function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
24529
|
-
return new Promise((
|
|
24869
|
+
return new Promise((resolve47) => {
|
|
24530
24870
|
const child = spawn4(binary, args, {
|
|
24531
24871
|
env: env ?? process.env,
|
|
24532
24872
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -24540,7 +24880,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
|
24540
24880
|
if (settled) return;
|
|
24541
24881
|
settled = true;
|
|
24542
24882
|
clearTimeout(timer3);
|
|
24543
|
-
|
|
24883
|
+
resolve47(result);
|
|
24544
24884
|
};
|
|
24545
24885
|
const timer3 = setTimeout(() => {
|
|
24546
24886
|
timedOut = true;
|
|
@@ -24839,7 +25179,7 @@ function createDashboardServer(options) {
|
|
|
24839
25179
|
(async () => {
|
|
24840
25180
|
try {
|
|
24841
25181
|
const configResult = await migrateLegacyConfig(
|
|
24842
|
-
|
|
25182
|
+
resolve46(syntaurRoot(), "config.md")
|
|
24843
25183
|
);
|
|
24844
25184
|
const projectResult = await migrateLegacyProjectFiles(projectsDir);
|
|
24845
25185
|
const summary = summarizeMigration(projectResult, configResult);
|
|
@@ -25329,6 +25669,7 @@ function createDashboardServer(options) {
|
|
|
25329
25669
|
app.use("/api/schedules", createSchedulesRouter(broadcast));
|
|
25330
25670
|
app.use("/api/usage", createUsageRouter(projectsDir, assignmentsDir2));
|
|
25331
25671
|
app.use("/api", createEventsRouter(projectsDir, assignmentsDir2));
|
|
25672
|
+
app.use("/api", createInboxRouter(projectsDir, assignmentsDir2));
|
|
25332
25673
|
app.use("/api/agent-sessions", createAgentSessionsRouter(projectsDir, broadcast, assignmentsDir2));
|
|
25333
25674
|
app.use("/api/config/agents", createAgentsRouter());
|
|
25334
25675
|
app.use(
|
|
@@ -25361,14 +25702,14 @@ function createDashboardServer(options) {
|
|
|
25361
25702
|
app.use("/api/backup", createBackupRouter());
|
|
25362
25703
|
if (serveStaticUi && dashboardDistPath) {
|
|
25363
25704
|
const sendOpts = { dotfiles: "allow" };
|
|
25364
|
-
app.use("/assets", express.static(
|
|
25705
|
+
app.use("/assets", express.static(resolve46(dashboardDistPath, "assets"), sendOpts));
|
|
25365
25706
|
app.use(express.static(dashboardDistPath, { ...sendOpts, index: false, fallthrough: true }));
|
|
25366
25707
|
app.get("{*path}", async (req2, res) => {
|
|
25367
25708
|
if (req2.path.startsWith("/api") || req2.path === "/ws" || req2.path.startsWith("/assets")) {
|
|
25368
25709
|
res.status(404).json({ error: "Not Found" });
|
|
25369
25710
|
return;
|
|
25370
25711
|
}
|
|
25371
|
-
const indexPath =
|
|
25712
|
+
const indexPath = resolve46(dashboardDistPath, "index.html");
|
|
25372
25713
|
if (!await fileExists(indexPath)) {
|
|
25373
25714
|
res.status(503).send(
|
|
25374
25715
|
'Dashboard not built. Run "npm run build:dashboard" first.'
|
|
@@ -25402,8 +25743,8 @@ function createDashboardServer(options) {
|
|
|
25402
25743
|
if (!await migrationGate()) return;
|
|
25403
25744
|
try {
|
|
25404
25745
|
const context = await resolveDeriveContext2();
|
|
25405
|
-
const projectDir = projectSlug ?
|
|
25406
|
-
const path = projectDir ?
|
|
25746
|
+
const projectDir = projectSlug ? resolve46(projectsDir, projectSlug) : null;
|
|
25747
|
+
const path = projectDir ? resolve46(projectDir, "assignments", assignmentSlug, "assignment.md") : resolve46(assignmentsDir2, assignmentSlug, "assignment.md");
|
|
25407
25748
|
if (!await fileExists(path)) return;
|
|
25408
25749
|
const result = await recomputeAndWrite2(path, {
|
|
25409
25750
|
cause: "derive",
|
|
@@ -25456,8 +25797,8 @@ function createDashboardServer(options) {
|
|
|
25456
25797
|
serversDir: serversDir2,
|
|
25457
25798
|
playbooksDir: playbooksDir2,
|
|
25458
25799
|
todosDir: todosDir2,
|
|
25459
|
-
dbPath:
|
|
25460
|
-
configPath:
|
|
25800
|
+
dbPath: resolve46(syntaurRoot(), "syntaur.db"),
|
|
25801
|
+
configPath: resolve46(syntaurRoot(), "config.md"),
|
|
25461
25802
|
onMessage: broadcast,
|
|
25462
25803
|
onAssignmentChanged: (projectSlug, assignmentSlug) => {
|
|
25463
25804
|
void recomputeOne(projectSlug, assignmentSlug);
|
|
@@ -25493,7 +25834,7 @@ function createDashboardServer(options) {
|
|
|
25493
25834
|
}
|
|
25494
25835
|
});
|
|
25495
25836
|
server.listen(port, () => {
|
|
25496
|
-
const portFile =
|
|
25837
|
+
const portFile = resolve46(syntaurRoot(), "dashboard-port");
|
|
25497
25838
|
writeFile8(portFile, String(port), "utf-8").catch(() => {
|
|
25498
25839
|
});
|
|
25499
25840
|
resolvePromise();
|
|
@@ -25513,7 +25854,7 @@ function createDashboardServer(options) {
|
|
|
25513
25854
|
client.terminate();
|
|
25514
25855
|
}
|
|
25515
25856
|
clients.clear();
|
|
25516
|
-
const portFile =
|
|
25857
|
+
const portFile = resolve46(syntaurRoot(), "dashboard-port");
|
|
25517
25858
|
await unlink9(portFile).catch(() => {
|
|
25518
25859
|
});
|
|
25519
25860
|
server.closeAllConnections?.();
|