@tiangong-lca/cli 0.0.1

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 (96) hide show
  1. package/README.md +368 -0
  2. package/bin/tiangong.d.ts +1 -0
  3. package/bin/tiangong.js +32 -0
  4. package/dist/scripts/assert-full-coverage.js +77 -0
  5. package/dist/scripts/assert-full-coverage.js.map +1 -0
  6. package/dist/src/cli.js +2740 -0
  7. package/dist/src/cli.js.map +1 -0
  8. package/dist/src/lib/artifacts.js +86 -0
  9. package/dist/src/lib/artifacts.js.map +1 -0
  10. package/dist/src/lib/dotenv.js +36 -0
  11. package/dist/src/lib/dotenv.js.map +1 -0
  12. package/dist/src/lib/env.js +139 -0
  13. package/dist/src/lib/env.js.map +1 -0
  14. package/dist/src/lib/errors.js +38 -0
  15. package/dist/src/lib/errors.js.map +1 -0
  16. package/dist/src/lib/flow-build-alias-map.js +299 -0
  17. package/dist/src/lib/flow-build-alias-map.js.map +1 -0
  18. package/dist/src/lib/flow-get.js +81 -0
  19. package/dist/src/lib/flow-get.js.map +1 -0
  20. package/dist/src/lib/flow-governance.js +164 -0
  21. package/dist/src/lib/flow-governance.js.map +1 -0
  22. package/dist/src/lib/flow-list.js +187 -0
  23. package/dist/src/lib/flow-list.js.map +1 -0
  24. package/dist/src/lib/flow-publish-reviewed-data.js +808 -0
  25. package/dist/src/lib/flow-publish-reviewed-data.js.map +1 -0
  26. package/dist/src/lib/flow-publish-version.js +541 -0
  27. package/dist/src/lib/flow-publish-version.js.map +1 -0
  28. package/dist/src/lib/flow-read.js +310 -0
  29. package/dist/src/lib/flow-read.js.map +1 -0
  30. package/dist/src/lib/flow-regen-product.js +1396 -0
  31. package/dist/src/lib/flow-regen-product.js.map +1 -0
  32. package/dist/src/lib/flow-remediate.js +821 -0
  33. package/dist/src/lib/flow-remediate.js.map +1 -0
  34. package/dist/src/lib/http.js +45 -0
  35. package/dist/src/lib/http.js.map +1 -0
  36. package/dist/src/lib/io.js +30 -0
  37. package/dist/src/lib/io.js.map +1 -0
  38. package/dist/src/lib/kb-search.js +83 -0
  39. package/dist/src/lib/kb-search.js.map +1 -0
  40. package/dist/src/lib/lifecyclemodel-auto-build.js +1328 -0
  41. package/dist/src/lib/lifecyclemodel-auto-build.js.map +1 -0
  42. package/dist/src/lib/lifecyclemodel-orchestrate.js +1556 -0
  43. package/dist/src/lib/lifecyclemodel-orchestrate.js.map +1 -0
  44. package/dist/src/lib/lifecyclemodel-publish-build.js +296 -0
  45. package/dist/src/lib/lifecyclemodel-publish-build.js.map +1 -0
  46. package/dist/src/lib/lifecyclemodel-publish-resulting-process.js +101 -0
  47. package/dist/src/lib/lifecyclemodel-publish-resulting-process.js.map +1 -0
  48. package/dist/src/lib/lifecyclemodel-resulting-process.js +1152 -0
  49. package/dist/src/lib/lifecyclemodel-resulting-process.js.map +1 -0
  50. package/dist/src/lib/lifecyclemodel-validate-build.js +238 -0
  51. package/dist/src/lib/lifecyclemodel-validate-build.js.map +1 -0
  52. package/dist/src/lib/llm.js +383 -0
  53. package/dist/src/lib/llm.js.map +1 -0
  54. package/dist/src/lib/process-auto-build.js +717 -0
  55. package/dist/src/lib/process-auto-build.js.map +1 -0
  56. package/dist/src/lib/process-batch-build.js +409 -0
  57. package/dist/src/lib/process-batch-build.js.map +1 -0
  58. package/dist/src/lib/process-get.js +66 -0
  59. package/dist/src/lib/process-get.js.map +1 -0
  60. package/dist/src/lib/process-publish-build.js +451 -0
  61. package/dist/src/lib/process-publish-build.js.map +1 -0
  62. package/dist/src/lib/process-resume-build.js +364 -0
  63. package/dist/src/lib/process-resume-build.js.map +1 -0
  64. package/dist/src/lib/publish.js +562 -0
  65. package/dist/src/lib/publish.js.map +1 -0
  66. package/dist/src/lib/remote.js +88 -0
  67. package/dist/src/lib/remote.js.map +1 -0
  68. package/dist/src/lib/review-flow.js +1110 -0
  69. package/dist/src/lib/review-flow.js.map +1 -0
  70. package/dist/src/lib/review-lifecyclemodel.js +727 -0
  71. package/dist/src/lib/review-lifecyclemodel.js.map +1 -0
  72. package/dist/src/lib/review-process.js +702 -0
  73. package/dist/src/lib/review-process.js.map +1 -0
  74. package/dist/src/lib/run.js +106 -0
  75. package/dist/src/lib/run.js.map +1 -0
  76. package/dist/src/lib/state-lock.js +150 -0
  77. package/dist/src/lib/state-lock.js.map +1 -0
  78. package/dist/src/lib/supabase-client.js +269 -0
  79. package/dist/src/lib/supabase-client.js.map +1 -0
  80. package/dist/src/lib/supabase-json-ordered-write.js +186 -0
  81. package/dist/src/lib/supabase-json-ordered-write.js.map +1 -0
  82. package/dist/src/lib/supabase-rest.js +138 -0
  83. package/dist/src/lib/supabase-rest.js.map +1 -0
  84. package/dist/src/lib/supabase-session.js +424 -0
  85. package/dist/src/lib/supabase-session.js.map +1 -0
  86. package/dist/src/lib/tidas-sdk-package-validator.js +493 -0
  87. package/dist/src/lib/tidas-sdk-package-validator.js.map +1 -0
  88. package/dist/src/lib/unstructured.js +203 -0
  89. package/dist/src/lib/unstructured.js.map +1 -0
  90. package/dist/src/lib/user-api-key.js +75 -0
  91. package/dist/src/lib/user-api-key.js.map +1 -0
  92. package/dist/src/lib/validation.js +191 -0
  93. package/dist/src/lib/validation.js.map +1 -0
  94. package/dist/src/main.js +38 -0
  95. package/dist/src/main.js.map +1 -0
  96. package/package.json +81 -0
@@ -0,0 +1,383 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { resolveEnv } from './env.js';
5
+ import { CliError } from './errors.js';
6
+ import { readJsonArtifact, writeJsonArtifact, writeJsonLinesArtifact } from './artifacts.js';
7
+ export const LLM_ENV_KEYS = {
8
+ baseUrl: 'TIANGONG_LCA_REVIEW_LLM_BASE_URL',
9
+ apiKey: 'TIANGONG_LCA_REVIEW_LLM_API_KEY',
10
+ model: 'TIANGONG_LCA_REVIEW_LLM_MODEL',
11
+ };
12
+ export const LLM_ENV_SPECS = [
13
+ {
14
+ key: LLM_ENV_KEYS.baseUrl,
15
+ required: true,
16
+ description: 'Review LLM OpenAI-compatible Responses API base URL',
17
+ },
18
+ {
19
+ key: LLM_ENV_KEYS.apiKey,
20
+ required: true,
21
+ description: 'LLM API key',
22
+ },
23
+ {
24
+ key: LLM_ENV_KEYS.model,
25
+ required: true,
26
+ description: 'LLM model identifier',
27
+ },
28
+ ];
29
+ function defaultSleep(ms) {
30
+ return new Promise((resolve) => {
31
+ setTimeout(resolve, ms);
32
+ });
33
+ }
34
+ function requireLlmRuntime(env) {
35
+ if (!env.baseUrl) {
36
+ throw new CliError(`Missing LLM base URL. Set ${LLM_ENV_KEYS.baseUrl}.`, {
37
+ code: 'LLM_BASE_URL_REQUIRED',
38
+ exitCode: 2,
39
+ });
40
+ }
41
+ if (!env.apiKey) {
42
+ throw new CliError(`Missing LLM API key. Set ${LLM_ENV_KEYS.apiKey}.`, {
43
+ code: 'LLM_API_KEY_REQUIRED',
44
+ exitCode: 2,
45
+ });
46
+ }
47
+ if (!env.model) {
48
+ throw new CliError(`Missing LLM model. Set ${LLM_ENV_KEYS.model}.`, {
49
+ code: 'LLM_MODEL_REQUIRED',
50
+ exitCode: 2,
51
+ });
52
+ }
53
+ return {
54
+ baseUrl: env.baseUrl,
55
+ apiKey: env.apiKey,
56
+ model: env.model,
57
+ };
58
+ }
59
+ function buildLlmUrl(baseUrl) {
60
+ return `${baseUrl.replace(/\/+$/u, '')}/responses`;
61
+ }
62
+ function normalizeContext(context) {
63
+ if (context === undefined || context === null) {
64
+ return '';
65
+ }
66
+ if (typeof context === 'string') {
67
+ return context;
68
+ }
69
+ return JSON.stringify(context);
70
+ }
71
+ function buildTextOptions(input) {
72
+ const options = { ...(input.text ?? {}) };
73
+ if (input.responseFormat !== undefined) {
74
+ options.format = input.responseFormat;
75
+ }
76
+ return Object.keys(options).length ? options : undefined;
77
+ }
78
+ function buildRequestBody(model, input) {
79
+ const textOptions = buildTextOptions(input);
80
+ const body = {
81
+ model,
82
+ input: [
83
+ {
84
+ role: 'system',
85
+ content: [{ type: 'input_text', text: input.prompt }],
86
+ },
87
+ {
88
+ role: 'user',
89
+ content: [{ type: 'input_text', text: normalizeContext(input.context) }],
90
+ },
91
+ ],
92
+ };
93
+ if (textOptions) {
94
+ body.text = textOptions;
95
+ }
96
+ return body;
97
+ }
98
+ function hashRequest(baseUrl, body) {
99
+ return createHash('sha256').update(JSON.stringify({ baseUrl, body })).digest('hex');
100
+ }
101
+ function buildCachePath(cacheDir, promptHash) {
102
+ if (!cacheDir) {
103
+ return null;
104
+ }
105
+ return path.join(cacheDir, `${promptHash}.json`);
106
+ }
107
+ function getUsageValue(value, key) {
108
+ if (value && typeof value === 'object' && key in value) {
109
+ return value[key];
110
+ }
111
+ return undefined;
112
+ }
113
+ function coerceInteger(value) {
114
+ if (typeof value === 'number' && Number.isFinite(value)) {
115
+ return Math.trunc(value);
116
+ }
117
+ if (typeof value === 'string' && value.trim()) {
118
+ const parsed = Number(value);
119
+ if (Number.isFinite(parsed)) {
120
+ return Math.trunc(parsed);
121
+ }
122
+ }
123
+ return undefined;
124
+ }
125
+ export function extractLlmOutput(payload) {
126
+ if (payload && typeof payload === 'object') {
127
+ const directText = payload.output_text;
128
+ if (typeof directText === 'string' && directText) {
129
+ return directText;
130
+ }
131
+ const output = payload.output;
132
+ if (Array.isArray(output)) {
133
+ const parts = [];
134
+ output.forEach((item) => {
135
+ if (!item || typeof item !== 'object') {
136
+ return;
137
+ }
138
+ if (item.type !== 'message') {
139
+ return;
140
+ }
141
+ const content = item.content;
142
+ if (!Array.isArray(content)) {
143
+ return;
144
+ }
145
+ content.forEach((entry) => {
146
+ if (!entry || typeof entry !== 'object') {
147
+ return;
148
+ }
149
+ if (entry.type !== 'output_text') {
150
+ return;
151
+ }
152
+ const text = entry.text;
153
+ if (typeof text === 'string' && text) {
154
+ parts.push(text);
155
+ }
156
+ });
157
+ });
158
+ return parts.join('\n');
159
+ }
160
+ }
161
+ return '';
162
+ }
163
+ export function extractLlmUsage(payload) {
164
+ if (!payload || typeof payload !== 'object') {
165
+ return null;
166
+ }
167
+ const usageObject = getUsageValue(payload, 'usage');
168
+ if (!usageObject || typeof usageObject !== 'object') {
169
+ return null;
170
+ }
171
+ const usage = {};
172
+ const inputTokens = coerceInteger(getUsageValue(usageObject, 'input_tokens'));
173
+ const outputTokens = coerceInteger(getUsageValue(usageObject, 'output_tokens'));
174
+ const totalTokens = coerceInteger(getUsageValue(usageObject, 'total_tokens'));
175
+ const inputDetails = getUsageValue(usageObject, 'input_tokens_details');
176
+ const outputDetails = getUsageValue(usageObject, 'output_tokens_details');
177
+ const cachedInputTokens = coerceInteger(getUsageValue(inputDetails, 'cached_tokens'));
178
+ const reasoningTokens = coerceInteger(getUsageValue(outputDetails, 'reasoning_tokens'));
179
+ if (inputTokens !== undefined) {
180
+ usage.inputTokens = inputTokens;
181
+ }
182
+ if (outputTokens !== undefined) {
183
+ usage.outputTokens = outputTokens;
184
+ }
185
+ if (totalTokens !== undefined) {
186
+ usage.totalTokens = totalTokens;
187
+ }
188
+ else if (inputTokens !== undefined && outputTokens !== undefined) {
189
+ usage.totalTokens = inputTokens + outputTokens;
190
+ }
191
+ if (cachedInputTokens !== undefined) {
192
+ usage.cachedInputTokens = cachedInputTokens;
193
+ }
194
+ if (reasoningTokens !== undefined) {
195
+ usage.reasoningTokens = reasoningTokens;
196
+ }
197
+ return Object.keys(usage).length ? usage : null;
198
+ }
199
+ function loadCachedResult(cachePath) {
200
+ const payload = readJsonArtifact(cachePath);
201
+ if (!payload || typeof payload !== 'object' || typeof payload.output !== 'string') {
202
+ throw new CliError(`LLM cache payload is invalid: ${cachePath}`, {
203
+ code: 'LLM_CACHE_INVALID',
204
+ exitCode: 2,
205
+ });
206
+ }
207
+ return {
208
+ output: payload.output,
209
+ usage: payload.usage ?? null,
210
+ cacheHit: true,
211
+ promptHash: path.basename(cachePath, '.json'),
212
+ };
213
+ }
214
+ function buildTraceRecord(options) {
215
+ const record = {
216
+ timestamp: options.now.toISOString(),
217
+ runId: options.runId?.trim() || 'unknown',
218
+ module: options.module?.trim() || 'unknown',
219
+ stage: options.stage?.trim() || 'unknown',
220
+ promptHash: options.promptHash,
221
+ model: options.model,
222
+ latencyMs: Math.round(options.latencyMs * 100) / 100,
223
+ cacheHit: options.cacheHit,
224
+ status: options.status,
225
+ };
226
+ if (options.usage) {
227
+ record.usage = options.usage;
228
+ }
229
+ if (options.error) {
230
+ record.error = options.error.slice(0, 500);
231
+ }
232
+ return record;
233
+ }
234
+ function writeTrace(tracePath, record) {
235
+ if (!tracePath) {
236
+ return;
237
+ }
238
+ writeJsonLinesArtifact(tracePath, record, { append: true });
239
+ }
240
+ function isRetryableStatus(status) {
241
+ return status === 408 || status === 429 || status >= 500;
242
+ }
243
+ function isJsonContentType(contentType) {
244
+ return (contentType ?? '').toLowerCase().includes('application/json');
245
+ }
246
+ async function requestLlm(url, body, options, apiKey) {
247
+ const response = await options.fetchImpl(url, {
248
+ method: 'POST',
249
+ headers: {
250
+ Authorization: `Bearer ${apiKey}`,
251
+ 'Content-Type': 'application/json',
252
+ },
253
+ body: JSON.stringify(body),
254
+ signal: AbortSignal.timeout(options.timeoutMs),
255
+ });
256
+ const rawText = await response.text();
257
+ const contentType = response.headers.get('content-type');
258
+ if (!isJsonContentType(contentType)) {
259
+ throw new CliError(`LLM response was not valid JSON for ${url}`, {
260
+ code: 'LLM_INVALID_JSON',
261
+ exitCode: 1,
262
+ details: rawText,
263
+ });
264
+ }
265
+ let payload;
266
+ try {
267
+ payload = JSON.parse(rawText);
268
+ }
269
+ catch (error) {
270
+ throw new CliError(`LLM response was not valid JSON for ${url}`, {
271
+ code: 'LLM_INVALID_JSON',
272
+ exitCode: 1,
273
+ details: String(error),
274
+ });
275
+ }
276
+ if (!response.ok) {
277
+ const httpError = new CliError(`LLM request failed with HTTP ${response.status} from ${url}`, {
278
+ code: 'LLM_REQUEST_FAILED',
279
+ exitCode: 1,
280
+ details: payload,
281
+ });
282
+ httpError.retryable = isRetryableStatus(response.status);
283
+ throw httpError;
284
+ }
285
+ return {
286
+ output: extractLlmOutput(payload),
287
+ usage: extractLlmUsage(payload),
288
+ };
289
+ }
290
+ export function readLlmRuntimeEnv(env) {
291
+ return {
292
+ baseUrl: resolveEnv(LLM_ENV_SPECS[0], env).value,
293
+ apiKey: resolveEnv(LLM_ENV_SPECS[1], env).value,
294
+ model: resolveEnv(LLM_ENV_SPECS[2], env).value,
295
+ };
296
+ }
297
+ export async function invokeLlm(options) {
298
+ const runtime = requireLlmRuntime(options.env);
299
+ const url = buildLlmUrl(runtime.baseUrl);
300
+ const body = buildRequestBody(runtime.model, options.input);
301
+ const promptHash = hashRequest(runtime.baseUrl, body);
302
+ const cachePath = buildCachePath(options.cacheDir, promptHash);
303
+ const startedAt = process.hrtime.bigint();
304
+ const now = options.now ?? (() => new Date());
305
+ if (cachePath && existsSync(cachePath)) {
306
+ const cachedResult = loadCachedResult(cachePath);
307
+ writeTrace(options.tracePath, buildTraceRecord({
308
+ now: now(),
309
+ runId: options.runId,
310
+ module: options.module,
311
+ stage: options.stage,
312
+ promptHash,
313
+ model: runtime.model,
314
+ latencyMs: Number(process.hrtime.bigint() - startedAt) / 1_000_000,
315
+ cacheHit: true,
316
+ status: 'ok',
317
+ usage: cachedResult.usage,
318
+ }));
319
+ return cachedResult;
320
+ }
321
+ const maxAttempts = Math.max(options.maxAttempts ?? 3, 1);
322
+ const sleep = options.sleep ?? defaultSleep;
323
+ const retryDelayMs = Math.max(options.retryDelayMs ?? 1_000, 0);
324
+ let lastError = new CliError('LLM invocation failed without a terminal result.', {
325
+ code: 'LLM_INVOCATION_INCOMPLETE',
326
+ exitCode: 1,
327
+ });
328
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
329
+ try {
330
+ const result = await requestLlm(url, body, options, runtime.apiKey);
331
+ if (cachePath) {
332
+ writeJsonArtifact(cachePath, {
333
+ output: result.output,
334
+ usage: result.usage,
335
+ });
336
+ }
337
+ writeTrace(options.tracePath, buildTraceRecord({
338
+ now: now(),
339
+ runId: options.runId,
340
+ module: options.module,
341
+ stage: options.stage,
342
+ promptHash,
343
+ model: runtime.model,
344
+ latencyMs: Number(process.hrtime.bigint() - startedAt) / 1_000_000,
345
+ cacheHit: false,
346
+ status: 'ok',
347
+ usage: result.usage,
348
+ }));
349
+ return {
350
+ output: result.output,
351
+ usage: result.usage,
352
+ cacheHit: false,
353
+ promptHash,
354
+ };
355
+ }
356
+ catch (error) {
357
+ lastError = error;
358
+ const retryable = Boolean(error instanceof CliError &&
359
+ 'retryable' in error &&
360
+ error.retryable);
361
+ if (attempt < maxAttempts - 1 && (!(error instanceof CliError) || retryable)) {
362
+ await sleep(retryDelayMs * (attempt + 1));
363
+ continue;
364
+ }
365
+ break;
366
+ }
367
+ }
368
+ writeTrace(options.tracePath, buildTraceRecord({
369
+ now: now(),
370
+ runId: options.runId,
371
+ module: options.module,
372
+ stage: options.stage,
373
+ promptHash,
374
+ model: runtime.model,
375
+ latencyMs: Number(process.hrtime.bigint() - startedAt) / 1_000_000,
376
+ cacheHit: false,
377
+ status: 'error',
378
+ usage: null,
379
+ error: String(lastError),
380
+ }));
381
+ throw lastError;
382
+ }
383
+ //# sourceMappingURL=llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.js","sourceRoot":"","sources":["../../../src/lib/llm.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport type { EnvSpec } from './env.js';\nimport { resolveEnv } from './env.js';\nimport { CliError } from './errors.js';\nimport type { FetchLike } from './http.js';\nimport { readJsonArtifact, writeJsonArtifact, writeJsonLinesArtifact } from './artifacts.js';\n\nexport const LLM_ENV_KEYS = {\n baseUrl: 'TIANGONG_LCA_REVIEW_LLM_BASE_URL',\n apiKey: 'TIANGONG_LCA_REVIEW_LLM_API_KEY',\n model: 'TIANGONG_LCA_REVIEW_LLM_MODEL',\n} as const;\n\nexport const LLM_ENV_SPECS: EnvSpec[] = [\n {\n key: LLM_ENV_KEYS.baseUrl,\n required: true,\n description: 'Review LLM OpenAI-compatible Responses API base URL',\n },\n {\n key: LLM_ENV_KEYS.apiKey,\n required: true,\n description: 'LLM API key',\n },\n {\n key: LLM_ENV_KEYS.model,\n required: true,\n description: 'LLM model identifier',\n },\n];\n\nexport type LlmRuntimeEnv = {\n baseUrl: string | null;\n apiKey: string | null;\n model: string | null;\n};\n\nexport type LlmUsage = {\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n cachedInputTokens?: number;\n reasoningTokens?: number;\n};\n\nexport type LlmInvocationInput = {\n prompt: string;\n context?: unknown;\n responseFormat?: unknown;\n text?: Record<string, unknown>;\n};\n\nexport type LlmInvocationResult = {\n output: string;\n usage: LlmUsage | null;\n cacheHit: boolean;\n promptHash: string;\n};\n\nexport type InvokeLlmOptions = {\n env: LlmRuntimeEnv;\n input: LlmInvocationInput;\n fetchImpl: FetchLike;\n timeoutMs: number;\n maxAttempts?: number;\n sleep?: (ms: number) => Promise<void>;\n retryDelayMs?: number;\n cacheDir?: string | null;\n tracePath?: string | null;\n runId?: string | null;\n module?: string;\n stage?: string;\n now?: () => Date;\n};\n\ntype SuccessfulLlmResponse = {\n output: string;\n usage: LlmUsage | null;\n};\n\ntype CachedLlmResult = {\n output: string;\n usage?: LlmUsage | null;\n};\n\ntype RequiredLlmRuntimeEnv = {\n baseUrl: string;\n apiKey: string;\n model: string;\n};\n\ntype LlmTraceRecord = {\n timestamp: string;\n runId: string;\n module: string;\n stage: string;\n promptHash: string;\n model: string;\n latencyMs: number;\n cacheHit: boolean;\n status: 'ok' | 'error';\n usage?: LlmUsage;\n error?: string;\n};\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nfunction requireLlmRuntime(env: LlmRuntimeEnv): RequiredLlmRuntimeEnv {\n if (!env.baseUrl) {\n throw new CliError(`Missing LLM base URL. Set ${LLM_ENV_KEYS.baseUrl}.`, {\n code: 'LLM_BASE_URL_REQUIRED',\n exitCode: 2,\n });\n }\n\n if (!env.apiKey) {\n throw new CliError(`Missing LLM API key. Set ${LLM_ENV_KEYS.apiKey}.`, {\n code: 'LLM_API_KEY_REQUIRED',\n exitCode: 2,\n });\n }\n\n if (!env.model) {\n throw new CliError(`Missing LLM model. Set ${LLM_ENV_KEYS.model}.`, {\n code: 'LLM_MODEL_REQUIRED',\n exitCode: 2,\n });\n }\n\n return {\n baseUrl: env.baseUrl,\n apiKey: env.apiKey,\n model: env.model,\n };\n}\n\nfunction buildLlmUrl(baseUrl: string): string {\n return `${baseUrl.replace(/\\/+$/u, '')}/responses`;\n}\n\nfunction normalizeContext(context: unknown): string {\n if (context === undefined || context === null) {\n return '';\n }\n\n if (typeof context === 'string') {\n return context;\n }\n\n return JSON.stringify(context);\n}\n\nfunction buildTextOptions(input: LlmInvocationInput): Record<string, unknown> | undefined {\n const options = { ...(input.text ?? {}) };\n if (input.responseFormat !== undefined) {\n options.format = input.responseFormat;\n }\n\n return Object.keys(options).length ? options : undefined;\n}\n\nfunction buildRequestBody(model: string, input: LlmInvocationInput): Record<string, unknown> {\n const textOptions = buildTextOptions(input);\n const body: Record<string, unknown> = {\n model,\n input: [\n {\n role: 'system',\n content: [{ type: 'input_text', text: input.prompt }],\n },\n {\n role: 'user',\n content: [{ type: 'input_text', text: normalizeContext(input.context) }],\n },\n ],\n };\n\n if (textOptions) {\n body.text = textOptions;\n }\n\n return body;\n}\n\nfunction hashRequest(baseUrl: string, body: Record<string, unknown>): string {\n return createHash('sha256').update(JSON.stringify({ baseUrl, body })).digest('hex');\n}\n\nfunction buildCachePath(cacheDir: string | null | undefined, promptHash: string): string | null {\n if (!cacheDir) {\n return null;\n }\n\n return path.join(cacheDir, `${promptHash}.json`);\n}\n\nfunction getUsageValue(value: unknown, key: string): unknown {\n if (value && typeof value === 'object' && key in value) {\n return (value as Record<string, unknown>)[key];\n }\n\n return undefined;\n}\n\nfunction coerceInteger(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return Math.trunc(value);\n }\n\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) {\n return Math.trunc(parsed);\n }\n }\n\n return undefined;\n}\n\nexport function extractLlmOutput(payload: unknown): string {\n if (payload && typeof payload === 'object') {\n const directText = (payload as { output_text?: unknown }).output_text;\n if (typeof directText === 'string' && directText) {\n return directText;\n }\n\n const output = (payload as { output?: unknown }).output;\n if (Array.isArray(output)) {\n const parts: string[] = [];\n output.forEach((item) => {\n if (!item || typeof item !== 'object') {\n return;\n }\n\n if ((item as { type?: unknown }).type !== 'message') {\n return;\n }\n\n const content = (item as { content?: unknown }).content;\n if (!Array.isArray(content)) {\n return;\n }\n\n content.forEach((entry) => {\n if (!entry || typeof entry !== 'object') {\n return;\n }\n\n if ((entry as { type?: unknown }).type !== 'output_text') {\n return;\n }\n\n const text = (entry as { text?: unknown }).text;\n if (typeof text === 'string' && text) {\n parts.push(text);\n }\n });\n });\n return parts.join('\\n');\n }\n }\n\n return '';\n}\n\nexport function extractLlmUsage(payload: unknown): LlmUsage | null {\n if (!payload || typeof payload !== 'object') {\n return null;\n }\n\n const usageObject = getUsageValue(payload, 'usage');\n if (!usageObject || typeof usageObject !== 'object') {\n return null;\n }\n\n const usage: LlmUsage = {};\n const inputTokens = coerceInteger(getUsageValue(usageObject, 'input_tokens'));\n const outputTokens = coerceInteger(getUsageValue(usageObject, 'output_tokens'));\n const totalTokens = coerceInteger(getUsageValue(usageObject, 'total_tokens'));\n const inputDetails = getUsageValue(usageObject, 'input_tokens_details');\n const outputDetails = getUsageValue(usageObject, 'output_tokens_details');\n const cachedInputTokens = coerceInteger(getUsageValue(inputDetails, 'cached_tokens'));\n const reasoningTokens = coerceInteger(getUsageValue(outputDetails, 'reasoning_tokens'));\n\n if (inputTokens !== undefined) {\n usage.inputTokens = inputTokens;\n }\n\n if (outputTokens !== undefined) {\n usage.outputTokens = outputTokens;\n }\n\n if (totalTokens !== undefined) {\n usage.totalTokens = totalTokens;\n } else if (inputTokens !== undefined && outputTokens !== undefined) {\n usage.totalTokens = inputTokens + outputTokens;\n }\n\n if (cachedInputTokens !== undefined) {\n usage.cachedInputTokens = cachedInputTokens;\n }\n\n if (reasoningTokens !== undefined) {\n usage.reasoningTokens = reasoningTokens;\n }\n\n return Object.keys(usage).length ? usage : null;\n}\n\nfunction loadCachedResult(cachePath: string): LlmInvocationResult {\n const payload = readJsonArtifact(cachePath) as CachedLlmResult;\n if (!payload || typeof payload !== 'object' || typeof payload.output !== 'string') {\n throw new CliError(`LLM cache payload is invalid: ${cachePath}`, {\n code: 'LLM_CACHE_INVALID',\n exitCode: 2,\n });\n }\n\n return {\n output: payload.output,\n usage: payload.usage ?? null,\n cacheHit: true,\n promptHash: path.basename(cachePath, '.json'),\n };\n}\n\nfunction buildTraceRecord(options: {\n now: Date;\n runId: string | null | undefined;\n module: string | undefined;\n stage: string | undefined;\n promptHash: string;\n model: string;\n latencyMs: number;\n cacheHit: boolean;\n status: 'ok' | 'error';\n usage: LlmUsage | null;\n error?: string;\n}): LlmTraceRecord {\n const record: LlmTraceRecord = {\n timestamp: options.now.toISOString(),\n runId: options.runId?.trim() || 'unknown',\n module: options.module?.trim() || 'unknown',\n stage: options.stage?.trim() || 'unknown',\n promptHash: options.promptHash,\n model: options.model,\n latencyMs: Math.round(options.latencyMs * 100) / 100,\n cacheHit: options.cacheHit,\n status: options.status,\n };\n\n if (options.usage) {\n record.usage = options.usage;\n }\n\n if (options.error) {\n record.error = options.error.slice(0, 500);\n }\n\n return record;\n}\n\nfunction writeTrace(tracePath: string | null | undefined, record: LlmTraceRecord): void {\n if (!tracePath) {\n return;\n }\n\n writeJsonLinesArtifact(tracePath, record, { append: true });\n}\n\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 429 || status >= 500;\n}\n\nfunction isJsonContentType(contentType: string | null): boolean {\n return (contentType ?? '').toLowerCase().includes('application/json');\n}\n\nasync function requestLlm(\n url: string,\n body: Record<string, unknown>,\n options: Pick<InvokeLlmOptions, 'fetchImpl' | 'timeoutMs'>,\n apiKey: string,\n): Promise<SuccessfulLlmResponse> {\n const response = await options.fetchImpl(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(options.timeoutMs),\n });\n\n const rawText = await response.text();\n const contentType = response.headers.get('content-type');\n\n if (!isJsonContentType(contentType)) {\n throw new CliError(`LLM response was not valid JSON for ${url}`, {\n code: 'LLM_INVALID_JSON',\n exitCode: 1,\n details: rawText,\n });\n }\n\n let payload: unknown;\n try {\n payload = JSON.parse(rawText);\n } catch (error) {\n throw new CliError(`LLM response was not valid JSON for ${url}`, {\n code: 'LLM_INVALID_JSON',\n exitCode: 1,\n details: String(error),\n });\n }\n\n if (!response.ok) {\n const httpError = new CliError(`LLM request failed with HTTP ${response.status} from ${url}`, {\n code: 'LLM_REQUEST_FAILED',\n exitCode: 1,\n details: payload,\n });\n (httpError as CliError & { retryable?: boolean }).retryable = isRetryableStatus(\n response.status,\n );\n throw httpError;\n }\n\n return {\n output: extractLlmOutput(payload),\n usage: extractLlmUsage(payload),\n };\n}\n\nexport function readLlmRuntimeEnv(env: NodeJS.ProcessEnv): LlmRuntimeEnv {\n return {\n baseUrl: resolveEnv(LLM_ENV_SPECS[0], env).value,\n apiKey: resolveEnv(LLM_ENV_SPECS[1], env).value,\n model: resolveEnv(LLM_ENV_SPECS[2], env).value,\n };\n}\n\nexport async function invokeLlm(options: InvokeLlmOptions): Promise<LlmInvocationResult> {\n const runtime = requireLlmRuntime(options.env);\n const url = buildLlmUrl(runtime.baseUrl);\n const body = buildRequestBody(runtime.model, options.input);\n const promptHash = hashRequest(runtime.baseUrl, body);\n const cachePath = buildCachePath(options.cacheDir, promptHash);\n const startedAt = process.hrtime.bigint();\n const now = options.now ?? (() => new Date());\n\n if (cachePath && existsSync(cachePath)) {\n const cachedResult = loadCachedResult(cachePath);\n writeTrace(\n options.tracePath,\n buildTraceRecord({\n now: now(),\n runId: options.runId,\n module: options.module,\n stage: options.stage,\n promptHash,\n model: runtime.model,\n latencyMs: Number(process.hrtime.bigint() - startedAt) / 1_000_000,\n cacheHit: true,\n status: 'ok',\n usage: cachedResult.usage,\n }),\n );\n return cachedResult;\n }\n\n const maxAttempts = Math.max(options.maxAttempts ?? 3, 1);\n const sleep = options.sleep ?? defaultSleep;\n const retryDelayMs = Math.max(options.retryDelayMs ?? 1_000, 0);\n let lastError: unknown = new CliError('LLM invocation failed without a terminal result.', {\n code: 'LLM_INVOCATION_INCOMPLETE',\n exitCode: 1,\n });\n\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n try {\n const result = await requestLlm(url, body, options, runtime.apiKey);\n if (cachePath) {\n writeJsonArtifact(cachePath, {\n output: result.output,\n usage: result.usage,\n });\n }\n\n writeTrace(\n options.tracePath,\n buildTraceRecord({\n now: now(),\n runId: options.runId,\n module: options.module,\n stage: options.stage,\n promptHash,\n model: runtime.model,\n latencyMs: Number(process.hrtime.bigint() - startedAt) / 1_000_000,\n cacheHit: false,\n status: 'ok',\n usage: result.usage,\n }),\n );\n return {\n output: result.output,\n usage: result.usage,\n cacheHit: false,\n promptHash,\n };\n } catch (error) {\n lastError = error;\n const retryable = Boolean(\n error instanceof CliError &&\n 'retryable' in error &&\n (error as CliError & { retryable?: boolean }).retryable,\n );\n\n if (attempt < maxAttempts - 1 && (!(error instanceof CliError) || retryable)) {\n await sleep(retryDelayMs * (attempt + 1));\n continue;\n }\n break;\n }\n }\n\n writeTrace(\n options.tracePath,\n buildTraceRecord({\n now: now(),\n runId: options.runId,\n module: options.module,\n stage: options.stage,\n promptHash,\n model: runtime.model,\n latencyMs: Number(process.hrtime.bigint() - startedAt) / 1_000_000,\n cacheHit: false,\n status: 'error',\n usage: null,\n error: String(lastError),\n }),\n );\n\n throw lastError;\n}\n"]}