@offerpilot/axiomruntime 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 (99) hide show
  1. package/README.md +185 -0
  2. package/dist/cli/commands/add.js +29 -0
  3. package/dist/cli/commands/context.js +29 -0
  4. package/dist/cli/commands/doctor.js +62 -0
  5. package/dist/cli/commands/edit.js +85 -0
  6. package/dist/cli/commands/help.js +16 -0
  7. package/dist/cli/commands/list.js +25 -0
  8. package/dist/cli/commands/log.js +63 -0
  9. package/dist/cli/commands/memory.js +241 -0
  10. package/dist/cli/commands/report.js +35 -0
  11. package/dist/cli/commands/session.js +74 -0
  12. package/dist/cli/commands/setup.js +86 -0
  13. package/dist/cli/commands/status.js +50 -0
  14. package/dist/cli/commands/telegram.js +701 -0
  15. package/dist/cli/commands/use.js +108 -0
  16. package/dist/cli/commands/version.js +12 -0
  17. package/dist/cli/index.js +276 -0
  18. package/dist/cli/output/table.js +22 -0
  19. package/dist/cli/prompts/prompt.js +37 -0
  20. package/dist/cli/registry.js +16 -0
  21. package/dist/core/config/cache-store.js +193 -0
  22. package/dist/core/config/json-store.js +114 -0
  23. package/dist/core/config/paths.js +85 -0
  24. package/dist/core/config/providers-store.js +89 -0
  25. package/dist/core/config/schema.js +60 -0
  26. package/dist/core/config/session-store.js +30 -0
  27. package/dist/core/config/usage-store.js +18 -0
  28. package/dist/core/context/context-service.js +186 -0
  29. package/dist/core/integrations/integration-state.js +105 -0
  30. package/dist/core/logs/log-service.js +56 -0
  31. package/dist/core/memory/embedding-check.js +121 -0
  32. package/dist/core/memory/memory-config.js +122 -0
  33. package/dist/core/models/model-discovery.js +430 -0
  34. package/dist/core/models/model-filter.js +13 -0
  35. package/dist/core/providers/provider-service.js +212 -0
  36. package/dist/core/reports/report-service.js +166 -0
  37. package/dist/core/runner/command-resolver.js +60 -0
  38. package/dist/core/runner/engine-registry.js +93 -0
  39. package/dist/core/runner/fallback.js +114 -0
  40. package/dist/core/runner/openai-usage-http.js +82 -0
  41. package/dist/core/runner/openai-usage-proxy.js +1 -0
  42. package/dist/core/runner/openai-usage-recording.js +172 -0
  43. package/dist/core/runner/openai-usage-responses.js +469 -0
  44. package/dist/core/runner/openai-usage-server.js +319 -0
  45. package/dist/core/runner/openai-usage-types.js +1 -0
  46. package/dist/core/runner/tool-runner.js +138 -0
  47. package/dist/core/sessions/session-service.js +47 -0
  48. package/dist/core/status/doctor-service.js +391 -0
  49. package/dist/core/status/status-service.js +60 -0
  50. package/dist/core/types.js +1 -0
  51. package/dist/core/usage/pricing.js +113 -0
  52. package/dist/core/usage/usage-service.js +30 -0
  53. package/dist/core/utils/is-record.js +3 -0
  54. package/dist/server/index.js +28 -0
  55. package/dist/server/runtime-server.js +430 -0
  56. package/dist/telegram/bot-registry.js +80 -0
  57. package/dist/telegram/bot.js +128 -0
  58. package/dist/telegram/config.js +235 -0
  59. package/dist/telegram/engine/claude-engine.js +240 -0
  60. package/dist/telegram/engine/codex-engine.js +437 -0
  61. package/dist/telegram/engine/engine-utils.js +67 -0
  62. package/dist/telegram/engine/process-utils.js +132 -0
  63. package/dist/telegram/engine/registry.js +31 -0
  64. package/dist/telegram/engine/types.js +1 -0
  65. package/dist/telegram/handler-registry.js +28 -0
  66. package/dist/telegram/handlers/callback.js +311 -0
  67. package/dist/telegram/handlers/command.js +272 -0
  68. package/dist/telegram/handlers/document.js +108 -0
  69. package/dist/telegram/handlers/memory.js +305 -0
  70. package/dist/telegram/handlers/message.js +701 -0
  71. package/dist/telegram/handlers/provider.js +332 -0
  72. package/dist/telegram/handlers/setup.js +527 -0
  73. package/dist/telegram/handlers/usage.js +124 -0
  74. package/dist/telegram/index.js +93 -0
  75. package/dist/telegram/interaction/approval.js +108 -0
  76. package/dist/telegram/interaction/command-menu.js +253 -0
  77. package/dist/telegram/interaction/formatter.js +487 -0
  78. package/dist/telegram/interaction/keyboards.js +145 -0
  79. package/dist/telegram/interaction/progress-reporter.js +160 -0
  80. package/dist/telegram/interaction/prompt-middleware.js +168 -0
  81. package/dist/telegram/interaction/result-store.js +41 -0
  82. package/dist/telegram/interaction/token-budget.js +21 -0
  83. package/dist/telegram/interaction/tool-name.js +41 -0
  84. package/dist/telegram/lifecycle-registry.js +47 -0
  85. package/dist/telegram/log.js +46 -0
  86. package/dist/telegram/memory/memory-inject.js +52 -0
  87. package/dist/telegram/memory/memory-service.js +413 -0
  88. package/dist/telegram/memory/memory-store.js +216 -0
  89. package/dist/telegram/memory/types.js +1 -0
  90. package/dist/telegram/network-retry.js +22 -0
  91. package/dist/telegram/network.js +53 -0
  92. package/dist/telegram/session/manager.js +229 -0
  93. package/dist/telegram/session/store.js +363 -0
  94. package/dist/telegram/session/types.js +1 -0
  95. package/dist/telegram/supervisor.js +57 -0
  96. package/dist/telegram/templates/messages.js +1 -0
  97. package/docs/README.md +98 -0
  98. package/docs/USAGE.html +853 -0
  99. package/package.json +57 -0
@@ -0,0 +1,469 @@
1
+ import { fetch } from "undici";
2
+ export function isResponsesStreamRequest(incomingUrl, requestBody) {
3
+ const local = new URL(incomingUrl, "http://127.0.0.1");
4
+ if (local.pathname !== "/responses" && local.pathname !== "/v1/responses")
5
+ return false;
6
+ const body = parseJsonObject(requestBody);
7
+ return body?.stream === true;
8
+ }
9
+ /**
10
+ * Decide whether a failed native `/responses` attempt should be retried by
11
+ * translating the request through chat completions. Only "the upstream does not
12
+ * speak the Responses protocol" cases fall back; transient/no-channel failures
13
+ * are passed through so provider rotation handles them instead of wasting a
14
+ * chat attempt that would fail the same way.
15
+ */
16
+ export function shouldFallbackResponsesToChat(outcome) {
17
+ const effectiveStatus = resolveEffectiveStatus(outcome);
18
+ if (effectiveStatus >= 200 && effectiveStatus < 300 && isEventStream(outcome.contentType)) {
19
+ return false;
20
+ }
21
+ if (isTransientStatus(effectiveStatus))
22
+ return false;
23
+ if (mentionsResponsesUnsupported(outcome.bodyText))
24
+ return true;
25
+ // Classic "this route/endpoint does not exist" from chat-only gateways.
26
+ return effectiveStatus === 400 || effectiveStatus === 404 || effectiveStatus === 405 || effectiveStatus === 501;
27
+ }
28
+ function resolveEffectiveStatus(outcome) {
29
+ // Many domestic new-api gateways wrap the real error in an HTTP 200 body,
30
+ // e.g. `{"error":{"message":"... status_code=400 ..."}}`. Trust the wrapped
31
+ // status when the transport status looks successful but the body is an error.
32
+ if (outcome.status >= 200 && outcome.status < 300 && !isEventStream(outcome.contentType)) {
33
+ const wrapped = extractWrappedStatus(outcome.bodyText);
34
+ if (wrapped !== undefined)
35
+ return wrapped;
36
+ }
37
+ return outcome.status;
38
+ }
39
+ function extractWrappedStatus(bodyText) {
40
+ const compact = bodyText.replace(/\s+/g, " ");
41
+ const match = compact.match(/status[_-]?code["']?\s*[:=]\s*["']?(\d{3})/i);
42
+ return match ? Number(match[1]) : undefined;
43
+ }
44
+ function mentionsResponsesUnsupported(bodyText) {
45
+ const message = bodyText.replace(/\s+/g, " ");
46
+ const rejection = "(?:不支持|不支援|does not support|doesn't support|not supported|unsupported|invalid|no such)";
47
+ const protocol = "(?:responses|/v1/responses|response api|responses api)";
48
+ return new RegExp(`${rejection}.{0,160}${protocol}`, "i").test(message)
49
+ || new RegExp(`${protocol}.{0,160}${rejection}`, "i").test(message);
50
+ }
51
+ function isEventStream(contentType) {
52
+ return contentType.toLowerCase().includes("text/event-stream");
53
+ }
54
+ function isTransientStatus(status) {
55
+ return status === 408 || status === 409 || status === 425 || status === 429
56
+ || status === 500 || status === 502 || status === 503 || status === 504
57
+ || (status >= 520 && status <= 527) || status === 530;
58
+ }
59
+ export async function buildChatCompletionsResponsesSse(input) {
60
+ const responsesRequest = parseJsonObject(input.requestBody);
61
+ if (!responsesRequest) {
62
+ return {
63
+ status: 400,
64
+ statusText: "Bad Request",
65
+ contentType: "application/json; charset=utf-8",
66
+ body: Buffer.from(JSON.stringify({ error: { message: "Invalid Responses request body." } }))
67
+ };
68
+ }
69
+ const chatResponse = await fetch(buildChatCompletionsUrl(input.upstreamBaseUrl), {
70
+ method: "POST",
71
+ headers: {
72
+ authorization: `Bearer ${input.apiKey}`,
73
+ "content-type": "application/json",
74
+ accept: "application/json"
75
+ },
76
+ body: JSON.stringify(buildChatCompletionsRequest(responsesRequest)),
77
+ dispatcher: input.dispatcher
78
+ });
79
+ const text = await chatResponse.text();
80
+ if (!chatResponse.ok) {
81
+ return {
82
+ status: chatResponse.status,
83
+ statusText: chatResponse.statusText,
84
+ contentType: chatResponse.headers.get("content-type") ?? "application/json; charset=utf-8",
85
+ body: Buffer.from(text)
86
+ };
87
+ }
88
+ const chatJson = parseJsonTextObject(text);
89
+ if (!chatJson) {
90
+ return {
91
+ status: 502,
92
+ statusText: "Bad Gateway",
93
+ contentType: "application/json; charset=utf-8",
94
+ body: Buffer.from(JSON.stringify({ error: { message: "Chat completions fallback returned invalid JSON." } }))
95
+ };
96
+ }
97
+ return {
98
+ status: 200,
99
+ statusText: "OK",
100
+ contentType: "text/event-stream; charset=utf-8",
101
+ body: Buffer.from(buildResponsesSseFromChatCompletion(chatJson, responsesRequest))
102
+ };
103
+ }
104
+ function buildChatCompletionsUrl(upstreamBaseUrl) {
105
+ return `${upstreamBaseUrl.replace(/\/+$/, "")}/chat/completions`;
106
+ }
107
+ function buildChatCompletionsRequest(responsesRequest) {
108
+ const request = {
109
+ model: typeof responsesRequest.model === "string" ? responsesRequest.model : undefined,
110
+ messages: buildChatMessages(responsesRequest),
111
+ stream: false
112
+ };
113
+ const tools = buildChatTools(responsesRequest.tools);
114
+ if (tools.length)
115
+ request.tools = tools;
116
+ const toolChoice = buildChatToolChoice(responsesRequest.tool_choice);
117
+ if (toolChoice !== undefined)
118
+ request.tool_choice = toolChoice;
119
+ for (const [sourceKey, targetKey] of [
120
+ ["parallel_tool_calls", "parallel_tool_calls"],
121
+ ["max_output_tokens", "max_tokens"],
122
+ ["temperature", "temperature"],
123
+ ["top_p", "top_p"]
124
+ ]) {
125
+ const value = responsesRequest[sourceKey];
126
+ if (value !== undefined && value !== null)
127
+ request[targetKey] = value;
128
+ }
129
+ return request;
130
+ }
131
+ function buildChatMessages(responsesRequest) {
132
+ const messages = [];
133
+ if (typeof responsesRequest.instructions === "string" && responsesRequest.instructions.trim()) {
134
+ messages.push({ role: "system", content: responsesRequest.instructions.trim() });
135
+ }
136
+ const input = responsesRequest.input;
137
+ if (typeof input === "string") {
138
+ messages.push({ role: "user", content: input });
139
+ }
140
+ else if (Array.isArray(input)) {
141
+ for (const item of input) {
142
+ const message = responsesItemToChatMessage(item);
143
+ if (message)
144
+ messages.push(message);
145
+ }
146
+ }
147
+ if (!messages.length) {
148
+ messages.push({ role: "user", content: "" });
149
+ }
150
+ return messages;
151
+ }
152
+ function responsesItemToChatMessage(item) {
153
+ if (!isPlainObject(item))
154
+ return null;
155
+ const type = typeof item.type === "string" ? item.type : "";
156
+ if (type === "function_call") {
157
+ const name = typeof item.name === "string" ? item.name : "";
158
+ if (!name)
159
+ return null;
160
+ return {
161
+ role: "assistant",
162
+ content: null,
163
+ tool_calls: [{
164
+ id: typeof item.call_id === "string" ? item.call_id : typeof item.id === "string" ? item.id : `call_${Date.now()}`,
165
+ type: "function",
166
+ function: {
167
+ name,
168
+ arguments: stringifyToolArguments(item.arguments)
169
+ }
170
+ }]
171
+ };
172
+ }
173
+ if (type === "function_call_output") {
174
+ return {
175
+ role: "tool",
176
+ tool_call_id: typeof item.call_id === "string" ? item.call_id : "",
177
+ content: contentToText(item.output)
178
+ };
179
+ }
180
+ const role = normalizeChatRole(item.role);
181
+ if (!role)
182
+ return null;
183
+ const content = contentToText(item.content);
184
+ if (!content && role !== "system")
185
+ return null;
186
+ return { role, content };
187
+ }
188
+ function normalizeChatRole(value) {
189
+ if (value === "developer" || value === "system")
190
+ return "system";
191
+ if (value === "user")
192
+ return "user";
193
+ if (value === "assistant")
194
+ return "assistant";
195
+ if (value === "tool")
196
+ return "tool";
197
+ return null;
198
+ }
199
+ function contentToText(value) {
200
+ if (typeof value === "string")
201
+ return value;
202
+ if (Array.isArray(value)) {
203
+ return value
204
+ .map((item) => {
205
+ if (typeof item === "string")
206
+ return item;
207
+ if (isPlainObject(item)) {
208
+ for (const key of ["text", "output_text", "input_text", "content", "output"]) {
209
+ const nested = item[key];
210
+ if (typeof nested === "string")
211
+ return nested;
212
+ }
213
+ }
214
+ return "";
215
+ })
216
+ .filter((item) => item.trim())
217
+ .join("\n\n");
218
+ }
219
+ if (value === undefined || value === null)
220
+ return "";
221
+ return typeof value === "object" ? JSON.stringify(value) : String(value);
222
+ }
223
+ function stringifyToolArguments(value) {
224
+ if (typeof value === "string")
225
+ return value;
226
+ if (value === undefined || value === null)
227
+ return "{}";
228
+ return JSON.stringify(value);
229
+ }
230
+ function buildChatTools(value) {
231
+ if (!Array.isArray(value))
232
+ return [];
233
+ const tools = [];
234
+ for (const tool of value) {
235
+ if (!isPlainObject(tool) || tool.type !== "function")
236
+ continue;
237
+ const functionSpec = isPlainObject(tool.function) ? tool.function : tool;
238
+ const name = typeof functionSpec.name === "string" ? functionSpec.name : "";
239
+ if (!name)
240
+ continue;
241
+ const next = {
242
+ type: "function",
243
+ function: {
244
+ name,
245
+ description: typeof functionSpec.description === "string" ? functionSpec.description : "",
246
+ parameters: isPlainObject(functionSpec.parameters) ? functionSpec.parameters : { type: "object", properties: {} }
247
+ }
248
+ };
249
+ const strict = functionSpec.strict ?? tool.strict;
250
+ if (typeof strict === "boolean" && isPlainObject(next.function)) {
251
+ next.function.strict = strict;
252
+ }
253
+ tools.push(next);
254
+ }
255
+ return tools;
256
+ }
257
+ function buildChatToolChoice(value) {
258
+ if (value === "auto" || value === "none" || value === "required")
259
+ return value;
260
+ if (!isPlainObject(value))
261
+ return undefined;
262
+ if (value.type === "function" && typeof value.name === "string") {
263
+ return { type: "function", function: { name: value.name } };
264
+ }
265
+ if (isPlainObject(value.function) && typeof value.function.name === "string") {
266
+ return { type: "function", function: { name: value.function.name } };
267
+ }
268
+ return undefined;
269
+ }
270
+ function buildResponsesSseFromChatCompletion(chatCompletion, responsesRequest) {
271
+ const model = typeof chatCompletion.model === "string"
272
+ ? chatCompletion.model
273
+ : typeof responsesRequest.model === "string"
274
+ ? responsesRequest.model
275
+ : "unknown";
276
+ const responseId = typeof chatCompletion.id === "string" ? chatCompletion.id.replace(/^chatcmpl-/, "resp_") : `resp_${Date.now()}`;
277
+ const outputItems = buildResponsesOutputItems(chatCompletion);
278
+ const startedResponse = buildResponsesResponse({
279
+ id: responseId,
280
+ model,
281
+ status: "in_progress",
282
+ output: [],
283
+ usage: null
284
+ });
285
+ const completedResponse = buildResponsesResponse({
286
+ id: responseId,
287
+ model,
288
+ status: "completed",
289
+ output: outputItems,
290
+ usage: convertChatUsage(chatCompletion.usage)
291
+ });
292
+ const chunks = [
293
+ writeSse("response.created", { type: "response.created", response: startedResponse }),
294
+ writeSse("response.in_progress", { type: "response.in_progress", response: startedResponse })
295
+ ];
296
+ outputItems.forEach((item, index) => {
297
+ if (item.type === "message") {
298
+ chunks.push(...buildMessageSseChunks(item, index));
299
+ }
300
+ else if (item.type === "function_call") {
301
+ chunks.push(...buildFunctionCallSseChunks(item, index));
302
+ }
303
+ });
304
+ chunks.push(writeSse("response.completed", { type: "response.completed", response: completedResponse }));
305
+ chunks.push("data: [DONE]\n\n");
306
+ return chunks.join("");
307
+ }
308
+ function buildResponsesOutputItems(chatCompletion) {
309
+ const choices = Array.isArray(chatCompletion.choices) ? chatCompletion.choices : [];
310
+ const firstChoice = choices.find(isPlainObject);
311
+ const message = isPlainObject(firstChoice?.message) ? firstChoice.message : {};
312
+ const output = [];
313
+ const content = typeof message.content === "string" ? message.content : "";
314
+ if (content) {
315
+ output.push({
316
+ id: `msg_${Date.now()}`,
317
+ type: "message",
318
+ status: "completed",
319
+ role: "assistant",
320
+ content: [{ type: "output_text", text: content, annotations: [] }]
321
+ });
322
+ }
323
+ if (Array.isArray(message.tool_calls)) {
324
+ message.tool_calls.forEach((toolCall, index) => {
325
+ if (!isPlainObject(toolCall) || !isPlainObject(toolCall.function))
326
+ return;
327
+ const name = typeof toolCall.function.name === "string" ? toolCall.function.name : "";
328
+ if (!name)
329
+ return;
330
+ output.push({
331
+ id: `fc_${Date.now()}_${index}`,
332
+ type: "function_call",
333
+ status: "completed",
334
+ call_id: typeof toolCall.id === "string" ? toolCall.id : `call_${Date.now()}_${index}`,
335
+ name,
336
+ arguments: stringifyToolArguments(toolCall.function.arguments)
337
+ });
338
+ });
339
+ }
340
+ return output;
341
+ }
342
+ function buildResponsesResponse(input) {
343
+ return {
344
+ id: input.id,
345
+ object: "response",
346
+ created_at: Math.floor(Date.now() / 1000),
347
+ status: input.status,
348
+ error: null,
349
+ incomplete_details: null,
350
+ instructions: null,
351
+ max_output_tokens: null,
352
+ model: input.model,
353
+ output: input.output,
354
+ parallel_tool_calls: true,
355
+ previous_response_id: null,
356
+ reasoning: { effort: null, summary: null },
357
+ store: false,
358
+ temperature: 1,
359
+ text: { format: { type: "text" } },
360
+ tool_choice: "auto",
361
+ tools: [],
362
+ top_p: 1,
363
+ truncation: "disabled",
364
+ usage: input.usage
365
+ };
366
+ }
367
+ function buildMessageSseChunks(item, outputIndex) {
368
+ const content = Array.isArray(item.content) ? item.content : [];
369
+ const firstPart = content.find(isPlainObject) ?? { type: "output_text", text: "", annotations: [] };
370
+ const text = typeof firstPart.text === "string" ? firstPart.text : "";
371
+ const itemId = typeof item.id === "string" ? item.id : `msg_${Date.now()}`;
372
+ const inProgressItem = { ...item, id: itemId, status: "in_progress", content: [] };
373
+ return [
374
+ writeSse("response.output_item.added", { type: "response.output_item.added", output_index: outputIndex, item: inProgressItem }),
375
+ writeSse("response.content_part.added", {
376
+ type: "response.content_part.added",
377
+ item_id: itemId,
378
+ output_index: outputIndex,
379
+ content_index: 0,
380
+ part: { type: "output_text", text: "", annotations: [] }
381
+ }),
382
+ writeSse("response.output_text.delta", {
383
+ type: "response.output_text.delta",
384
+ item_id: itemId,
385
+ output_index: outputIndex,
386
+ content_index: 0,
387
+ delta: text
388
+ }),
389
+ writeSse("response.output_text.done", {
390
+ type: "response.output_text.done",
391
+ item_id: itemId,
392
+ output_index: outputIndex,
393
+ content_index: 0,
394
+ text
395
+ }),
396
+ writeSse("response.content_part.done", {
397
+ type: "response.content_part.done",
398
+ item_id: itemId,
399
+ output_index: outputIndex,
400
+ content_index: 0,
401
+ part: firstPart
402
+ }),
403
+ writeSse("response.output_item.done", { type: "response.output_item.done", output_index: outputIndex, item })
404
+ ];
405
+ }
406
+ function buildFunctionCallSseChunks(item, outputIndex) {
407
+ const itemId = typeof item.id === "string" ? item.id : `fc_${Date.now()}`;
408
+ const args = typeof item.arguments === "string" ? item.arguments : "{}";
409
+ return [
410
+ writeSse("response.output_item.added", {
411
+ type: "response.output_item.added",
412
+ output_index: outputIndex,
413
+ item: { ...item, id: itemId, status: "in_progress", arguments: "" }
414
+ }),
415
+ writeSse("response.function_call_arguments.delta", {
416
+ type: "response.function_call_arguments.delta",
417
+ item_id: itemId,
418
+ output_index: outputIndex,
419
+ delta: args
420
+ }),
421
+ writeSse("response.function_call_arguments.done", {
422
+ type: "response.function_call_arguments.done",
423
+ item_id: itemId,
424
+ output_index: outputIndex,
425
+ arguments: args
426
+ }),
427
+ writeSse("response.output_item.done", { type: "response.output_item.done", output_index: outputIndex, item })
428
+ ];
429
+ }
430
+ function convertChatUsage(value) {
431
+ if (!isPlainObject(value))
432
+ return null;
433
+ const inputTokens = readNumber(value.prompt_tokens) ?? readNumber(value.input_tokens) ?? 0;
434
+ const outputTokens = readNumber(value.completion_tokens) ?? readNumber(value.output_tokens) ?? 0;
435
+ const totalTokens = readNumber(value.total_tokens) ?? inputTokens + outputTokens;
436
+ const promptDetails = isPlainObject(value.prompt_tokens_details) ? value.prompt_tokens_details : {};
437
+ const inputDetails = isPlainObject(value.input_tokens_details) ? value.input_tokens_details : {};
438
+ const cachedTokens = readNumber(promptDetails.cached_tokens) ?? readNumber(inputDetails.cached_tokens) ?? 0;
439
+ return {
440
+ input_tokens: inputTokens,
441
+ output_tokens: outputTokens,
442
+ total_tokens: totalTokens,
443
+ input_tokens_details: { cached_tokens: cachedTokens }
444
+ };
445
+ }
446
+ function readNumber(value) {
447
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
448
+ }
449
+ function writeSse(event, data) {
450
+ return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
451
+ }
452
+ function parseJsonObject(body) {
453
+ const text = body.toString("utf8").trim();
454
+ if (!text || !text.startsWith("{"))
455
+ return null;
456
+ return parseJsonTextObject(text);
457
+ }
458
+ function parseJsonTextObject(text) {
459
+ try {
460
+ const parsed = JSON.parse(text);
461
+ return isPlainObject(parsed) ? parsed : null;
462
+ }
463
+ catch {
464
+ return null;
465
+ }
466
+ }
467
+ function isPlainObject(value) {
468
+ return typeof value === "object" && value !== null && !Array.isArray(value);
469
+ }