@lumenflow/cli 3.14.0 → 3.16.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 (143) hide show
  1. package/dist/chunk-2D2VOCA4.js +37 -0
  2. package/dist/chunk-2D5KFYGX.js +284 -0
  3. package/dist/chunk-2GXVIN57.js +14072 -0
  4. package/dist/chunk-2MQ7HZWZ.js +26 -0
  5. package/dist/chunk-2UFQ3A3C.js +643 -0
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-4N74J3UT.js +15 -0
  8. package/dist/chunk-5GTOXFYR.js +392 -0
  9. package/dist/chunk-5VY6MQMC.js +240 -0
  10. package/dist/chunk-67XVPMRY.js +1297 -0
  11. package/dist/chunk-6HO4GWJE.js +164 -0
  12. package/dist/chunk-6W5XHWYV.js +1890 -0
  13. package/dist/chunk-6X4EMYJQ.js +64 -0
  14. package/dist/chunk-6XYXI2NQ.js +772 -0
  15. package/dist/chunk-7ANSOV6Q.js +285 -0
  16. package/dist/chunk-A624LFLB.js +1380 -0
  17. package/dist/chunk-ADN5NHG4.js +126 -0
  18. package/dist/chunk-B7YJYJKG.js +33 -0
  19. package/dist/chunk-CCLHCPKG.js +210 -0
  20. package/dist/chunk-CK36VROC.js +1584 -0
  21. package/dist/chunk-D3UOFRSB.js +81 -0
  22. package/dist/chunk-DFR4DJBM.js +230 -0
  23. package/dist/chunk-DSYBDHYH.js +79 -0
  24. package/dist/chunk-DWMLTXKQ.js +1176 -0
  25. package/dist/chunk-E3REJTAJ.js +28 -0
  26. package/dist/chunk-EA3IVO64.js +633 -0
  27. package/dist/chunk-EK2AKZKD.js +55 -0
  28. package/dist/chunk-ELD7JTTT.js +343 -0
  29. package/dist/chunk-EX6TT2XI.js +195 -0
  30. package/dist/chunk-EXINSFZE.js +82 -0
  31. package/dist/chunk-EZ6ZBYBM.js +510 -0
  32. package/dist/chunk-FBKAPTJ2.js +16 -0
  33. package/dist/chunk-FVLV5RYH.js +1118 -0
  34. package/dist/chunk-GDNSBQVK.js +2485 -0
  35. package/dist/chunk-GPQHMBNN.js +278 -0
  36. package/dist/chunk-GTFJB67L.js +68 -0
  37. package/dist/chunk-HANJXVKW.js +1127 -0
  38. package/dist/chunk-HEVS5YLD.js +269 -0
  39. package/dist/chunk-HMEVZKPQ.js +9 -0
  40. package/dist/chunk-HRGSYNLM.js +3511 -0
  41. package/dist/chunk-ISZR5N4K.js +60 -0
  42. package/dist/chunk-J6SUPR2C.js +226 -0
  43. package/dist/chunk-JERYVEIZ.js +244 -0
  44. package/dist/chunk-JHHWGL2N.js +87 -0
  45. package/dist/chunk-JONWQUB5.js +775 -0
  46. package/dist/chunk-K2DIWWDM.js +1766 -0
  47. package/dist/chunk-KY4PGL5V.js +969 -0
  48. package/dist/chunk-L737LQ4C.js +1285 -0
  49. package/dist/chunk-LFTWYIB2.js +497 -0
  50. package/dist/chunk-LV47RFNJ.js +41 -0
  51. package/dist/chunk-MKSAITI7.js +15 -0
  52. package/dist/chunk-MZ7RKIX4.js +212 -0
  53. package/dist/chunk-NAP6CFSO.js +84 -0
  54. package/dist/chunk-ND6MY37M.js +16 -0
  55. package/dist/chunk-NMG736UR.js +683 -0
  56. package/dist/chunk-NRAXROED.js +32 -0
  57. package/dist/chunk-NRIZR3A7.js +690 -0
  58. package/dist/chunk-NX43BG3M.js +233 -0
  59. package/dist/chunk-O645XLSI.js +297 -0
  60. package/dist/chunk-OMJD6A3S.js +235 -0
  61. package/dist/chunk-QB6SJD4T.js +430 -0
  62. package/dist/chunk-QFSTL4J3.js +276 -0
  63. package/dist/chunk-QLGDFMFX.js +212 -0
  64. package/dist/chunk-RIAAGL2E.js +13 -0
  65. package/dist/chunk-RWO5XMZ6.js +86 -0
  66. package/dist/chunk-RXRKBBSM.js +149 -0
  67. package/dist/chunk-RZOZMML6.js +363 -0
  68. package/dist/chunk-U7I7FS7T.js +113 -0
  69. package/dist/chunk-UI42RODY.js +717 -0
  70. package/dist/chunk-UTVMVSCO.js +519 -0
  71. package/dist/chunk-V6OJGLBA.js +1746 -0
  72. package/dist/chunk-W2JHVH7D.js +152 -0
  73. package/dist/chunk-WD3Y7VQN.js +280 -0
  74. package/dist/chunk-WOCTQ5MS.js +303 -0
  75. package/dist/chunk-WZR3ZUNN.js +696 -0
  76. package/dist/chunk-XGI665H7.js +150 -0
  77. package/dist/chunk-XKY65P2T.js +304 -0
  78. package/dist/chunk-Y4CQZY65.js +57 -0
  79. package/dist/chunk-YFEXKLVE.js +194 -0
  80. package/dist/chunk-YHO3HS5X.js +287 -0
  81. package/dist/chunk-YLS7AZSX.js +738 -0
  82. package/dist/chunk-ZE473AO6.js +49 -0
  83. package/dist/chunk-ZF747T3O.js +644 -0
  84. package/dist/chunk-ZHCZHZH3.js +43 -0
  85. package/dist/chunk-ZZNZX2XY.js +87 -0
  86. package/dist/constants-7QAP3VQ4.js +23 -0
  87. package/dist/dist-IY3UUMWK.js +33 -0
  88. package/dist/docs-sync.js +60 -25
  89. package/dist/docs-sync.js.map +1 -1
  90. package/dist/gates-runners.js +43 -2
  91. package/dist/gates-runners.js.map +1 -1
  92. package/dist/init-templates.js +26 -219
  93. package/dist/init-templates.js.map +1 -1
  94. package/dist/invariants-runner-W5RGHCSU.js +27 -0
  95. package/dist/lane-lock-6J36HD5O.js +35 -0
  96. package/dist/lumenflow-upgrade.js +60 -0
  97. package/dist/lumenflow-upgrade.js.map +1 -1
  98. package/dist/mem-checkpoint-core-EANG2GVN.js +14 -0
  99. package/dist/mem-signal-core-2LZ2WYHW.js +19 -0
  100. package/dist/memory-store-OLB5FO7K.js +18 -0
  101. package/dist/plan-edit.js +19 -24
  102. package/dist/plan-edit.js.map +1 -1
  103. package/dist/plan-promote.js +15 -23
  104. package/dist/plan-promote.js.map +1 -1
  105. package/dist/plan-resolve.js +111 -0
  106. package/dist/plan-resolve.js.map +1 -0
  107. package/dist/public-manifest.js +2 -2
  108. package/dist/public-manifest.js.map +1 -1
  109. package/dist/service-6BYCOCO5.js +13 -0
  110. package/dist/spawn-policy-resolver-NTSZYQ6R.js +17 -0
  111. package/dist/spawn-task-builder-R4E2BHSW.js +22 -0
  112. package/dist/sync-templates.js +12 -0
  113. package/dist/sync-templates.js.map +1 -1
  114. package/dist/wu-claim-validation.js +9 -1
  115. package/dist/wu-claim-validation.js.map +1 -1
  116. package/dist/wu-done-pr-WLFFFEPJ.js +25 -0
  117. package/dist/wu-done-validation-3J5E36FE.js +30 -0
  118. package/dist/wu-done.js +42 -1
  119. package/dist/wu-done.js.map +1 -1
  120. package/dist/wu-duplicate-id-detector-5S7JHELK.js +232 -0
  121. package/dist/wu-edit-operations.js +7 -6
  122. package/dist/wu-edit-operations.js.map +1 -1
  123. package/dist/wu-edit.js +23 -3
  124. package/dist/wu-edit.js.map +1 -1
  125. package/dist/wu-spawn-prompt-builders.js +38 -1
  126. package/dist/wu-spawn-prompt-builders.js.map +1 -1
  127. package/package.json +8 -8
  128. package/packs/sidekick/.turbo/turbo-build.log +1 -1
  129. package/packs/sidekick/.turbo/turbo-typecheck.log +4 -0
  130. package/packs/sidekick/package.json +1 -1
  131. package/packs/software-delivery/.turbo/turbo-build.log +1 -1
  132. package/packs/software-delivery/.turbo/turbo-typecheck.log +4 -0
  133. package/packs/software-delivery/package.json +1 -1
  134. package/templates/core/AGENTS.md.template +19 -0
  135. package/templates/core/LUMENFLOW.md.template +13 -2
  136. package/templates/core/UPGRADING.md.template +6 -6
  137. package/templates/core/ai/onboarding/first-15-mins.md.template +1 -1
  138. package/templates/core/ai/onboarding/first-wu-mistakes.md.template +10 -0
  139. package/templates/core/ai/onboarding/quick-ref-commands.md.template +11 -8
  140. package/templates/core/ai/onboarding/starting-prompt.md.template +1 -1
  141. package/templates/core/ai/onboarding/wu-sizing-guide.md.template +11 -2
  142. package/templates/vendors/cursor/.cursor/rules/lumenflow.md.template +9 -1
  143. package/templates/vendors/windsurf/.windsurf/rules/lumenflow.md.template +9 -1
@@ -0,0 +1,772 @@
1
+ import {
2
+ CONFIG_FILES,
3
+ FILE_EXTENSIONS,
4
+ LUMENFLOW_PATHS,
5
+ STDIO,
6
+ STRING_LITERALS
7
+ } from "./chunk-DWMLTXKQ.js";
8
+
9
+ // ../core/dist/telemetry.js
10
+ import { appendFileSync, mkdirSync, existsSync, readFileSync, writeFileSync } from "fs";
11
+ import { execSync } from "child_process";
12
+ import path from "path";
13
+ import YAML from "yaml";
14
+ var TELEMETRY_DIR = LUMENFLOW_PATHS.TELEMETRY;
15
+ var GATES_LOG = `${TELEMETRY_DIR}/gates${FILE_EXTENSIONS.NDJSON}`;
16
+ var LLM_CLASSIFICATION_LOG = `${TELEMETRY_DIR}/llm-classification${FILE_EXTENSIONS.NDJSON}`;
17
+ var COSTS_LOG = `${TELEMETRY_DIR}/costs${FILE_EXTENSIONS.NDJSON}`;
18
+ var FLOW_LOG = LUMENFLOW_PATHS.FLOW_LOG;
19
+ var DORA_LOG = `${TELEMETRY_DIR}/dora${FILE_EXTENSIONS.NDJSON}`;
20
+ var WORKSPACE_FILE = CONFIG_FILES.WORKSPACE_CONFIG;
21
+ var CLOUD_SYNC_STATE_FILE = `${TELEMETRY_DIR}/cloud-sync-state${FILE_EXTENSIONS.JSON}`;
22
+ var CLOUD_SYNC_LOG_PREFIX = "[telemetry:cloud-sync]";
23
+ var PACK_KEY_SOFTWARE_DELIVERY = "software_delivery";
24
+ var CONTROL_PLANE_FIELD = "control_plane";
25
+ var CONTROL_PLANE_AUTH_FIELD = "auth";
26
+ var CONTROL_PLANE_ENDPOINT_FIELD = "endpoint";
27
+ var CONTROL_PLANE_SYNC_INTERVAL_FIELD = "sync_interval";
28
+ var CONTROL_PLANE_BATCH_SIZE_FIELD = "batch_size";
29
+ var CONTROL_PLANE_TIMEOUT_MS_FIELD = "timeout_ms";
30
+ var CONTROL_PLANE_TOKEN_ENV_FIELD = "token_env";
31
+ var WORKSPACE_ID_FIELD = "id";
32
+ var HTTP = {
33
+ METHOD_POST: "POST",
34
+ HEADER_AUTHORIZATION: "authorization",
35
+ HEADER_CONTENT_TYPE: "content-type",
36
+ CONTENT_TYPE_JSON: "application/json"
37
+ };
38
+ var CONTROL_PLANE_API_PATH = {
39
+ TELEMETRY: "/api/v1/telemetry"
40
+ };
41
+ var DEFAULT_BATCH_SIZE = 100;
42
+ var DEFAULT_TIMEOUT_MS = 1e4;
43
+ var MS_PER_SECOND = 1e3;
44
+ var ERROR_NAME = {
45
+ ABORT: "AbortError"
46
+ };
47
+ var CONTROL_PLANE_TOKEN_ENV_PATTERN = /^[A-Z][A-Z0-9_]*$/;
48
+ var METRIC_NAME = {
49
+ GATES_DURATION_MS: "gates.duration_ms",
50
+ FLOW_EVENT: "flow.event",
51
+ DORA_METRIC: "dora",
52
+ COST_USD: "cost.usd",
53
+ RAW_GATES: "telemetry.raw.gates",
54
+ RAW_FLOW: "telemetry.raw.flow",
55
+ RAW_DORA: "telemetry.raw.dora",
56
+ RAW_COSTS: "telemetry.raw.costs"
57
+ };
58
+ var TELEMETRY_SOURCE = {
59
+ GATES: "gates",
60
+ FLOW: "flow",
61
+ DORA: "dora",
62
+ COSTS: "costs"
63
+ };
64
+ function ensureTelemetryDir() {
65
+ try {
66
+ mkdirSync(TELEMETRY_DIR, { recursive: true });
67
+ } catch {
68
+ }
69
+ }
70
+ function emit(filePath, event) {
71
+ ensureTelemetryDir();
72
+ const line = `${JSON.stringify(event)}${STRING_LITERALS.NEWLINE}`;
73
+ try {
74
+ appendFileSync(filePath, line, { encoding: "utf-8" });
75
+ } catch (err) {
76
+ const message = err instanceof Error ? err.message : String(err);
77
+ console.error(`[telemetry] Failed to emit to ${filePath}:`, message);
78
+ }
79
+ }
80
+ function emitGateEvent(data) {
81
+ const event = {
82
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
83
+ wu_id: data.wu_id || null,
84
+ lane: data.lane || null,
85
+ gate_name: data.gate_name,
86
+ passed: data.passed,
87
+ duration_ms: data.duration_ms
88
+ };
89
+ emit(GATES_LOG, event);
90
+ }
91
+ function getCurrentWU() {
92
+ try {
93
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", {
94
+ encoding: "utf-8",
95
+ stdio: [STDIO.PIPE, STDIO.PIPE, STDIO.IGNORE]
96
+ }).trim();
97
+ const match = branch.match(/wu-(\d+)/i);
98
+ if (match) {
99
+ return `WU-${match[1]}`.toUpperCase();
100
+ }
101
+ } catch {
102
+ }
103
+ return null;
104
+ }
105
+ function getCurrentLane() {
106
+ try {
107
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", {
108
+ encoding: "utf-8",
109
+ stdio: [STDIO.PIPE, STDIO.PIPE, STDIO.IGNORE]
110
+ }).trim();
111
+ const match = branch.match(/^lane\/([^/]+)\//i);
112
+ const laneSegment = match?.[1];
113
+ if (laneSegment) {
114
+ return laneSegment.charAt(0).toUpperCase() + laneSegment.slice(1).toLowerCase();
115
+ }
116
+ } catch {
117
+ }
118
+ return null;
119
+ }
120
+ function emitWUFlowEvent(event, logPath = FLOW_LOG) {
121
+ const logDir = path.dirname(logPath);
122
+ if (!existsSync(logDir)) {
123
+ mkdirSync(logDir, { recursive: true });
124
+ }
125
+ const line = JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...event });
126
+ try {
127
+ appendFileSync(logPath, `${line}${STRING_LITERALS.NEWLINE}`, { encoding: "utf-8" });
128
+ } catch (err) {
129
+ const message = err instanceof Error ? err.message : String(err);
130
+ console.error(`[telemetry] Failed to emit flow event: ${message}`);
131
+ }
132
+ }
133
+ function emitDoraTelemetry(record) {
134
+ ensureTelemetryDir();
135
+ const line = JSON.stringify({
136
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
137
+ metric: record.metric,
138
+ value: record.value,
139
+ tier: record.tier
140
+ });
141
+ try {
142
+ appendFileSync(DORA_LOG, `${line}${STRING_LITERALS.NEWLINE}`, { encoding: "utf-8" });
143
+ } catch (err) {
144
+ const message = err instanceof Error ? err.message : String(err);
145
+ console.error(`[telemetry] Failed to emit DORA telemetry: ${message}`);
146
+ }
147
+ }
148
+ function isRecord(value) {
149
+ return typeof value === "object" && value !== null;
150
+ }
151
+ function asNonEmptyString(value) {
152
+ if (typeof value !== "string") {
153
+ return void 0;
154
+ }
155
+ const trimmed = value.trim();
156
+ return trimmed.length > 0 ? trimmed : void 0;
157
+ }
158
+ function asPositiveInteger(value) {
159
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
160
+ return void 0;
161
+ }
162
+ return value;
163
+ }
164
+ function asFiniteNumber(value) {
165
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
166
+ }
167
+ function asPrimitiveTagValue(value) {
168
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
169
+ return value;
170
+ }
171
+ return void 0;
172
+ }
173
+ function normalizeEndpoint(endpoint) {
174
+ return endpoint.endsWith("/") ? endpoint.slice(0, endpoint.length - 1) : endpoint;
175
+ }
176
+ function normalizeTimestamp(value, fallbackIso) {
177
+ const rawTimestamp = asNonEmptyString(value);
178
+ return rawTimestamp ?? fallbackIso;
179
+ }
180
+ function warnCloudSync(logger, message) {
181
+ logger?.warn?.(`${CLOUD_SYNC_LOG_PREFIX} ${message}`);
182
+ }
183
+ function createDefaultCloudSyncState() {
184
+ return {
185
+ version: 1,
186
+ lastSyncAtMs: 0,
187
+ files: {
188
+ gates: {
189
+ offset: 0
190
+ },
191
+ flow: {
192
+ offset: 0
193
+ },
194
+ dora: {
195
+ offset: 0
196
+ },
197
+ costs: {
198
+ offset: 0
199
+ }
200
+ }
201
+ };
202
+ }
203
+ function normalizeStateOffset(value) {
204
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
205
+ return 0;
206
+ }
207
+ return value;
208
+ }
209
+ function loadCloudSyncState(statePath) {
210
+ if (!existsSync(statePath)) {
211
+ return createDefaultCloudSyncState();
212
+ }
213
+ try {
214
+ const raw = readFileSync(statePath, "utf-8");
215
+ const parsed = JSON.parse(raw);
216
+ if (!isRecord(parsed)) {
217
+ return createDefaultCloudSyncState();
218
+ }
219
+ const filesRaw = Reflect.get(parsed, "files");
220
+ const gatesRaw = isRecord(filesRaw) ? Reflect.get(filesRaw, TELEMETRY_SOURCE.GATES) : void 0;
221
+ const flowRaw = isRecord(filesRaw) ? Reflect.get(filesRaw, TELEMETRY_SOURCE.FLOW) : void 0;
222
+ const doraRaw = isRecord(filesRaw) ? Reflect.get(filesRaw, TELEMETRY_SOURCE.DORA) : void 0;
223
+ const costsRaw = isRecord(filesRaw) ? Reflect.get(filesRaw, TELEMETRY_SOURCE.COSTS) : void 0;
224
+ const gatesOffset = isRecord(gatesRaw) ? normalizeStateOffset(Reflect.get(gatesRaw, "offset")) : 0;
225
+ const flowOffset = isRecord(flowRaw) ? normalizeStateOffset(Reflect.get(flowRaw, "offset")) : 0;
226
+ const doraOffset = isRecord(doraRaw) ? normalizeStateOffset(Reflect.get(doraRaw, "offset")) : 0;
227
+ const costsOffset = isRecord(costsRaw) ? normalizeStateOffset(Reflect.get(costsRaw, "offset")) : 0;
228
+ const lastSyncAtMs = normalizeStateOffset(Reflect.get(parsed, "lastSyncAtMs"));
229
+ return {
230
+ version: 1,
231
+ lastSyncAtMs,
232
+ files: {
233
+ gates: {
234
+ offset: gatesOffset
235
+ },
236
+ flow: {
237
+ offset: flowOffset
238
+ },
239
+ dora: {
240
+ offset: doraOffset
241
+ },
242
+ costs: {
243
+ offset: costsOffset
244
+ }
245
+ }
246
+ };
247
+ } catch {
248
+ return createDefaultCloudSyncState();
249
+ }
250
+ }
251
+ function saveCloudSyncState(statePath, state) {
252
+ mkdirSync(path.dirname(statePath), { recursive: true });
253
+ writeFileSync(statePath, `${JSON.stringify(state, null, 2)}${STRING_LITERALS.NEWLINE}`, {
254
+ encoding: "utf-8"
255
+ });
256
+ }
257
+ function getSourceOffset(state, source) {
258
+ if (source === TELEMETRY_SOURCE.GATES) {
259
+ return state.files.gates.offset;
260
+ }
261
+ if (source === TELEMETRY_SOURCE.DORA) {
262
+ return state.files.dora.offset;
263
+ }
264
+ if (source === TELEMETRY_SOURCE.COSTS) {
265
+ return state.files.costs.offset;
266
+ }
267
+ return state.files.flow.offset;
268
+ }
269
+ function setSourceOffset(state, source, offset) {
270
+ if (source === TELEMETRY_SOURCE.GATES) {
271
+ state.files.gates.offset = offset;
272
+ return;
273
+ }
274
+ if (source === TELEMETRY_SOURCE.DORA) {
275
+ state.files.dora.offset = offset;
276
+ return;
277
+ }
278
+ if (source === TELEMETRY_SOURCE.COSTS) {
279
+ state.files.costs.offset = offset;
280
+ return;
281
+ }
282
+ state.files.flow.offset = offset;
283
+ }
284
+ function resolveTelemetryPath(workspaceRoot, source) {
285
+ if (source === TELEMETRY_SOURCE.GATES) {
286
+ return path.join(workspaceRoot, GATES_LOG);
287
+ }
288
+ if (source === TELEMETRY_SOURCE.DORA) {
289
+ return path.join(workspaceRoot, DORA_LOG);
290
+ }
291
+ if (source === TELEMETRY_SOURCE.COSTS) {
292
+ return path.join(workspaceRoot, COSTS_LOG);
293
+ }
294
+ return path.join(workspaceRoot, FLOW_LOG);
295
+ }
296
+ function mapGatesEventToTelemetryRecord(payload, fallbackIso) {
297
+ const gateName = asNonEmptyString(Reflect.get(payload, "gate_name")) ?? "unknown";
298
+ const passedRaw = Reflect.get(payload, "passed");
299
+ const passed = typeof passedRaw === "boolean" ? passedRaw : false;
300
+ const durationMs = asFiniteNumber(Reflect.get(payload, "duration_ms")) ?? 0;
301
+ const wuId = asPrimitiveTagValue(Reflect.get(payload, "wu_id"));
302
+ const lane = asPrimitiveTagValue(Reflect.get(payload, "lane"));
303
+ const tags = {
304
+ source: TELEMETRY_SOURCE.GATES,
305
+ gate_name: gateName,
306
+ passed
307
+ };
308
+ if (wuId !== void 0) {
309
+ tags.wu_id = wuId;
310
+ }
311
+ if (lane !== void 0) {
312
+ tags.lane = lane;
313
+ }
314
+ return {
315
+ metric: METRIC_NAME.GATES_DURATION_MS,
316
+ value: durationMs,
317
+ timestamp: normalizeTimestamp(Reflect.get(payload, "timestamp"), fallbackIso),
318
+ tags
319
+ };
320
+ }
321
+ function mapFlowEventToTelemetryRecord(payload, fallbackIso) {
322
+ const script = asNonEmptyString(Reflect.get(payload, "script")) ?? "unknown";
323
+ const step = asNonEmptyString(Reflect.get(payload, "step")) ?? asNonEmptyString(Reflect.get(payload, "event_type")) ?? "event";
324
+ const value = asFiniteNumber(Reflect.get(payload, "duration_ms")) ?? 1;
325
+ const status = asPrimitiveTagValue(Reflect.get(payload, "status"));
326
+ const wuId = asPrimitiveTagValue(Reflect.get(payload, "wu_id"));
327
+ const lane = asPrimitiveTagValue(Reflect.get(payload, "lane"));
328
+ const tags = {
329
+ source: TELEMETRY_SOURCE.FLOW,
330
+ script,
331
+ step
332
+ };
333
+ if (status !== void 0) {
334
+ tags.status = status;
335
+ }
336
+ if (wuId !== void 0) {
337
+ tags.wu_id = wuId;
338
+ }
339
+ if (lane !== void 0) {
340
+ tags.lane = lane;
341
+ }
342
+ return {
343
+ metric: METRIC_NAME.FLOW_EVENT,
344
+ value,
345
+ timestamp: normalizeTimestamp(Reflect.get(payload, "timestamp"), fallbackIso),
346
+ tags
347
+ };
348
+ }
349
+ function mapDoraEventToTelemetryRecord(payload, fallbackIso) {
350
+ const metric = asNonEmptyString(Reflect.get(payload, "metric")) ?? "dora.unknown";
351
+ const value = asFiniteNumber(Reflect.get(payload, "value")) ?? 0;
352
+ const tier = asNonEmptyString(Reflect.get(payload, "tier"));
353
+ const tags = {
354
+ source: TELEMETRY_SOURCE.DORA
355
+ };
356
+ if (tier !== void 0) {
357
+ tags.tier = tier;
358
+ }
359
+ return {
360
+ metric,
361
+ value,
362
+ timestamp: normalizeTimestamp(Reflect.get(payload, "timestamp"), fallbackIso),
363
+ tags
364
+ };
365
+ }
366
+ function mapCostEventToTelemetryRecord(payload, fallbackIso) {
367
+ const costUsd = asFiniteNumber(Reflect.get(payload, "cost_usd")) ?? 0;
368
+ const inputTokens = asFiniteNumber(Reflect.get(payload, "input_tokens"));
369
+ const outputTokens = asFiniteNumber(Reflect.get(payload, "output_tokens"));
370
+ const operation = asPrimitiveTagValue(Reflect.get(payload, "operation"));
371
+ const model = asPrimitiveTagValue(Reflect.get(payload, "model"));
372
+ const wuId = asPrimitiveTagValue(Reflect.get(payload, "wu_id"));
373
+ const agentId = asPrimitiveTagValue(Reflect.get(payload, "agent_id"));
374
+ const sessionId = asPrimitiveTagValue(Reflect.get(payload, "session_id"));
375
+ const tags = {
376
+ source: TELEMETRY_SOURCE.COSTS,
377
+ source_type: "cost"
378
+ };
379
+ if (operation !== void 0) {
380
+ tags.operation = operation;
381
+ }
382
+ if (model !== void 0) {
383
+ tags.model = model;
384
+ }
385
+ if (wuId !== void 0) {
386
+ tags.wu_id = wuId;
387
+ }
388
+ if (agentId !== void 0) {
389
+ tags.agent_id = agentId;
390
+ }
391
+ if (sessionId !== void 0) {
392
+ tags.session_id = sessionId;
393
+ }
394
+ if (inputTokens !== void 0) {
395
+ tags.input_tokens = inputTokens;
396
+ }
397
+ if (outputTokens !== void 0) {
398
+ tags.output_tokens = outputTokens;
399
+ }
400
+ return {
401
+ metric: METRIC_NAME.COST_USD,
402
+ value: costUsd,
403
+ timestamp: normalizeTimestamp(Reflect.get(payload, "timestamp"), fallbackIso),
404
+ tags
405
+ };
406
+ }
407
+ function resolveRawMetricName(source) {
408
+ if (source === TELEMETRY_SOURCE.GATES)
409
+ return METRIC_NAME.RAW_GATES;
410
+ if (source === TELEMETRY_SOURCE.DORA)
411
+ return METRIC_NAME.RAW_DORA;
412
+ if (source === TELEMETRY_SOURCE.COSTS)
413
+ return METRIC_NAME.RAW_COSTS;
414
+ return METRIC_NAME.RAW_FLOW;
415
+ }
416
+ function mapTelemetryLineToRecord(source, payload, fallbackIso) {
417
+ if (!isRecord(payload)) {
418
+ return {
419
+ metric: resolveRawMetricName(source),
420
+ value: 1,
421
+ timestamp: fallbackIso,
422
+ tags: {
423
+ source
424
+ }
425
+ };
426
+ }
427
+ if (source === TELEMETRY_SOURCE.GATES) {
428
+ return mapGatesEventToTelemetryRecord(payload, fallbackIso);
429
+ }
430
+ if (source === TELEMETRY_SOURCE.DORA) {
431
+ return mapDoraEventToTelemetryRecord(payload, fallbackIso);
432
+ }
433
+ if (source === TELEMETRY_SOURCE.COSTS) {
434
+ return mapCostEventToTelemetryRecord(payload, fallbackIso);
435
+ }
436
+ return mapFlowEventToTelemetryRecord(payload, fallbackIso);
437
+ }
438
+ function readTelemetryLinesFromOffset(input) {
439
+ if (!existsSync(input.filePath)) {
440
+ return {
441
+ items: [],
442
+ malformedLines: 0,
443
+ effectiveOffset: 0
444
+ };
445
+ }
446
+ const fileBuffer = readFileSync(input.filePath);
447
+ const fileSize = fileBuffer.byteLength;
448
+ const effectiveOffset = Math.min(input.initialOffset, fileSize);
449
+ const sliceBuffer = fileBuffer.subarray(effectiveOffset);
450
+ const content = sliceBuffer.toString("utf-8");
451
+ let malformedLines = 0;
452
+ const items = [];
453
+ let cursor = 0;
454
+ let runningOffset = effectiveOffset;
455
+ while (cursor < content.length) {
456
+ const newlineIndex = content.indexOf(STRING_LITERALS.NEWLINE, cursor);
457
+ if (newlineIndex < 0) {
458
+ break;
459
+ }
460
+ const lineWithNewline = content.slice(cursor, newlineIndex + 1);
461
+ runningOffset += Buffer.byteLength(lineWithNewline, "utf8");
462
+ const line = content.slice(cursor, newlineIndex).trim();
463
+ cursor = newlineIndex + 1;
464
+ if (line.length === 0) {
465
+ items.push({
466
+ record: null,
467
+ offsetAfterLine: runningOffset
468
+ });
469
+ continue;
470
+ }
471
+ try {
472
+ const parsed = JSON.parse(line);
473
+ items.push({
474
+ record: mapTelemetryLineToRecord(input.source, parsed, input.fallbackIso),
475
+ offsetAfterLine: runningOffset
476
+ });
477
+ } catch {
478
+ malformedLines += 1;
479
+ warnCloudSync(input.logger, `Skipping malformed NDJSON line in ${input.filePath} at offset ${runningOffset}.`);
480
+ items.push({
481
+ record: null,
482
+ offsetAfterLine: runningOffset
483
+ });
484
+ }
485
+ }
486
+ return {
487
+ items,
488
+ malformedLines,
489
+ effectiveOffset
490
+ };
491
+ }
492
+ function safeResponseText(response) {
493
+ return response.text().catch(() => "");
494
+ }
495
+ async function pushTelemetryBatch(input) {
496
+ const controller = new AbortController();
497
+ const timeout = setTimeout(() => controller.abort(), input.timeoutMs);
498
+ try {
499
+ const response = await input.fetchFn(`${input.endpoint}${CONTROL_PLANE_API_PATH.TELEMETRY}`, {
500
+ method: HTTP.METHOD_POST,
501
+ headers: {
502
+ [HTTP.HEADER_AUTHORIZATION]: `Bearer ${input.token}`,
503
+ [HTTP.HEADER_CONTENT_TYPE]: HTTP.CONTENT_TYPE_JSON
504
+ },
505
+ body: JSON.stringify({
506
+ workspace_id: input.workspaceId,
507
+ records: input.records
508
+ }),
509
+ signal: controller.signal
510
+ });
511
+ if (!response.ok) {
512
+ const responseText = await safeResponseText(response);
513
+ const suffix = responseText.length > 0 ? `: ${responseText}` : "";
514
+ throw new Error(`HTTP ${response.status}${suffix}`);
515
+ }
516
+ } catch (error) {
517
+ if (error instanceof Error && error.name === ERROR_NAME.ABORT) {
518
+ throw new Error(`request timed out after ${input.timeoutMs}ms`, { cause: error });
519
+ }
520
+ throw error;
521
+ } finally {
522
+ clearTimeout(timeout);
523
+ }
524
+ }
525
+ function detectMisnestedControlPlane(workspace) {
526
+ const rootControlPlane = Reflect.get(workspace, CONTROL_PLANE_FIELD);
527
+ if (isRecord(rootControlPlane)) {
528
+ return null;
529
+ }
530
+ const softwareDelivery = Reflect.get(workspace, PACK_KEY_SOFTWARE_DELIVERY);
531
+ if (!isRecord(softwareDelivery)) {
532
+ return null;
533
+ }
534
+ const nestedControlPlane = Reflect.get(softwareDelivery, CONTROL_PLANE_FIELD);
535
+ if (!isRecord(nestedControlPlane)) {
536
+ return null;
537
+ }
538
+ const detectedPath = `${PACK_KEY_SOFTWARE_DELIVERY}.${CONTROL_PLANE_FIELD}`;
539
+ return {
540
+ message: `${CONTROL_PLANE_FIELD} is misnested under ${PACK_KEY_SOFTWARE_DELIVERY}. It must be a root-level key in workspace.yaml.`,
541
+ detectedPath,
542
+ remediation: `Move ${CONTROL_PLANE_FIELD} to the workspace root. Run: pnpm config:set --key control_plane.<sub-key> --value <value>`
543
+ };
544
+ }
545
+ function resolveCloudSyncConfig(input) {
546
+ const workspacePath = path.join(input.workspaceRoot, WORKSPACE_FILE);
547
+ if (!existsSync(workspacePath)) {
548
+ return null;
549
+ }
550
+ let parsedWorkspace;
551
+ try {
552
+ parsedWorkspace = YAML.parse(readFileSync(workspacePath, "utf-8"));
553
+ } catch (error) {
554
+ const message = error instanceof Error ? error.message : String(error);
555
+ warnCloudSync(input.logger, `Unable to parse workspace config: ${message}`);
556
+ return null;
557
+ }
558
+ if (!isRecord(parsedWorkspace)) {
559
+ return null;
560
+ }
561
+ const workspaceId = asNonEmptyString(Reflect.get(parsedWorkspace, WORKSPACE_ID_FIELD));
562
+ if (!workspaceId) {
563
+ warnCloudSync(input.logger, "Skipping sync: workspace id is missing in workspace.yaml.");
564
+ return null;
565
+ }
566
+ const controlPlaneRaw = Reflect.get(parsedWorkspace, CONTROL_PLANE_FIELD);
567
+ if (!isRecord(controlPlaneRaw)) {
568
+ const misnesting = detectMisnestedControlPlane(parsedWorkspace);
569
+ if (misnesting) {
570
+ warnCloudSync(input.logger, misnesting.message);
571
+ warnCloudSync(input.logger, misnesting.remediation);
572
+ }
573
+ return null;
574
+ }
575
+ const endpoint = asNonEmptyString(Reflect.get(controlPlaneRaw, CONTROL_PLANE_ENDPOINT_FIELD));
576
+ const syncIntervalSeconds = asPositiveInteger(Reflect.get(controlPlaneRaw, CONTROL_PLANE_SYNC_INTERVAL_FIELD));
577
+ const batchSize = asPositiveInteger(Reflect.get(controlPlaneRaw, CONTROL_PLANE_BATCH_SIZE_FIELD)) ?? DEFAULT_BATCH_SIZE;
578
+ const timeoutMs = asPositiveInteger(Reflect.get(controlPlaneRaw, CONTROL_PLANE_TIMEOUT_MS_FIELD)) ?? DEFAULT_TIMEOUT_MS;
579
+ if (!endpoint || !syncIntervalSeconds) {
580
+ warnCloudSync(input.logger, "Skipping sync: control_plane endpoint and sync_interval must be configured.");
581
+ return null;
582
+ }
583
+ const authRaw = Reflect.get(controlPlaneRaw, CONTROL_PLANE_AUTH_FIELD);
584
+ if (!isRecord(authRaw)) {
585
+ warnCloudSync(input.logger, "Skipping sync: control_plane.auth is missing.");
586
+ return null;
587
+ }
588
+ const tokenEnv = asNonEmptyString(Reflect.get(authRaw, CONTROL_PLANE_TOKEN_ENV_FIELD));
589
+ if (!tokenEnv || !CONTROL_PLANE_TOKEN_ENV_PATTERN.test(tokenEnv)) {
590
+ warnCloudSync(input.logger, "Skipping sync: control_plane.auth.token_env is invalid.");
591
+ return null;
592
+ }
593
+ const tokenValue = input.environment[tokenEnv];
594
+ const token = typeof tokenValue === "string" ? tokenValue.trim() : "";
595
+ if (token.length === 0) {
596
+ warnCloudSync(input.logger, `Skipping sync: missing cloud auth token in env "${tokenEnv}".`);
597
+ return null;
598
+ }
599
+ try {
600
+ void new URL(endpoint);
601
+ } catch {
602
+ warnCloudSync(input.logger, `Skipping sync: invalid control_plane endpoint "${endpoint}".`);
603
+ return null;
604
+ }
605
+ return {
606
+ workspaceId,
607
+ endpoint: normalizeEndpoint(endpoint),
608
+ token,
609
+ syncIntervalMs: syncIntervalSeconds * MS_PER_SECOND,
610
+ batchSize,
611
+ timeoutMs
612
+ };
613
+ }
614
+ async function syncNdjsonTelemetryToCloud(options = {}) {
615
+ const workspaceRoot = options.workspaceRoot ?? process.cwd();
616
+ const logger = options.logger;
617
+ const fetchFn = options.fetchFn ?? fetch;
618
+ const now = options.now ?? Date.now;
619
+ const environment = options.environment ?? process.env;
620
+ const statePath = path.join(workspaceRoot, CLOUD_SYNC_STATE_FILE);
621
+ const config = resolveCloudSyncConfig({
622
+ workspaceRoot,
623
+ environment,
624
+ logger
625
+ });
626
+ if (config === null) {
627
+ return {
628
+ recordsRead: 0,
629
+ recordsSent: 0,
630
+ malformedLines: 0,
631
+ batchesAttempted: 0,
632
+ batchesSucceeded: 0,
633
+ skippedReason: "control-plane-unavailable"
634
+ };
635
+ }
636
+ const state = loadCloudSyncState(statePath);
637
+ const nowMs = now();
638
+ if (state.lastSyncAtMs > 0 && nowMs - state.lastSyncAtMs < config.syncIntervalMs) {
639
+ return {
640
+ recordsRead: 0,
641
+ recordsSent: 0,
642
+ malformedLines: 0,
643
+ batchesAttempted: 0,
644
+ batchesSucceeded: 0,
645
+ skippedReason: "sync-interval-not-elapsed"
646
+ };
647
+ }
648
+ const fallbackIso = new Date(nowMs).toISOString();
649
+ const nextState = {
650
+ version: 1,
651
+ lastSyncAtMs: state.lastSyncAtMs,
652
+ files: {
653
+ gates: { offset: state.files.gates.offset },
654
+ flow: { offset: state.files.flow.offset },
655
+ dora: { offset: state.files.dora.offset },
656
+ costs: { offset: state.files.costs.offset }
657
+ }
658
+ };
659
+ let malformedLines = 0;
660
+ let recordsRead = 0;
661
+ let recordsSent = 0;
662
+ let batchesAttempted = 0;
663
+ let batchesSucceeded = 0;
664
+ let syncFailed = false;
665
+ let stateChanged = false;
666
+ for (const source of [
667
+ TELEMETRY_SOURCE.GATES,
668
+ TELEMETRY_SOURCE.FLOW,
669
+ TELEMETRY_SOURCE.DORA,
670
+ TELEMETRY_SOURCE.COSTS
671
+ ]) {
672
+ const filePath = resolveTelemetryPath(workspaceRoot, source);
673
+ const sourceLines = readTelemetryLinesFromOffset({
674
+ filePath,
675
+ source,
676
+ initialOffset: getSourceOffset(nextState, source),
677
+ logger,
678
+ fallbackIso
679
+ });
680
+ malformedLines += sourceLines.malformedLines;
681
+ let acknowledgedOffset = sourceLines.effectiveOffset;
682
+ let index = 0;
683
+ while (index < sourceLines.items.length) {
684
+ const currentLine = sourceLines.items[index];
685
+ if (currentLine?.record === null) {
686
+ acknowledgedOffset = currentLine.offsetAfterLine;
687
+ stateChanged = true;
688
+ index += 1;
689
+ continue;
690
+ }
691
+ const batch = [];
692
+ let lastBatchOffset = acknowledgedOffset;
693
+ while (index < sourceLines.items.length && sourceLines.items[index]?.record !== null && batch.length < config.batchSize) {
694
+ const line = sourceLines.items[index];
695
+ if (!line || line.record === null) {
696
+ break;
697
+ }
698
+ batch.push(line.record);
699
+ lastBatchOffset = line.offsetAfterLine;
700
+ index += 1;
701
+ }
702
+ recordsRead += batch.length;
703
+ batchesAttempted += 1;
704
+ try {
705
+ await pushTelemetryBatch({
706
+ endpoint: config.endpoint,
707
+ workspaceId: config.workspaceId,
708
+ token: config.token,
709
+ timeoutMs: config.timeoutMs,
710
+ records: batch,
711
+ fetchFn
712
+ });
713
+ recordsSent += batch.length;
714
+ batchesSucceeded += 1;
715
+ acknowledgedOffset = lastBatchOffset;
716
+ stateChanged = true;
717
+ } catch (error) {
718
+ const message = error instanceof Error ? error.message : String(error);
719
+ warnCloudSync(logger, `Telemetry sync failed: ${message}`);
720
+ syncFailed = true;
721
+ break;
722
+ }
723
+ }
724
+ setSourceOffset(nextState, source, acknowledgedOffset);
725
+ if (syncFailed) {
726
+ break;
727
+ }
728
+ }
729
+ if (stateChanged) {
730
+ if (!syncFailed) {
731
+ nextState.lastSyncAtMs = nowMs;
732
+ }
733
+ saveCloudSyncState(statePath, nextState);
734
+ } else if (!syncFailed) {
735
+ nextState.lastSyncAtMs = nowMs;
736
+ saveCloudSyncState(statePath, nextState);
737
+ }
738
+ return {
739
+ recordsRead,
740
+ recordsSent,
741
+ malformedLines,
742
+ batchesAttempted,
743
+ batchesSucceeded,
744
+ ...syncFailed ? { skippedReason: "sync-failed" } : {}
745
+ };
746
+ }
747
+
748
+ // ../core/dist/spawn-policy-resolver.js
749
+ function emitMethodologyTelemetry(config, policy) {
750
+ if (!config.telemetry?.methodology?.enabled) {
751
+ return;
752
+ }
753
+ const event = {
754
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
755
+ event_type: "methodology.selection",
756
+ methodology_testing: policy.testing,
757
+ methodology_architecture: policy.architecture,
758
+ event_context: "spawn"
759
+ };
760
+ emit(LUMENFLOW_PATHS.METHODOLOGY_LOG, event);
761
+ }
762
+
763
+ export {
764
+ emit,
765
+ emitGateEvent,
766
+ getCurrentWU,
767
+ getCurrentLane,
768
+ emitWUFlowEvent,
769
+ emitDoraTelemetry,
770
+ syncNdjsonTelemetryToCloud,
771
+ emitMethodologyTelemetry
772
+ };