@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.
Files changed (177) hide show
  1. package/client/dist/assets/{EmbeddedTerminal-N1SJ3XX7.js → EmbeddedTerminal-5QvKe3_a.js} +20 -20
  2. package/client/dist/assets/{abnfDiagram-VRR7QNED-BcoPVcm6.js → abnfDiagram-VRR7QNED-CXi9HegQ.js} +1 -1
  3. package/client/dist/assets/{arc-BhjELRo0.js → arc-DLh4GiqR.js} +1 -1
  4. package/client/dist/assets/{architectureDiagram-ZJ3FMSHR-DbNXtiqW.js → architectureDiagram-ZJ3FMSHR-DUypQ8dQ.js} +1 -1
  5. package/client/dist/assets/{blockDiagram-677ZJIJ3-Bq-jwL4e.js → blockDiagram-677ZJIJ3-D2cVMKJG.js} +1 -1
  6. package/client/dist/assets/{c4Diagram-LMCZKHZV-CjFKHlUb.js → c4Diagram-LMCZKHZV-DtruBGJw.js} +1 -1
  7. package/client/dist/assets/channel-C_k-9jsa.js +1 -0
  8. package/client/dist/assets/{chunk-2Q5K7J3B-C6RrKKpc.js → chunk-2Q5K7J3B-CAfQkFw2.js} +1 -1
  9. package/client/dist/assets/{chunk-32BRIVSS-BWuO7O6S.js → chunk-32BRIVSS-CPWXoLnQ.js} +1 -1
  10. package/client/dist/assets/{chunk-5VM5RSS4-BjYoiHmo.js → chunk-5VM5RSS4-xVc3oppQ.js} +1 -1
  11. package/client/dist/assets/{chunk-EX3LRPZG-CyQYieee.js → chunk-EX3LRPZG-CUpl6fRR.js} +1 -1
  12. package/client/dist/assets/{chunk-JWPE2WC7-CY9fT_5d.js → chunk-JWPE2WC7-NejUp56x.js} +1 -1
  13. package/client/dist/assets/{chunk-MOJQB5TN-DCcb0M8e.js → chunk-MOJQB5TN-C9-2lLg0.js} +1 -1
  14. package/client/dist/assets/{chunk-RYQCIY6F-CVd_mjJU.js → chunk-RYQCIY6F-BAPtwABy.js} +1 -1
  15. package/client/dist/assets/{chunk-V7JOEXUC-qBo5EfYb.js → chunk-V7JOEXUC-Dwzihw26.js} +1 -1
  16. package/client/dist/assets/{chunk-VR4S4FIN-C1TLpczn.js → chunk-VR4S4FIN-9-iArFJM.js} +1 -1
  17. package/client/dist/assets/{chunk-XXDRQBXY-DePb8ZtI.js → chunk-XXDRQBXY-DiWHOrqe.js} +1 -1
  18. package/client/dist/assets/classDiagram-OUVF2IWQ-CsEPq9Ac.js +1 -0
  19. package/client/dist/assets/classDiagram-v2-EOCWNBFH-CsEPq9Ac.js +1 -0
  20. package/client/dist/assets/{cose-bilkent-JH36ORCC-BJNddNgC.js → cose-bilkent-JH36ORCC-Cp04EOcH.js} +1 -1
  21. package/client/dist/assets/{cynefin-VYW2F7L2-BtJssOuy.js → cynefin-VYW2F7L2-D--GN1v8.js} +1 -1
  22. package/client/dist/assets/{cynefinDiagram-TSTJHNR4-BS8F0pjM.js → cynefinDiagram-TSTJHNR4-BGH0o_qp.js} +1 -1
  23. package/client/dist/assets/{dagre-VKFMJZFB-C4A2MxdE.js → dagre-VKFMJZFB-DSKkIKlj.js} +1 -1
  24. package/client/dist/assets/{diagram-FQU43EPY-vX6qE1sj.js → diagram-FQU43EPY-Cs_C4lb-.js} +1 -1
  25. package/client/dist/assets/{diagram-G47NLZAW-B5P1Rrem.js → diagram-G47NLZAW-oDtAA8O_.js} +1 -1
  26. package/client/dist/assets/{diagram-NH7WQ7WH-u7LHeyDa.js → diagram-NH7WQ7WH-BiFMjqSH.js} +1 -1
  27. package/client/dist/assets/{diagram-OA4YK3LP-CZhgtAtD.js → diagram-OA4YK3LP-Ccs3thCj.js} +1 -1
  28. package/client/dist/assets/{diagram-WEI45ONY-9k80njcz.js → diagram-WEI45ONY-CdZNqgO3.js} +1 -1
  29. package/client/dist/assets/{ebnfDiagram-CCIWWBDH-B3ES3AxZ.js → ebnfDiagram-CCIWWBDH-CapTA0VL.js} +1 -1
  30. package/client/dist/assets/{erDiagram-Q63AITRT-BSLAY2HJ.js → erDiagram-Q63AITRT-BcRNEP8s.js} +1 -1
  31. package/client/dist/assets/{flowDiagram-23GEKE2U-BIse3jvS.js → flowDiagram-23GEKE2U-CJ_zF7e_.js} +1 -1
  32. package/client/dist/assets/{ganttDiagram-NO4QXBWP-BCV_RM7c.js → ganttDiagram-NO4QXBWP-CnJdcg9A.js} +1 -1
  33. package/client/dist/assets/{gitGraphDiagram-IHSO6WYX-ClkOuEG1.js → gitGraphDiagram-IHSO6WYX-YTUQ_ELk.js} +1 -1
  34. package/client/dist/assets/index-6nZ1bt0a.css +1 -0
  35. package/client/dist/assets/index-irQhGNpn.js +828 -0
  36. package/client/dist/assets/{infoDiagram-FWYZ7A6U-Cu6-m8O7.js → infoDiagram-FWYZ7A6U-CLOP_ZWD.js} +1 -1
  37. package/client/dist/assets/{ishikawaDiagram-FXEZZL3T-D62Cg1g-.js → ishikawaDiagram-FXEZZL3T-aFTIzr7o.js} +1 -1
  38. package/client/dist/assets/{journeyDiagram-5HDEW3XC-DHMljiqG.js → journeyDiagram-5HDEW3XC-CbPJB4xd.js} +1 -1
  39. package/client/dist/assets/{kanban-definition-HUTT4EX6-CvELrwMl.js → kanban-definition-HUTT4EX6-BoyhytIS.js} +1 -1
  40. package/client/dist/assets/{linear-DpT0hsvX.js → linear-1VXA-uBg.js} +1 -1
  41. package/client/dist/assets/{mermaid.core-CSwHP-lX.js → mermaid.core-D_lO2yK9.js} +4 -4
  42. package/client/dist/assets/{mindmap-definition-LN4V7U3C-BPaYhqXE.js → mindmap-definition-LN4V7U3C-iFK7LXMx.js} +1 -1
  43. package/client/dist/assets/{pegDiagram-2B236MQR-Bn0zJHpH.js → pegDiagram-2B236MQR-DNmcmfA-.js} +1 -1
  44. package/client/dist/assets/{pieDiagram-ENE6RG2P-CpZW49Cw.js → pieDiagram-ENE6RG2P-ekx83O5b.js} +1 -1
  45. package/client/dist/assets/{quadrantDiagram-ABIIQ3AL--El8Y1Vr.js → quadrantDiagram-ABIIQ3AL-DayyD1l8.js} +1 -1
  46. package/client/dist/assets/{railroadDiagram-RFXS5EU6-Bb4L-n6k.js → railroadDiagram-RFXS5EU6-Bdgz7S6G.js} +1 -1
  47. package/client/dist/assets/{requirementDiagram-TGXJPOKE-BYmMcf0e.js → requirementDiagram-TGXJPOKE-szP3Muld.js} +1 -1
  48. package/client/dist/assets/{sankeyDiagram-HTMAVEWB-C2JWG0lB.js → sankeyDiagram-HTMAVEWB-Ct3rzgUj.js} +1 -1
  49. package/client/dist/assets/{sequenceDiagram-DBY2YBRQ-ck4oQ3Sx.js → sequenceDiagram-DBY2YBRQ-_69EfmOU.js} +1 -1
  50. package/client/dist/assets/{sizeCapture-X5ZJPWSS-Cn7ZoV_6.js → sizeCapture-X5ZJPWSS-BSOU06J2.js} +1 -1
  51. package/client/dist/assets/{stateDiagram-2N3HPSRC-Cfps2GyX.js → stateDiagram-2N3HPSRC-HmOmFwQm.js} +1 -1
  52. package/client/dist/assets/stateDiagram-v2-6OUMAXLB-BW8H33dv.js +1 -0
  53. package/client/dist/assets/{swimlanes-5IMT3BWC-qzrx_9r_.js → swimlanes-5IMT3BWC-CVg_4oT7.js} +2 -2
  54. package/client/dist/assets/swimlanesDiagram-G3AALYLV-BaRlxcPD.js +8 -0
  55. package/client/dist/assets/{timeline-definition-FHXFAJF6-cdJrYjL1.js → timeline-definition-FHXFAJF6-CBBaDKXm.js} +1 -1
  56. package/client/dist/assets/{vennDiagram-L72KCM5P-CsADhf78.js → vennDiagram-L72KCM5P-CBXiWPMc.js} +1 -1
  57. package/client/dist/assets/{wardleyDiagram-EHGQE667-CiIy8tCK.js → wardleyDiagram-EHGQE667-uhBCZoW0.js} +1 -1
  58. package/client/dist/assets/{xychartDiagram-FW5EYKEG-4dCIrB0W.js → xychartDiagram-FW5EYKEG-xxE_wY5O.js} +1 -1
  59. package/client/dist/index.html +2 -2
  60. package/lib/server.mjs +11 -3
  61. package/lib/util.mjs +70 -1
  62. package/package.json +68 -68
  63. package/server/dist/core/grade-runner.d.ts +4 -4
  64. package/server/dist/core/grade-runner.d.ts.map +1 -1
  65. package/server/dist/core/grade-runner.js +51 -26
  66. package/server/dist/core/grade-runner.js.map +1 -1
  67. package/server/dist/core/grade-target.d.ts +8 -0
  68. package/server/dist/core/grade-target.d.ts.map +1 -1
  69. package/server/dist/core/grade-target.js +10 -0
  70. package/server/dist/core/grade-target.js.map +1 -1
  71. package/server/dist/core/triage-cli-runner.d.ts.map +1 -1
  72. package/server/dist/core/triage-cli-runner.js +29 -10
  73. package/server/dist/core/triage-cli-runner.js.map +1 -1
  74. package/server/dist/core/uv-runner.d.ts +23 -0
  75. package/server/dist/core/uv-runner.d.ts.map +1 -0
  76. package/server/dist/core/uv-runner.js +39 -0
  77. package/server/dist/core/uv-runner.js.map +1 -0
  78. package/server/dist/external/file/exists-routes.d.ts +7 -0
  79. package/server/dist/external/file/exists-routes.d.ts.map +1 -0
  80. package/server/dist/external/file/exists-routes.js +91 -0
  81. package/server/dist/external/file/exists-routes.js.map +1 -0
  82. package/server/dist/external/org/audit-log.d.ts +27 -0
  83. package/server/dist/external/org/audit-log.d.ts.map +1 -0
  84. package/server/dist/external/org/audit-log.js +92 -0
  85. package/server/dist/external/org/audit-log.js.map +1 -0
  86. package/server/dist/external/org/beat-register.d.ts +15 -22
  87. package/server/dist/external/org/beat-register.d.ts.map +1 -1
  88. package/server/dist/external/org/beat-register.js +37 -32
  89. package/server/dist/external/org/beat-register.js.map +1 -1
  90. package/server/dist/external/org/file-read.d.ts +16 -0
  91. package/server/dist/external/org/file-read.d.ts.map +1 -1
  92. package/server/dist/external/org/file-read.js +86 -46
  93. package/server/dist/external/org/file-read.js.map +1 -1
  94. package/server/dist/external/org/file-write-core.d.ts +18 -0
  95. package/server/dist/external/org/file-write-core.d.ts.map +1 -0
  96. package/server/dist/external/org/file-write-core.js +97 -0
  97. package/server/dist/external/org/file-write-core.js.map +1 -0
  98. package/server/dist/external/org/file-write.d.ts +25 -0
  99. package/server/dist/external/org/file-write.d.ts.map +1 -1
  100. package/server/dist/external/org/file-write.js +82 -132
  101. package/server/dist/external/org/file-write.js.map +1 -1
  102. package/server/dist/external/org/last-run.d.ts +25 -16
  103. package/server/dist/external/org/last-run.d.ts.map +1 -1
  104. package/server/dist/external/org/last-run.js +78 -64
  105. package/server/dist/external/org/last-run.js.map +1 -1
  106. package/server/dist/external/org/lead-doc-read.d.ts +21 -0
  107. package/server/dist/external/org/lead-doc-read.d.ts.map +1 -0
  108. package/server/dist/external/org/lead-doc-read.js +84 -0
  109. package/server/dist/external/org/lead-doc-read.js.map +1 -0
  110. package/server/dist/external/org/mount.d.ts +12 -0
  111. package/server/dist/external/org/mount.d.ts.map +1 -0
  112. package/server/dist/external/org/mount.js +34 -0
  113. package/server/dist/external/org/mount.js.map +1 -0
  114. package/server/dist/external/org/org-chart-lookup.d.ts +13 -0
  115. package/server/dist/external/org/org-chart-lookup.d.ts.map +1 -1
  116. package/server/dist/external/org/org-chart-lookup.js +40 -5
  117. package/server/dist/external/org/org-chart-lookup.js.map +1 -1
  118. package/server/dist/external/org/org-chart.d.ts +21 -12
  119. package/server/dist/external/org/org-chart.d.ts.map +1 -1
  120. package/server/dist/external/org/org-chart.js +50 -32
  121. package/server/dist/external/org/org-chart.js.map +1 -1
  122. package/server/dist/external/org/role-extract.d.ts +8 -0
  123. package/server/dist/external/org/role-extract.d.ts.map +1 -0
  124. package/server/dist/external/org/role-extract.js +72 -0
  125. package/server/dist/external/org/role-extract.js.map +1 -0
  126. package/server/dist/external/org/usage.d.ts +15 -11
  127. package/server/dist/external/org/usage.d.ts.map +1 -1
  128. package/server/dist/external/org/usage.js +64 -54
  129. package/server/dist/external/org/usage.js.map +1 -1
  130. package/server/dist/external/routes.d.ts +1 -1
  131. package/server/dist/external/routes.d.ts.map +1 -1
  132. package/server/dist/external/routes.js +10 -11
  133. package/server/dist/external/routes.js.map +1 -1
  134. package/server/dist/routes/grade.d.ts.map +1 -1
  135. package/server/dist/routes/grade.js +3 -2
  136. package/server/dist/routes/grade.js.map +1 -1
  137. package/server/dist/routes/org-leads-composite.d.ts +21 -0
  138. package/server/dist/routes/org-leads-composite.d.ts.map +1 -0
  139. package/server/dist/routes/org-leads-composite.js +95 -0
  140. package/server/dist/routes/org-leads-composite.js.map +1 -0
  141. package/server/dist/routes/org.d.ts +15 -0
  142. package/server/dist/routes/org.d.ts.map +1 -0
  143. package/server/dist/routes/org.js +198 -0
  144. package/server/dist/routes/org.js.map +1 -0
  145. package/server/dist/terminal/pty-manager.d.ts +9 -0
  146. package/server/dist/terminal/pty-manager.d.ts.map +1 -1
  147. package/server/dist/terminal/pty-manager.js +15 -0
  148. package/server/dist/terminal/pty-manager.js.map +1 -1
  149. package/server/dist/terminal/replay-snapshot.d.ts +10 -1
  150. package/server/dist/terminal/replay-snapshot.d.ts.map +1 -1
  151. package/server/dist/terminal/replay-snapshot.js +36 -1
  152. package/server/dist/terminal/replay-snapshot.js.map +1 -1
  153. package/server/dist/terminal/ws-upgrade-handler.d.ts +17 -1
  154. package/server/dist/terminal/ws-upgrade-handler.d.ts.map +1 -1
  155. package/server/dist/terminal/ws-upgrade-handler.fixtures.d.ts +1 -0
  156. package/server/dist/terminal/ws-upgrade-handler.fixtures.d.ts.map +1 -1
  157. package/server/dist/terminal/ws-upgrade-handler.fixtures.js +3 -2
  158. package/server/dist/terminal/ws-upgrade-handler.fixtures.js.map +1 -1
  159. package/server/dist/terminal/ws-upgrade-handler.js +45 -4
  160. package/server/dist/terminal/ws-upgrade-handler.js.map +1 -1
  161. package/server/dist/test/python-spawn-guard-util.d.ts +14 -0
  162. package/server/dist/test/python-spawn-guard-util.d.ts.map +1 -0
  163. package/server/dist/test/python-spawn-guard-util.js +124 -0
  164. package/server/dist/test/python-spawn-guard-util.js.map +1 -0
  165. package/server/dist/types/org.d.ts +115 -0
  166. package/server/dist/types/org.d.ts.map +1 -0
  167. package/server/dist/types/org.js +16 -0
  168. package/server/dist/types/org.js.map +1 -0
  169. package/server/package.json +43 -43
  170. package/client/dist/assets/MarkdownEditorModal-DL5uF6Nn.js +0 -212
  171. package/client/dist/assets/channel-DjcCSejz.js +0 -1
  172. package/client/dist/assets/classDiagram-OUVF2IWQ-SNWikNvk.js +0 -1
  173. package/client/dist/assets/classDiagram-v2-EOCWNBFH-SNWikNvk.js +0 -1
  174. package/client/dist/assets/index-B1mzucik.js +0 -609
  175. package/client/dist/assets/index-C-w52nAQ.css +0 -1
  176. package/client/dist/assets/stateDiagram-v2-6OUMAXLB-DF7QbVyC.js +0 -1
  177. 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;AAE7C,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM;AACd;;yDAEyD;AACzD,UAAU,GAAE,OAAO,QAAmB,GACrC,iBAAiB,CAoEnB"}
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
- export function readLeadOrgInfo(leadsRoot, leadId,
33
- /** Test seam for the O_NOFOLLOW open below (e.g. to simulate an ELOOP a
34
- * mocked final-component symlink would produce, without needing a REAL
35
- * symlink on disk). Defaults to the real `openSync`. */
36
- openSyncFn = openSync) {
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":"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"}
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
- export interface OrgChartLeadView {
3
- domain: string;
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,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACzC;AAeD,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,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CA+C9E"}
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
- export function registerOrgChartRoute(app, deps) {
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
- app.get("/api/external/org/org-chart", async (c) => {
75
- const target = path.join(leadsRoot, "org-chart.json");
76
- // External-review fix (HIGH, security): this named endpoint previously
77
- // went straight to readFileSync (follows symlinks) with no lstat check
78
- // and no realpath containment guard — a symlinked org-chart.json could
79
- // serve an arbitrary external file's contents through this authenticated
80
- // route if it happened to satisfy the lightweight structural shape.
81
- try {
82
- if (lstat(target).isSymbolicLink()) {
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
- catch (err) {
97
- const code = err?.code;
98
- if (code === "ENOENT") {
99
- return c.json({ error: "org_chart_missing" }, 404);
100
- }
101
- return c.json({ error: "org_chart_read_failed", detail: String(err).slice(0, 200) }, 500);
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
- const orgChart = parseOrgChart(raw);
104
- if (!orgChart) {
105
- return c.json({ error: "org_chart_invalid" }, 502);
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 c.json(orgChart);
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":"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"}
1
+ {"version":3,"file":"org-chart.js","sourceRoot":"","sources":["../../../src/external/org/org-chart.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export type RoleExtraction = {
2
+ measured: false;
3
+ } | {
4
+ measured: true;
5
+ text: string;
6
+ };
7
+ export declare function extractRoleSentence(charterMarkdown: string): RoleExtraction;
8
+ //# sourceMappingURL=role-extract.d.ts.map
@@ -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](url) -> 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;AAG5E,OAAO,EAAE,+BAA+B,EAAE,CAAC;AAE3C,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAA;CAAE,GACnC;IACE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,IAAI,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAsBN,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,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CA2ExE"}
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"}