@svenroth-ai/shipwright 0.24.7-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-D2BLGWdW.js → EmbeddedTerminal-5QvKe3_a.js} +20 -20
- package/client/dist/assets/{abnfDiagram-VRR7QNED-C-W_2Jd4.js → abnfDiagram-VRR7QNED-CXi9HegQ.js} +1 -1
- package/client/dist/assets/{arc-CpqwESP5.js → arc-DLh4GiqR.js} +1 -1
- package/client/dist/assets/{architectureDiagram-ZJ3FMSHR-DZVZ895V.js → architectureDiagram-ZJ3FMSHR-DUypQ8dQ.js} +1 -1
- package/client/dist/assets/{blockDiagram-677ZJIJ3-CCCZiAA7.js → blockDiagram-677ZJIJ3-D2cVMKJG.js} +1 -1
- package/client/dist/assets/{c4Diagram-LMCZKHZV-Bg1beZAE.js → c4Diagram-LMCZKHZV-DtruBGJw.js} +1 -1
- package/client/dist/assets/channel-C_k-9jsa.js +1 -0
- package/client/dist/assets/{chunk-2Q5K7J3B-CoaXSarh.js → chunk-2Q5K7J3B-CAfQkFw2.js} +1 -1
- package/client/dist/assets/{chunk-32BRIVSS-CeFXS-CM.js → chunk-32BRIVSS-CPWXoLnQ.js} +1 -1
- package/client/dist/assets/{chunk-5VM5RSS4-QyJMVe0m.js → chunk-5VM5RSS4-xVc3oppQ.js} +1 -1
- package/client/dist/assets/{chunk-EX3LRPZG-BIOtWVF8.js → chunk-EX3LRPZG-CUpl6fRR.js} +1 -1
- package/client/dist/assets/{chunk-JWPE2WC7-Dcq-lWyX.js → chunk-JWPE2WC7-NejUp56x.js} +1 -1
- package/client/dist/assets/{chunk-MOJQB5TN-C95323ja.js → chunk-MOJQB5TN-C9-2lLg0.js} +1 -1
- package/client/dist/assets/{chunk-RYQCIY6F-C0HPwpWy.js → chunk-RYQCIY6F-BAPtwABy.js} +1 -1
- package/client/dist/assets/{chunk-V7JOEXUC-BPrTbZqA.js → chunk-V7JOEXUC-Dwzihw26.js} +1 -1
- package/client/dist/assets/{chunk-VR4S4FIN--BCnNoqt.js → chunk-VR4S4FIN-9-iArFJM.js} +1 -1
- package/client/dist/assets/{chunk-XXDRQBXY-Dj8vtcXf.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-y51otZKz.js → cose-bilkent-JH36ORCC-Cp04EOcH.js} +1 -1
- package/client/dist/assets/{cynefin-VYW2F7L2-BRfS3cHo.js → cynefin-VYW2F7L2-D--GN1v8.js} +1 -1
- package/client/dist/assets/{cynefinDiagram-TSTJHNR4-Dpg2RtfL.js → cynefinDiagram-TSTJHNR4-BGH0o_qp.js} +1 -1
- package/client/dist/assets/{dagre-VKFMJZFB-BdZtXgKZ.js → dagre-VKFMJZFB-DSKkIKlj.js} +1 -1
- package/client/dist/assets/{diagram-FQU43EPY-K-RCwWxW.js → diagram-FQU43EPY-Cs_C4lb-.js} +1 -1
- package/client/dist/assets/{diagram-G47NLZAW-CHNn7F2-.js → diagram-G47NLZAW-oDtAA8O_.js} +1 -1
- package/client/dist/assets/{diagram-NH7WQ7WH-B3zu6xib.js → diagram-NH7WQ7WH-BiFMjqSH.js} +1 -1
- package/client/dist/assets/{diagram-OA4YK3LP-Wp5ju7L7.js → diagram-OA4YK3LP-Ccs3thCj.js} +1 -1
- package/client/dist/assets/{diagram-WEI45ONY-C94GpsRk.js → diagram-WEI45ONY-CdZNqgO3.js} +1 -1
- package/client/dist/assets/{ebnfDiagram-CCIWWBDH-DXrTPOrP.js → ebnfDiagram-CCIWWBDH-CapTA0VL.js} +1 -1
- package/client/dist/assets/{erDiagram-Q63AITRT-CGejnW0f.js → erDiagram-Q63AITRT-BcRNEP8s.js} +1 -1
- package/client/dist/assets/{flowDiagram-23GEKE2U-CFJMM637.js → flowDiagram-23GEKE2U-CJ_zF7e_.js} +1 -1
- package/client/dist/assets/{ganttDiagram-NO4QXBWP-DnaH94Su.js → ganttDiagram-NO4QXBWP-CnJdcg9A.js} +1 -1
- package/client/dist/assets/{gitGraphDiagram-IHSO6WYX-DT-D0gbn.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-DW3Ps00w.js → infoDiagram-FWYZ7A6U-CLOP_ZWD.js} +1 -1
- package/client/dist/assets/{ishikawaDiagram-FXEZZL3T-DjxjIT3n.js → ishikawaDiagram-FXEZZL3T-aFTIzr7o.js} +1 -1
- package/client/dist/assets/{journeyDiagram-5HDEW3XC-DTQRI93Y.js → journeyDiagram-5HDEW3XC-CbPJB4xd.js} +1 -1
- package/client/dist/assets/{kanban-definition-HUTT4EX6-BeDvbiEs.js → kanban-definition-HUTT4EX6-BoyhytIS.js} +1 -1
- package/client/dist/assets/{linear-B4foxDlh.js → linear-1VXA-uBg.js} +1 -1
- package/client/dist/assets/{mermaid.core-c-L4qgSB.js → mermaid.core-D_lO2yK9.js} +4 -4
- package/client/dist/assets/{mindmap-definition-LN4V7U3C-dh8v0J8T.js → mindmap-definition-LN4V7U3C-iFK7LXMx.js} +1 -1
- package/client/dist/assets/{pegDiagram-2B236MQR-D5vNrtFh.js → pegDiagram-2B236MQR-DNmcmfA-.js} +1 -1
- package/client/dist/assets/{pieDiagram-ENE6RG2P-CIjg_6lR.js → pieDiagram-ENE6RG2P-ekx83O5b.js} +1 -1
- package/client/dist/assets/{quadrantDiagram-ABIIQ3AL-CF37qWQD.js → quadrantDiagram-ABIIQ3AL-DayyD1l8.js} +1 -1
- package/client/dist/assets/{railroadDiagram-RFXS5EU6-x0Hn30Kj.js → railroadDiagram-RFXS5EU6-Bdgz7S6G.js} +1 -1
- package/client/dist/assets/{requirementDiagram-TGXJPOKE-BE2QnVbN.js → requirementDiagram-TGXJPOKE-szP3Muld.js} +1 -1
- package/client/dist/assets/{sankeyDiagram-HTMAVEWB-CUv6cIlV.js → sankeyDiagram-HTMAVEWB-Ct3rzgUj.js} +1 -1
- package/client/dist/assets/{sequenceDiagram-DBY2YBRQ-C3ACj5LS.js → sequenceDiagram-DBY2YBRQ-_69EfmOU.js} +1 -1
- package/client/dist/assets/{sizeCapture-X5ZJPWSS-DTMIb5gX.js → sizeCapture-X5ZJPWSS-BSOU06J2.js} +1 -1
- package/client/dist/assets/{stateDiagram-2N3HPSRC-DI1Z8P74.js → stateDiagram-2N3HPSRC-HmOmFwQm.js} +1 -1
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-BW8H33dv.js +1 -0
- package/client/dist/assets/{swimlanes-5IMT3BWC-CLSr9w29.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-BTWmUhDh.js → timeline-definition-FHXFAJF6-CBBaDKXm.js} +1 -1
- package/client/dist/assets/{vennDiagram-L72KCM5P-Ce1dlLCM.js → vennDiagram-L72KCM5P-CBXiWPMc.js} +1 -1
- package/client/dist/assets/{wardleyDiagram-EHGQE667-ClOE-aae.js → wardleyDiagram-EHGQE667-uhBCZoW0.js} +1 -1
- package/client/dist/assets/{xychartDiagram-FW5EYKEG-Fa2pqWKw.js → xychartDiagram-FW5EYKEG-xxE_wY5O.js} +1 -1
- package/client/dist/index.html +2 -2
- 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 +1 -1
- package/client/dist/assets/MarkdownEditorModal-CP5yG9OU.js +0 -212
- package/client/dist/assets/channel-BNXbCvDS.js +0 -1
- package/client/dist/assets/classDiagram-OUVF2IWQ-w-wCtLx3.js +0 -1
- package/client/dist/assets/classDiagram-v2-EOCWNBFH-w-wCtLx3.js +0 -1
- package/client/dist/assets/index-CU2QJgvx.js +0 -609
- package/client/dist/assets/index-ftvVcUT8.css +0 -1
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-DvwiHlJv.js +0 -1
- package/client/dist/assets/swimlanesDiagram-G3AALYLV-JTQZTlZ3.js +0 -8
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/org/audit-log.ts — bounded, paginated read of a lead's
|
|
3
|
+
* `audit.jsonl` for the Docs block's "Open log" tile (AC-2, Design Notes
|
|
4
|
+
* "Docs block — view-only contract"). Never serves the whole growing file
|
|
5
|
+
* to the browser at once: the RESPONSE is always capped at `limit` entries,
|
|
6
|
+
* newest first, with a `nextCursor` to page into older entries.
|
|
7
|
+
*
|
|
8
|
+
* Known limitation, stated rather than hidden: the current read still loads
|
|
9
|
+
* the whole file into memory server-side before slicing a page off it (no
|
|
10
|
+
* disk-level tail/backward-chunk read, unlike the transcript JSONL tail
|
|
11
|
+
* reader in `core/session-jsonl-io.ts`, which exists for a much larger,
|
|
12
|
+
* continuously-streamed file). Acceptable for a per-lead audit log at this
|
|
13
|
+
* iterate's scale; a real backward-chunked reader is a follow-up if a log
|
|
14
|
+
* grows large enough to make this measurably slow.
|
|
15
|
+
*
|
|
16
|
+
* Same allowlist posture as `lead-doc-read.ts`: `leadId` is validated
|
|
17
|
+
* against the shared `LEAD_ID_RE`, the filename suffix is a hardcoded
|
|
18
|
+
* constant, and this stays OUT of `_helpers.ts`'s six-target allowlist —
|
|
19
|
+
* it's a new read only the plain `/api/org/*` surface serves.
|
|
20
|
+
*/
|
|
21
|
+
import { closeSync, constants as fsConstants, fstatSync, openSync, readFileSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { pathGuard, realPathGuard } from "../../core/path-guard.js";
|
|
24
|
+
import { LEAD_ID_RE } from "./_helpers.js";
|
|
25
|
+
const AUDIT_FILENAME = "audit.jsonl";
|
|
26
|
+
const DEFAULT_LIMIT = 50;
|
|
27
|
+
const MAX_LIMIT = 200;
|
|
28
|
+
function clampLimit(raw) {
|
|
29
|
+
if (raw === undefined || !Number.isFinite(raw))
|
|
30
|
+
return DEFAULT_LIMIT;
|
|
31
|
+
return Math.min(MAX_LIMIT, Math.max(1, Math.trunc(raw)));
|
|
32
|
+
}
|
|
33
|
+
/** Pure core — reads and paginates `<leadsRoot>/<leadId>/audit.jsonl`. */
|
|
34
|
+
export function auditLogCore(deps, params) {
|
|
35
|
+
const { leadsRoot } = deps;
|
|
36
|
+
const open = deps.openSync ?? openSync;
|
|
37
|
+
const { leadId } = params;
|
|
38
|
+
const limit = clampLimit(params.limit);
|
|
39
|
+
const before = params.before !== undefined && Number.isFinite(params.before) && params.before > 0
|
|
40
|
+
? Math.trunc(params.before)
|
|
41
|
+
: 0;
|
|
42
|
+
if (!leadId || !LEAD_ID_RE.test(leadId)) {
|
|
43
|
+
return { status: 400, body: { error: "invalid_lead_id" } };
|
|
44
|
+
}
|
|
45
|
+
const guard = pathGuard(leadsRoot, join(leadId, AUDIT_FILENAME));
|
|
46
|
+
if (!guard.ok) {
|
|
47
|
+
return { status: 400, body: { error: "path_traversal", detail: guard.reason } };
|
|
48
|
+
}
|
|
49
|
+
let fd;
|
|
50
|
+
try {
|
|
51
|
+
fd = open(guard.absolute, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const code = err?.code;
|
|
55
|
+
if (code === "ENOENT") {
|
|
56
|
+
return { status: 404, body: { error: "not_found" } };
|
|
57
|
+
}
|
|
58
|
+
if (code === "ELOOP") {
|
|
59
|
+
return { status: 403, body: { error: "symlink_forbidden" } };
|
|
60
|
+
}
|
|
61
|
+
return { status: 500, body: { error: "file_stat_failed", detail: String(err).slice(0, 200) } };
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
if (!fstatSync(fd).isFile()) {
|
|
65
|
+
return { status: 400, body: { error: "not_a_file" } };
|
|
66
|
+
}
|
|
67
|
+
const realGuard = realPathGuard(leadsRoot, guard.absolute);
|
|
68
|
+
if (!realGuard.ok) {
|
|
69
|
+
return { status: 403, body: { error: "symlink_forbidden", detail: realGuard.reason } };
|
|
70
|
+
}
|
|
71
|
+
const text = readFileSync(fd, "utf8");
|
|
72
|
+
const lines = text.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
73
|
+
const newestFirst = lines.slice().reverse();
|
|
74
|
+
const page = newestFirst.slice(before, before + limit).map((raw) => {
|
|
75
|
+
try {
|
|
76
|
+
return { raw, parsed: JSON.parse(raw) };
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { raw, parsed: null };
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const nextCursor = before + limit < newestFirst.length ? before + limit : null;
|
|
83
|
+
return { status: 200, body: { entries: page, total: newestFirst.length, nextCursor } };
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
return { status: 500, body: { error: "file_read_failed", detail: String(err).slice(0, 200) } };
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
closeSync(fd);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=audit-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.js","sourceRoot":"","sources":["../../../src/external/org/audit-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,SAAS,EAAE,SAAS,IAAI,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAK3C,MAAM,cAAc,GAAG,aAAa,CAAC;AACrC,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,SAAS,GAAG,GAAG,CAAC;AAmBtB,SAAS,UAAU,CAAC,GAAuB;IACzC,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,aAAa,CAAC;IACrE,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,IAAkB,EAAE,MAAsB;IACrE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC;IACvC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAC/F,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;QAC3B,CAAC,CAAC,CAAC,CAAC;IAEN,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,CAAC;IAC7D,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;IACjE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;IAClF,CAAC;IAED,IAAI,EAAU,CAAC;IACf,IAAI,CAAC;QACH,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,EAAE,IAAI,CAAC;QAClD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;QACvD,CAAC;QACD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC;QAC/D,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;IACjG,CAAC;IAED,IAAI,CAAC;QACH,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,CAAC;QACxD,CAAC;QACD,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC3D,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAClB,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;QACzF,CAAC;QAED,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrE,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAiB,EAAE;YAChF,IAAI,CAAC;gBACH,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,EAAE,CAAC;YACrE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAE/E,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC;IACzF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;IACjG,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;AACH,CAAC"}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import type { Hono } from "hono";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
beatId: string;
|
|
5
|
-
leadId: string;
|
|
6
|
-
pid: number;
|
|
7
|
-
startedAt: string;
|
|
8
|
-
closedAt: string | null;
|
|
9
|
-
}
|
|
2
|
+
import type { BeatRegisterEntryView, BeatRegisterHealthResponse } from "../../types/org.js";
|
|
3
|
+
export type { BeatRegisterEntryView, BeatRegisterHealthResponse, } from "../../types/org.js";
|
|
10
4
|
export interface BeatRegisterFile {
|
|
11
5
|
version: 1;
|
|
12
6
|
entries: BeatRegisterEntryView[];
|
|
@@ -78,23 +72,22 @@ export declare function readRegisterFileTolerant(absolute: string): {
|
|
|
78
72
|
} | {
|
|
79
73
|
ok: false;
|
|
80
74
|
};
|
|
81
|
-
export type BeatRegisterHealthResponse = {
|
|
82
|
-
leadId: string;
|
|
83
|
-
status: "clear";
|
|
84
|
-
} | {
|
|
85
|
-
leadId: string;
|
|
86
|
-
status: "open";
|
|
87
|
-
entry: BeatRegisterEntryView;
|
|
88
|
-
} | {
|
|
89
|
-
leadId: string;
|
|
90
|
-
status: "fault";
|
|
91
|
-
reason: "duplicate-session-id";
|
|
92
|
-
sessionId: string;
|
|
93
|
-
entries: BeatRegisterEntryView[];
|
|
94
|
-
};
|
|
95
75
|
export interface BeatRegisterRouteDeps {
|
|
96
76
|
leadsRoot: string;
|
|
97
77
|
lstatSync?: LstatFn;
|
|
98
78
|
}
|
|
79
|
+
export type BeatRegisterHealthCoreResult = {
|
|
80
|
+
status: 200;
|
|
81
|
+
body: BeatRegisterHealthResponse;
|
|
82
|
+
} | {
|
|
83
|
+
status: 400 | 403 | 500 | 502;
|
|
84
|
+
body: {
|
|
85
|
+
error: string;
|
|
86
|
+
leadId?: string;
|
|
87
|
+
detail?: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
/** Pure core — shared by the secret-gated route and the plain-surface proxy. */
|
|
91
|
+
export declare function beatRegisterHealthCore(deps: BeatRegisterRouteDeps, leadId: string): BeatRegisterHealthCoreResult;
|
|
99
92
|
export declare function registerBeatRegisterHealthRoute(app: Hono, deps: BeatRegisterRouteDeps): void;
|
|
100
93
|
//# sourceMappingURL=beat-register.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"beat-register.d.ts","sourceRoot":"","sources":["../../../src/external/org/beat-register.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"beat-register.d.ts","sourceRoot":"","sources":["../../../src/external/org/beat-register.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAOjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAE5F,YAAY,EACV,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAM5B,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC;AAgBD;;;0BAG0B;AAC1B,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,gBAAgB,CAIrE;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,qBAAqB,CAAA;CAAE,GAChD;IACE,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC,CAAC;AAEN;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,cAAc,CAiB7E;AAOD,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK;IAAE,cAAc,IAAI,OAAO,CAAA;CAAE,CAAC;AAEnE,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,GACb;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAkBvF;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAgBpE;AAED;;2EAE2E;AAC3E,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GACf;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAuBtD;AAMD,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,4BAA4B,GACpC;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,0BAA0B,CAAA;CAAE,GACjD;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,sBAAsB,CACpC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,EAAE,MAAM,GACb,4BAA4B,CAsC9B;AAED,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,qBAAqB,GAAG,IAAI,CAK5F"}
|
|
@@ -158,41 +158,46 @@ export function readRegisterFileTolerant(absolute) {
|
|
|
158
158
|
}
|
|
159
159
|
return { ok: true, file: parsed };
|
|
160
160
|
}
|
|
161
|
-
|
|
161
|
+
/** Pure core — shared by the secret-gated route and the plain-surface proxy. */
|
|
162
|
+
export function beatRegisterHealthCore(deps, leadId) {
|
|
162
163
|
const { leadsRoot } = deps;
|
|
163
164
|
const lstat = deps.lstatSync ?? ((p) => lstatSync(p));
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (code === "ENOENT") {
|
|
177
|
-
const body = { leadId, status: "clear" };
|
|
178
|
-
return c.json(body, 200);
|
|
179
|
-
}
|
|
180
|
-
return c.json({ error: "beat_register_read_failed", detail: String(err).slice(0, 200) }, 500);
|
|
181
|
-
}
|
|
182
|
-
if (lst.isSymbolicLink()) {
|
|
183
|
-
return c.json({ error: "symlink_forbidden", leadId }, 403);
|
|
184
|
-
}
|
|
185
|
-
const containment = realPathGuard(leadsRoot, absolute);
|
|
186
|
-
if (!containment.ok) {
|
|
187
|
-
return c.json({ error: "path_traversal", detail: containment.reason }, 400);
|
|
188
|
-
}
|
|
189
|
-
const read = readRegisterFileTolerant(absolute);
|
|
190
|
-
if (!read.ok) {
|
|
191
|
-
return c.json({ error: "beat_register_invalid", leadId }, 502);
|
|
165
|
+
if (!LEAD_ID_RE.test(leadId)) {
|
|
166
|
+
return { status: 400, body: { error: "invalid_lead_id", leadId } };
|
|
167
|
+
}
|
|
168
|
+
const absolute = registerPathFor(leadsRoot, leadId);
|
|
169
|
+
let lst;
|
|
170
|
+
try {
|
|
171
|
+
lst = lstat(absolute);
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
const code = err?.code;
|
|
175
|
+
if (code === "ENOENT") {
|
|
176
|
+
return { status: 200, body: { leadId, status: "clear" } };
|
|
192
177
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
178
|
+
return {
|
|
179
|
+
status: 500,
|
|
180
|
+
body: { error: "beat_register_read_failed", detail: String(err).slice(0, 200) },
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (lst.isSymbolicLink()) {
|
|
184
|
+
return { status: 403, body: { error: "symlink_forbidden", leadId } };
|
|
185
|
+
}
|
|
186
|
+
const containment = realPathGuard(leadsRoot, absolute);
|
|
187
|
+
if (!containment.ok) {
|
|
188
|
+
return { status: 400, body: { error: "path_traversal", detail: containment.reason } };
|
|
189
|
+
}
|
|
190
|
+
const read = readRegisterFileTolerant(absolute);
|
|
191
|
+
if (!read.ok) {
|
|
192
|
+
return { status: 502, body: { error: "beat_register_invalid", leadId } };
|
|
193
|
+
}
|
|
194
|
+
const health = evaluateRegisterHealth(read.file);
|
|
195
|
+
return { status: 200, body: { leadId, ...health } };
|
|
196
|
+
}
|
|
197
|
+
export function registerBeatRegisterHealthRoute(app, deps) {
|
|
198
|
+
app.get("/api/external/org/leads/:leadId/beat-register", async (c) => {
|
|
199
|
+
const result = beatRegisterHealthCore(deps, c.req.param("leadId"));
|
|
200
|
+
return c.json(result.body, result.status);
|
|
196
201
|
});
|
|
197
202
|
}
|
|
198
203
|
//# sourceMappingURL=beat-register.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"beat-register.js","sourceRoot":"","sources":["../../../src/external/org/beat-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrG,OAAO,IAAI,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"beat-register.js","sourceRoot":"","sources":["../../../src/external/org/beat-register.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,5 +10,21 @@ export interface OrgFileReadDeps {
|
|
|
10
10
|
*/
|
|
11
11
|
openSync?: typeof openSync;
|
|
12
12
|
}
|
|
13
|
+
export type OrgFileReadCoreResult = {
|
|
14
|
+
status: 200;
|
|
15
|
+
kind: "file";
|
|
16
|
+
body: Buffer;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
} | {
|
|
19
|
+
status: 400 | 403 | 404 | 500;
|
|
20
|
+
kind: "json";
|
|
21
|
+
body: {
|
|
22
|
+
error: string;
|
|
23
|
+
path?: string;
|
|
24
|
+
detail?: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/** Pure core — shared by the secret-gated route and the plain-surface proxy. */
|
|
28
|
+
export declare function orgFileReadCore(deps: OrgFileReadDeps, relpath: string | undefined): OrgFileReadCoreResult;
|
|
13
29
|
export declare function registerOrgFileRead(app: Hono, deps: OrgFileReadDeps): void;
|
|
14
30
|
//# sourceMappingURL=file-read.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-read.d.ts","sourceRoot":"","sources":["../../../src/external/org/file-read.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAkD,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAMjG,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,QAAQ,CAAC;CAC5B;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"file-read.d.ts","sourceRoot":"","sources":["../../../src/external/org/file-read.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAkD,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAMjG,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,QAAQ,CAAC;CAC5B;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC5E;IACE,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACzD,CAAC;AAEN,gFAAgF;AAChF,wBAAgB,eAAe,CAC7B,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,qBAAqB,CAoFvB;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,CAW1E"}
|
|
@@ -20,61 +20,101 @@ import { closeSync, constants as fsConstants, fstatSync, openSync, readFileSync
|
|
|
20
20
|
import { realPathGuard } from "../../core/path-guard.js";
|
|
21
21
|
import { resolveOrgAllowlistedTarget } from "./_helpers.js";
|
|
22
22
|
import { fileFingerprint } from "../file/_helpers.js";
|
|
23
|
-
|
|
23
|
+
/** Pure core — shared by the secret-gated route and the plain-surface proxy. */
|
|
24
|
+
export function orgFileReadCore(deps, relpath) {
|
|
24
25
|
const { leadsRoot } = deps;
|
|
25
26
|
const open = deps.openSync ?? openSync;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (!relpath || relpath.length === 0) {
|
|
28
|
+
return { status: 400, kind: "json", body: { error: "path_required" } };
|
|
29
|
+
}
|
|
30
|
+
const target = resolveOrgAllowlistedTarget(leadsRoot, relpath);
|
|
31
|
+
if (!target.ok) {
|
|
32
|
+
const status = target.reason === "not_allowlisted" ? 403 : 400;
|
|
33
|
+
const err = target.reason === "traversal" ? "path_traversal" : target.reason;
|
|
34
|
+
return { status, kind: "json", body: { error: err, detail: target.reason } };
|
|
35
|
+
}
|
|
36
|
+
// External-review fix (MEDIUM, spec): `realPathGuard` used to run BEFORE
|
|
37
|
+
// the open below. Its own docstring requires the target to already exist
|
|
38
|
+
// (it calls `realpathSync`, which throws ENOENT for a missing path) —
|
|
39
|
+
// calling it first meant a genuinely-missing allowlisted target (e.g. an
|
|
40
|
+
// as-yet-uncreated `conventions.md`) reported 400 `path_traversal`
|
|
41
|
+
// instead of 404 `not_found`, so the shared-documents viewer's not-found
|
|
42
|
+
// state (`OrgDocViewerModal`) was unreachable for any missing doc. Fixed
|
|
43
|
+
// by reordering to the same open-first pattern `lead-doc-read.ts` /
|
|
44
|
+
// `audit-log.ts` already use — see their comments for the rationale.
|
|
45
|
+
//
|
|
46
|
+
// CodeQL js/file-system-race fix: a separate lstat-check + stat + read
|
|
47
|
+
// (each its own syscall against the PATH) leaves a window where the
|
|
48
|
+
// final component could be swapped for a symlink between checks. Open
|
|
49
|
+
// ONCE with O_NOFOLLOW — the kernel atomically refuses a symlinked final
|
|
50
|
+
// component (ELOOP), so there is no gap left to race — then fstat/read
|
|
51
|
+
// the SAME fd, so what gets served is provably what got checked.
|
|
52
|
+
let fd;
|
|
53
|
+
try {
|
|
54
|
+
fd = open(target.absolute, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
const code = err?.code;
|
|
58
|
+
if (code === "ENOENT") {
|
|
59
|
+
return { status: 404, kind: "json", body: { error: "not_found", path: relpath } };
|
|
60
|
+
}
|
|
61
|
+
if (code === "ELOOP") {
|
|
62
|
+
return { status: 403, kind: "json", body: { error: "symlink_forbidden", path: relpath } };
|
|
30
63
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
64
|
+
return {
|
|
65
|
+
status: 500,
|
|
66
|
+
kind: "json",
|
|
67
|
+
body: { error: "file_stat_failed", detail: String(err).slice(0, 200) },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
if (!fstatSync(fd).isFile()) {
|
|
72
|
+
return { status: 400, kind: "json", body: { error: "not_a_file", path: relpath } };
|
|
36
73
|
}
|
|
74
|
+
// Existence is now confirmed (the open above succeeded) — realPathGuard's
|
|
75
|
+
// own contract requires exactly this ordering.
|
|
37
76
|
const realGuard = realPathGuard(leadsRoot, target.absolute);
|
|
38
77
|
if (!realGuard.ok) {
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// ONCE with O_NOFOLLOW — the kernel atomically refuses a symlinked final
|
|
45
|
-
// component (ELOOP), so there is no gap left to race — then fstat/read
|
|
46
|
-
// the SAME fd, so what gets served is provably what got checked.
|
|
47
|
-
let fd;
|
|
48
|
-
try {
|
|
49
|
-
fd = open(target.absolute, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
const code = err?.code;
|
|
53
|
-
if (code === "ENOENT") {
|
|
54
|
-
return c.json({ error: "not_found", path: relpath }, 404);
|
|
55
|
-
}
|
|
56
|
-
if (code === "ELOOP") {
|
|
57
|
-
return c.json({ error: "symlink_forbidden", path: relpath }, 403);
|
|
58
|
-
}
|
|
59
|
-
return c.json({ error: "file_stat_failed", detail: String(err).slice(0, 200) }, 500);
|
|
78
|
+
return {
|
|
79
|
+
status: 403,
|
|
80
|
+
kind: "json",
|
|
81
|
+
body: { error: "symlink_forbidden", detail: realGuard.reason, path: relpath },
|
|
82
|
+
};
|
|
60
83
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
const body = readFileSync(fd);
|
|
85
|
+
return {
|
|
86
|
+
status: 200,
|
|
87
|
+
kind: "file",
|
|
88
|
+
body,
|
|
89
|
+
headers: {
|
|
90
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
91
|
+
"X-Content-Type-Options": "nosniff",
|
|
92
|
+
ETag: `"${fileFingerprint(body)}"`,
|
|
93
|
+
"Cache-Control": "private, max-age=0, must-revalidate",
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
return {
|
|
99
|
+
status: 500,
|
|
100
|
+
kind: "json",
|
|
101
|
+
body: { error: "file_read_failed", detail: String(err).slice(0, 200) },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
closeSync(fd);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function registerOrgFileRead(app, deps) {
|
|
109
|
+
app.get("/api/external/org/file", async (c) => {
|
|
110
|
+
const result = orgFileReadCore(deps, c.req.query("path"));
|
|
111
|
+
if (result.kind === "json") {
|
|
112
|
+
return c.json(result.body, result.status);
|
|
74
113
|
}
|
|
75
|
-
|
|
76
|
-
|
|
114
|
+
for (const [key, value] of Object.entries(result.headers)) {
|
|
115
|
+
c.header(key, value);
|
|
77
116
|
}
|
|
117
|
+
return c.body(new Uint8Array(result.body), result.status);
|
|
78
118
|
});
|
|
79
119
|
}
|
|
80
120
|
//# sourceMappingURL=file-read.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-read.js","sourceRoot":"","sources":["../../../src/external/org/file-read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,SAAS,EAAE,SAAS,IAAI,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjG,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"file-read.js","sourceRoot":"","sources":["../../../src/external/org/file-read.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type WriteOutcome = {
|
|
2
|
+
ok: true;
|
|
3
|
+
status: 200;
|
|
4
|
+
body: {
|
|
5
|
+
written: true;
|
|
6
|
+
fingerprint: string;
|
|
7
|
+
size: number;
|
|
8
|
+
};
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
status: number;
|
|
12
|
+
body: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export declare function performWrite(absoluteTarget: string, leadsRoot: string, relpath: string, body: string, ifMatch: string | undefined, lstat: (path: string) => {
|
|
15
|
+
isSymbolicLink(): boolean;
|
|
16
|
+
isFile(): boolean;
|
|
17
|
+
}): WriteOutcome;
|
|
18
|
+
//# sourceMappingURL=file-write-core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-write-core.d.ts","sourceRoot":"","sources":["../../../src/external/org/file-write-core.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACrF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAEjE,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;IAAE,cAAc,IAAI,OAAO,CAAC;IAAC,MAAM,IAAI,OAAO,CAAA;CAAE,GACxE,YAAY,CAgFd"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/org/file-write-core.ts — the low-level atomic write-with-
|
|
3
|
+
* precondition primitive `file-write.ts`'s `orgFileWriteCore` calls.
|
|
4
|
+
*
|
|
5
|
+
* Split out purely to stay under the 300-line file guideline (CLAUDE.md) —
|
|
6
|
+
* mirrors the existing `beat-register-release.ts` /
|
|
7
|
+
* `beat-register-release-core.ts` split in this same directory. No new
|
|
8
|
+
* behavior; `performWrite`'s body and `WriteOutcome`'s shape are unchanged
|
|
9
|
+
* from before the split.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, writeFileSync, renameSync, unlinkSync, existsSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { realPathGuard } from "../../core/path-guard.js";
|
|
14
|
+
import { fileFingerprint, unquoteEtag } from "../file/_helpers.js";
|
|
15
|
+
export function performWrite(absoluteTarget, leadsRoot, relpath, body, ifMatch, lstat) {
|
|
16
|
+
let lst;
|
|
17
|
+
try {
|
|
18
|
+
lst = lstat(absoluteTarget);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const code = err?.code;
|
|
22
|
+
if (code === "ENOENT") {
|
|
23
|
+
return { ok: false, status: 404, body: { error: "not_found", path: relpath } };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
status: 500,
|
|
28
|
+
body: { error: "file_stat_failed", detail: String(err).slice(0, 200) },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (lst.isSymbolicLink()) {
|
|
32
|
+
return { ok: false, status: 403, body: { error: "symlink_forbidden", path: relpath } };
|
|
33
|
+
}
|
|
34
|
+
if (!lst.isFile()) {
|
|
35
|
+
return { ok: false, status: 400, body: { error: "not_a_file", path: relpath } };
|
|
36
|
+
}
|
|
37
|
+
const realGuard = realPathGuard(leadsRoot, absoluteTarget);
|
|
38
|
+
if (!realGuard.ok) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
status: 400,
|
|
42
|
+
body: { error: "path_traversal", detail: realGuard.reason },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (!ifMatch) {
|
|
46
|
+
return { ok: false, status: 400, body: { error: "precondition_required" } };
|
|
47
|
+
}
|
|
48
|
+
let current;
|
|
49
|
+
try {
|
|
50
|
+
current = readFileSync(absoluteTarget);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const code = err?.code;
|
|
54
|
+
if (code === "ENOENT") {
|
|
55
|
+
return { ok: false, status: 404, body: { error: "not_found", path: relpath } };
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
status: 500,
|
|
60
|
+
body: { error: "file_read_failed", detail: String(err).slice(0, 200) },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const currentFingerprint = fileFingerprint(current);
|
|
64
|
+
if (unquoteEtag(ifMatch) !== currentFingerprint) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
status: 409,
|
|
68
|
+
body: { error: "fingerprint_mismatch", currentFingerprint },
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const nextBuf = Buffer.from(body, "utf8");
|
|
72
|
+
const tmp = join(dirname(absoluteTarget), `.org-write.tmp-${process.pid}-${Date.now()}`);
|
|
73
|
+
try {
|
|
74
|
+
writeFileSync(tmp, nextBuf);
|
|
75
|
+
renameSync(tmp, absoluteTarget);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
try {
|
|
79
|
+
if (existsSync(tmp))
|
|
80
|
+
unlinkSync(tmp);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* swallow */
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
status: 500,
|
|
88
|
+
body: { error: "write_failed", detail: String(err).slice(0, 200), path: relpath },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
ok: true,
|
|
93
|
+
status: 200,
|
|
94
|
+
body: { written: true, fingerprint: fileFingerprint(nextBuf), size: nextBuf.length },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=file-write-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-write-core.js","sourceRoot":"","sources":["../../../src/external/org/file-write-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMnE,MAAM,UAAU,YAAY,CAC1B,cAAsB,EACtB,SAAiB,EACjB,OAAe,EACf,IAAY,EACZ,OAA2B,EAC3B,KAAyE;IAEzE,IAAI,GAAG,CAAC;IACR,IAAI,CAAC;QACH,GAAG,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,EAAE,IAAI,CAAC;QAClD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;QACjF,CAAC;QACD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;SACvE,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,cAAc,EAAE,EAAE,CAAC;QACzB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;IACzF,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;IAClF,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAC3D,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE;SAC5D,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAAE,CAAC;IAC9E,CAAC;IACD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,EAAE,IAAI,CAAC;QAClD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;QACjF,CAAC;QACD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;SACvE,CAAC;IACJ,CAAC;IACD,MAAM,kBAAkB,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,WAAW,CAAC,OAAO,CAAC,KAAK,kBAAkB,EAAE,CAAC;QAChD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE;SAC5D,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,kBAAkB,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACzF,IAAI,CAAC;QACH,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC5B,UAAU,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,GAAG,CAAC;gBAAE,UAAU,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,aAAa;QACf,CAAC;QACD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;SAClF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE;KACrF,CAAC;AACJ,CAAC"}
|