@svenroth-ai/shipwright 0.24.6-next.0 → 0.25.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/client/dist/assets/{EmbeddedTerminal-N1SJ3XX7.js → EmbeddedTerminal-5QvKe3_a.js} +20 -20
- package/client/dist/assets/{abnfDiagram-VRR7QNED-BcoPVcm6.js → abnfDiagram-VRR7QNED-CXi9HegQ.js} +1 -1
- package/client/dist/assets/{arc-BhjELRo0.js → arc-DLh4GiqR.js} +1 -1
- package/client/dist/assets/{architectureDiagram-ZJ3FMSHR-DbNXtiqW.js → architectureDiagram-ZJ3FMSHR-DUypQ8dQ.js} +1 -1
- package/client/dist/assets/{blockDiagram-677ZJIJ3-Bq-jwL4e.js → blockDiagram-677ZJIJ3-D2cVMKJG.js} +1 -1
- package/client/dist/assets/{c4Diagram-LMCZKHZV-CjFKHlUb.js → c4Diagram-LMCZKHZV-DtruBGJw.js} +1 -1
- package/client/dist/assets/channel-C_k-9jsa.js +1 -0
- package/client/dist/assets/{chunk-2Q5K7J3B-C6RrKKpc.js → chunk-2Q5K7J3B-CAfQkFw2.js} +1 -1
- package/client/dist/assets/{chunk-32BRIVSS-BWuO7O6S.js → chunk-32BRIVSS-CPWXoLnQ.js} +1 -1
- package/client/dist/assets/{chunk-5VM5RSS4-BjYoiHmo.js → chunk-5VM5RSS4-xVc3oppQ.js} +1 -1
- package/client/dist/assets/{chunk-EX3LRPZG-CyQYieee.js → chunk-EX3LRPZG-CUpl6fRR.js} +1 -1
- package/client/dist/assets/{chunk-JWPE2WC7-CY9fT_5d.js → chunk-JWPE2WC7-NejUp56x.js} +1 -1
- package/client/dist/assets/{chunk-MOJQB5TN-DCcb0M8e.js → chunk-MOJQB5TN-C9-2lLg0.js} +1 -1
- package/client/dist/assets/{chunk-RYQCIY6F-CVd_mjJU.js → chunk-RYQCIY6F-BAPtwABy.js} +1 -1
- package/client/dist/assets/{chunk-V7JOEXUC-qBo5EfYb.js → chunk-V7JOEXUC-Dwzihw26.js} +1 -1
- package/client/dist/assets/{chunk-VR4S4FIN-C1TLpczn.js → chunk-VR4S4FIN-9-iArFJM.js} +1 -1
- package/client/dist/assets/{chunk-XXDRQBXY-DePb8ZtI.js → chunk-XXDRQBXY-DiWHOrqe.js} +1 -1
- package/client/dist/assets/classDiagram-OUVF2IWQ-CsEPq9Ac.js +1 -0
- package/client/dist/assets/classDiagram-v2-EOCWNBFH-CsEPq9Ac.js +1 -0
- package/client/dist/assets/{cose-bilkent-JH36ORCC-BJNddNgC.js → cose-bilkent-JH36ORCC-Cp04EOcH.js} +1 -1
- package/client/dist/assets/{cynefin-VYW2F7L2-BtJssOuy.js → cynefin-VYW2F7L2-D--GN1v8.js} +1 -1
- package/client/dist/assets/{cynefinDiagram-TSTJHNR4-BS8F0pjM.js → cynefinDiagram-TSTJHNR4-BGH0o_qp.js} +1 -1
- package/client/dist/assets/{dagre-VKFMJZFB-C4A2MxdE.js → dagre-VKFMJZFB-DSKkIKlj.js} +1 -1
- package/client/dist/assets/{diagram-FQU43EPY-vX6qE1sj.js → diagram-FQU43EPY-Cs_C4lb-.js} +1 -1
- package/client/dist/assets/{diagram-G47NLZAW-B5P1Rrem.js → diagram-G47NLZAW-oDtAA8O_.js} +1 -1
- package/client/dist/assets/{diagram-NH7WQ7WH-u7LHeyDa.js → diagram-NH7WQ7WH-BiFMjqSH.js} +1 -1
- package/client/dist/assets/{diagram-OA4YK3LP-CZhgtAtD.js → diagram-OA4YK3LP-Ccs3thCj.js} +1 -1
- package/client/dist/assets/{diagram-WEI45ONY-9k80njcz.js → diagram-WEI45ONY-CdZNqgO3.js} +1 -1
- package/client/dist/assets/{ebnfDiagram-CCIWWBDH-B3ES3AxZ.js → ebnfDiagram-CCIWWBDH-CapTA0VL.js} +1 -1
- package/client/dist/assets/{erDiagram-Q63AITRT-BSLAY2HJ.js → erDiagram-Q63AITRT-BcRNEP8s.js} +1 -1
- package/client/dist/assets/{flowDiagram-23GEKE2U-BIse3jvS.js → flowDiagram-23GEKE2U-CJ_zF7e_.js} +1 -1
- package/client/dist/assets/{ganttDiagram-NO4QXBWP-BCV_RM7c.js → ganttDiagram-NO4QXBWP-CnJdcg9A.js} +1 -1
- package/client/dist/assets/{gitGraphDiagram-IHSO6WYX-ClkOuEG1.js → gitGraphDiagram-IHSO6WYX-YTUQ_ELk.js} +1 -1
- package/client/dist/assets/index-6nZ1bt0a.css +1 -0
- package/client/dist/assets/index-irQhGNpn.js +828 -0
- package/client/dist/assets/{infoDiagram-FWYZ7A6U-Cu6-m8O7.js → infoDiagram-FWYZ7A6U-CLOP_ZWD.js} +1 -1
- package/client/dist/assets/{ishikawaDiagram-FXEZZL3T-D62Cg1g-.js → ishikawaDiagram-FXEZZL3T-aFTIzr7o.js} +1 -1
- package/client/dist/assets/{journeyDiagram-5HDEW3XC-DHMljiqG.js → journeyDiagram-5HDEW3XC-CbPJB4xd.js} +1 -1
- package/client/dist/assets/{kanban-definition-HUTT4EX6-CvELrwMl.js → kanban-definition-HUTT4EX6-BoyhytIS.js} +1 -1
- package/client/dist/assets/{linear-DpT0hsvX.js → linear-1VXA-uBg.js} +1 -1
- package/client/dist/assets/{mermaid.core-CSwHP-lX.js → mermaid.core-D_lO2yK9.js} +4 -4
- package/client/dist/assets/{mindmap-definition-LN4V7U3C-BPaYhqXE.js → mindmap-definition-LN4V7U3C-iFK7LXMx.js} +1 -1
- package/client/dist/assets/{pegDiagram-2B236MQR-Bn0zJHpH.js → pegDiagram-2B236MQR-DNmcmfA-.js} +1 -1
- package/client/dist/assets/{pieDiagram-ENE6RG2P-CpZW49Cw.js → pieDiagram-ENE6RG2P-ekx83O5b.js} +1 -1
- package/client/dist/assets/{quadrantDiagram-ABIIQ3AL--El8Y1Vr.js → quadrantDiagram-ABIIQ3AL-DayyD1l8.js} +1 -1
- package/client/dist/assets/{railroadDiagram-RFXS5EU6-Bb4L-n6k.js → railroadDiagram-RFXS5EU6-Bdgz7S6G.js} +1 -1
- package/client/dist/assets/{requirementDiagram-TGXJPOKE-BYmMcf0e.js → requirementDiagram-TGXJPOKE-szP3Muld.js} +1 -1
- package/client/dist/assets/{sankeyDiagram-HTMAVEWB-C2JWG0lB.js → sankeyDiagram-HTMAVEWB-Ct3rzgUj.js} +1 -1
- package/client/dist/assets/{sequenceDiagram-DBY2YBRQ-ck4oQ3Sx.js → sequenceDiagram-DBY2YBRQ-_69EfmOU.js} +1 -1
- package/client/dist/assets/{sizeCapture-X5ZJPWSS-Cn7ZoV_6.js → sizeCapture-X5ZJPWSS-BSOU06J2.js} +1 -1
- package/client/dist/assets/{stateDiagram-2N3HPSRC-Cfps2GyX.js → stateDiagram-2N3HPSRC-HmOmFwQm.js} +1 -1
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-BW8H33dv.js +1 -0
- package/client/dist/assets/{swimlanes-5IMT3BWC-qzrx_9r_.js → swimlanes-5IMT3BWC-CVg_4oT7.js} +2 -2
- package/client/dist/assets/swimlanesDiagram-G3AALYLV-BaRlxcPD.js +8 -0
- package/client/dist/assets/{timeline-definition-FHXFAJF6-cdJrYjL1.js → timeline-definition-FHXFAJF6-CBBaDKXm.js} +1 -1
- package/client/dist/assets/{vennDiagram-L72KCM5P-CsADhf78.js → vennDiagram-L72KCM5P-CBXiWPMc.js} +1 -1
- package/client/dist/assets/{wardleyDiagram-EHGQE667-CiIy8tCK.js → wardleyDiagram-EHGQE667-uhBCZoW0.js} +1 -1
- package/client/dist/assets/{xychartDiagram-FW5EYKEG-4dCIrB0W.js → xychartDiagram-FW5EYKEG-xxE_wY5O.js} +1 -1
- package/client/dist/index.html +2 -2
- package/lib/server.mjs +11 -3
- package/lib/util.mjs +70 -1
- package/package.json +68 -68
- package/server/dist/core/grade-runner.d.ts +4 -4
- package/server/dist/core/grade-runner.d.ts.map +1 -1
- package/server/dist/core/grade-runner.js +51 -26
- package/server/dist/core/grade-runner.js.map +1 -1
- package/server/dist/core/grade-target.d.ts +8 -0
- package/server/dist/core/grade-target.d.ts.map +1 -1
- package/server/dist/core/grade-target.js +10 -0
- package/server/dist/core/grade-target.js.map +1 -1
- package/server/dist/core/triage-cli-runner.d.ts.map +1 -1
- package/server/dist/core/triage-cli-runner.js +29 -10
- package/server/dist/core/triage-cli-runner.js.map +1 -1
- package/server/dist/core/uv-runner.d.ts +23 -0
- package/server/dist/core/uv-runner.d.ts.map +1 -0
- package/server/dist/core/uv-runner.js +39 -0
- package/server/dist/core/uv-runner.js.map +1 -0
- package/server/dist/external/file/exists-routes.d.ts +7 -0
- package/server/dist/external/file/exists-routes.d.ts.map +1 -0
- package/server/dist/external/file/exists-routes.js +91 -0
- package/server/dist/external/file/exists-routes.js.map +1 -0
- package/server/dist/external/org/audit-log.d.ts +27 -0
- package/server/dist/external/org/audit-log.d.ts.map +1 -0
- package/server/dist/external/org/audit-log.js +92 -0
- package/server/dist/external/org/audit-log.js.map +1 -0
- package/server/dist/external/org/beat-register.d.ts +15 -22
- package/server/dist/external/org/beat-register.d.ts.map +1 -1
- package/server/dist/external/org/beat-register.js +37 -32
- package/server/dist/external/org/beat-register.js.map +1 -1
- package/server/dist/external/org/file-read.d.ts +16 -0
- package/server/dist/external/org/file-read.d.ts.map +1 -1
- package/server/dist/external/org/file-read.js +86 -46
- package/server/dist/external/org/file-read.js.map +1 -1
- package/server/dist/external/org/file-write-core.d.ts +18 -0
- package/server/dist/external/org/file-write-core.d.ts.map +1 -0
- package/server/dist/external/org/file-write-core.js +97 -0
- package/server/dist/external/org/file-write-core.js.map +1 -0
- package/server/dist/external/org/file-write.d.ts +25 -0
- package/server/dist/external/org/file-write.d.ts.map +1 -1
- package/server/dist/external/org/file-write.js +82 -132
- package/server/dist/external/org/file-write.js.map +1 -1
- package/server/dist/external/org/last-run.d.ts +25 -16
- package/server/dist/external/org/last-run.d.ts.map +1 -1
- package/server/dist/external/org/last-run.js +78 -64
- package/server/dist/external/org/last-run.js.map +1 -1
- package/server/dist/external/org/lead-doc-read.d.ts +21 -0
- package/server/dist/external/org/lead-doc-read.d.ts.map +1 -0
- package/server/dist/external/org/lead-doc-read.js +84 -0
- package/server/dist/external/org/lead-doc-read.js.map +1 -0
- package/server/dist/external/org/mount.d.ts +12 -0
- package/server/dist/external/org/mount.d.ts.map +1 -0
- package/server/dist/external/org/mount.js +34 -0
- package/server/dist/external/org/mount.js.map +1 -0
- package/server/dist/external/org/org-chart-lookup.d.ts +13 -0
- package/server/dist/external/org/org-chart-lookup.d.ts.map +1 -1
- package/server/dist/external/org/org-chart-lookup.js +40 -5
- package/server/dist/external/org/org-chart-lookup.js.map +1 -1
- package/server/dist/external/org/org-chart.d.ts +21 -12
- package/server/dist/external/org/org-chart.d.ts.map +1 -1
- package/server/dist/external/org/org-chart.js +50 -32
- package/server/dist/external/org/org-chart.js.map +1 -1
- package/server/dist/external/org/role-extract.d.ts +8 -0
- package/server/dist/external/org/role-extract.d.ts.map +1 -0
- package/server/dist/external/org/role-extract.js +72 -0
- package/server/dist/external/org/role-extract.js.map +1 -0
- package/server/dist/external/org/usage.d.ts +15 -11
- package/server/dist/external/org/usage.d.ts.map +1 -1
- package/server/dist/external/org/usage.js +64 -54
- package/server/dist/external/org/usage.js.map +1 -1
- package/server/dist/external/routes.d.ts +1 -1
- package/server/dist/external/routes.d.ts.map +1 -1
- package/server/dist/external/routes.js +10 -11
- package/server/dist/external/routes.js.map +1 -1
- package/server/dist/routes/grade.d.ts.map +1 -1
- package/server/dist/routes/grade.js +3 -2
- package/server/dist/routes/grade.js.map +1 -1
- package/server/dist/routes/org-leads-composite.d.ts +21 -0
- package/server/dist/routes/org-leads-composite.d.ts.map +1 -0
- package/server/dist/routes/org-leads-composite.js +95 -0
- package/server/dist/routes/org-leads-composite.js.map +1 -0
- package/server/dist/routes/org.d.ts +15 -0
- package/server/dist/routes/org.d.ts.map +1 -0
- package/server/dist/routes/org.js +198 -0
- package/server/dist/routes/org.js.map +1 -0
- package/server/dist/terminal/pty-manager.d.ts +9 -0
- package/server/dist/terminal/pty-manager.d.ts.map +1 -1
- package/server/dist/terminal/pty-manager.js +15 -0
- package/server/dist/terminal/pty-manager.js.map +1 -1
- package/server/dist/terminal/replay-snapshot.d.ts +10 -1
- package/server/dist/terminal/replay-snapshot.d.ts.map +1 -1
- package/server/dist/terminal/replay-snapshot.js +36 -1
- package/server/dist/terminal/replay-snapshot.js.map +1 -1
- package/server/dist/terminal/ws-upgrade-handler.d.ts +17 -1
- package/server/dist/terminal/ws-upgrade-handler.d.ts.map +1 -1
- package/server/dist/terminal/ws-upgrade-handler.fixtures.d.ts +1 -0
- package/server/dist/terminal/ws-upgrade-handler.fixtures.d.ts.map +1 -1
- package/server/dist/terminal/ws-upgrade-handler.fixtures.js +3 -2
- package/server/dist/terminal/ws-upgrade-handler.fixtures.js.map +1 -1
- package/server/dist/terminal/ws-upgrade-handler.js +45 -4
- package/server/dist/terminal/ws-upgrade-handler.js.map +1 -1
- package/server/dist/test/python-spawn-guard-util.d.ts +14 -0
- package/server/dist/test/python-spawn-guard-util.d.ts.map +1 -0
- package/server/dist/test/python-spawn-guard-util.js +124 -0
- package/server/dist/test/python-spawn-guard-util.js.map +1 -0
- package/server/dist/types/org.d.ts +115 -0
- package/server/dist/types/org.d.ts.map +1 -0
- package/server/dist/types/org.js +16 -0
- package/server/dist/types/org.js.map +1 -0
- package/server/package.json +43 -43
- package/client/dist/assets/MarkdownEditorModal-DL5uF6Nn.js +0 -212
- package/client/dist/assets/channel-DjcCSejz.js +0 -1
- package/client/dist/assets/classDiagram-OUVF2IWQ-SNWikNvk.js +0 -1
- package/client/dist/assets/classDiagram-v2-EOCWNBFH-SNWikNvk.js +0 -1
- package/client/dist/assets/index-B1mzucik.js +0 -609
- package/client/dist/assets/index-C-w52nAQ.css +0 -1
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-DF7QbVyC.js +0 -1
- package/client/dist/assets/swimlanesDiagram-G3AALYLV-Dz7OXAbB.js +0 -8
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/org/lead-doc-read.ts — GET-only read for a lead's `learnings.md`.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately NOT part of `_helpers.ts`'s six-target allowlist — that
|
|
5
|
+
* allowlist's own docstring calls it a fixed, shared contract with the
|
|
6
|
+
* secret-gated family (`external/org/routes.ts`), and widening it would
|
|
7
|
+
* change what BOTH families accept. `learnings.md` is a read this iterate's
|
|
8
|
+
* new plain `/api/org/*` surface needs (Docs block, AC-2) that the gated
|
|
9
|
+
* family never exposed and doesn't need to; this module — and the sibling
|
|
10
|
+
* `audit-log.ts` for `audit.jsonl` — exist to add it additively, without
|
|
11
|
+
* touching that stable contract.
|
|
12
|
+
*
|
|
13
|
+
* `leadId` is validated against `LEAD_ID_RE` (same validator every other
|
|
14
|
+
* org route shares — never a second one) and the filename suffix is a
|
|
15
|
+
* hardcoded constant, never client-supplied — so unlike the six-target
|
|
16
|
+
* allowlist's raw `path=` input, there is no string for a traversal
|
|
17
|
+
* sequence to hide in. `pathGuard` runs anyway as defense in depth against
|
|
18
|
+
* a future caller changing that; `realPathGuard` (which needs the target to
|
|
19
|
+
* already exist — see its own docstring) is applied AFTER the open, unlike
|
|
20
|
+
* `file-read.ts`'s ordering, precisely so a genuinely-missing file 404s
|
|
21
|
+
* instead of realpathSync's ENOENT reporting as `path_traversal` first.
|
|
22
|
+
*/
|
|
23
|
+
import { closeSync, constants as fsConstants, fstatSync, openSync, readFileSync } from "node:fs";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { pathGuard, realPathGuard } from "../../core/path-guard.js";
|
|
26
|
+
import { LEAD_ID_RE } from "./_helpers.js";
|
|
27
|
+
import { fileFingerprint } from "../file/_helpers.js";
|
|
28
|
+
const LEARNINGS_FILENAME = "learnings.md";
|
|
29
|
+
/** Pure core — reads `<leadsRoot>/<leadId>/learnings.md`. */
|
|
30
|
+
export function leadLearningsReadCore(deps, leadId) {
|
|
31
|
+
const { leadsRoot } = deps;
|
|
32
|
+
const open = deps.openSync ?? openSync;
|
|
33
|
+
if (!leadId || !LEAD_ID_RE.test(leadId)) {
|
|
34
|
+
return { status: 400, kind: "json", body: { error: "invalid_lead_id" } };
|
|
35
|
+
}
|
|
36
|
+
const guard = pathGuard(leadsRoot, join(leadId, LEARNINGS_FILENAME));
|
|
37
|
+
if (!guard.ok) {
|
|
38
|
+
return { status: 400, kind: "json", body: { error: "path_traversal", detail: guard.reason } };
|
|
39
|
+
}
|
|
40
|
+
let fd;
|
|
41
|
+
try {
|
|
42
|
+
fd = open(guard.absolute, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const code = err?.code;
|
|
46
|
+
if (code === "ENOENT") {
|
|
47
|
+
return { status: 404, kind: "json", body: { error: "not_found" } };
|
|
48
|
+
}
|
|
49
|
+
if (code === "ELOOP") {
|
|
50
|
+
return { status: 403, kind: "json", body: { error: "symlink_forbidden" } };
|
|
51
|
+
}
|
|
52
|
+
return { status: 500, kind: "json", body: { error: "file_stat_failed", detail: String(err).slice(0, 200) } };
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
if (!fstatSync(fd).isFile()) {
|
|
56
|
+
return { status: 400, kind: "json", body: { error: "not_a_file" } };
|
|
57
|
+
}
|
|
58
|
+
// Existence is now confirmed (the open above succeeded) — realPathGuard's
|
|
59
|
+
// own contract requires exactly this ordering.
|
|
60
|
+
const realGuard = realPathGuard(leadsRoot, guard.absolute);
|
|
61
|
+
if (!realGuard.ok) {
|
|
62
|
+
return { status: 403, kind: "json", body: { error: "symlink_forbidden", detail: realGuard.reason } };
|
|
63
|
+
}
|
|
64
|
+
const body = readFileSync(fd);
|
|
65
|
+
return {
|
|
66
|
+
status: 200,
|
|
67
|
+
kind: "file",
|
|
68
|
+
body,
|
|
69
|
+
headers: {
|
|
70
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
71
|
+
"X-Content-Type-Options": "nosniff",
|
|
72
|
+
ETag: `"${fileFingerprint(body)}"`,
|
|
73
|
+
"Cache-Control": "private, max-age=0, must-revalidate",
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return { status: 500, kind: "json", body: { error: "file_read_failed", detail: String(err).slice(0, 200) } };
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
closeSync(fd);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=lead-doc-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lead-doc-read.js","sourceRoot":"","sources":["../../../src/external/org/lead-doc-read.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Hono } from "hono";
|
|
2
|
+
import { type OrgRouterDeps } from "./routes.js";
|
|
3
|
+
export interface MountOrgRoutersDeps {
|
|
4
|
+
honoHost?: string;
|
|
5
|
+
leadsRoot?: string;
|
|
6
|
+
leadsRouteSecret?: string;
|
|
7
|
+
orgLstatSync?: OrgRouterDeps["lstatSync"];
|
|
8
|
+
orgWithDecisionsLock?: OrgRouterDeps["withDecisionsLock"];
|
|
9
|
+
}
|
|
10
|
+
/** FR-04.38 — mounted only when `honoHost` + `leadsRoot` are both provided. */
|
|
11
|
+
export declare function mountOrgRouters(app: Hono, deps: MountOrgRoutersDeps): void;
|
|
12
|
+
//# sourceMappingURL=mount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/external/org/mount.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGlE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC1C,oBAAoB,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;CAC3D;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAuB1E"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/org/mount.ts — composes BOTH the secret-gated `/api/external/org/*`
|
|
3
|
+
* family and the plain-surface `/api/org/*` proxy onto one parent Hono app.
|
|
4
|
+
*
|
|
5
|
+
* Split out of `external/routes.ts` (grandfathered at the 300-line bloat
|
|
6
|
+
* ceiling — CLAUDE.md) purely to avoid ratcheting it; no behavior change.
|
|
7
|
+
* Mounting both from one call site is NOT the "route-mount collision" the
|
|
8
|
+
* iterate spec's Design Notes warn against — that note is about the new
|
|
9
|
+
* router never calling a `registerXRoute` from this family (it doesn't;
|
|
10
|
+
* `routes/org.ts` only imports the extracted pure cores). Which file issues
|
|
11
|
+
* the two `app.route()` calls is unrelated to that guard.
|
|
12
|
+
*/
|
|
13
|
+
import { createOrgRouter } from "./routes.js";
|
|
14
|
+
import { createOrgApiRouter } from "../../routes/org.js";
|
|
15
|
+
/** FR-04.38 — mounted only when `honoHost` + `leadsRoot` are both provided. */
|
|
16
|
+
export function mountOrgRouters(app, deps) {
|
|
17
|
+
const { honoHost, leadsRoot, leadsRouteSecret, orgLstatSync, orgWithDecisionsLock } = deps;
|
|
18
|
+
if (!honoHost || !leadsRoot)
|
|
19
|
+
return;
|
|
20
|
+
app.route("/", createOrgRouter({
|
|
21
|
+
honoHost,
|
|
22
|
+
leadsRoot,
|
|
23
|
+
leadsRouteSecret,
|
|
24
|
+
lstatSync: orgLstatSync,
|
|
25
|
+
withDecisionsLock: orgWithDecisionsLock,
|
|
26
|
+
}));
|
|
27
|
+
app.route("/", createOrgApiRouter({
|
|
28
|
+
honoHost,
|
|
29
|
+
leadsRoot,
|
|
30
|
+
lstatSync: orgLstatSync,
|
|
31
|
+
withDecisionsLock: orgWithDecisionsLock,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=mount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../../../src/external/org/mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,eAAe,EAAsB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAUzD,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,GAAS,EAAE,IAAyB;IAClE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,GAAG,IAAI,CAAC;IAC3F,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS;QAAE,OAAO;IAEpC,GAAG,CAAC,KAAK,CACP,GAAG,EACH,eAAe,CAAC;QACd,QAAQ;QACR,SAAS;QACT,gBAAgB;QAChB,SAAS,EAAE,YAAY;QACvB,iBAAiB,EAAE,oBAAoB;KACxC,CAAC,CACH,CAAC;IACF,GAAG,CAAC,KAAK,CACP,GAAG,EACH,kBAAkB,CAAC;QACjB,QAAQ;QACR,SAAS;QACT,SAAS,EAAE,YAAY;QACvB,iBAAiB,EAAE,oBAAoB;KACxC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -13,4 +13,17 @@ export declare function readLeadOrgInfo(leadsRoot: string, leadId: string,
|
|
|
13
13
|
* mocked final-component symlink would produce, without needing a REAL
|
|
14
14
|
* symlink on disk). Defaults to the real `openSync`. */
|
|
15
15
|
openSyncFn?: typeof openSync): LeadOrgInfoResult;
|
|
16
|
+
/**
|
|
17
|
+
* Reads + parses `org-chart.json` ONCE and returns a lookup callback good
|
|
18
|
+
* for every lead in the roster — for a composite, multi-lead read where
|
|
19
|
+
* `readLeadOrgInfo`'s per-call re-open+re-parse would recreate the N+1
|
|
20
|
+
* pattern the composite endpoint exists to avoid.
|
|
21
|
+
*/
|
|
22
|
+
export declare function readAllLeadOrgInfo(leadsRoot: string, openSyncFn?: typeof openSync): {
|
|
23
|
+
ok: true;
|
|
24
|
+
forLead: (leadId: string) => LeadOrgInfoResult;
|
|
25
|
+
} | {
|
|
26
|
+
ok: false;
|
|
27
|
+
reason: "org_chart_missing" | "org_chart_symlink" | "org_chart_invalid";
|
|
28
|
+
};
|
|
16
29
|
//# sourceMappingURL=org-chart-lookup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"org-chart-lookup.d.ts","sourceRoot":"","sources":["../../../src/external/org/org-chart-lookup.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAkD,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAGjG,MAAM,MAAM,iBAAiB,GACzB,mBAAmB,GACnB,mBAAmB,GACnB,mBAAmB,GACnB,gBAAgB,CAAC;AAErB,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACpD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"org-chart-lookup.d.ts","sourceRoot":"","sources":["../../../src/external/org/org-chart-lookup.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAkD,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAGjG,MAAM,MAAM,iBAAiB,GACzB,mBAAmB,GACnB,mBAAmB,GACnB,mBAAmB,GACnB,gBAAgB,CAAC;AAErB,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACpD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAiG7C,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM;AACd;;yDAEyD;AACzD,UAAU,GAAE,OAAO,QAAmB,GACrC,iBAAiB,CAInB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,OAAO,QAAmB,GAEpC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAA;CAAE,GAC5D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,GAAG,mBAAmB,CAAA;CAAE,CAIzF"}
|
|
@@ -29,11 +29,20 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { closeSync, constants as fsConstants, fstatSync, openSync, readFileSync } from "node:fs";
|
|
31
31
|
import path from "node:path";
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
|
|
32
|
+
/**
|
|
33
|
+
* The O_NOFOLLOW-open + fstat + JSON.parse steps, isolated from the
|
|
34
|
+
* per-lead lookup below — this is the ONE disk read/parse. Split out so
|
|
35
|
+
* `readAllLeadOrgInfo` can share it across every lead in a single request
|
|
36
|
+
* instead of `readLeadOrgInfo`'s per-lead re-open+re-parse (external-review
|
|
37
|
+
* fix, MEDIUM/spec: `GET /api/org/leads` was re-parsing `org-chart.json`
|
|
38
|
+
* once per lead via this function — see the iterate spec's Design Notes).
|
|
39
|
+
* The composite route also calls `orgChartCore` separately for the roster
|
|
40
|
+
* body, so a request still parses the file AT MOST TWICE, never per-lead —
|
|
41
|
+
* that two-parse ceiling, not a single shared parse, is what this fixes
|
|
42
|
+
* (code-review fix: the design doc previously overclaimed "parses the
|
|
43
|
+
* chart once").
|
|
44
|
+
*/
|
|
45
|
+
function readOrgChartRaw(leadsRoot, openSyncFn) {
|
|
37
46
|
const target = path.join(leadsRoot, "org-chart.json");
|
|
38
47
|
let fd;
|
|
39
48
|
try {
|
|
@@ -72,6 +81,10 @@ openSyncFn = openSync) {
|
|
|
72
81
|
if (typeof parsed !== "object" || parsed === null) {
|
|
73
82
|
return { ok: false, reason: "org_chart_invalid" };
|
|
74
83
|
}
|
|
84
|
+
return { ok: true, parsed };
|
|
85
|
+
}
|
|
86
|
+
/** Looks up ONE lead's `triggers.cron` + `reports_to` in an already-parsed chart. */
|
|
87
|
+
function lookupLeadOrgInfo(parsed, leadId) {
|
|
75
88
|
const leadsField = parsed.leads;
|
|
76
89
|
if (typeof leadsField !== "object" || leadsField === null || Array.isArray(leadsField)) {
|
|
77
90
|
return { ok: false, reason: "org_chart_invalid" };
|
|
@@ -97,4 +110,26 @@ openSyncFn = openSync) {
|
|
|
97
110
|
: null;
|
|
98
111
|
return { ok: true, cron, reportsTo };
|
|
99
112
|
}
|
|
113
|
+
export function readLeadOrgInfo(leadsRoot, leadId,
|
|
114
|
+
/** Test seam for the O_NOFOLLOW open below (e.g. to simulate an ELOOP a
|
|
115
|
+
* mocked final-component symlink would produce, without needing a REAL
|
|
116
|
+
* symlink on disk). Defaults to the real `openSync`. */
|
|
117
|
+
openSyncFn = openSync) {
|
|
118
|
+
const raw = readOrgChartRaw(leadsRoot, openSyncFn);
|
|
119
|
+
if (!raw.ok)
|
|
120
|
+
return raw;
|
|
121
|
+
return lookupLeadOrgInfo(raw.parsed, leadId);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Reads + parses `org-chart.json` ONCE and returns a lookup callback good
|
|
125
|
+
* for every lead in the roster — for a composite, multi-lead read where
|
|
126
|
+
* `readLeadOrgInfo`'s per-call re-open+re-parse would recreate the N+1
|
|
127
|
+
* pattern the composite endpoint exists to avoid.
|
|
128
|
+
*/
|
|
129
|
+
export function readAllLeadOrgInfo(leadsRoot, openSyncFn = openSync) {
|
|
130
|
+
const raw = readOrgChartRaw(leadsRoot, openSyncFn);
|
|
131
|
+
if (!raw.ok)
|
|
132
|
+
return raw;
|
|
133
|
+
return { ok: true, forLead: (leadId) => lookupLeadOrgInfo(raw.parsed, leadId) };
|
|
134
|
+
}
|
|
100
135
|
//# sourceMappingURL=org-chart-lookup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"org-chart-lookup.js","sourceRoot":"","sources":["../../../src/external/org/org-chart-lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,SAAS,EAAE,SAAS,IAAI,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"org-chart-lookup.js","sourceRoot":"","sources":["../../../src/external/org/org-chart-lookup.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
import type { Hono } from "hono";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
name: string;
|
|
5
|
-
reports_to: string | null;
|
|
6
|
-
manages: string[];
|
|
7
|
-
charter_path: string;
|
|
8
|
-
}
|
|
9
|
-
export interface OrgChartView {
|
|
10
|
-
version: number;
|
|
11
|
-
po: string;
|
|
12
|
-
leads: Record<string, OrgChartLeadView>;
|
|
13
|
-
}
|
|
2
|
+
import type { OrgChartView } from "../../types/org.js";
|
|
3
|
+
export type { OrgChartLeadView, OrgChartView } from "../../types/org.js";
|
|
14
4
|
/** Structural-only check — NOT the full leadwright Zod schema. See file header. */
|
|
15
5
|
export declare function parseOrgChart(raw: string): OrgChartView | null;
|
|
16
6
|
export interface OrgChartRouteDeps {
|
|
@@ -21,5 +11,24 @@ export interface OrgChartRouteDeps {
|
|
|
21
11
|
isSymbolicLink(): boolean;
|
|
22
12
|
};
|
|
23
13
|
}
|
|
14
|
+
export type OrgChartCoreResult = {
|
|
15
|
+
status: 200;
|
|
16
|
+
body: OrgChartView;
|
|
17
|
+
} | {
|
|
18
|
+
status: 403 | 404 | 500 | 502;
|
|
19
|
+
body: {
|
|
20
|
+
error: string;
|
|
21
|
+
detail?: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Pure core — no Hono dependency, so both the secret-gated
|
|
26
|
+
* `/api/external/org/org-chart` route AND the plain-surface `/api/org/*`
|
|
27
|
+
* proxy (browser-facing, no secret, see `server/src/routes/org.ts`) share
|
|
28
|
+
* this ONE implementation of the symlink/containment/parse guards
|
|
29
|
+
* (Internal Plan Review fix — the guards previously lived only inside this
|
|
30
|
+
* handler closure and could not have been reused without duplication).
|
|
31
|
+
*/
|
|
32
|
+
export declare function orgChartCore(deps: OrgChartRouteDeps): OrgChartCoreResult;
|
|
24
33
|
export declare function registerOrgChartRoute(app: Hono, deps: OrgChartRouteDeps): void;
|
|
25
34
|
//# sourceMappingURL=org-chart.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"org-chart.d.ts","sourceRoot":"","sources":["../../../src/external/org/org-chart.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,
|
|
1
|
+
{"version":3,"file":"org-chart.d.ts","sourceRoot":"","sources":["../../../src/external/org/org-chart.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEzE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAezE,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA2B9D;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB;0EACsE;IACtE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,cAAc,IAAI,OAAO,CAAA;KAAE,CAAC;CAC7D;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GACnC;IAAE,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEhF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,iBAAiB,GAAG,kBAAkB,CA4CxE;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAK9E"}
|
|
@@ -68,43 +68,61 @@ export function parseOrgChart(raw) {
|
|
|
68
68
|
}
|
|
69
69
|
return { version: obj.version, po: obj.po, leads };
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Pure core — no Hono dependency, so both the secret-gated
|
|
73
|
+
* `/api/external/org/org-chart` route AND the plain-surface `/api/org/*`
|
|
74
|
+
* proxy (browser-facing, no secret, see `server/src/routes/org.ts`) share
|
|
75
|
+
* this ONE implementation of the symlink/containment/parse guards
|
|
76
|
+
* (Internal Plan Review fix — the guards previously lived only inside this
|
|
77
|
+
* handler closure and could not have been reused without duplication).
|
|
78
|
+
*/
|
|
79
|
+
export function orgChartCore(deps) {
|
|
72
80
|
const { leadsRoot } = deps;
|
|
73
81
|
const lstat = deps.lstatSync ?? ((p) => lstatSync(p));
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return c.json({ error: "symlink_forbidden" }, 403);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
catch (err) {
|
|
87
|
-
const code = err?.code;
|
|
88
|
-
if (code !== "ENOENT") {
|
|
89
|
-
return c.json({ error: "org_chart_stat_failed", detail: String(err).slice(0, 200) }, 500);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
let raw;
|
|
93
|
-
try {
|
|
94
|
-
raw = readFileSync(target, "utf8");
|
|
82
|
+
const target = path.join(leadsRoot, "org-chart.json");
|
|
83
|
+
// External-review fix (HIGH, security): this named endpoint previously
|
|
84
|
+
// went straight to readFileSync (follows symlinks) with no lstat check
|
|
85
|
+
// and no realpath containment guard — a symlinked org-chart.json could
|
|
86
|
+
// serve an arbitrary external file's contents through this authenticated
|
|
87
|
+
// route if it happened to satisfy the lightweight structural shape.
|
|
88
|
+
try {
|
|
89
|
+
if (lstat(target).isSymbolicLink()) {
|
|
90
|
+
return { status: 403, body: { error: "symlink_forbidden" } };
|
|
95
91
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
const code = err?.code;
|
|
95
|
+
if (code !== "ENOENT") {
|
|
96
|
+
return {
|
|
97
|
+
status: 500,
|
|
98
|
+
body: { error: "org_chart_stat_failed", detail: String(err).slice(0, 200) },
|
|
99
|
+
};
|
|
102
100
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
}
|
|
102
|
+
let raw;
|
|
103
|
+
try {
|
|
104
|
+
raw = readFileSync(target, "utf8");
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
const code = err?.code;
|
|
108
|
+
if (code === "ENOENT") {
|
|
109
|
+
return { status: 404, body: { error: "org_chart_missing" } };
|
|
106
110
|
}
|
|
107
|
-
return
|
|
111
|
+
return {
|
|
112
|
+
status: 500,
|
|
113
|
+
body: { error: "org_chart_read_failed", detail: String(err).slice(0, 200) },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const orgChart = parseOrgChart(raw);
|
|
117
|
+
if (!orgChart) {
|
|
118
|
+
return { status: 502, body: { error: "org_chart_invalid" } };
|
|
119
|
+
}
|
|
120
|
+
return { status: 200, body: orgChart };
|
|
121
|
+
}
|
|
122
|
+
export function registerOrgChartRoute(app, deps) {
|
|
123
|
+
app.get("/api/external/org/org-chart", async (c) => {
|
|
124
|
+
const result = orgChartCore(deps);
|
|
125
|
+
return c.json(result.body, result.status);
|
|
108
126
|
});
|
|
109
127
|
}
|
|
110
128
|
//# sourceMappingURL=org-chart.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"org-chart.js","sourceRoot":"","sources":["../../../src/external/org/org-chart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"org-chart.js","sourceRoot":"","sources":["../../../src/external/org/org-chart.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-extract.d.ts","sourceRoot":"","sources":["../../../src/external/org/role-extract.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAiBpF,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,GAAG,cAAc,CA6C3E"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/org/role-extract.ts — extracts the one-sentence role summary
|
|
3
|
+
* shown in a lead card's Role block from a lead's `charter.md`.
|
|
4
|
+
*
|
|
5
|
+
* Precise rule (iterate spec Design Notes, "Role extraction"): skip YAML
|
|
6
|
+
* front matter and heading lines, select the first paragraph of plain prose
|
|
7
|
+
* (skipping code fences and blockquote-only content), strip markdown
|
|
8
|
+
* link/image syntax from the extracted text, take the first sentence up to
|
|
9
|
+
* a bounded character cap. Returns `{ measured: false }` — never a dumped
|
|
10
|
+
* first paragraph — when no usable prose sentence exists.
|
|
11
|
+
*
|
|
12
|
+
* Text-processing only, not a full markdown AST parse — the rule above is
|
|
13
|
+
* expressed entirely as line-shape skips + a regex strip, which a real
|
|
14
|
+
* parser would not make simpler for a single-sentence extraction.
|
|
15
|
+
*/
|
|
16
|
+
const ROLE_SENTENCE_MAX_CHARS = 240;
|
|
17
|
+
function stripMarkdownLinksAndImages(text) {
|
|
18
|
+
//  -> alt ; [text](url) -> text
|
|
19
|
+
return text.replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
20
|
+
}
|
|
21
|
+
function firstSentence(text) {
|
|
22
|
+
const m = /^(.*?[.!?])(\s|$)/.exec(text);
|
|
23
|
+
const sentence = m ? m[1] : text;
|
|
24
|
+
return sentence.length > ROLE_SENTENCE_MAX_CHARS
|
|
25
|
+
? sentence.slice(0, ROLE_SENTENCE_MAX_CHARS).trimEnd()
|
|
26
|
+
: sentence;
|
|
27
|
+
}
|
|
28
|
+
export function extractRoleSentence(charterMarkdown) {
|
|
29
|
+
let lines = charterMarkdown.split(/\r?\n/);
|
|
30
|
+
// Skip a leading YAML front-matter block (--- ... ---).
|
|
31
|
+
if (lines[0]?.trim() === "---") {
|
|
32
|
+
const closeIdx = lines.slice(1).findIndex((l) => l.trim() === "---");
|
|
33
|
+
if (closeIdx !== -1) {
|
|
34
|
+
lines = lines.slice(closeIdx + 2);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const paragraphLines = [];
|
|
38
|
+
let inCodeFence = false;
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
|
|
42
|
+
inCodeFence = !inCodeFence;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (inCodeFence)
|
|
46
|
+
continue;
|
|
47
|
+
if (trimmed.length === 0) {
|
|
48
|
+
if (paragraphLines.length > 0)
|
|
49
|
+
break; // paragraph ended
|
|
50
|
+
continue; // skip leading blank lines
|
|
51
|
+
}
|
|
52
|
+
if (/^#{1,6}\s/.test(trimmed))
|
|
53
|
+
continue; // heading line
|
|
54
|
+
if (trimmed.startsWith(">"))
|
|
55
|
+
continue; // blockquote-only content
|
|
56
|
+
paragraphLines.push(trimmed);
|
|
57
|
+
}
|
|
58
|
+
const paragraph = paragraphLines.join(" ").trim();
|
|
59
|
+
if (paragraph.length === 0) {
|
|
60
|
+
return { measured: false };
|
|
61
|
+
}
|
|
62
|
+
const stripped = stripMarkdownLinksAndImages(paragraph).trim();
|
|
63
|
+
if (stripped.length === 0) {
|
|
64
|
+
return { measured: false };
|
|
65
|
+
}
|
|
66
|
+
const sentence = firstSentence(stripped).trim();
|
|
67
|
+
if (sentence.length === 0) {
|
|
68
|
+
return { measured: false };
|
|
69
|
+
}
|
|
70
|
+
return { measured: true, text: sentence };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=role-extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-extract.js","sourceRoot":"","sources":["../../../src/external/org/role-extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
import type { Hono } from "hono";
|
|
2
2
|
import { LEADS_USAGE_REFRESH_INTERVAL_MS } from "./_helpers.js";
|
|
3
|
+
import type { UsageResponse } from "../../types/org.js";
|
|
3
4
|
export { LEADS_USAGE_REFRESH_INTERVAL_MS };
|
|
4
|
-
export type UsageResponse
|
|
5
|
-
leadId: string;
|
|
6
|
-
measured: false;
|
|
7
|
-
} | {
|
|
8
|
-
leadId: string;
|
|
9
|
-
measured: true;
|
|
10
|
-
costUsd: number;
|
|
11
|
-
runCount: number;
|
|
12
|
-
windowDays: number;
|
|
13
|
-
asOf: string;
|
|
14
|
-
};
|
|
5
|
+
export type { UsageResponse } from "../../types/org.js";
|
|
15
6
|
export interface UsageRouteDeps {
|
|
16
7
|
leadsRoot: string;
|
|
17
8
|
/** Injectable for tests (code-review: this branch was previously
|
|
@@ -20,5 +11,18 @@ export interface UsageRouteDeps {
|
|
|
20
11
|
isSymbolicLink(): boolean;
|
|
21
12
|
};
|
|
22
13
|
}
|
|
14
|
+
export type UsageCoreResult = {
|
|
15
|
+
status: 200;
|
|
16
|
+
body: UsageResponse;
|
|
17
|
+
} | {
|
|
18
|
+
status: 400 | 403 | 500 | 502;
|
|
19
|
+
body: {
|
|
20
|
+
error: string;
|
|
21
|
+
leadId?: string;
|
|
22
|
+
detail?: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/** Pure core — shared by the secret-gated route and the plain-surface proxy. */
|
|
26
|
+
export declare function usageCore(deps: UsageRouteDeps, leadId: string): UsageCoreResult;
|
|
23
27
|
export declare function registerUsageRoute(app: Hono, deps: UsageRouteDeps): void;
|
|
24
28
|
//# sourceMappingURL=usage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../src/external/org/usage.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,OAAO,EAAc,+BAA+B,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../src/external/org/usage.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,OAAO,EAAc,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAE5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,+BAA+B,EAAE,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAsBxD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB;kFAC8E;IAC9E,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,cAAc,IAAI,OAAO,CAAA;KAAE,CAAC;CAC7D;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAE,GACpC;IAAE,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEjG,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,CAwE/E;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAKxE"}
|