@kodelyth/codex 2026.5.42 → 2026.6.2

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 (138) hide show
  1. package/package.json +17 -2
  2. package/doctor-contract-api.test.ts +0 -44
  3. package/doctor-contract-api.ts +0 -68
  4. package/harness.ts +0 -72
  5. package/index.test.ts +0 -230
  6. package/index.ts +0 -66
  7. package/media-understanding-provider.test.ts +0 -486
  8. package/media-understanding-provider.ts +0 -521
  9. package/prompt-overlay-runtime-contract.test.ts +0 -48
  10. package/prompt-overlay.ts +0 -21
  11. package/provider-catalog.ts +0 -83
  12. package/provider-discovery.ts +0 -45
  13. package/provider.test.ts +0 -384
  14. package/provider.ts +0 -243
  15. package/src/app-server/app-inventory-cache.test.ts +0 -176
  16. package/src/app-server/app-inventory-cache.ts +0 -324
  17. package/src/app-server/approval-bridge.test.ts +0 -1471
  18. package/src/app-server/approval-bridge.ts +0 -1211
  19. package/src/app-server/auth-bridge.test.ts +0 -1449
  20. package/src/app-server/auth-bridge.ts +0 -614
  21. package/src/app-server/auth-profile-runtime-contract.test.ts +0 -239
  22. package/src/app-server/capabilities.ts +0 -27
  23. package/src/app-server/client-factory.ts +0 -24
  24. package/src/app-server/client.test.ts +0 -563
  25. package/src/app-server/client.ts +0 -715
  26. package/src/app-server/compact.test.ts +0 -710
  27. package/src/app-server/compact.ts +0 -500
  28. package/src/app-server/computer-use.test.ts +0 -788
  29. package/src/app-server/computer-use.ts +0 -683
  30. package/src/app-server/config.test.ts +0 -879
  31. package/src/app-server/config.ts +0 -1038
  32. package/src/app-server/context-engine-projection.test.ts +0 -252
  33. package/src/app-server/context-engine-projection.ts +0 -403
  34. package/src/app-server/delivery-no-reply-runtime-contract.test.ts +0 -80
  35. package/src/app-server/dynamic-tool-diagnostics.ts +0 -73
  36. package/src/app-server/dynamic-tool-profile.ts +0 -69
  37. package/src/app-server/dynamic-tools.test.ts +0 -1302
  38. package/src/app-server/dynamic-tools.ts +0 -623
  39. package/src/app-server/elicitation-bridge.test.ts +0 -1056
  40. package/src/app-server/elicitation-bridge.ts +0 -783
  41. package/src/app-server/event-projector.test.ts +0 -2668
  42. package/src/app-server/event-projector.ts +0 -2057
  43. package/src/app-server/image-payload-sanitizer.test.ts +0 -49
  44. package/src/app-server/image-payload-sanitizer.ts +0 -167
  45. package/src/app-server/klaw-owned-tool-runtime-contract.test.ts +0 -456
  46. package/src/app-server/local-runtime-attribution.ts +0 -39
  47. package/src/app-server/managed-binary.test.ts +0 -139
  48. package/src/app-server/managed-binary.ts +0 -193
  49. package/src/app-server/models.test.ts +0 -246
  50. package/src/app-server/models.ts +0 -172
  51. package/src/app-server/native-hook-relay.test.ts +0 -271
  52. package/src/app-server/native-hook-relay.ts +0 -150
  53. package/src/app-server/native-subagent-task-mirror.test.ts +0 -573
  54. package/src/app-server/native-subagent-task-mirror.ts +0 -497
  55. package/src/app-server/outcome-fallback-runtime-contract.test.ts +0 -404
  56. package/src/app-server/plugin-activation.test.ts +0 -336
  57. package/src/app-server/plugin-activation.ts +0 -283
  58. package/src/app-server/plugin-app-cache-key.ts +0 -74
  59. package/src/app-server/plugin-approval-roundtrip.ts +0 -122
  60. package/src/app-server/plugin-inventory.test.ts +0 -355
  61. package/src/app-server/plugin-inventory.ts +0 -357
  62. package/src/app-server/plugin-thread-config.test.ts +0 -865
  63. package/src/app-server/plugin-thread-config.ts +0 -455
  64. package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +0 -33
  65. package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +0 -199
  66. package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +0 -102
  67. package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +0 -227
  68. package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +0 -2630
  69. package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +0 -2630
  70. package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +0 -1659
  71. package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +0 -1655
  72. package/src/app-server/protocol-validators.test.ts +0 -75
  73. package/src/app-server/protocol-validators.ts +0 -203
  74. package/src/app-server/protocol.ts +0 -520
  75. package/src/app-server/rate-limit-cache.ts +0 -48
  76. package/src/app-server/rate-limits.test.ts +0 -202
  77. package/src/app-server/rate-limits.ts +0 -583
  78. package/src/app-server/request.ts +0 -73
  79. package/src/app-server/run-attempt.context-engine.test.ts +0 -1004
  80. package/src/app-server/run-attempt.test.ts +0 -9477
  81. package/src/app-server/run-attempt.ts +0 -4683
  82. package/src/app-server/run-attempt.vision-tools.test.ts +0 -35
  83. package/src/app-server/schema-normalization-runtime-contract.test.ts +0 -206
  84. package/src/app-server/session-binding.test.ts +0 -303
  85. package/src/app-server/session-binding.ts +0 -398
  86. package/src/app-server/session-history.ts +0 -44
  87. package/src/app-server/shared-client.test.ts +0 -589
  88. package/src/app-server/shared-client.ts +0 -289
  89. package/src/app-server/side-question.test.ts +0 -1175
  90. package/src/app-server/side-question.ts +0 -1007
  91. package/src/app-server/test-support.ts +0 -48
  92. package/src/app-server/thread-lifecycle.test.ts +0 -447
  93. package/src/app-server/thread-lifecycle.ts +0 -939
  94. package/src/app-server/thread-lifecycle.user-mcp-servers.test.ts +0 -442
  95. package/src/app-server/timeout.ts +0 -9
  96. package/src/app-server/tool-progress-normalization.ts +0 -77
  97. package/src/app-server/trajectory.test.ts +0 -205
  98. package/src/app-server/trajectory.ts +0 -365
  99. package/src/app-server/transcript-mirror.test.ts +0 -524
  100. package/src/app-server/transcript-mirror.ts +0 -208
  101. package/src/app-server/transcript-repair-runtime-contract.test.ts +0 -44
  102. package/src/app-server/transport-stdio.test.ts +0 -171
  103. package/src/app-server/transport-stdio.ts +0 -107
  104. package/src/app-server/transport-websocket.test.ts +0 -69
  105. package/src/app-server/transport-websocket.ts +0 -90
  106. package/src/app-server/transport.ts +0 -117
  107. package/src/app-server/user-input-bridge.test.ts +0 -249
  108. package/src/app-server/user-input-bridge.ts +0 -316
  109. package/src/app-server/version.ts +0 -4
  110. package/src/app-server/vision-tools.ts +0 -12
  111. package/src/command-account.ts +0 -544
  112. package/src/command-formatters.ts +0 -425
  113. package/src/command-handlers.ts +0 -2004
  114. package/src/command-rpc.test.ts +0 -16
  115. package/src/command-rpc.ts +0 -142
  116. package/src/commands.test.ts +0 -3312
  117. package/src/commands.ts +0 -65
  118. package/src/conversation-binding-data.ts +0 -124
  119. package/src/conversation-binding.test.ts +0 -599
  120. package/src/conversation-binding.ts +0 -561
  121. package/src/conversation-control.test.ts +0 -126
  122. package/src/conversation-control.ts +0 -303
  123. package/src/conversation-turn-collector.test.ts +0 -191
  124. package/src/conversation-turn-collector.ts +0 -186
  125. package/src/conversation-turn-input.test.ts +0 -141
  126. package/src/conversation-turn-input.ts +0 -106
  127. package/src/manifest.test.ts +0 -20
  128. package/src/migration/apply.ts +0 -501
  129. package/src/migration/helpers.ts +0 -55
  130. package/src/migration/plan.ts +0 -461
  131. package/src/migration/provider.test.ts +0 -1741
  132. package/src/migration/provider.ts +0 -41
  133. package/src/migration/source.ts +0 -643
  134. package/src/migration/targets.ts +0 -25
  135. package/src/node-cli-sessions.test.ts +0 -180
  136. package/src/node-cli-sessions.ts +0 -711
  137. package/test-api.ts +0 -82
  138. package/tsconfig.json +0 -16
@@ -1,1038 +0,0 @@
1
- import { createHmac, randomBytes } from "node:crypto";
2
- import { readFileSync } from "node:fs";
3
- import { hostname as readHostName } from "node:os";
4
- import { z } from "zod";
5
- import type { CodexSandboxPolicy, CodexServiceTier } from "./protocol.js";
6
-
7
- const START_OPTIONS_KEY_SECRET_SYMBOL = Symbol.for("klaw.codexAppServerStartOptionsKeySecret");
8
- const START_OPTIONS_KEY_SECRET = getStartOptionsKeySecret();
9
- const UNIX_CODEX_REQUIREMENTS_PATH = "/etc/codex/requirements.toml";
10
- const WINDOWS_CODEX_REQUIREMENTS_SUFFIX = "\\OpenAI\\Codex\\requirements.toml";
11
-
12
- type CodexAppServerTransportMode = "stdio" | "websocket";
13
- type CodexAppServerPolicyMode = "yolo" | "guardian";
14
- type CodexAppServerDefaultPolicy = {
15
- mode: CodexAppServerPolicyMode;
16
- approvalPolicy?: CodexAppServerApprovalPolicy;
17
- approvalsReviewer?: CodexAppServerApprovalsReviewer;
18
- sandbox?: CodexAppServerSandboxMode;
19
- };
20
- export type CodexAppServerApprovalPolicy = "never" | "on-request" | "on-failure" | "untrusted";
21
- export type CodexAppServerEffectiveApprovalPolicy =
22
- | CodexAppServerApprovalPolicy
23
- | {
24
- granular: {
25
- mcp_elicitations: boolean;
26
- rules: boolean;
27
- sandbox_approval: boolean;
28
- request_permissions?: boolean;
29
- skill_approval?: boolean;
30
- };
31
- };
32
- export type CodexAppServerSandboxMode = "read-only" | "workspace-write" | "danger-full-access";
33
- type CodexAppServerApprovalsReviewer = "user" | "auto_review" | "guardian_subagent";
34
- type CodexAppServerCommandSource = "managed" | "resolved-managed" | "config" | "env";
35
- export type CodexDynamicToolsLoading = "searchable" | "direct";
36
- export type CodexPluginDestructivePolicy = boolean;
37
-
38
- export const CODEX_PLUGINS_MARKETPLACE_NAME = "openai-curated";
39
-
40
- export type CodexComputerUseConfig = {
41
- enabled?: boolean;
42
- autoInstall?: boolean;
43
- marketplaceDiscoveryTimeoutMs?: number;
44
- marketplaceSource?: string;
45
- marketplacePath?: string;
46
- marketplaceName?: string;
47
- pluginName?: string;
48
- mcpServerName?: string;
49
- };
50
-
51
- export type ResolvedCodexComputerUseConfig = {
52
- enabled: boolean;
53
- autoInstall: boolean;
54
- marketplaceDiscoveryTimeoutMs: number;
55
- pluginName: string;
56
- mcpServerName: string;
57
- marketplaceSource?: string;
58
- marketplacePath?: string;
59
- marketplaceName?: string;
60
- };
61
-
62
- export type CodexPluginEntryConfig = {
63
- enabled?: boolean;
64
- marketplaceName?: string;
65
- pluginName?: string;
66
- allow_destructive_actions?: CodexPluginDestructivePolicy;
67
- };
68
-
69
- export type CodexPluginsConfig = {
70
- enabled?: boolean;
71
- allow_destructive_actions?: CodexPluginDestructivePolicy;
72
- plugins?: Record<string, CodexPluginEntryConfig>;
73
- };
74
-
75
- export type ResolvedCodexPluginPolicy = {
76
- configKey: string;
77
- marketplaceName: typeof CODEX_PLUGINS_MARKETPLACE_NAME;
78
- pluginName: string;
79
- enabled: boolean;
80
- allowDestructiveActions: CodexPluginDestructivePolicy;
81
- };
82
-
83
- export type ResolvedCodexPluginsPolicy = {
84
- configured: boolean;
85
- enabled: boolean;
86
- allowDestructiveActions: CodexPluginDestructivePolicy;
87
- pluginPolicies: ResolvedCodexPluginPolicy[];
88
- };
89
-
90
- export type CodexAppServerStartOptions = {
91
- transport: CodexAppServerTransportMode;
92
- command: string;
93
- commandSource?: CodexAppServerCommandSource;
94
- args: string[];
95
- url?: string;
96
- authToken?: string;
97
- headers: Record<string, string>;
98
- env?: Record<string, string>;
99
- clearEnv?: string[];
100
- };
101
-
102
- export type CodexAppServerRuntimeOptions = {
103
- start: CodexAppServerStartOptions;
104
- codeModeOnly: boolean;
105
- requestTimeoutMs: number;
106
- turnCompletionIdleTimeoutMs: number;
107
- approvalPolicy: CodexAppServerEffectiveApprovalPolicy;
108
- sandbox: CodexAppServerSandboxMode;
109
- approvalsReviewer: CodexAppServerApprovalsReviewer;
110
- serviceTier?: CodexServiceTier;
111
- };
112
-
113
- export type CodexPluginConfig = {
114
- codexDynamicToolsLoading?: CodexDynamicToolsLoading;
115
- codexDynamicToolsExclude?: string[];
116
- discovery?: {
117
- enabled?: boolean;
118
- timeoutMs?: number;
119
- };
120
- computerUse?: CodexComputerUseConfig;
121
- codexPlugins?: CodexPluginsConfig;
122
- appServer?: {
123
- mode?: CodexAppServerPolicyMode;
124
- transport?: CodexAppServerTransportMode;
125
- command?: string;
126
- args?: string[] | string;
127
- url?: string;
128
- authToken?: string;
129
- headers?: Record<string, string>;
130
- clearEnv?: string[];
131
- codeModeOnly?: boolean;
132
- requestTimeoutMs?: number;
133
- turnCompletionIdleTimeoutMs?: number;
134
- approvalPolicy?: CodexAppServerApprovalPolicy;
135
- sandbox?: CodexAppServerSandboxMode;
136
- approvalsReviewer?: CodexAppServerApprovalsReviewer;
137
- serviceTier?: CodexServiceTier | null;
138
- defaultWorkspaceDir?: string;
139
- };
140
- };
141
-
142
- export const CODEX_APP_SERVER_CONFIG_KEYS = [
143
- "mode",
144
- "transport",
145
- "command",
146
- "args",
147
- "url",
148
- "authToken",
149
- "headers",
150
- "clearEnv",
151
- "codeModeOnly",
152
- "requestTimeoutMs",
153
- "turnCompletionIdleTimeoutMs",
154
- "approvalPolicy",
155
- "sandbox",
156
- "approvalsReviewer",
157
- "serviceTier",
158
- "defaultWorkspaceDir",
159
- ] as const;
160
-
161
- export const CODEX_COMPUTER_USE_CONFIG_KEYS = [
162
- "enabled",
163
- "autoInstall",
164
- "marketplaceDiscoveryTimeoutMs",
165
- "marketplaceSource",
166
- "marketplacePath",
167
- "marketplaceName",
168
- "pluginName",
169
- "mcpServerName",
170
- ] as const;
171
-
172
- export const CODEX_PLUGINS_CONFIG_KEYS = [
173
- "enabled",
174
- "allow_destructive_actions",
175
- "plugins",
176
- ] as const;
177
-
178
- export const CODEX_PLUGIN_ENTRY_CONFIG_KEYS = [
179
- "enabled",
180
- "marketplaceName",
181
- "pluginName",
182
- "allow_destructive_actions",
183
- ] as const;
184
-
185
- const DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME = "computer-use";
186
- const DEFAULT_CODEX_COMPUTER_USE_MCP_SERVER_NAME = "computer-use";
187
- const DEFAULT_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS = 60_000;
188
-
189
- const codexAppServerTransportSchema = z.enum(["stdio", "websocket"]);
190
- const codexAppServerPolicyModeSchema = z.enum(["yolo", "guardian"]);
191
- const codexAppServerApprovalPolicySchema = z.enum([
192
- "never",
193
- "on-request",
194
- "on-failure",
195
- "untrusted",
196
- ]);
197
- const codexAppServerSandboxSchema = z.enum(["read-only", "workspace-write", "danger-full-access"]);
198
- const codexAppServerApprovalsReviewerSchema = z.enum(["user", "auto_review", "guardian_subagent"]);
199
- const codexDynamicToolsLoadingSchema = z.enum(["searchable", "direct"]);
200
- const codexAppServerServiceTierSchema = z
201
- .preprocess(
202
- (value) => (value === null ? null : normalizeCodexServiceTier(value)),
203
- z.string().trim().min(1).nullable().optional(),
204
- )
205
- .optional();
206
-
207
- const codexPluginEntryConfigSchema = z
208
- .object({
209
- enabled: z.boolean().optional(),
210
- marketplaceName: z.literal(CODEX_PLUGINS_MARKETPLACE_NAME).optional(),
211
- pluginName: z.string().trim().min(1).optional(),
212
- allow_destructive_actions: z.boolean().optional(),
213
- })
214
- .strict();
215
-
216
- const codexPluginsConfigSchema = z
217
- .object({
218
- enabled: z.boolean().optional(),
219
- allow_destructive_actions: z.boolean().optional(),
220
- plugins: z.record(z.string(), codexPluginEntryConfigSchema).optional(),
221
- })
222
- .strict();
223
-
224
- const codexPluginConfigSchema = z
225
- .object({
226
- codexDynamicToolsLoading: codexDynamicToolsLoadingSchema.optional(),
227
- codexDynamicToolsExclude: z.array(z.string()).optional(),
228
- discovery: z
229
- .object({
230
- enabled: z.boolean().optional(),
231
- timeoutMs: z.number().positive().optional(),
232
- })
233
- .strict()
234
- .optional(),
235
- computerUse: z
236
- .object({
237
- enabled: z.boolean().optional(),
238
- autoInstall: z.boolean().optional(),
239
- marketplaceDiscoveryTimeoutMs: z.number().positive().optional(),
240
- marketplaceSource: z.string().optional(),
241
- marketplacePath: z.string().optional(),
242
- marketplaceName: z.string().optional(),
243
- pluginName: z.string().optional(),
244
- mcpServerName: z.string().optional(),
245
- })
246
- .strict()
247
- .optional(),
248
- codexPlugins: z.unknown().optional(),
249
- appServer: z
250
- .object({
251
- mode: codexAppServerPolicyModeSchema.optional(),
252
- transport: codexAppServerTransportSchema.optional(),
253
- command: z.string().optional(),
254
- args: z.union([z.array(z.string()), z.string()]).optional(),
255
- url: z.string().optional(),
256
- authToken: z.string().optional(),
257
- headers: z.record(z.string(), z.string()).optional(),
258
- clearEnv: z.array(z.string()).optional(),
259
- codeModeOnly: z.boolean().optional(),
260
- requestTimeoutMs: z.number().positive().optional(),
261
- turnCompletionIdleTimeoutMs: z.number().positive().optional(),
262
- approvalPolicy: codexAppServerApprovalPolicySchema.optional(),
263
- sandbox: codexAppServerSandboxSchema.optional(),
264
- approvalsReviewer: codexAppServerApprovalsReviewerSchema.optional(),
265
- serviceTier: codexAppServerServiceTierSchema,
266
- defaultWorkspaceDir: z.string().optional(),
267
- })
268
- .strict()
269
- .optional(),
270
- })
271
- .strict();
272
-
273
- export function readCodexPluginConfig(value: unknown): CodexPluginConfig {
274
- const parsed = codexPluginConfigSchema.safeParse(value);
275
- if (!parsed.success) {
276
- return {};
277
- }
278
- const { codexPlugins: rawCodexPlugins, ...config } = parsed.data;
279
- const plugins = codexPluginsConfigSchema.safeParse(rawCodexPlugins);
280
- if (!plugins.success) {
281
- return config;
282
- }
283
- return { ...config, ...(plugins.data ? { codexPlugins: plugins.data } : {}) };
284
- }
285
-
286
- export function resolveCodexPluginsPolicy(pluginConfig?: unknown): ResolvedCodexPluginsPolicy {
287
- const config = readCodexPluginConfig(pluginConfig).codexPlugins;
288
- const configured = config !== undefined;
289
- const enabled = config?.enabled === true;
290
- const allowDestructiveActions = config?.allow_destructive_actions ?? true;
291
- const pluginPolicies = Object.entries(config?.plugins ?? {})
292
- .flatMap(([configKey, entry]): ResolvedCodexPluginPolicy[] => {
293
- if (entry.marketplaceName !== CODEX_PLUGINS_MARKETPLACE_NAME || !entry.pluginName) {
294
- return [];
295
- }
296
- return [
297
- {
298
- configKey,
299
- marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
300
- pluginName: entry.pluginName,
301
- enabled: enabled && entry.enabled !== false,
302
- allowDestructiveActions: entry.allow_destructive_actions ?? allowDestructiveActions,
303
- },
304
- ];
305
- })
306
- .toSorted((left, right) => left.configKey.localeCompare(right.configKey));
307
- return {
308
- configured,
309
- enabled,
310
- allowDestructiveActions,
311
- pluginPolicies,
312
- };
313
- }
314
-
315
- export function resolveCodexAppServerRuntimeOptions(
316
- params: {
317
- pluginConfig?: unknown;
318
- env?: NodeJS.ProcessEnv;
319
- requirementsToml?: string | null;
320
- requirementsPath?: string;
321
- readRequirementsFile?: (path: string) => string | undefined;
322
- platform?: NodeJS.Platform;
323
- hostName?: string;
324
- } = {},
325
- ): CodexAppServerRuntimeOptions {
326
- const env = params.env ?? process.env;
327
- const config = readCodexPluginConfig(params.pluginConfig).appServer ?? {};
328
- const transport = resolveTransport(config.transport);
329
- const configCommand = readNonEmptyString(config.command);
330
- const envCommand = readNonEmptyString(env.KLAW_CODEX_APP_SERVER_BIN);
331
- const command = configCommand ?? envCommand ?? "codex";
332
- const commandSource: CodexAppServerCommandSource = configCommand
333
- ? "config"
334
- : envCommand
335
- ? "env"
336
- : "managed";
337
- const args = resolveArgs(config.args, env.KLAW_CODEX_APP_SERVER_ARGS);
338
- const headers = normalizeHeaders(config.headers);
339
- const clearEnv = normalizeStringList(config.clearEnv);
340
- const authToken = readNonEmptyString(config.authToken);
341
- const url = readNonEmptyString(config.url);
342
- const explicitPolicyMode =
343
- resolvePolicyMode(config.mode) ?? resolvePolicyMode(env.KLAW_CODEX_APP_SERVER_MODE);
344
- const defaultPolicy = explicitPolicyMode
345
- ? undefined
346
- : resolveDefaultCodexAppServerPolicy({
347
- transport,
348
- env,
349
- requirementsToml: params.requirementsToml,
350
- requirementsPath: params.requirementsPath,
351
- readRequirementsFile: params.readRequirementsFile,
352
- platform: params.platform,
353
- hostName: params.hostName,
354
- });
355
- const policyMode = explicitPolicyMode ?? defaultPolicy?.mode ?? "yolo";
356
- const serviceTier = normalizeCodexServiceTier(config.serviceTier);
357
- if (transport === "websocket" && !url) {
358
- throw new Error(
359
- "plugins.entries.codex.config.appServer.url is required when appServer.transport is websocket",
360
- );
361
- }
362
-
363
- return {
364
- start: {
365
- transport,
366
- command,
367
- commandSource,
368
- args: args.length > 0 ? args : ["app-server", "--listen", "stdio://"],
369
- ...(url ? { url } : {}),
370
- ...(authToken ? { authToken } : {}),
371
- headers,
372
- ...(transport === "stdio" && clearEnv.length > 0 ? { clearEnv } : {}),
373
- },
374
- codeModeOnly: config.codeModeOnly === true,
375
- requestTimeoutMs: normalizePositiveNumber(config.requestTimeoutMs, 60_000),
376
- turnCompletionIdleTimeoutMs: normalizePositiveNumber(
377
- config.turnCompletionIdleTimeoutMs,
378
- 60_000,
379
- ),
380
- approvalPolicy:
381
- resolveApprovalPolicy(config.approvalPolicy) ??
382
- resolveApprovalPolicy(env.KLAW_CODEX_APP_SERVER_APPROVAL_POLICY) ??
383
- defaultPolicy?.approvalPolicy ??
384
- (policyMode === "guardian" ? "on-request" : "never"),
385
- sandbox:
386
- resolveSandbox(config.sandbox) ??
387
- resolveSandbox(env.KLAW_CODEX_APP_SERVER_SANDBOX) ??
388
- defaultPolicy?.sandbox ??
389
- (policyMode === "guardian" ? "workspace-write" : "danger-full-access"),
390
- approvalsReviewer:
391
- resolveApprovalsReviewer(config.approvalsReviewer) ??
392
- defaultPolicy?.approvalsReviewer ??
393
- (policyMode === "guardian" ? "auto_review" : "user"),
394
- ...(serviceTier ? { serviceTier } : {}),
395
- };
396
- }
397
-
398
- export function isCodexAppServerApprovalPolicyAllowedByRequirements(
399
- policy: CodexAppServerApprovalPolicy,
400
- params: {
401
- env?: NodeJS.ProcessEnv;
402
- requirementsToml?: string | null;
403
- requirementsPath?: string;
404
- readRequirementsFile?: (path: string) => string | undefined;
405
- platform?: NodeJS.Platform;
406
- } = {},
407
- ): boolean {
408
- const content = readCodexRequirementsToml(params);
409
- if (content === undefined) {
410
- return true;
411
- }
412
- const allowedApprovalPolicies = parseAllowedApprovalPoliciesFromCodexRequirements(content);
413
- return allowedApprovalPolicies === undefined || allowedApprovalPolicies.has(policy);
414
- }
415
-
416
- export function resolveCodexComputerUseConfig(
417
- params: {
418
- pluginConfig?: unknown;
419
- env?: NodeJS.ProcessEnv;
420
- overrides?: Partial<CodexComputerUseConfig>;
421
- } = {},
422
- ): ResolvedCodexComputerUseConfig {
423
- const env = params.env ?? process.env;
424
- const config = readCodexPluginConfig(params.pluginConfig).computerUse ?? {};
425
- const marketplaceSource =
426
- readNonEmptyString(params.overrides?.marketplaceSource) ??
427
- readNonEmptyString(config.marketplaceSource) ??
428
- readNonEmptyString(env.KLAW_CODEX_COMPUTER_USE_MARKETPLACE_SOURCE);
429
- const marketplacePath =
430
- readNonEmptyString(params.overrides?.marketplacePath) ??
431
- readNonEmptyString(config.marketplacePath) ??
432
- readNonEmptyString(env.KLAW_CODEX_COMPUTER_USE_MARKETPLACE_PATH);
433
- const marketplaceName =
434
- readNonEmptyString(params.overrides?.marketplaceName) ??
435
- readNonEmptyString(config.marketplaceName) ??
436
- readNonEmptyString(env.KLAW_CODEX_COMPUTER_USE_MARKETPLACE_NAME);
437
- const autoInstall =
438
- params.overrides?.autoInstall ??
439
- config.autoInstall ??
440
- readBooleanEnv(env.KLAW_CODEX_COMPUTER_USE_AUTO_INSTALL) ??
441
- false;
442
- const marketplaceDiscoveryTimeoutMs = normalizePositiveNumber(
443
- params.overrides?.marketplaceDiscoveryTimeoutMs ??
444
- config.marketplaceDiscoveryTimeoutMs ??
445
- readNumberEnv(env.KLAW_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS),
446
- DEFAULT_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS,
447
- );
448
- const enabled =
449
- params.overrides?.enabled ??
450
- config.enabled ??
451
- readBooleanEnv(env.KLAW_CODEX_COMPUTER_USE) ??
452
- Boolean(autoInstall || marketplaceSource || marketplacePath || marketplaceName);
453
-
454
- return {
455
- enabled,
456
- autoInstall,
457
- marketplaceDiscoveryTimeoutMs,
458
- pluginName:
459
- readNonEmptyString(params.overrides?.pluginName) ??
460
- readNonEmptyString(config.pluginName) ??
461
- readNonEmptyString(env.KLAW_CODEX_COMPUTER_USE_PLUGIN_NAME) ??
462
- DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME,
463
- mcpServerName:
464
- readNonEmptyString(params.overrides?.mcpServerName) ??
465
- readNonEmptyString(config.mcpServerName) ??
466
- readNonEmptyString(env.KLAW_CODEX_COMPUTER_USE_MCP_SERVER_NAME) ??
467
- DEFAULT_CODEX_COMPUTER_USE_MCP_SERVER_NAME,
468
- ...(marketplaceSource ? { marketplaceSource } : {}),
469
- ...(marketplacePath ? { marketplacePath } : {}),
470
- ...(marketplaceName ? { marketplaceName } : {}),
471
- };
472
- }
473
-
474
- export function codexAppServerStartOptionsKey(
475
- options: CodexAppServerStartOptions,
476
- params: { authProfileId?: string; agentDir?: string } = {},
477
- ): string {
478
- return JSON.stringify({
479
- transport: options.transport,
480
- command: options.command,
481
- commandSource: options.commandSource ?? null,
482
- args: options.args,
483
- url: options.url ?? null,
484
- authToken: hashSecretForKey(options.authToken, "authToken"),
485
- headers: Object.entries(options.headers).toSorted(([left], [right]) =>
486
- left.localeCompare(right),
487
- ),
488
- env: Object.entries(options.env ?? {})
489
- .toSorted(([left], [right]) => left.localeCompare(right))
490
- .map(([key, value]) => [key, hashSecretForKey(value, `env:${key}`)]),
491
- clearEnv: [...(options.clearEnv ?? [])].toSorted(),
492
- authProfileId: params.authProfileId ?? null,
493
- agentDir: params.agentDir ?? null,
494
- });
495
- }
496
-
497
- export function codexSandboxPolicyForTurn(
498
- mode: CodexAppServerSandboxMode,
499
- cwd: string,
500
- ): CodexSandboxPolicy {
501
- if (mode === "danger-full-access") {
502
- return { type: "dangerFullAccess" };
503
- }
504
- if (mode === "read-only") {
505
- return { type: "readOnly", networkAccess: false };
506
- }
507
- return {
508
- type: "workspaceWrite",
509
- writableRoots: [cwd],
510
- networkAccess: false,
511
- excludeTmpdirEnvVar: false,
512
- excludeSlashTmp: false,
513
- };
514
- }
515
-
516
- export function withMcpElicitationsApprovalPolicy(
517
- policy: CodexAppServerEffectiveApprovalPolicy,
518
- ): CodexAppServerEffectiveApprovalPolicy {
519
- if (typeof policy !== "string") {
520
- return {
521
- granular: {
522
- ...policy.granular,
523
- mcp_elicitations: true,
524
- },
525
- };
526
- }
527
- if (policy === "never") {
528
- return {
529
- granular: {
530
- mcp_elicitations: true,
531
- rules: false,
532
- sandbox_approval: false,
533
- },
534
- };
535
- }
536
- return {
537
- granular: {
538
- mcp_elicitations: true,
539
- rules: true,
540
- sandbox_approval: true,
541
- },
542
- };
543
- }
544
-
545
- function resolveTransport(value: unknown): CodexAppServerTransportMode {
546
- return value === "websocket" ? "websocket" : "stdio";
547
- }
548
-
549
- function resolvePolicyMode(value: unknown): CodexAppServerPolicyMode | undefined {
550
- return value === "guardian" || value === "yolo" ? value : undefined;
551
- }
552
-
553
- function resolveDefaultCodexAppServerPolicy(params: {
554
- transport: CodexAppServerTransportMode;
555
- env?: NodeJS.ProcessEnv;
556
- requirementsToml?: string | null;
557
- requirementsPath?: string;
558
- readRequirementsFile?: (path: string) => string | undefined;
559
- platform?: NodeJS.Platform;
560
- hostName?: string;
561
- }): CodexAppServerDefaultPolicy {
562
- if (params.transport !== "stdio") {
563
- return { mode: "yolo" };
564
- }
565
- const content = readCodexRequirementsToml(params);
566
- if (content === undefined) {
567
- return { mode: "yolo" };
568
- }
569
- const allowedSandboxModes = parseAllowedSandboxModesFromCodexRequirements(
570
- content,
571
- readNonEmptyString(params.hostName) ?? readHostName(),
572
- );
573
- const allowedApprovalPolicies = parseAllowedApprovalPoliciesFromCodexRequirements(content);
574
- const allowedApprovalsReviewers = parseAllowedApprovalsReviewersFromCodexRequirements(content);
575
- const yoloSandboxAllowed =
576
- allowedSandboxModes === undefined || allowedSandboxModes.has("danger-full-access");
577
- const yoloApprovalAllowed =
578
- allowedApprovalPolicies === undefined || allowedApprovalPolicies.has("never");
579
- const yoloReviewerAllowed =
580
- allowedApprovalsReviewers === undefined || allowedApprovalsReviewers.has("user");
581
- if (yoloSandboxAllowed && yoloApprovalAllowed && yoloReviewerAllowed) {
582
- return { mode: "yolo" };
583
- }
584
- return {
585
- mode: "guardian",
586
- approvalPolicy: selectGuardianApprovalPolicy(allowedApprovalPolicies),
587
- approvalsReviewer: selectGuardianApprovalsReviewer(allowedApprovalsReviewers),
588
- sandbox: selectGuardianSandbox(allowedSandboxModes),
589
- };
590
- }
591
-
592
- function readCodexRequirementsToml(params: {
593
- env?: NodeJS.ProcessEnv;
594
- requirementsToml?: string | null;
595
- requirementsPath?: string;
596
- readRequirementsFile?: (path: string) => string | undefined;
597
- platform?: NodeJS.Platform;
598
- }): string | undefined {
599
- if (params.requirementsToml !== undefined) {
600
- return params.requirementsToml ?? undefined;
601
- }
602
- const path =
603
- readNonEmptyString(params.requirementsPath) ??
604
- resolveCodexRequirementsPath(params.env ?? process.env, params.platform ?? process.platform);
605
- try {
606
- if (params.readRequirementsFile) {
607
- return params.readRequirementsFile(path);
608
- }
609
- return readFileSync(path, "utf8");
610
- } catch {
611
- return undefined;
612
- }
613
- }
614
-
615
- function resolveCodexRequirementsPath(env: NodeJS.ProcessEnv, platform: NodeJS.Platform): string {
616
- if (platform === "win32") {
617
- const programData = readNonEmptyString(env.ProgramData) ?? "C:\\ProgramData";
618
- return `${programData.replace(/[\\/]+$/, "")}${WINDOWS_CODEX_REQUIREMENTS_SUFFIX}`;
619
- }
620
- return UNIX_CODEX_REQUIREMENTS_PATH;
621
- }
622
-
623
- function parseAllowedSandboxModesFromCodexRequirements(
624
- content: string,
625
- hostName: string,
626
- ): Set<CodexAppServerSandboxMode> | undefined {
627
- const remoteSandboxModes = parseMatchingRemoteSandboxModesFromCodexRequirements(
628
- content,
629
- hostName,
630
- );
631
- if (remoteSandboxModes !== undefined) {
632
- return remoteSandboxModes;
633
- }
634
- const values = parseTopLevelRequirementsStringArray(content, "allowed_sandbox_modes");
635
- return parseRequirementsSandboxModes(values);
636
- }
637
-
638
- function parseAllowedApprovalPoliciesFromCodexRequirements(
639
- content: string,
640
- ): Set<CodexAppServerApprovalPolicy> | undefined {
641
- const values = parseTopLevelRequirementsStringArray(content, "allowed_approval_policies");
642
- if (values === undefined) {
643
- return undefined;
644
- }
645
- const normalizedPolicies = values
646
- .map((entry) => normalizeRequirementsApprovalPolicy(entry))
647
- .filter((entry): entry is CodexAppServerApprovalPolicy => entry !== undefined);
648
- return normalizedPolicies.length > 0 ? new Set(normalizedPolicies) : undefined;
649
- }
650
-
651
- function parseAllowedApprovalsReviewersFromCodexRequirements(
652
- content: string,
653
- ): Set<CodexAppServerApprovalsReviewer> | undefined {
654
- const values = parseTopLevelRequirementsStringArray(content, "allowed_approvals_reviewers");
655
- if (values === undefined) {
656
- return undefined;
657
- }
658
- const normalizedReviewers = values
659
- .map((entry) => normalizeRequirementsApprovalsReviewer(entry))
660
- .filter((entry): entry is CodexAppServerApprovalsReviewer => entry !== undefined);
661
- return normalizedReviewers.length > 0 ? new Set(normalizedReviewers) : undefined;
662
- }
663
-
664
- function parseMatchingRemoteSandboxModesFromCodexRequirements(
665
- content: string,
666
- hostName: string,
667
- ): Set<CodexAppServerSandboxMode> | undefined {
668
- const normalizedHostName = normalizeRequirementsHostName(hostName);
669
- if (normalizedHostName === undefined) {
670
- return undefined;
671
- }
672
- for (const section of parseTomlArrayTableSections(content, "remote_sandbox_config")) {
673
- const patterns = parseRequirementsStringArray(section, "hostname_patterns");
674
- if (!patterns || !requirementsHostNameMatchesAnyPattern(normalizedHostName, patterns)) {
675
- continue;
676
- }
677
- return parseRequirementsSandboxModes(
678
- parseRequirementsStringArray(section, "allowed_sandbox_modes"),
679
- );
680
- }
681
- return undefined;
682
- }
683
-
684
- function parseRequirementsSandboxModes(
685
- values: string[] | undefined,
686
- ): Set<CodexAppServerSandboxMode> | undefined {
687
- if (values === undefined) {
688
- return undefined;
689
- }
690
- const normalizedModes = values
691
- .map((entry) => normalizeRequirementsSandboxMode(entry))
692
- .filter((entry): entry is CodexAppServerSandboxMode => entry !== undefined);
693
- return normalizedModes.length > 0 ? new Set(normalizedModes) : undefined;
694
- }
695
-
696
- function parseTopLevelRequirementsStringArray(content: string, key: string): string[] | undefined {
697
- const topLevelContent = stripTomlLineComments(content).slice(0, firstTomlTableOffset(content));
698
- return parseRequirementsStringArray(topLevelContent, key);
699
- }
700
-
701
- function parseRequirementsStringArray(content: string, key: string): string[] | undefined {
702
- const match = content.match(new RegExp(`(?:^|\\n)\\s*${key}\\s*=\\s*\\[([\\s\\S]*?)\\]`));
703
- if (!match) {
704
- return undefined;
705
- }
706
- const arrayBody = match[1] ?? "";
707
- const stringMatches = [...arrayBody.matchAll(/"([^"\\]*(?:\\.[^"\\]*)*)"|'([^']*)'/g)];
708
- if (stringMatches.length === 0 && arrayBody.trim().length > 0) {
709
- return undefined;
710
- }
711
- return stringMatches.map((entry) => entry[1] ?? entry[2] ?? "");
712
- }
713
-
714
- function parseTomlArrayTableSections(content: string, table: string): string[] {
715
- const strippedContent = stripTomlLineComments(content);
716
- const escapedTable = table.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
717
- const headerPattern = new RegExp(`^\\s*\\[\\[\\s*${escapedTable}\\s*\\]\\]\\s*$`, "gm");
718
- const sections: string[] = [];
719
- for (
720
- let match = headerPattern.exec(strippedContent);
721
- match;
722
- match = headerPattern.exec(strippedContent)
723
- ) {
724
- const sectionStart = headerPattern.lastIndex;
725
- const rest = strippedContent.slice(sectionStart);
726
- const nextTableOffset = rest.search(/^\s*\[/m);
727
- sections.push(nextTableOffset === -1 ? rest : rest.slice(0, nextTableOffset));
728
- }
729
- return sections;
730
- }
731
-
732
- function firstTomlTableOffset(content: string): number {
733
- const match = content.match(/^\s*\[[^\]\n]/m);
734
- return match?.index ?? content.length;
735
- }
736
-
737
- function stripTomlLineComments(value: string): string {
738
- let output = "";
739
- let quote: '"' | "'" | undefined;
740
- let escaped = false;
741
- for (let index = 0; index < value.length; index += 1) {
742
- const char = value[index] ?? "";
743
- if (quote) {
744
- output += char;
745
- if (quote === '"' && escaped) {
746
- escaped = false;
747
- continue;
748
- }
749
- if (quote === '"' && char === "\\") {
750
- escaped = true;
751
- continue;
752
- }
753
- if (char === quote) {
754
- quote = undefined;
755
- }
756
- continue;
757
- }
758
- if (char === '"' || char === "'") {
759
- quote = char;
760
- output += char;
761
- continue;
762
- }
763
- if (char === "#") {
764
- while (index < value.length && value[index] !== "\n") {
765
- index += 1;
766
- }
767
- if (value[index] === "\n") {
768
- output += "\n";
769
- }
770
- continue;
771
- }
772
- output += char;
773
- }
774
- return output;
775
- }
776
-
777
- function normalizeRequirementsSandboxMode(value: string): CodexAppServerSandboxMode | undefined {
778
- const compact = value.replace(/[\s_-]/g, "").toLowerCase();
779
- if (compact === "readonly") {
780
- return "read-only";
781
- }
782
- if (compact === "workspacewrite") {
783
- return "workspace-write";
784
- }
785
- if (compact === "dangerfullaccess") {
786
- return "danger-full-access";
787
- }
788
- return undefined;
789
- }
790
-
791
- function normalizeRequirementsHostName(value: string): string | undefined {
792
- const normalized = value.trim().replace(/\.+$/g, "").toLowerCase();
793
- return normalized.length > 0 ? normalized : undefined;
794
- }
795
-
796
- function requirementsHostNameMatchesAnyPattern(hostName: string, patterns: string[]): boolean {
797
- return patterns.some((pattern) => {
798
- const normalizedPattern = normalizeRequirementsHostName(pattern);
799
- return normalizedPattern !== undefined && globPatternMatches(hostName, normalizedPattern);
800
- });
801
- }
802
-
803
- function globPatternMatches(value: string, pattern: string): boolean {
804
- let regex = "^";
805
- for (const char of pattern) {
806
- if (char === "*") {
807
- regex += ".*";
808
- } else if (char === "?") {
809
- regex += ".";
810
- } else {
811
- regex += char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
812
- }
813
- }
814
- regex += "$";
815
- return new RegExp(regex).test(value);
816
- }
817
-
818
- function normalizeRequirementsApprovalPolicy(
819
- value: string,
820
- ): CodexAppServerApprovalPolicy | undefined {
821
- const normalized = value.trim().toLowerCase();
822
- return resolveApprovalPolicy(normalized);
823
- }
824
-
825
- function normalizeRequirementsApprovalsReviewer(
826
- value: string,
827
- ): CodexAppServerApprovalsReviewer | undefined {
828
- const normalized = value.trim().toLowerCase();
829
- return resolveApprovalsReviewer(normalized);
830
- }
831
-
832
- function selectGuardianApprovalPolicy(
833
- allowedApprovalPolicies: Set<CodexAppServerApprovalPolicy> | undefined,
834
- ): CodexAppServerApprovalPolicy {
835
- if (allowedApprovalPolicies === undefined || allowedApprovalPolicies.has("on-request")) {
836
- return "on-request";
837
- }
838
- if (allowedApprovalPolicies.has("on-failure")) {
839
- return "on-failure";
840
- }
841
- if (allowedApprovalPolicies.has("untrusted")) {
842
- return "untrusted";
843
- }
844
- if (allowedApprovalPolicies.has("never")) {
845
- return "never";
846
- }
847
- return "on-request";
848
- }
849
-
850
- function selectGuardianApprovalsReviewer(
851
- allowedApprovalsReviewers: Set<CodexAppServerApprovalsReviewer> | undefined,
852
- ): CodexAppServerApprovalsReviewer {
853
- if (allowedApprovalsReviewers === undefined || allowedApprovalsReviewers.has("auto_review")) {
854
- return "auto_review";
855
- }
856
- if (allowedApprovalsReviewers.has("guardian_subagent")) {
857
- return "guardian_subagent";
858
- }
859
- if (allowedApprovalsReviewers.has("user")) {
860
- return "user";
861
- }
862
- return "auto_review";
863
- }
864
-
865
- function selectGuardianSandbox(
866
- allowedSandboxModes: Set<CodexAppServerSandboxMode> | undefined,
867
- ): CodexAppServerSandboxMode {
868
- if (allowedSandboxModes === undefined || allowedSandboxModes.has("workspace-write")) {
869
- return "workspace-write";
870
- }
871
- if (allowedSandboxModes.has("read-only")) {
872
- return "read-only";
873
- }
874
- if (allowedSandboxModes.has("danger-full-access")) {
875
- return "danger-full-access";
876
- }
877
- return "workspace-write";
878
- }
879
-
880
- function resolveApprovalPolicy(value: unknown): CodexAppServerApprovalPolicy | undefined {
881
- return value === "on-request" ||
882
- value === "on-failure" ||
883
- value === "untrusted" ||
884
- value === "never"
885
- ? value
886
- : undefined;
887
- }
888
-
889
- function resolveSandbox(value: unknown): CodexAppServerSandboxMode | undefined {
890
- return value === "read-only" || value === "workspace-write" || value === "danger-full-access"
891
- ? value
892
- : undefined;
893
- }
894
-
895
- function resolveApprovalsReviewer(value: unknown): CodexAppServerApprovalsReviewer | undefined {
896
- return value === "auto_review" || value === "guardian_subagent" || value === "user"
897
- ? value
898
- : undefined;
899
- }
900
-
901
- export function normalizeCodexServiceTier(value: unknown): CodexServiceTier | undefined {
902
- if (typeof value !== "string") {
903
- return undefined;
904
- }
905
- const trimmed = value.trim();
906
- if (!trimmed) {
907
- return undefined;
908
- }
909
- const normalized = trimmed.toLowerCase();
910
- if (normalized === "fast" || normalized === "priority") {
911
- return "priority";
912
- }
913
- if (normalized === "flex") {
914
- return "flex";
915
- }
916
- return trimmed;
917
- }
918
-
919
- export function isCodexFastServiceTier(value: unknown): boolean {
920
- return normalizeCodexServiceTier(value) === "priority";
921
- }
922
-
923
- function normalizePositiveNumber(value: unknown, fallback: number): number {
924
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
925
- }
926
-
927
- function normalizeHeaders(value: unknown): Record<string, string> {
928
- if (!value || typeof value !== "object" || Array.isArray(value)) {
929
- return {};
930
- }
931
- return Object.fromEntries(
932
- Object.entries(value)
933
- .map(([key, child]) => [key.trim(), readNonEmptyString(child)] as const)
934
- .filter((entry): entry is readonly [string, string] => Boolean(entry[0] && entry[1])),
935
- );
936
- }
937
-
938
- function normalizeStringList(value: unknown): string[] {
939
- if (!Array.isArray(value)) {
940
- return [];
941
- }
942
- return value
943
- .map((entry) => readNonEmptyString(entry))
944
- .filter((entry): entry is string => entry !== undefined);
945
- }
946
-
947
- function readBooleanEnv(value: string | undefined): boolean | undefined {
948
- if (value === undefined) {
949
- return undefined;
950
- }
951
- const normalized = value.trim().toLowerCase();
952
- if (["1", "true", "yes", "on"].includes(normalized)) {
953
- return true;
954
- }
955
- if (["0", "false", "no", "off"].includes(normalized)) {
956
- return false;
957
- }
958
- return undefined;
959
- }
960
-
961
- function readNumberEnv(value: string | undefined): number | undefined {
962
- if (value === undefined) {
963
- return undefined;
964
- }
965
- const parsed = Number(value);
966
- return Number.isFinite(parsed) ? parsed : undefined;
967
- }
968
-
969
- function resolveArgs(configArgs: unknown, envArgs: string | undefined): string[] {
970
- if (Array.isArray(configArgs)) {
971
- return configArgs
972
- .map((entry) => readNonEmptyString(entry))
973
- .filter((entry): entry is string => entry !== undefined);
974
- }
975
- if (typeof configArgs === "string") {
976
- return splitShellWords(configArgs);
977
- }
978
- return splitShellWords(envArgs ?? "");
979
- }
980
-
981
- function readNonEmptyString(value: unknown): string | undefined {
982
- if (typeof value !== "string") {
983
- return undefined;
984
- }
985
- const trimmed = value.trim();
986
- return trimmed || undefined;
987
- }
988
-
989
- function hashSecretForKey(value: string | undefined, label: string): string | null {
990
- if (!value) {
991
- return null;
992
- }
993
- return createHmac("sha256", START_OPTIONS_KEY_SECRET)
994
- .update(label)
995
- .update("\0")
996
- .update(value)
997
- .digest("hex");
998
- }
999
-
1000
- function getStartOptionsKeySecret(): Buffer {
1001
- const globalState = globalThis as typeof globalThis & {
1002
- [START_OPTIONS_KEY_SECRET_SYMBOL]?: Buffer;
1003
- };
1004
- globalState[START_OPTIONS_KEY_SECRET_SYMBOL] ??= randomBytes(32);
1005
- return globalState[START_OPTIONS_KEY_SECRET_SYMBOL];
1006
- }
1007
-
1008
- function splitShellWords(value: string): string[] {
1009
- const words: string[] = [];
1010
- let current = "";
1011
- let quote: '"' | "'" | null = null;
1012
- for (const char of value) {
1013
- if (quote) {
1014
- if (char === quote) {
1015
- quote = null;
1016
- } else {
1017
- current += char;
1018
- }
1019
- continue;
1020
- }
1021
- if (char === '"' || char === "'") {
1022
- quote = char;
1023
- continue;
1024
- }
1025
- if (/\s/.test(char)) {
1026
- if (current) {
1027
- words.push(current);
1028
- current = "";
1029
- }
1030
- continue;
1031
- }
1032
- current += char;
1033
- }
1034
- if (current) {
1035
- words.push(current);
1036
- }
1037
- return words;
1038
- }