@launchsecure/launch-kit 0.0.26 → 0.0.28
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/dist/beacon/beacon.mjs +1003 -440
- package/dist/beacon/beacon.mjs.map +1 -1
- package/dist/beacon/beacon.umd.js +45 -24
- package/dist/beacon/beacon.umd.js.map +1 -1
- package/dist/beacon/types/capture/events.d.ts +20 -0
- package/dist/beacon/types/capture/events.d.ts.map +1 -0
- package/dist/beacon/types/element.d.ts +1 -0
- package/dist/beacon/types/element.d.ts.map +1 -1
- package/dist/beacon/types/index.d.ts +2 -1
- package/dist/beacon/types/index.d.ts.map +1 -1
- package/dist/beacon/types/monitor/dom.d.ts +13 -0
- package/dist/beacon/types/monitor/dom.d.ts.map +1 -0
- package/dist/beacon/types/monitor/index.d.ts +19 -0
- package/dist/beacon/types/monitor/index.d.ts.map +1 -0
- package/dist/beacon/types/monitor/network.d.ts +12 -0
- package/dist/beacon/types/monitor/network.d.ts.map +1 -0
- package/dist/beacon/types/monitor/transport.d.ts +27 -0
- package/dist/beacon/types/monitor/transport.d.ts.map +1 -0
- package/dist/beacon/types/monitor/types.d.ts +117 -0
- package/dist/beacon/types/monitor/types.d.ts.map +1 -0
- package/dist/beacon/types/types.d.ts +10 -0
- package/dist/beacon/types/types.d.ts.map +1 -1
- package/dist/beacon/types/ui/drawer.d.ts +3 -1
- package/dist/beacon/types/ui/drawer.d.ts.map +1 -1
- package/dist/beacon/types/ui/monitor-panel.d.ts +19 -0
- package/dist/beacon/types/ui/monitor-panel.d.ts.map +1 -0
- package/dist/chart-client/assets/index-CJ4mgRRF.css +1 -0
- package/dist/chart-client/assets/{index-Bk1hawjD.js → index-Ccy-DpI-.js} +46 -42
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-DI5qSR_w.css +32 -0
- package/dist/client/assets/index-Dp0_okva.js +294 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-C_-vAM9L.css +1 -0
- package/dist/council-client/index.html +2 -2
- package/dist/deck-client/assets/{_baseUniq-C2xT_eYu.js → _baseUniq-W2JQDmje.js} +1 -1
- package/dist/deck-client/assets/{arc-CmVL9pGd.js → arc-DIBWAId9.js} +1 -1
- package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-BSFgdjve.js → architectureDiagram-Q4EWVU46-CAIRMvJK.js} +1 -1
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-DuLzscvP.js → blockDiagram-DXYQGD6D-BeNaNiOi.js} +1 -1
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CfCJB8eY.js → c4Diagram-AHTNJAMY-B9Ozi62h.js} +1 -1
- package/dist/deck-client/assets/channel-CRdozqbp.js +1 -0
- package/dist/deck-client/assets/{chunk-4BX2VUAB-DxmLYTWZ.js → chunk-4BX2VUAB-D7AZ47dt.js} +1 -1
- package/dist/deck-client/assets/{chunk-4TB4RGXK-CCnf7GFE.js → chunk-4TB4RGXK-DnVnNPcI.js} +1 -1
- package/dist/deck-client/assets/{chunk-55IACEB6-Db9DApcj.js → chunk-55IACEB6-UKYs-YNd.js} +1 -1
- package/dist/deck-client/assets/{chunk-EDXVE4YY-DmYDq8ZI.js → chunk-EDXVE4YY-D43b-SKn.js} +1 -1
- package/dist/deck-client/assets/{chunk-FMBD7UC4-BGhUlF20.js → chunk-FMBD7UC4-QzBAoyyW.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-CpEnicQZ.js → chunk-OYMX7WX6-Cjif4r6W.js} +1 -1
- package/dist/deck-client/assets/{chunk-QZHKN3VN-Doa7LKwf.js → chunk-QZHKN3VN-CqLDirEI.js} +1 -1
- package/dist/deck-client/assets/{chunk-YZCP3GAM-CpkIlH6V.js → chunk-YZCP3GAM-_FQvmMs4.js} +1 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-lIZMp57W.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-lIZMp57W.js +1 -0
- package/dist/deck-client/assets/clone-BtWeSTyJ.js +1 -0
- package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-Bkh8Bfcb.js → cose-bilkent-S5V4N54A-rfrocesE.js} +1 -1
- package/dist/deck-client/assets/{dagre-KV5264BT-Bp0XpTgH.js → dagre-KV5264BT-Bv_7DJat.js} +1 -1
- package/dist/deck-client/assets/{diagram-5BDNPKRD-ZHiyGYPQ.js → diagram-5BDNPKRD-4F1414G5.js} +1 -1
- package/dist/deck-client/assets/{diagram-G4DWMVQ6-BW-Q8_H5.js → diagram-G4DWMVQ6-C4-Pszqm.js} +1 -1
- package/dist/deck-client/assets/{diagram-MMDJMWI5-6I3LTafu.js → diagram-MMDJMWI5-B647TIx9.js} +1 -1
- package/dist/deck-client/assets/{diagram-TYMM5635-CyM5YK28.js → diagram-TYMM5635-BFAqpezd.js} +1 -1
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-CjNxVJHk.js → erDiagram-SMLLAGMA-BfBfrJOC.js} +1 -1
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-BDQHuAJR.js → flowDiagram-DWJPFMVM-DX9YAYes.js} +1 -1
- package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-B7MnkpbP.js → ganttDiagram-T4ZO3ILL-DCuiy7wF.js} +1 -1
- package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-C9dZAcYD.js → gitGraphDiagram-UUTBAWPF-CGp1IXUh.js} +1 -1
- package/dist/deck-client/assets/{graph-CjdBnzUy.js → graph-B7g8aoxv.js} +1 -1
- package/dist/deck-client/assets/{index-DeIVPW63.js → index-Dg1r-WSN.js} +3 -3
- package/dist/deck-client/assets/index-DsIZ3LqL.css +1 -0
- package/dist/deck-client/assets/{infoDiagram-42DDH7IO-C7d3iRC3.js → infoDiagram-42DDH7IO-L3fahMkF.js} +1 -1
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-BcYGKj09.js → ishikawaDiagram-UXIWVN3A-aS_EjWBZ.js} +1 -1
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-DqFlRrOL.js → journeyDiagram-VCZTEJTY-djTSQZF9.js} +1 -1
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-BJhPp1NR.js → kanban-definition-6JOO6SKY-CcTHo4CM.js} +1 -1
- package/dist/deck-client/assets/{layout-DIeS6GvK.js → layout-mEJiadb7.js} +1 -1
- package/dist/deck-client/assets/{linear-He_yJy5H.js → linear-XgTKqyRu.js} +1 -1
- package/dist/deck-client/assets/{min-DQ6Kx06t.js → min-Ct9jZdpd.js} +1 -1
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-sQ62L8T2.js → mindmap-definition-QFDTVHPH-BaFxCGNU.js} +1 -1
- package/dist/deck-client/assets/{pieDiagram-DEJITSTG-BqCWmU2K.js → pieDiagram-DEJITSTG-CIbYYjtw.js} +1 -1
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-rQ1TJOoe.js → quadrantDiagram-34T5L4WZ-D9EtCOvh.js} +1 -1
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BO2MPBOM.js → requirementDiagram-MS252O5E-xeni9eVG.js} +1 -1
- package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BgsHEVex.js → sankeyDiagram-XADWPNL6-LYeknz9h.js} +1 -1
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-B3j1yMLU.js → sequenceDiagram-FGHM5R23-RDbsKFZf.js} +1 -1
- package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-C8jFlZou.js → stateDiagram-FHFEXIEX-BH1Zjglk.js} +1 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BrV78NDR.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-tM-qo4Zk.js → timeline-definition-GMOUNBTQ-IFXxKptt.js} +1 -1
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-B0-6kOEu.js → vennDiagram-DHZGUBPP-D-sLkQs9.js} +1 -1
- package/dist/deck-client/assets/{wardley-RL74JXVD-HpBk07P-.js → wardley-RL74JXVD-C010F8l4.js} +1 -1
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-BkA1NLDE.js → wardleyDiagram-NUSXRM2D-BTjjuDU3.js} +1 -1
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CEKGSuI-.js → xychartDiagram-5P7HB3ND-AYbv92n-.js} +1 -1
- package/dist/deck-client/index.html +2 -2
- package/dist/server/beacon-monitor-entry.js +353 -0
- package/dist/server/chart-serve.js +3836 -3750
- package/dist/server/cli.js +8789 -8219
- package/dist/server/council-entry.js +17 -5
- package/dist/server/council-serve.js +8 -3
- package/dist/server/course-entry.js +246 -0
- package/dist/server/deck-mcp-entry.js +24 -12
- package/dist/server/deck-serve.js +11 -8
- package/dist/server/graph-mcp-entry.js +5005 -4865
- package/dist/server/init-entry.js +939 -0
- package/dist/server/orbit-entry.js +2435 -0
- package/dist/server/parse-worker-entry.js +4721 -0
- package/dist/server/recall-entry.js +356 -18
- package/package.json +11 -4
- package/scaffolds/ls-marketplace/.claude-plugin/marketplace.json +15 -0
- package/scaffolds/ls-marketplace/plugins/ls/.claude-plugin/plugin.json +28 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/activate-beacon.md +216 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-array.md +92 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-clear.md +68 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-pulse.md +80 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-scan.md +62 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/show-mcp-status.md +109 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/standup.md +177 -0
- package/scaffolds/migrate-safety/.github/workflows/backup-on-migration.yml +72 -0
- package/scaffolds/migrate-safety/docs/migrations-runbook.md +172 -0
- package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +294 -0
- package/scaffolds/recall-hook/scripts/ensure-recall.sh +69 -0
- package/dist/chart-client/assets/index-DpaGa3bY.css +0 -1
- package/dist/client/assets/index-Bfel4OQ5.css +0 -32
- package/dist/client/assets/index-eC-WuUWB.js +0 -291
- package/dist/council-client/assets/index-P5kMsT5a.css +0 -1
- package/dist/deck-client/assets/channel-B4aNO8ZB.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-BHTI0yWz.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-BHTI0yWz.js +0 -1
- package/dist/deck-client/assets/clone-HduFm7qU.js +0 -1
- package/dist/deck-client/assets/index-LKZDAS9S.css +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BoqepHW0.js +0 -1
- /package/dist/council-client/assets/{index-Cs_MVXHf.js → index-Dt4zWKSj.js} +0 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/server/beacon-monitor-entry.ts
|
|
5
|
+
var import_node_crypto = require("node:crypto");
|
|
6
|
+
var import_node_path2 = require("node:path");
|
|
7
|
+
|
|
8
|
+
// src/server/beacon/ndjson-writer.ts
|
|
9
|
+
var import_node_fs = require("node:fs");
|
|
10
|
+
var import_node_path = require("node:path");
|
|
11
|
+
function openNdjsonWriter(opts) {
|
|
12
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(opts.path), { recursive: true });
|
|
13
|
+
const stream = (0, import_node_fs.createWriteStream)(opts.path, { flags: "a" });
|
|
14
|
+
let total = 0;
|
|
15
|
+
let closed = false;
|
|
16
|
+
stream.on("error", (err) => {
|
|
17
|
+
opts.onError?.(err);
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
write(event) {
|
|
21
|
+
if (closed) return;
|
|
22
|
+
let line;
|
|
23
|
+
try {
|
|
24
|
+
line = JSON.stringify(event);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
opts.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
stream.write(line + "\n");
|
|
30
|
+
total += 1;
|
|
31
|
+
},
|
|
32
|
+
count() {
|
|
33
|
+
return total;
|
|
34
|
+
},
|
|
35
|
+
path() {
|
|
36
|
+
return opts.path;
|
|
37
|
+
},
|
|
38
|
+
close() {
|
|
39
|
+
return new Promise((resolveClose) => {
|
|
40
|
+
if (closed) {
|
|
41
|
+
resolveClose();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
closed = true;
|
|
45
|
+
stream.end(() => resolveClose());
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/server/beacon/server.ts
|
|
52
|
+
var import_node_http = require("node:http");
|
|
53
|
+
var MAX_BODY_BYTES = 1048576;
|
|
54
|
+
var TOKEN_PATH_PREFIX = "/m/";
|
|
55
|
+
function writeCors(res, origin) {
|
|
56
|
+
res.setHeader("Access-Control-Allow-Origin", origin && origin !== "null" ? origin : "*");
|
|
57
|
+
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
58
|
+
res.setHeader("Access-Control-Allow-Headers", "content-type");
|
|
59
|
+
res.setHeader("Access-Control-Max-Age", "600");
|
|
60
|
+
res.setHeader("Vary", "Origin");
|
|
61
|
+
}
|
|
62
|
+
function readBody(req) {
|
|
63
|
+
return new Promise((resolve2, reject) => {
|
|
64
|
+
const chunks = [];
|
|
65
|
+
let total = 0;
|
|
66
|
+
req.on("data", (chunk) => {
|
|
67
|
+
total += chunk.length;
|
|
68
|
+
if (total > MAX_BODY_BYTES) {
|
|
69
|
+
reject(new Error("payload too large"));
|
|
70
|
+
req.destroy();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
chunks.push(chunk);
|
|
74
|
+
});
|
|
75
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString("utf8")));
|
|
76
|
+
req.on("error", (err) => reject(err));
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function isMonitorBatchEnvelope(value) {
|
|
80
|
+
if (!value || typeof value !== "object") return false;
|
|
81
|
+
const v = value;
|
|
82
|
+
if (typeof v.sessionId !== "string") return false;
|
|
83
|
+
if (!Array.isArray(v.events)) return false;
|
|
84
|
+
if (!v.meta || typeof v.meta !== "object") return false;
|
|
85
|
+
const meta = v.meta;
|
|
86
|
+
if (typeof meta.url !== "string" || typeof meta.userAgent !== "string") return false;
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
function startBeaconServer(opts) {
|
|
90
|
+
return new Promise((resolveStart, rejectStart) => {
|
|
91
|
+
const server = (0, import_node_http.createServer)(async (req, res) => {
|
|
92
|
+
writeCors(res, req.headers.origin);
|
|
93
|
+
if (req.method === "OPTIONS") {
|
|
94
|
+
res.writeHead(204);
|
|
95
|
+
res.end();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (req.method !== "POST") {
|
|
99
|
+
res.writeHead(405, { "content-type": "text/plain" });
|
|
100
|
+
res.end("method not allowed");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const url = req.url ?? "/";
|
|
104
|
+
const pathOnly = url.split("?")[0] ?? "/";
|
|
105
|
+
if (!pathOnly.startsWith(TOKEN_PATH_PREFIX)) {
|
|
106
|
+
res.writeHead(404, { "content-type": "text/plain" });
|
|
107
|
+
res.end("not found");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const provided = pathOnly.slice(TOKEN_PATH_PREFIX.length).replace(/\/+$/, "");
|
|
111
|
+
if (provided !== opts.token) {
|
|
112
|
+
res.writeHead(403, { "content-type": "text/plain" });
|
|
113
|
+
res.end("bad token");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
let bodyText;
|
|
117
|
+
try {
|
|
118
|
+
bodyText = await readBody(req);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
opts.onBadBatch?.(`body read failed: ${err.message}`, req);
|
|
121
|
+
res.writeHead(413, { "content-type": "text/plain" });
|
|
122
|
+
res.end("payload error");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
let parsed;
|
|
126
|
+
try {
|
|
127
|
+
parsed = JSON.parse(bodyText);
|
|
128
|
+
} catch {
|
|
129
|
+
opts.onBadBatch?.("invalid JSON", req);
|
|
130
|
+
res.writeHead(400, { "content-type": "text/plain" });
|
|
131
|
+
res.end("invalid json");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (!isMonitorBatchEnvelope(parsed)) {
|
|
135
|
+
opts.onBadBatch?.("schema mismatch", req);
|
|
136
|
+
res.writeHead(400, { "content-type": "text/plain" });
|
|
137
|
+
res.end("bad batch shape");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
opts.onBatch(parsed, req);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
opts.onServerError?.(err instanceof Error ? err : new Error(String(err)));
|
|
144
|
+
}
|
|
145
|
+
res.writeHead(204);
|
|
146
|
+
res.end();
|
|
147
|
+
});
|
|
148
|
+
server.on("error", (err) => {
|
|
149
|
+
opts.onServerError?.(err);
|
|
150
|
+
rejectStart(err);
|
|
151
|
+
});
|
|
152
|
+
server.listen(opts.port, opts.host, () => {
|
|
153
|
+
const addr = server.address();
|
|
154
|
+
const port = addr ? addr.port : opts.port;
|
|
155
|
+
const url = `http://${opts.host}:${port}/m/${opts.token}`;
|
|
156
|
+
resolveStart({
|
|
157
|
+
url,
|
|
158
|
+
port,
|
|
159
|
+
close() {
|
|
160
|
+
return new Promise((resolveClose) => {
|
|
161
|
+
server.close(() => resolveClose());
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/server/beacon-monitor-entry.ts
|
|
170
|
+
function parseMonitorFlags(argv) {
|
|
171
|
+
const flags = { port: 9876, host: "127.0.0.1", quiet: false };
|
|
172
|
+
for (let i = 0; i < argv.length; i++) {
|
|
173
|
+
const a = argv[i];
|
|
174
|
+
if (a === "--port") {
|
|
175
|
+
const n = Number(argv[++i]);
|
|
176
|
+
if (!Number.isInteger(n) || n < 0 || n > 65535) {
|
|
177
|
+
die(`--port must be an integer in 0..65535 (got "${argv[i]}")`);
|
|
178
|
+
}
|
|
179
|
+
flags.port = n;
|
|
180
|
+
} else if (a === "--host") {
|
|
181
|
+
const v = argv[++i];
|
|
182
|
+
if (!v) die("--host requires a value");
|
|
183
|
+
flags.host = v;
|
|
184
|
+
} else if (a === "--out") {
|
|
185
|
+
const v = argv[++i];
|
|
186
|
+
if (!v) die("--out requires a path");
|
|
187
|
+
flags.out = v;
|
|
188
|
+
} else if (a === "--token") {
|
|
189
|
+
const v = argv[++i];
|
|
190
|
+
if (!v) die("--token requires a value");
|
|
191
|
+
if (!/^[A-Za-z0-9_-]+$/.test(v)) die("--token must be alphanumeric (plus _ or -)");
|
|
192
|
+
flags.token = v;
|
|
193
|
+
} else if (a === "--quiet") {
|
|
194
|
+
flags.quiet = true;
|
|
195
|
+
} else if (a === "--help" || a === "-h") {
|
|
196
|
+
printHelp();
|
|
197
|
+
process.exit(0);
|
|
198
|
+
} else if (a.startsWith("--")) {
|
|
199
|
+
die(`unknown flag: ${a}`);
|
|
200
|
+
} else {
|
|
201
|
+
die(`unexpected argument: ${a}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return flags;
|
|
205
|
+
}
|
|
206
|
+
function mintToken() {
|
|
207
|
+
return (0, import_node_crypto.randomBytes)(4).toString("hex");
|
|
208
|
+
}
|
|
209
|
+
async function runMonitor(argv) {
|
|
210
|
+
const flags = parseMonitorFlags(argv);
|
|
211
|
+
const token = flags.token ?? mintToken();
|
|
212
|
+
const projectRoot = process.cwd();
|
|
213
|
+
const outPath = flags.out ? (0, import_node_path2.resolve)(projectRoot, flags.out) : (0, import_node_path2.resolve)(projectRoot, ".launchsecure", `beacon-${token}.ndjson`);
|
|
214
|
+
const writer = openNdjsonWriter({
|
|
215
|
+
path: outPath,
|
|
216
|
+
onError: (err) => {
|
|
217
|
+
process.stderr.write(`
|
|
218
|
+
[launch-beacon] ndjson write error: ${err.message}
|
|
219
|
+
`);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
const stats = {
|
|
223
|
+
events: 0,
|
|
224
|
+
badBatches: 0,
|
|
225
|
+
lastKind: "",
|
|
226
|
+
lastAt: 0
|
|
227
|
+
};
|
|
228
|
+
let server;
|
|
229
|
+
try {
|
|
230
|
+
server = await startBeaconServer({
|
|
231
|
+
port: flags.port,
|
|
232
|
+
host: flags.host,
|
|
233
|
+
token,
|
|
234
|
+
onBatch: (batch) => {
|
|
235
|
+
for (const raw of batch.events) {
|
|
236
|
+
writer.write(raw);
|
|
237
|
+
stats.events += 1;
|
|
238
|
+
const ev = raw;
|
|
239
|
+
if (typeof ev.kind === "string") stats.lastKind = ev.kind;
|
|
240
|
+
if (typeof ev.ts === "number") stats.lastAt = ev.ts;
|
|
241
|
+
}
|
|
242
|
+
draw();
|
|
243
|
+
},
|
|
244
|
+
onBadBatch: (reason) => {
|
|
245
|
+
stats.badBatches += 1;
|
|
246
|
+
process.stderr.write(`
|
|
247
|
+
[launch-beacon] bad batch: ${reason}
|
|
248
|
+
`);
|
|
249
|
+
},
|
|
250
|
+
onServerError: (err) => {
|
|
251
|
+
process.stderr.write(`
|
|
252
|
+
[launch-beacon] server error: ${err.message}
|
|
253
|
+
`);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
} catch (err) {
|
|
257
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
258
|
+
if (msg.includes("EADDRINUSE")) {
|
|
259
|
+
die(`port ${flags.port} already in use \u2014 pass --port <n> to choose another`);
|
|
260
|
+
}
|
|
261
|
+
die(`failed to start: ${msg}`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
process.stdout.write(`
|
|
265
|
+
launch-beacon monitor \u2014 listening
|
|
266
|
+
`);
|
|
267
|
+
process.stdout.write(` paste URL ${server.url}
|
|
268
|
+
`);
|
|
269
|
+
process.stdout.write(` writing ${outPath}
|
|
270
|
+
`);
|
|
271
|
+
process.stdout.write(` session ${token}
|
|
272
|
+
|
|
273
|
+
`);
|
|
274
|
+
let heartbeatTimer = null;
|
|
275
|
+
const draw = () => {
|
|
276
|
+
if (flags.quiet) return;
|
|
277
|
+
const since = stats.lastAt ? Math.max(0, Math.round((Date.now() - stats.lastAt) / 1e3)) : null;
|
|
278
|
+
const sinceStr = since === null ? "\u2014" : since < 5 ? "just now" : `${since}s ago`;
|
|
279
|
+
const lastStr = stats.lastKind ? `${stats.lastKind} (${sinceStr})` : "\u2014";
|
|
280
|
+
const badStr = stats.badBatches > 0 ? ` | bad: ${stats.badBatches}` : "";
|
|
281
|
+
const line = ` events: ${stats.events}${badStr} | last: ${lastStr}`;
|
|
282
|
+
process.stdout.write(`\r\x1B[K${line}`);
|
|
283
|
+
};
|
|
284
|
+
if (!flags.quiet) {
|
|
285
|
+
heartbeatTimer = setInterval(draw, 1e3);
|
|
286
|
+
draw();
|
|
287
|
+
}
|
|
288
|
+
const shutdown = async (signal) => {
|
|
289
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
290
|
+
process.stdout.write(`
|
|
291
|
+
|
|
292
|
+
[launch-beacon] caught ${signal}, shutting down\u2026
|
|
293
|
+
`);
|
|
294
|
+
try {
|
|
295
|
+
await server.close();
|
|
296
|
+
} catch (err) {
|
|
297
|
+
process.stderr.write(`[launch-beacon] server close error: ${err.message}
|
|
298
|
+
`);
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
await writer.close();
|
|
302
|
+
} catch (err) {
|
|
303
|
+
process.stderr.write(`[launch-beacon] writer close error: ${err.message}
|
|
304
|
+
`);
|
|
305
|
+
}
|
|
306
|
+
process.stdout.write(` events written: ${writer.count()}
|
|
307
|
+
`);
|
|
308
|
+
process.stdout.write(` file: ${outPath}
|
|
309
|
+
`);
|
|
310
|
+
process.exit(0);
|
|
311
|
+
};
|
|
312
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
313
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
314
|
+
}
|
|
315
|
+
function printHelp() {
|
|
316
|
+
process.stdout.write(
|
|
317
|
+
`launch-beacon
|
|
318
|
+
|
|
319
|
+
Usage:
|
|
320
|
+
launch-beacon monitor [flags] Start the HTTP receiver for beacon monitor batches
|
|
321
|
+
|
|
322
|
+
Flags for "monitor":
|
|
323
|
+
--port <n> port to listen on (default: 9876)
|
|
324
|
+
--host <host> host to bind to (default: 127.0.0.1)
|
|
325
|
+
--out <path> output NDJSON file (default: .launchsecure/beacon-<token>.ndjson)
|
|
326
|
+
--token <str> use this token instead of a random 8-char hex
|
|
327
|
+
--quiet suppress the live heartbeat line
|
|
328
|
+
|
|
329
|
+
The printed URL is what you paste into the beacon debug panel. Beacon POSTs
|
|
330
|
+
monitor batches there; this process writes each event as a JSON line to the
|
|
331
|
+
output file. Ctrl+C shuts down cleanly.
|
|
332
|
+
`
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
async function main() {
|
|
336
|
+
const argv = process.argv.slice(2);
|
|
337
|
+
const subcommand = argv[0];
|
|
338
|
+
if (!subcommand || subcommand === "monitor") {
|
|
339
|
+
await runMonitor(subcommand === "monitor" ? argv.slice(1) : argv);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
|
|
343
|
+
printHelp();
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
die(`unknown subcommand: ${subcommand}`);
|
|
347
|
+
}
|
|
348
|
+
function die(msg) {
|
|
349
|
+
process.stderr.write(`[launch-beacon] ${msg}
|
|
350
|
+
`);
|
|
351
|
+
process.exit(2);
|
|
352
|
+
}
|
|
353
|
+
void main();
|