@compyle/unagent 1.0.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/LICENSE +21 -0
  2. package/README.md +547 -0
  3. package/dist/agents/agent-as-tool.d.ts +4 -0
  4. package/dist/agents/agent-as-tool.d.ts.map +1 -0
  5. package/dist/agents/agent-as-tool.js +105 -0
  6. package/dist/agents/agent-as-tool.js.map +1 -0
  7. package/dist/agents/define-agent.d.ts +101 -0
  8. package/dist/agents/define-agent.d.ts.map +1 -0
  9. package/dist/agents/define-agent.js +866 -0
  10. package/dist/agents/define-agent.js.map +1 -0
  11. package/dist/agents/index.d.ts +3 -0
  12. package/dist/agents/index.d.ts.map +1 -0
  13. package/dist/agents/index.js +3 -0
  14. package/dist/agents/index.js.map +1 -0
  15. package/dist/bridge/node-bridge.d.ts +2 -0
  16. package/dist/bridge/node-bridge.d.ts.map +1 -0
  17. package/dist/bridge/node-bridge.js +772 -0
  18. package/dist/bridge/node-bridge.js.map +1 -0
  19. package/dist/client/base-client.d.ts +86 -0
  20. package/dist/client/base-client.d.ts.map +1 -0
  21. package/dist/client/base-client.js +254 -0
  22. package/dist/client/base-client.js.map +1 -0
  23. package/dist/client/claude.d.ts +66 -0
  24. package/dist/client/claude.d.ts.map +1 -0
  25. package/dist/client/claude.js +846 -0
  26. package/dist/client/claude.js.map +1 -0
  27. package/dist/client/codex/codex-client.d.ts +78 -0
  28. package/dist/client/codex/codex-client.d.ts.map +1 -0
  29. package/dist/client/codex/codex-client.js +906 -0
  30. package/dist/client/codex/codex-client.js.map +1 -0
  31. package/dist/client/codex/codex-home.d.ts +6 -0
  32. package/dist/client/codex/codex-home.d.ts.map +1 -0
  33. package/dist/client/codex/codex-home.js +26 -0
  34. package/dist/client/codex/codex-home.js.map +1 -0
  35. package/dist/client/codex/config/config-loader.d.ts +8 -0
  36. package/dist/client/codex/config/config-loader.d.ts.map +1 -0
  37. package/dist/client/codex/config/config-loader.js +137 -0
  38. package/dist/client/codex/config/config-loader.js.map +1 -0
  39. package/dist/client/codex/config/config-types.d.ts +43 -0
  40. package/dist/client/codex/config/config-types.d.ts.map +1 -0
  41. package/dist/client/codex/config/config-types.js +2 -0
  42. package/dist/client/codex/config/config-types.js.map +1 -0
  43. package/dist/client/codex/config/config-writer.d.ts +24 -0
  44. package/dist/client/codex/config/config-writer.d.ts.map +1 -0
  45. package/dist/client/codex/config/config-writer.js +353 -0
  46. package/dist/client/codex/config/config-writer.js.map +1 -0
  47. package/dist/client/codex/config/index.d.ts +7 -0
  48. package/dist/client/codex/config/index.d.ts.map +1 -0
  49. package/dist/client/codex/config/index.js +6 -0
  50. package/dist/client/codex/config/index.js.map +1 -0
  51. package/dist/client/codex/config/mcp-config-generator.d.ts +7 -0
  52. package/dist/client/codex/config/mcp-config-generator.d.ts.map +1 -0
  53. package/dist/client/codex/config/mcp-config-generator.js +140 -0
  54. package/dist/client/codex/config/mcp-config-generator.js.map +1 -0
  55. package/dist/client/codex/config/tools-config-writer.d.ts +2 -0
  56. package/dist/client/codex/config/tools-config-writer.d.ts.map +1 -0
  57. package/dist/client/codex/config/tools-config-writer.js +21 -0
  58. package/dist/client/codex/config/tools-config-writer.js.map +1 -0
  59. package/dist/client/codex/index.d.ts +4 -0
  60. package/dist/client/codex/index.d.ts.map +1 -0
  61. package/dist/client/codex/index.js +3 -0
  62. package/dist/client/codex/index.js.map +1 -0
  63. package/dist/client/config-utils.d.ts +4 -0
  64. package/dist/client/config-utils.d.ts.map +1 -0
  65. package/dist/client/config-utils.js +32 -0
  66. package/dist/client/config-utils.js.map +1 -0
  67. package/dist/client/extra-headers.d.ts +12 -0
  68. package/dist/client/extra-headers.d.ts.map +1 -0
  69. package/dist/client/extra-headers.js +102 -0
  70. package/dist/client/extra-headers.js.map +1 -0
  71. package/dist/client/field-mappings.d.ts +3 -0
  72. package/dist/client/field-mappings.d.ts.map +1 -0
  73. package/dist/client/field-mappings.js +40 -0
  74. package/dist/client/field-mappings.js.map +1 -0
  75. package/dist/client/transformers/content-utils.d.ts +34 -0
  76. package/dist/client/transformers/content-utils.d.ts.map +1 -0
  77. package/dist/client/transformers/content-utils.js +237 -0
  78. package/dist/client/transformers/content-utils.js.map +1 -0
  79. package/dist/events/event-bus.d.ts +19 -0
  80. package/dist/events/event-bus.d.ts.map +1 -0
  81. package/dist/events/event-bus.js +134 -0
  82. package/dist/events/event-bus.js.map +1 -0
  83. package/dist/events/event-types.d.ts +53 -0
  84. package/dist/events/event-types.d.ts.map +1 -0
  85. package/dist/events/event-types.js +6 -0
  86. package/dist/events/event-types.js.map +1 -0
  87. package/dist/events/index.d.ts +4 -0
  88. package/dist/events/index.d.ts.map +1 -0
  89. package/dist/events/index.js +4 -0
  90. package/dist/events/index.js.map +1 -0
  91. package/dist/index.d.ts +12 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +11 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/messages/index.d.ts +3 -0
  96. package/dist/messages/index.d.ts.map +1 -0
  97. package/dist/messages/index.js +3 -0
  98. package/dist/messages/index.js.map +1 -0
  99. package/dist/messages/message-types.d.ts +60 -0
  100. package/dist/messages/message-types.d.ts.map +1 -0
  101. package/dist/messages/message-types.js +6 -0
  102. package/dist/messages/message-types.js.map +1 -0
  103. package/dist/messages/message-utils.d.ts +51 -0
  104. package/dist/messages/message-utils.d.ts.map +1 -0
  105. package/dist/messages/message-utils.js +343 -0
  106. package/dist/messages/message-utils.js.map +1 -0
  107. package/dist/tools/define-tool.d.ts +6 -0
  108. package/dist/tools/define-tool.d.ts.map +1 -0
  109. package/dist/tools/define-tool.js +83 -0
  110. package/dist/tools/define-tool.js.map +1 -0
  111. package/dist/tools/index.d.ts +6 -0
  112. package/dist/tools/index.d.ts.map +1 -0
  113. package/dist/tools/index.js +6 -0
  114. package/dist/tools/index.js.map +1 -0
  115. package/dist/tools/mcp-stdio-proxy.d.ts +2 -0
  116. package/dist/tools/mcp-stdio-proxy.d.ts.map +1 -0
  117. package/dist/tools/mcp-stdio-proxy.js +31 -0
  118. package/dist/tools/mcp-stdio-proxy.js.map +1 -0
  119. package/dist/tools/tool-call-context.d.ts +8 -0
  120. package/dist/tools/tool-call-context.d.ts.map +1 -0
  121. package/dist/tools/tool-call-context.js +44 -0
  122. package/dist/tools/tool-call-context.js.map +1 -0
  123. package/dist/tools/tool-registry.d.ts +14 -0
  124. package/dist/tools/tool-registry.d.ts.map +1 -0
  125. package/dist/tools/tool-registry.js +47 -0
  126. package/dist/tools/tool-registry.js.map +1 -0
  127. package/dist/tools/tool-result-converter.d.ts +3 -0
  128. package/dist/tools/tool-result-converter.d.ts.map +1 -0
  129. package/dist/tools/tool-result-converter.js +38 -0
  130. package/dist/tools/tool-result-converter.js.map +1 -0
  131. package/dist/tools/tool-service-manager.d.ts +12 -0
  132. package/dist/tools/tool-service-manager.d.ts.map +1 -0
  133. package/dist/tools/tool-service-manager.js +118 -0
  134. package/dist/tools/tool-service-manager.js.map +1 -0
  135. package/dist/tools/tool-stdio-mcp-service.d.ts +29 -0
  136. package/dist/tools/tool-stdio-mcp-service.d.ts.map +1 -0
  137. package/dist/tools/tool-stdio-mcp-service.js +185 -0
  138. package/dist/tools/tool-stdio-mcp-service.js.map +1 -0
  139. package/dist/tools/tool-types.d.ts +29 -0
  140. package/dist/tools/tool-types.d.ts.map +1 -0
  141. package/dist/tools/tool-types.js +2 -0
  142. package/dist/tools/tool-types.js.map +1 -0
  143. package/package.json +58 -0
@@ -0,0 +1,772 @@
1
+ import { createHash, randomUUID } from "crypto";
2
+ import { createInterface } from "readline";
3
+ import { once } from "events";
4
+ import { ClaudeClient } from "../client/claude.js";
5
+ import { CodexClient } from "../client/codex/index.js";
6
+ import { convertSnakeCaseToCamelCase } from "../client/config-utils.js";
7
+ import { CLAUDE_FIELD_MAPPINGS, CODEX_FIELD_MAPPINGS } from "../client/field-mappings.js";
8
+ import { applyCodexExtraHeaders, mergeHeaderRecords, normalizeExtraHeaders, resolveEnvHeaders, serializeAnthropicHeaders, } from "../client/extra-headers.js";
9
+ import { resolveCodexHomeDir } from "../client/codex/codex-home.js";
10
+ import { toolRegistry } from "../tools/tool-registry.js";
11
+ import { z } from "zod";
12
+ const CUSTOM_TOOLS_MCP_NAME = "custom-tools";
13
+ const HASH_EXCLUDED_KEYS = new Set([
14
+ "resume",
15
+ "resumesessionid",
16
+ "resume_session_id",
17
+ "sessionid",
18
+ "session_id",
19
+ "threadid",
20
+ "thread_id",
21
+ ]);
22
+ let activeClient = null;
23
+ let abortTimer = null;
24
+ let stdinInterface = null;
25
+ let pythonToolInvoker = null;
26
+ function requestAbort() {
27
+ if (!activeClient) {
28
+ process.exit(1);
29
+ return;
30
+ }
31
+ activeClient.abort();
32
+ if (!abortTimer) {
33
+ abortTimer = setTimeout(() => {
34
+ process.exit(1);
35
+ }, 5000);
36
+ }
37
+ }
38
+ process.on("SIGINT", requestAbort);
39
+ process.on("SIGTERM", requestAbort);
40
+ function serializeError(error) {
41
+ if (error instanceof Error) {
42
+ return {
43
+ name: error.name,
44
+ message: error.message,
45
+ stack: error.stack,
46
+ };
47
+ }
48
+ return {
49
+ name: "Error",
50
+ message: String(error),
51
+ };
52
+ }
53
+ function serializeEvent(event) {
54
+ if (event.type === "error") {
55
+ return {
56
+ ...event,
57
+ error: serializeError(event.error),
58
+ };
59
+ }
60
+ return event;
61
+ }
62
+ function toCallToolResult(result) {
63
+ if (result && typeof result === "object" && "content" in result) {
64
+ const contentVal = result.content;
65
+ const isError = Boolean(result.isError);
66
+ if (Array.isArray(contentVal)) {
67
+ return { content: contentVal, ...(isError ? { isError } : {}) };
68
+ }
69
+ if (typeof contentVal === "string") {
70
+ return {
71
+ content: [{ type: "text", text: contentVal }],
72
+ ...(isError ? { isError } : {}),
73
+ };
74
+ }
75
+ }
76
+ if (Array.isArray(result)) {
77
+ return { content: result };
78
+ }
79
+ if (typeof result === "string") {
80
+ return { content: [{ type: "text", text: result }] };
81
+ }
82
+ return { content: [{ type: "text", text: String(result) }] };
83
+ }
84
+ class PythonToolInvoker {
85
+ send;
86
+ pending = new Map();
87
+ constructor(send) {
88
+ this.send = send;
89
+ }
90
+ call(toolName, args) {
91
+ const requestId = randomUUID();
92
+ this.send({
93
+ type: "bridge.python_tool_call",
94
+ requestId,
95
+ tool: toolName,
96
+ args,
97
+ });
98
+ return new Promise((resolve, reject) => {
99
+ this.pending.set(requestId, { resolve, reject });
100
+ });
101
+ }
102
+ handleResult(payload) {
103
+ const entry = this.pending.get(payload.requestId);
104
+ if (!entry)
105
+ return;
106
+ this.pending.delete(payload.requestId);
107
+ if (payload.type === "bridge.python_tool_error" || payload.error) {
108
+ entry.reject(new Error(payload.error?.message ?? "Python tool error"));
109
+ return;
110
+ }
111
+ entry.resolve(payload.result);
112
+ }
113
+ shutdown() {
114
+ for (const [requestId, entry] of this.pending) {
115
+ entry.reject(new Error(`Python tool request ${requestId} cancelled`));
116
+ }
117
+ this.pending.clear();
118
+ }
119
+ }
120
+ function normalizePythonInputSchema(schema) {
121
+ if (!schema || typeof schema !== "object") {
122
+ return undefined;
123
+ }
124
+ if (isZodSchema(schema)) {
125
+ return schema;
126
+ }
127
+ const schemaRecord = schema;
128
+ const isObjectSchema = schemaRecord.type === "object" || "properties" in schemaRecord;
129
+ if (isObjectSchema) {
130
+ return buildObjectSchema(schemaRecord);
131
+ }
132
+ return buildSchema(schemaRecord);
133
+ }
134
+ function buildObjectSchema(schema) {
135
+ const properties = isRecord(schema.properties) ? schema.properties : {};
136
+ const required = new Set(Array.isArray(schema.required)
137
+ ? schema.required.filter((item) => typeof item === "string")
138
+ : []);
139
+ const shape = {};
140
+ for (const [name, propertySchema] of Object.entries(properties)) {
141
+ let fieldSchema = buildSchema(asRecord(propertySchema));
142
+ if (!required.has(name)) {
143
+ fieldSchema = fieldSchema.optional();
144
+ }
145
+ shape[name] = fieldSchema;
146
+ }
147
+ let objectSchema = z.object(shape);
148
+ if (schema.additionalProperties === false) {
149
+ objectSchema = objectSchema.strict();
150
+ }
151
+ else if (isRecord(schema.additionalProperties)) {
152
+ objectSchema = objectSchema.catchall(buildSchema(schema.additionalProperties));
153
+ }
154
+ return objectSchema;
155
+ }
156
+ function buildSchema(schema) {
157
+ if (schema.const !== undefined) {
158
+ return isLiteralValue(schema.const) ? z.literal(schema.const) : z.any();
159
+ }
160
+ const enumValues = Array.isArray(schema.enum) ? schema.enum : null;
161
+ if (enumValues && enumValues.length > 0) {
162
+ const literalValues = enumValues.filter(isLiteralValue);
163
+ if (literalValues.length === 0) {
164
+ return z.any();
165
+ }
166
+ if (literalValues.every((value) => typeof value === "string")) {
167
+ return z.enum(literalValues);
168
+ }
169
+ const literals = literalValues.map((value) => z.literal(value));
170
+ return buildUnion(literals);
171
+ }
172
+ const typeValue = schema.type;
173
+ if (Array.isArray(typeValue)) {
174
+ const types = typeValue.filter((entry) => typeof entry === "string");
175
+ const hasNull = types.includes("null");
176
+ const nonNullTypes = types.filter((entry) => entry !== "null");
177
+ let unionSchema;
178
+ if (nonNullTypes.length === 0) {
179
+ unionSchema = z.any();
180
+ }
181
+ else if (nonNullTypes.length === 1) {
182
+ unionSchema = buildSchema({ ...schema, type: nonNullTypes[0] });
183
+ }
184
+ else {
185
+ const unionItems = nonNullTypes.map((entry) => buildSchema({ ...schema, type: entry }));
186
+ unionSchema = buildUnion(unionItems);
187
+ }
188
+ return hasNull ? unionSchema.nullable() : unionSchema;
189
+ }
190
+ const schemaType = typeof typeValue === "string" ? typeValue : undefined;
191
+ let baseSchema;
192
+ switch (schemaType) {
193
+ case "string": {
194
+ let stringSchema = z.string();
195
+ if (typeof schema.minLength === "number") {
196
+ stringSchema = stringSchema.min(schema.minLength);
197
+ }
198
+ if (typeof schema.maxLength === "number") {
199
+ stringSchema = stringSchema.max(schema.maxLength);
200
+ }
201
+ if (typeof schema.pattern === "string") {
202
+ try {
203
+ stringSchema = stringSchema.regex(new RegExp(schema.pattern));
204
+ }
205
+ catch {
206
+ }
207
+ }
208
+ baseSchema = stringSchema;
209
+ break;
210
+ }
211
+ case "number": {
212
+ let numberSchema = z.number();
213
+ if (typeof schema.minimum === "number") {
214
+ numberSchema = numberSchema.min(schema.minimum);
215
+ }
216
+ if (typeof schema.maximum === "number") {
217
+ numberSchema = numberSchema.max(schema.maximum);
218
+ }
219
+ if (typeof schema.exclusiveMinimum === "number") {
220
+ numberSchema = numberSchema.gt(schema.exclusiveMinimum);
221
+ }
222
+ if (typeof schema.exclusiveMaximum === "number") {
223
+ numberSchema = numberSchema.lt(schema.exclusiveMaximum);
224
+ }
225
+ baseSchema = numberSchema;
226
+ break;
227
+ }
228
+ case "integer": {
229
+ let intSchema = z.number().int();
230
+ if (typeof schema.minimum === "number") {
231
+ intSchema = intSchema.min(schema.minimum);
232
+ }
233
+ if (typeof schema.maximum === "number") {
234
+ intSchema = intSchema.max(schema.maximum);
235
+ }
236
+ baseSchema = intSchema;
237
+ break;
238
+ }
239
+ case "boolean":
240
+ baseSchema = z.boolean();
241
+ break;
242
+ case "array": {
243
+ const itemSchema = isRecord(schema.items) ? buildSchema(schema.items) : z.any();
244
+ let arraySchema = z.array(itemSchema);
245
+ if (typeof schema.minItems === "number") {
246
+ arraySchema = arraySchema.min(schema.minItems);
247
+ }
248
+ if (typeof schema.maxItems === "number") {
249
+ arraySchema = arraySchema.max(schema.maxItems);
250
+ }
251
+ baseSchema = arraySchema;
252
+ break;
253
+ }
254
+ case "object":
255
+ baseSchema = buildObjectSchema(schema);
256
+ break;
257
+ case "null":
258
+ baseSchema = z.null();
259
+ break;
260
+ default:
261
+ baseSchema = z.any();
262
+ }
263
+ if (schema.nullable === true) {
264
+ return baseSchema.nullable();
265
+ }
266
+ return baseSchema;
267
+ }
268
+ function isZodSchema(value) {
269
+ if (!value || typeof value !== "object") {
270
+ return false;
271
+ }
272
+ const candidate = value;
273
+ return typeof candidate.parse === "function" && typeof candidate.safeParse === "function";
274
+ }
275
+ function isLiteralValue(value) {
276
+ if (value === null) {
277
+ return true;
278
+ }
279
+ const valueType = typeof value;
280
+ return valueType === "string" || valueType === "number" || valueType === "boolean";
281
+ }
282
+ function buildUnion(items) {
283
+ if (items.length === 0) {
284
+ return z.any();
285
+ }
286
+ const [first, ...rest] = items;
287
+ if (!first) {
288
+ return z.any();
289
+ }
290
+ if (rest.length === 0) {
291
+ return first;
292
+ }
293
+ return z.union([first, ...rest]);
294
+ }
295
+ function registerPythonTools(definitions, invoker) {
296
+ const now = new Date();
297
+ return definitions.map((toolDef) => {
298
+ const normalizedInputSchema = normalizePythonInputSchema(toolDef.inputSchema) ?? z.any();
299
+ const toolName = toolDef.name;
300
+ const registered = {
301
+ id: toolDef.id ?? `py-${toolName}-${randomUUID().slice(0, 8)}`,
302
+ name: toolName,
303
+ description: toolDef.description ?? "",
304
+ inputSchema: normalizedInputSchema,
305
+ createdAt: now,
306
+ handler: async (args) => {
307
+ const result = await invoker.call(toolName, args);
308
+ return toCallToolResult(result);
309
+ },
310
+ };
311
+ toolRegistry.register(registered);
312
+ return registered;
313
+ });
314
+ }
315
+ function handleIncomingLine(line) {
316
+ const trimmed = line.trim();
317
+ if (!trimmed)
318
+ return;
319
+ try {
320
+ const payload = JSON.parse(trimmed);
321
+ if (payload.type === "bridge.abort") {
322
+ if (activeClient) {
323
+ requestAbort();
324
+ }
325
+ return;
326
+ }
327
+ if (payload.type === "bridge.python_tool_result" ||
328
+ payload.type === "bridge.python_tool_error") {
329
+ pythonToolInvoker?.handleResult(payload);
330
+ }
331
+ }
332
+ catch {
333
+ }
334
+ }
335
+ function writePayload(payload) {
336
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
337
+ }
338
+ async function readInitialRequest() {
339
+ stdinInterface = createInterface({ input: process.stdin });
340
+ const [line] = (await once(stdinInterface, "line"));
341
+ return line;
342
+ }
343
+ function resolveProvider(params) {
344
+ const client = asRecord(params).client;
345
+ if (client === "claude" || client === "codex") {
346
+ return client;
347
+ }
348
+ return null;
349
+ }
350
+ function isRecord(value) {
351
+ return typeof value === "object" && value !== null && !Array.isArray(value);
352
+ }
353
+ function asRecord(value) {
354
+ if (!isRecord(value)) {
355
+ return {};
356
+ }
357
+ return value;
358
+ }
359
+ function resolveString(value) {
360
+ if (typeof value !== "string") {
361
+ return undefined;
362
+ }
363
+ const trimmed = value.trim();
364
+ return trimmed ? trimmed : undefined;
365
+ }
366
+ function shouldExcludeHashKey(key) {
367
+ return HASH_EXCLUDED_KEYS.has(key.toLowerCase());
368
+ }
369
+ function isCustomToolsMcpName(name) {
370
+ return name.toLowerCase() === CUSTOM_TOOLS_MCP_NAME;
371
+ }
372
+ function stripCustomToolsMcpFromValue(value) {
373
+ if (isRecord(value)) {
374
+ let removed = false;
375
+ const nextRecord = {};
376
+ for (const [name, config] of Object.entries(value)) {
377
+ if (isCustomToolsMcpName(name)) {
378
+ removed = true;
379
+ continue;
380
+ }
381
+ nextRecord[name] = config;
382
+ }
383
+ if (!removed) {
384
+ return value;
385
+ }
386
+ return Object.keys(nextRecord).length > 0 ? nextRecord : undefined;
387
+ }
388
+ if (Array.isArray(value)) {
389
+ let removed = false;
390
+ const filtered = [];
391
+ for (const entry of value) {
392
+ if (typeof entry === "string") {
393
+ if (isCustomToolsMcpName(entry)) {
394
+ removed = true;
395
+ continue;
396
+ }
397
+ filtered.push(entry);
398
+ continue;
399
+ }
400
+ if (isRecord(entry)) {
401
+ const nextEntry = stripCustomToolsMcpFromValue(entry);
402
+ if (nextEntry === undefined) {
403
+ removed = true;
404
+ continue;
405
+ }
406
+ if (nextEntry !== entry) {
407
+ removed = true;
408
+ }
409
+ filtered.push(nextEntry);
410
+ continue;
411
+ }
412
+ filtered.push(entry);
413
+ }
414
+ if (!removed) {
415
+ return value;
416
+ }
417
+ return filtered.length > 0 ? filtered : undefined;
418
+ }
419
+ return value;
420
+ }
421
+ function normalizeHashIgnoreList(value) {
422
+ if (!value) {
423
+ return [];
424
+ }
425
+ if (Array.isArray(value)) {
426
+ return value
427
+ .filter((item) => typeof item === "string" && item.trim().length > 0)
428
+ .map((item) => item.trim());
429
+ }
430
+ if (typeof value === "string") {
431
+ const trimmed = value.trim();
432
+ return trimmed.length > 0 ? [trimmed] : [];
433
+ }
434
+ return [];
435
+ }
436
+ function normalizeHashIgnoreKey(value) {
437
+ return value.replace(/_/g, "").toLowerCase();
438
+ }
439
+ function applyHashIgnoreToRecord(record, ignoreList) {
440
+ if (ignoreList.length === 0) {
441
+ return record;
442
+ }
443
+ const ignoreSet = new Set(ignoreList.map((item) => normalizeHashIgnoreKey(item)));
444
+ const filtered = {};
445
+ for (const [key, value] of Object.entries(record)) {
446
+ if (ignoreSet.has(normalizeHashIgnoreKey(key))) {
447
+ continue;
448
+ }
449
+ filtered[key] = value;
450
+ }
451
+ return filtered;
452
+ }
453
+ function normalizeHashValue(value, seen = new WeakSet()) {
454
+ if (value === null) {
455
+ return null;
456
+ }
457
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
458
+ return value;
459
+ }
460
+ if (typeof value === "bigint") {
461
+ return value.toString();
462
+ }
463
+ if (value instanceof Date) {
464
+ return value.toISOString();
465
+ }
466
+ if (Array.isArray(value)) {
467
+ return value.map((item) => {
468
+ const normalized = normalizeHashValue(item, seen);
469
+ return normalized === undefined ? null : normalized;
470
+ });
471
+ }
472
+ if (typeof value === "object") {
473
+ if (seen.has(value)) {
474
+ return "[Circular]";
475
+ }
476
+ seen.add(value);
477
+ const record = {};
478
+ for (const key of Object.keys(value).sort()) {
479
+ if (shouldExcludeHashKey(key)) {
480
+ continue;
481
+ }
482
+ const lowerKey = key.toLowerCase();
483
+ let nextValue = value[key];
484
+ if (lowerKey === "mcp_servers" || lowerKey === "mcpservers") {
485
+ nextValue = stripCustomToolsMcpFromValue(nextValue);
486
+ }
487
+ const normalized = normalizeHashValue(nextValue, seen);
488
+ if (normalized !== undefined) {
489
+ record[key] = normalized;
490
+ }
491
+ }
492
+ seen.delete(value);
493
+ return record;
494
+ }
495
+ return undefined;
496
+ }
497
+ function stableStringify(value) {
498
+ return JSON.stringify(normalizeHashValue(value));
499
+ }
500
+ function buildCodexHomeHashSourceFromConfig(provider, config) {
501
+ const homeDirHashIgnore = normalizeHashIgnoreList(config.homeDirHashIgnore ??
502
+ config.home_dir_hash_ignore);
503
+ const systemPrompt = resolveString(config.systemPrompt)
504
+ ?? resolveString(config.prompt);
505
+ const mcpServers = stripCustomToolsMcpFromValue(config.mcpServers);
506
+ const otherConfig = stripCustomToolsMcpFromValue(config.otherConfig);
507
+ const hashSource = {
508
+ client: provider,
509
+ description: config.description,
510
+ systemPrompt,
511
+ tools: config.tools,
512
+ allowedTools: config.allowedTools,
513
+ disallowedTools: config.disallowedTools,
514
+ model: config.model,
515
+ baseURL: config.baseURL,
516
+ mcpServers,
517
+ permissionMode: config.permissionMode,
518
+ settingSources: config.settingSources,
519
+ addDirs: config.addDirs,
520
+ cwd: config.cwd,
521
+ homeDir: config.homeDir,
522
+ skillDir: config.skillDir,
523
+ extraHeaders: config.extraHeaders,
524
+ otherConfig,
525
+ };
526
+ return applyHashIgnoreToRecord(hashSource, homeDirHashIgnore);
527
+ }
528
+ function createCodexHomeKeyFromConfig(provider, config) {
529
+ const hashSource = buildCodexHomeHashSourceFromConfig(provider, config);
530
+ const serialized = stableStringify(hashSource);
531
+ return createHash("sha256").update(serialized).digest("hex");
532
+ }
533
+ function normalizeBridgeConfig(provider, config) {
534
+ const fieldMappings = provider === "claude" ? CLAUDE_FIELD_MAPPINGS : CODEX_FIELD_MAPPINGS;
535
+ const normalizedConfig = convertSnakeCaseToCamelCase({ ...config }, fieldMappings);
536
+ const envRecord = isRecord(normalizedConfig.env)
537
+ ? { ...normalizedConfig.env }
538
+ : {};
539
+ const extraHeadersValue = normalizedConfig.extraHeaders ??
540
+ config.extra_headers;
541
+ const extraHeaders = normalizeExtraHeaders(extraHeadersValue);
542
+ if (extraHeaders) {
543
+ if (provider === "claude") {
544
+ const resolvedEnvHeaders = resolveEnvHeaders(extraHeaders.envHeaders, envRecord);
545
+ const mergedHeaders = mergeHeaderRecords(extraHeaders.headers, resolvedEnvHeaders);
546
+ const customHeaderValue = serializeAnthropicHeaders(mergedHeaders);
547
+ if (customHeaderValue) {
548
+ envRecord.ANTHROPIC_CUSTOM_HEADERS = customHeaderValue;
549
+ }
550
+ }
551
+ else {
552
+ const existingConfig = isRecord(normalizedConfig.config)
553
+ ? normalizedConfig.config
554
+ : {};
555
+ const updatedConfig = applyCodexExtraHeaders(existingConfig, extraHeaders);
556
+ if (Object.keys(updatedConfig).length > 0) {
557
+ normalizedConfig.config = updatedConfig;
558
+ }
559
+ }
560
+ }
561
+ let codexHomeDir;
562
+ if (provider === "codex") {
563
+ const explicitCodexHome = resolveString(envRecord.CODEX_HOME);
564
+ let codexHomeKey = resolveString(normalizedConfig.codexHomeKey) ??
565
+ resolveString(normalizedConfig.codex_home_key);
566
+ if (!explicitCodexHome && !codexHomeKey) {
567
+ codexHomeKey = createCodexHomeKeyFromConfig(provider, normalizedConfig);
568
+ normalizedConfig.codexHomeKey = codexHomeKey;
569
+ }
570
+ const resolution = resolveCodexHomeDir(explicitCodexHome, codexHomeKey);
571
+ codexHomeDir = resolution.codexHomeDir;
572
+ if (!explicitCodexHome) {
573
+ envRecord.CODEX_HOME = codexHomeDir;
574
+ if (normalizedConfig
575
+ .allowConfigWriteWithUserCodexHome === undefined) {
576
+ normalizedConfig
577
+ .allowConfigWriteWithUserCodexHome = true;
578
+ }
579
+ }
580
+ }
581
+ if (Object.keys(envRecord).length > 0 || "env" in normalizedConfig) {
582
+ normalizedConfig.env = envRecord;
583
+ }
584
+ return { config: normalizedConfig, codexHomeDir };
585
+ }
586
+ function createClient(provider, config) {
587
+ if (provider === "claude") {
588
+ const apiKey = config.apiKey ??
589
+ config.api_key ??
590
+ process.env.ANTHROPIC_API_KEY ??
591
+ "";
592
+ const clientConfig = {
593
+ ...config,
594
+ apiKey,
595
+ };
596
+ return new ClaudeClient(clientConfig);
597
+ }
598
+ const apiKey = config.apiKey ??
599
+ config.api_key ??
600
+ process.env.CODEX_API_KEY ??
601
+ process.env.OPENAI_API_KEY ??
602
+ "";
603
+ const clientConfig = {
604
+ ...config,
605
+ apiKey,
606
+ };
607
+ return new CodexClient(clientConfig);
608
+ }
609
+ function prepareConfig(provider, config) {
610
+ const normalized = normalizeBridgeConfig(provider, config).config;
611
+ const pythonToolsValue = normalized._pythonTools;
612
+ const pythonTools = Array.isArray(pythonToolsValue)
613
+ ? pythonToolsValue
614
+ : [];
615
+ if ("_pythonTools" in normalized) {
616
+ delete normalized._pythonTools;
617
+ }
618
+ let proxyTools = [];
619
+ if (pythonTools.length > 0) {
620
+ if (!pythonToolInvoker) {
621
+ pythonToolInvoker = new PythonToolInvoker(writePayload);
622
+ }
623
+ proxyTools = registerPythonTools(pythonTools, pythonToolInvoker);
624
+ }
625
+ const existingTools = Array.isArray(normalized.tools)
626
+ ? normalized.tools.filter((tool) => tool && typeof tool === "object" && "handler" in tool)
627
+ : [];
628
+ if (proxyTools.length > 0 || existingTools.length > 0) {
629
+ normalized.tools = [...existingTools, ...proxyTools];
630
+ }
631
+ return normalized;
632
+ }
633
+ async function runStream(client, params) {
634
+ activeClient = client;
635
+ const { events } = await client.runStream(params);
636
+ for await (const event of events) {
637
+ writePayload(serializeEvent(event));
638
+ }
639
+ }
640
+ async function runResume(client, params) {
641
+ activeClient = client;
642
+ const { events } = await client.resume(params);
643
+ for await (const event of events) {
644
+ writePayload(serializeEvent(event));
645
+ }
646
+ }
647
+ async function runOnce(client, params) {
648
+ activeClient = client;
649
+ const result = await client.run(params);
650
+ for (const event of result.events) {
651
+ writePayload(serializeEvent(event));
652
+ }
653
+ writePayload({
654
+ type: "bridge.result",
655
+ success: result.success,
656
+ data: result.data,
657
+ error: result.error ? serializeError(result.error) : undefined,
658
+ });
659
+ }
660
+ async function main() {
661
+ try {
662
+ const input = await readInitialRequest();
663
+ if (!input.trim()) {
664
+ throw new Error("Missing JSON request on stdin.");
665
+ }
666
+ let request;
667
+ try {
668
+ request = JSON.parse(input);
669
+ }
670
+ catch (error) {
671
+ throw new Error(`Invalid JSON request: ${error.message}`);
672
+ }
673
+ stdinInterface?.on("line", handleIncomingLine);
674
+ if (request.action === "normalizeConfig") {
675
+ const params = request.params ?? {};
676
+ const provider = resolveProvider(params);
677
+ if (!provider) {
678
+ throw new Error('Unable to resolve client. Set params.client to "claude" or "codex".');
679
+ }
680
+ const result = normalizeBridgeConfig(provider, asRecord(request.config));
681
+ writePayload({
682
+ type: "bridge.result",
683
+ success: true,
684
+ data: {
685
+ config: result.config,
686
+ ...(result.codexHomeDir ? { codexHomeDir: result.codexHomeDir } : {}),
687
+ },
688
+ });
689
+ writePayload({ type: "bridge.done", success: true });
690
+ return;
691
+ }
692
+ if (request.action === "prepareCodexConfig") {
693
+ await handlePrepareCodexConfig(request);
694
+ return;
695
+ }
696
+ const params = request.params ?? (request.query ? { query: request.query } : null);
697
+ if (request.action === "resume") {
698
+ if (!params) {
699
+ throw new Error("Missing params in request.");
700
+ }
701
+ const provider = resolveProvider(params);
702
+ if (!provider) {
703
+ throw new Error('Unable to resolve client. Set params.client to "claude" or "codex".');
704
+ }
705
+ const sessionId = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
706
+ if (!sessionId) {
707
+ throw new Error("Missing sessionId in request.");
708
+ }
709
+ const config = prepareConfig(provider, asRecord(request.config));
710
+ const client = createClient(provider, config);
711
+ await runResume(client, params);
712
+ writePayload({ type: "bridge.done", success: true });
713
+ return;
714
+ }
715
+ const provider = resolveProvider(params);
716
+ if (!provider) {
717
+ throw new Error('Unable to resolve client. Set params.client to "claude" or "codex".');
718
+ }
719
+ const query = typeof params?.query === "string" ? params.query.trim() : "";
720
+ if (!query) {
721
+ throw new Error("Missing query in request.");
722
+ }
723
+ const config = prepareConfig(provider, asRecord(request.config));
724
+ const client = createClient(provider, config);
725
+ const useStream = request.stream !== false;
726
+ if (useStream) {
727
+ await runStream(client, params);
728
+ }
729
+ else {
730
+ await runOnce(client, params);
731
+ }
732
+ writePayload({ type: "bridge.done", success: true });
733
+ }
734
+ finally {
735
+ pythonToolInvoker?.shutdown();
736
+ pythonToolInvoker = null;
737
+ stdinInterface?.close();
738
+ stdinInterface = null;
739
+ if (abortTimer) {
740
+ clearTimeout(abortTimer);
741
+ abortTimer = null;
742
+ }
743
+ activeClient = null;
744
+ }
745
+ }
746
+ async function handlePrepareCodexConfig(request) {
747
+ const config = prepareConfig("codex", asRecord(request.config));
748
+ const apiKey = config.apiKey ??
749
+ process.env.CODEX_API_KEY ??
750
+ process.env.OPENAI_API_KEY ??
751
+ "codex-placeholder";
752
+ const clientConfig = {
753
+ ...config,
754
+ apiKey,
755
+ };
756
+ const client = new CodexClient(clientConfig);
757
+ const codexHomeDir = client.codexHomeDir;
758
+ writePayload({
759
+ type: "bridge.result",
760
+ success: true,
761
+ data: { codexHomeDir }
762
+ });
763
+ writePayload({ type: "bridge.done", success: true });
764
+ }
765
+ main().catch((error) => {
766
+ writePayload({
767
+ type: "bridge.error",
768
+ error: serializeError(error),
769
+ });
770
+ process.exitCode = 1;
771
+ });
772
+ //# sourceMappingURL=node-bridge.js.map