codeksei 0.1.0 → 0.1.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 (68) hide show
  1. package/LICENSE +661 -661
  2. package/README.en.md +109 -47
  3. package/README.md +79 -58
  4. package/bin/cyberboss.js +1 -1
  5. package/package.json +86 -86
  6. package/scripts/open_shared_wechat_thread.sh +77 -77
  7. package/scripts/open_wechat_thread.sh +108 -108
  8. package/scripts/shared-common.js +144 -144
  9. package/scripts/shared-open.js +14 -14
  10. package/scripts/shared-start.js +5 -5
  11. package/scripts/shared-status.js +27 -27
  12. package/scripts/show_shared_status.sh +45 -45
  13. package/scripts/start_shared_app_server.sh +52 -52
  14. package/scripts/start_shared_wechat.sh +94 -94
  15. package/scripts/timeline-screenshot.sh +14 -14
  16. package/src/adapters/channel/weixin/account-store.js +99 -99
  17. package/src/adapters/channel/weixin/api-v2.js +50 -50
  18. package/src/adapters/channel/weixin/api.js +169 -169
  19. package/src/adapters/channel/weixin/context-token-store.js +84 -84
  20. package/src/adapters/channel/weixin/index.js +618 -604
  21. package/src/adapters/channel/weixin/legacy.js +579 -566
  22. package/src/adapters/channel/weixin/media-mime.js +22 -22
  23. package/src/adapters/channel/weixin/media-receive.js +370 -370
  24. package/src/adapters/channel/weixin/media-send.js +102 -102
  25. package/src/adapters/channel/weixin/message-utils-v2.js +282 -282
  26. package/src/adapters/channel/weixin/message-utils.js +199 -199
  27. package/src/adapters/channel/weixin/redact.js +41 -41
  28. package/src/adapters/channel/weixin/reminder-queue-store.js +101 -101
  29. package/src/adapters/channel/weixin/sync-buffer-store.js +35 -35
  30. package/src/adapters/runtime/codex/events.js +215 -215
  31. package/src/adapters/runtime/codex/index.js +109 -104
  32. package/src/adapters/runtime/codex/message-utils.js +95 -95
  33. package/src/adapters/runtime/codex/model-catalog.js +106 -106
  34. package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -75
  35. package/src/adapters/runtime/codex/rpc-client.js +339 -339
  36. package/src/adapters/runtime/codex/session-store.js +286 -286
  37. package/src/app/channel-send-file-cli.js +57 -57
  38. package/src/app/diary-write-cli.js +236 -88
  39. package/src/app/note-sync-cli.js +2 -2
  40. package/src/app/reminder-write-cli.js +215 -210
  41. package/src/app/review-cli.js +7 -5
  42. package/src/app/system-checkin-poller.js +64 -64
  43. package/src/app/system-send-cli.js +129 -129
  44. package/src/app/timeline-event-cli.js +28 -25
  45. package/src/app/timeline-screenshot-cli.js +103 -100
  46. package/src/core/app.js +1763 -1763
  47. package/src/core/branding.js +2 -1
  48. package/src/core/command-registry.js +381 -369
  49. package/src/core/config.js +30 -14
  50. package/src/core/default-targets.js +163 -163
  51. package/src/core/durable-note-schema.js +9 -8
  52. package/src/core/instructions-template.js +17 -16
  53. package/src/core/note-sync.js +8 -7
  54. package/src/core/path-utils.js +54 -0
  55. package/src/core/project-radar.js +11 -10
  56. package/src/core/review.js +48 -50
  57. package/src/core/stream-delivery.js +1162 -983
  58. package/src/core/system-message-dispatcher.js +68 -68
  59. package/src/core/system-message-queue-store.js +128 -128
  60. package/src/core/thread-state-store.js +96 -96
  61. package/src/core/timeline-screenshot-queue-store.js +134 -134
  62. package/src/core/timezone.js +436 -0
  63. package/src/core/workspace-bootstrap.js +9 -1
  64. package/src/index.js +148 -146
  65. package/src/integrations/timeline/index.js +130 -74
  66. package/src/integrations/timeline/state-sync.js +240 -0
  67. package/templates/weixin-instructions.md +12 -38
  68. package/templates/weixin-operations.md +29 -31
@@ -8,22 +8,22 @@ const IS_WINDOWS = os.platform() === "win32";
8
8
  const DEFAULT_CODEX_COMMAND = "codex";
9
9
  const WINDOWS_EXECUTABLE_SUFFIX_RE = /\.(cmd|exe|bat)$/i;
10
10
  const CODEX_CLIENT_INFO = PRIMARY_RPC_CLIENT_INFO;
11
-
11
+
12
12
  class CodexRpcClient {
13
- constructor({ endpoint = "", env = process.env, codexCommand = "", extraWritableRoots = [] }) {
14
- this.endpoint = endpoint;
15
- this.env = env;
16
- this.codexCommand = codexCommand || resolveDefaultCodexCommand(env);
17
- this.extraWritableRoots = normalizeWritableRoots(extraWritableRoots);
18
- this.mode = endpoint ? "websocket" : "spawn";
19
- this.socket = null;
20
- this.child = null;
21
- this.stdoutBuffer = "";
22
- this.pending = new Map();
23
- this.isReady = false;
24
- this.messageListeners = new Set();
25
- }
26
-
13
+ constructor({ endpoint = "", env = process.env, codexCommand = "", extraWritableRoots = [] }) {
14
+ this.endpoint = endpoint;
15
+ this.env = env;
16
+ this.codexCommand = codexCommand || resolveDefaultCodexCommand(env);
17
+ this.extraWritableRoots = normalizeWritableRoots(extraWritableRoots);
18
+ this.mode = endpoint ? "websocket" : "spawn";
19
+ this.socket = null;
20
+ this.child = null;
21
+ this.stdoutBuffer = "";
22
+ this.pending = new Map();
23
+ this.isReady = false;
24
+ this.messageListeners = new Set();
25
+ }
26
+
27
27
  async connect() {
28
28
  if (this.isConnected()) {
29
29
  return;
@@ -33,57 +33,57 @@ class CodexRpcClient {
33
33
  return;
34
34
  }
35
35
  await this.connectSpawn();
36
- }
37
-
38
- async connectSpawn() {
39
- const commandCandidates = buildCodexCommandCandidates(this.codexCommand);
40
- let child = null;
41
- let lastError = null;
42
-
43
- for (const command of commandCandidates) {
44
- try {
45
- const spawnSpec = buildSpawnSpec(command);
36
+ }
37
+
38
+ async connectSpawn() {
39
+ const commandCandidates = buildCodexCommandCandidates(this.codexCommand);
40
+ let child = null;
41
+ let lastError = null;
42
+
43
+ for (const command of commandCandidates) {
44
+ try {
45
+ const spawnSpec = buildSpawnSpec(command);
46
46
  child = spawn(spawnSpec.command, spawnSpec.args, {
47
47
  env: { ...this.env },
48
48
  stdio: ["pipe", "pipe", "pipe"],
49
49
  shell: false,
50
50
  windowsHide: true,
51
51
  });
52
- break;
53
- } catch (error) {
54
- lastError = error;
55
- if (error?.code !== "ENOENT" && error?.code !== "EINVAL") {
56
- throw error;
57
- }
58
- }
59
- }
60
-
61
- if (!child) {
62
- const attempted = commandCandidates.join(", ");
63
- const detail = lastError?.message ? `: ${lastError.message}` : "";
64
- throw new Error(`Unable to spawn Codex app-server. Tried ${attempted}${detail}.`);
65
- }
66
-
67
- this.child = child;
68
- child.on("error", () => {
69
- this.isReady = false;
70
- });
71
- child.stdout.on("data", (chunk) => {
72
- this.stdoutBuffer += chunk.toString("utf8");
73
- const lines = this.stdoutBuffer.split("\n");
74
- this.stdoutBuffer = lines.pop() || "";
75
- for (const line of lines) {
76
- const trimmed = line.trim();
77
- if (trimmed) {
78
- this.handleIncoming(trimmed);
79
- }
80
- }
81
- });
82
- child.on("close", () => {
83
- this.isReady = false;
84
- });
85
- }
86
-
52
+ break;
53
+ } catch (error) {
54
+ lastError = error;
55
+ if (error?.code !== "ENOENT" && error?.code !== "EINVAL") {
56
+ throw error;
57
+ }
58
+ }
59
+ }
60
+
61
+ if (!child) {
62
+ const attempted = commandCandidates.join(", ");
63
+ const detail = lastError?.message ? `: ${lastError.message}` : "";
64
+ throw new Error(`Unable to spawn Codex app-server. Tried ${attempted}${detail}.`);
65
+ }
66
+
67
+ this.child = child;
68
+ child.on("error", () => {
69
+ this.isReady = false;
70
+ });
71
+ child.stdout.on("data", (chunk) => {
72
+ this.stdoutBuffer += chunk.toString("utf8");
73
+ const lines = this.stdoutBuffer.split("\n");
74
+ this.stdoutBuffer = lines.pop() || "";
75
+ for (const line of lines) {
76
+ const trimmed = line.trim();
77
+ if (trimmed) {
78
+ this.handleIncoming(trimmed);
79
+ }
80
+ }
81
+ });
82
+ child.on("close", () => {
83
+ this.isReady = false;
84
+ });
85
+ }
86
+
87
87
  async connectWebSocket() {
88
88
  if (this.socket && this.socket.readyState === WebSocket.OPEN) {
89
89
  return;
@@ -135,142 +135,142 @@ class CodexRpcClient {
135
135
  }
136
136
  return Boolean(this.child && this.child.stdin && this.child.stdin.writable);
137
137
  }
138
-
139
- onMessage(listener) {
140
- this.messageListeners.add(listener);
141
- return () => this.messageListeners.delete(listener);
142
- }
143
-
144
- async initialize() {
145
- if (this.isReady) {
146
- return;
147
- }
148
- await this.sendRequest("initialize", {
149
- clientInfo: CODEX_CLIENT_INFO,
150
- capabilities: {
151
- experimentalApi: true,
152
- },
153
- });
154
- await this.sendNotification("initialized", null);
155
- this.isReady = true;
156
- }
157
-
158
- async sendUserMessage({ threadId, text, model = null, effort = null, accessMode = null, workspaceRoot = "" }) {
159
- const input = buildTurnInputPayload(text);
160
- return threadId
161
- ? this.sendRequest("turn/start", buildTurnStartParams({
162
- threadId,
163
- input,
164
- model,
165
- effort,
166
- accessMode,
167
- workspaceRoot,
168
- extraWritableRoots: this.extraWritableRoots,
169
- }))
170
- : this.sendRequest("thread/start", { input });
171
- }
172
-
173
- async startThread({ cwd }) {
174
- return this.sendRequest("thread/start", buildStartThreadParams(cwd));
175
- }
176
-
177
- async resumeThread({ threadId }) {
178
- const normalizedThreadId = normalizeNonEmptyString(threadId);
179
- if (!normalizedThreadId) {
180
- throw new Error("thread/resume requires a non-empty threadId");
181
- }
182
- return this.sendRequest("thread/resume", { threadId: normalizedThreadId });
183
- }
184
-
185
- async listThreads({ cursor = null, limit = 100, sortKey = "updated_at" } = {}) {
186
- return this.sendRequest("thread/list", buildListThreadsParams({
187
- cursor,
188
- limit,
189
- sortKey,
190
- }));
191
- }
192
-
193
- async listModels() {
194
- return this.sendRequest("model/list", {});
195
- }
196
-
197
- async cancelTurn({ threadId, turnId }) {
198
- const normalizedThreadId = normalizeNonEmptyString(threadId);
199
- const normalizedTurnId = normalizeNonEmptyString(turnId);
200
- if (!normalizedThreadId || !normalizedTurnId) {
201
- throw new Error("turn/cancel requires threadId and turnId");
202
- }
203
- return this.sendRequest("turn/cancel", {
204
- threadId: normalizedThreadId,
205
- turnId: normalizedTurnId,
206
- });
207
- }
208
-
209
- async cancelTurn({ threadId, turnId }) {
210
- const normalizedThreadId = normalizeNonEmptyString(threadId);
211
- const normalizedTurnId = normalizeNonEmptyString(turnId);
212
- if (!normalizedThreadId || !normalizedTurnId) {
213
- throw new Error("turn/cancel requires threadId and turnId");
214
- }
215
- return this.sendRequest("turn/cancel", {
216
- threadId: normalizedThreadId,
217
- turnId: normalizedTurnId,
218
- });
219
- }
220
-
138
+
139
+ onMessage(listener) {
140
+ this.messageListeners.add(listener);
141
+ return () => this.messageListeners.delete(listener);
142
+ }
143
+
144
+ async initialize() {
145
+ if (this.isReady) {
146
+ return;
147
+ }
148
+ await this.sendRequest("initialize", {
149
+ clientInfo: CODEX_CLIENT_INFO,
150
+ capabilities: {
151
+ experimentalApi: true,
152
+ },
153
+ });
154
+ await this.sendNotification("initialized", null);
155
+ this.isReady = true;
156
+ }
157
+
158
+ async sendUserMessage({ threadId, text, model = null, effort = null, accessMode = null, workspaceRoot = "" }) {
159
+ const input = buildTurnInputPayload(text);
160
+ return threadId
161
+ ? this.sendRequest("turn/start", buildTurnStartParams({
162
+ threadId,
163
+ input,
164
+ model,
165
+ effort,
166
+ accessMode,
167
+ workspaceRoot,
168
+ extraWritableRoots: this.extraWritableRoots,
169
+ }))
170
+ : this.sendRequest("thread/start", { input });
171
+ }
172
+
173
+ async startThread({ cwd }) {
174
+ return this.sendRequest("thread/start", buildStartThreadParams(cwd));
175
+ }
176
+
177
+ async resumeThread({ threadId }) {
178
+ const normalizedThreadId = normalizeNonEmptyString(threadId);
179
+ if (!normalizedThreadId) {
180
+ throw new Error("thread/resume requires a non-empty threadId");
181
+ }
182
+ return this.sendRequest("thread/resume", { threadId: normalizedThreadId });
183
+ }
184
+
185
+ async listThreads({ cursor = null, limit = 100, sortKey = "updated_at" } = {}) {
186
+ return this.sendRequest("thread/list", buildListThreadsParams({
187
+ cursor,
188
+ limit,
189
+ sortKey,
190
+ }));
191
+ }
192
+
193
+ async listModels() {
194
+ return this.sendRequest("model/list", {});
195
+ }
196
+
197
+ async cancelTurn({ threadId, turnId }) {
198
+ const normalizedThreadId = normalizeNonEmptyString(threadId);
199
+ const normalizedTurnId = normalizeNonEmptyString(turnId);
200
+ if (!normalizedThreadId || !normalizedTurnId) {
201
+ throw new Error("turn/cancel requires threadId and turnId");
202
+ }
203
+ return this.sendRequest("turn/cancel", {
204
+ threadId: normalizedThreadId,
205
+ turnId: normalizedTurnId,
206
+ });
207
+ }
208
+
209
+ async cancelTurn({ threadId, turnId }) {
210
+ const normalizedThreadId = normalizeNonEmptyString(threadId);
211
+ const normalizedTurnId = normalizeNonEmptyString(turnId);
212
+ if (!normalizedThreadId || !normalizedTurnId) {
213
+ throw new Error("turn/cancel requires threadId and turnId");
214
+ }
215
+ return this.sendRequest("turn/cancel", {
216
+ threadId: normalizedThreadId,
217
+ turnId: normalizedTurnId,
218
+ });
219
+ }
220
+
221
221
  async close() {
222
222
  this.rejectPending(new Error("Codex RPC client closed"));
223
223
  if (this.socket) {
224
224
  try {
225
225
  this.socket.close();
226
- } catch {
227
- // best effort
228
- }
229
- this.socket = null;
230
- }
231
- if (this.child) {
232
- try {
233
- this.child.kill();
234
- } catch {
235
- // best effort
236
- }
237
- this.child = null;
238
- }
226
+ } catch {
227
+ // best effort
228
+ }
229
+ this.socket = null;
230
+ }
231
+ if (this.child) {
232
+ try {
233
+ this.child.kill();
234
+ } catch {
235
+ // best effort
236
+ }
237
+ this.child = null;
238
+ }
239
239
  this.isReady = false;
240
240
  }
241
-
242
- async sendRequest(method, params) {
243
- const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
244
- const payload = JSON.stringify({ id, method, params });
245
- const responsePromise = new Promise((resolve, reject) => {
246
- this.pending.set(id, { resolve, reject });
247
- });
248
- this.sendRaw(payload);
249
- return responsePromise;
250
- }
251
-
252
- async sendNotification(method, params) {
253
- this.sendRaw(JSON.stringify({ method, params }));
254
- }
255
-
256
- async sendResponse(id, result) {
257
- if (id == null || id === "") {
258
- throw new Error("Codex RPC response requires a non-empty id");
259
- }
260
- this.sendRaw(JSON.stringify({ id, result }));
261
- }
262
-
241
+
242
+ async sendRequest(method, params) {
243
+ const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
244
+ const payload = JSON.stringify({ id, method, params });
245
+ const responsePromise = new Promise((resolve, reject) => {
246
+ this.pending.set(id, { resolve, reject });
247
+ });
248
+ this.sendRaw(payload);
249
+ return responsePromise;
250
+ }
251
+
252
+ async sendNotification(method, params) {
253
+ this.sendRaw(JSON.stringify({ method, params }));
254
+ }
255
+
256
+ async sendResponse(id, result) {
257
+ if (id == null || id === "") {
258
+ throw new Error("Codex RPC response requires a non-empty id");
259
+ }
260
+ this.sendRaw(JSON.stringify({ id, result }));
261
+ }
262
+
263
263
  sendRaw(payload) {
264
- if (this.mode === "websocket") {
265
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
266
- throw new Error("Codex websocket is not connected");
267
- }
268
- this.socket.send(payload);
269
- return;
270
- }
271
- if (!this.child || !this.child.stdin.writable) {
272
- throw new Error("Codex process stdin is not writable");
273
- }
264
+ if (this.mode === "websocket") {
265
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
266
+ throw new Error("Codex websocket is not connected");
267
+ }
268
+ this.socket.send(payload);
269
+ return;
270
+ }
271
+ if (!this.child || !this.child.stdin.writable) {
272
+ throw new Error("Codex process stdin is not writable");
273
+ }
274
274
  this.child.stdin.write(`${payload}\n`);
275
275
  }
276
276
 
@@ -287,157 +287,157 @@ class CodexRpcClient {
287
287
  }
288
288
  this.pending.clear();
289
289
  }
290
-
291
- handleIncoming(rawMessage) {
292
- let parsed = null;
293
- try {
294
- parsed = JSON.parse(rawMessage);
295
- } catch {
296
- return;
297
- }
298
-
299
- if (parsed && parsed.id != null && this.pending.has(String(parsed.id))) {
300
- const { resolve, reject } = this.pending.get(String(parsed.id));
301
- this.pending.delete(String(parsed.id));
302
- if (parsed.error) {
303
- reject(new Error(parsed.error.message || "Codex RPC request failed"));
304
- return;
305
- }
306
- resolve(parsed);
307
- return;
308
- }
309
-
310
- for (const listener of this.messageListeners) {
311
- listener(parsed);
312
- }
313
- }
314
- }
315
-
290
+
291
+ handleIncoming(rawMessage) {
292
+ let parsed = null;
293
+ try {
294
+ parsed = JSON.parse(rawMessage);
295
+ } catch {
296
+ return;
297
+ }
298
+
299
+ if (parsed && parsed.id != null && this.pending.has(String(parsed.id))) {
300
+ const { resolve, reject } = this.pending.get(String(parsed.id));
301
+ this.pending.delete(String(parsed.id));
302
+ if (parsed.error) {
303
+ reject(new Error(parsed.error.message || "Codex RPC request failed"));
304
+ return;
305
+ }
306
+ resolve(parsed);
307
+ return;
308
+ }
309
+
310
+ for (const listener of this.messageListeners) {
311
+ listener(parsed);
312
+ }
313
+ }
314
+ }
315
+
316
316
  function resolveDefaultCodexCommand(env = process.env) {
317
317
  return normalizeNonEmptyString(readPrefixedEnv(env, "CODEX_COMMAND")) || DEFAULT_CODEX_COMMAND;
318
318
  }
319
-
320
- function buildCodexCommandCandidates(configuredCommand) {
321
- const explicit = normalizeNonEmptyString(configuredCommand);
322
- if (explicit) {
323
- if (!IS_WINDOWS) {
324
- return [explicit];
325
- }
326
- const candidates = [explicit];
327
- if (!WINDOWS_EXECUTABLE_SUFFIX_RE.test(explicit)) {
328
- candidates.push(`${explicit}.cmd`, `${explicit}.exe`, `${explicit}.bat`);
329
- }
330
- return [...new Set(candidates)];
331
- }
332
- if (IS_WINDOWS) {
333
- return [DEFAULT_CODEX_COMMAND, `${DEFAULT_CODEX_COMMAND}.cmd`, `${DEFAULT_CODEX_COMMAND}.exe`, `${DEFAULT_CODEX_COMMAND}.bat`];
334
- }
335
- return [DEFAULT_CODEX_COMMAND];
336
- }
337
-
338
- function buildSpawnSpec(command) {
339
- if (IS_WINDOWS) {
340
- return {
341
- command: "cmd.exe",
342
- args: ["/c", command, "app-server"],
343
- };
344
- }
345
- return {
346
- command,
347
- args: ["app-server"],
348
- };
349
- }
350
-
351
- function normalizeNonEmptyString(value) {
352
- return typeof value === "string" && value.trim() ? value.trim() : "";
353
- }
354
-
355
- function buildStartThreadParams(cwd) {
356
- const normalizedCwd = normalizeNonEmptyString(cwd);
357
- return normalizedCwd ? { cwd: normalizedCwd } : {};
358
- }
359
-
360
- function buildListThreadsParams({ cursor, limit, sortKey }) {
361
- const params = { limit, sortKey };
362
- const normalizedCursor = normalizeNonEmptyString(cursor);
363
- if (normalizedCursor) {
364
- params.cursor = normalizedCursor;
365
- } else if (cursor != null) {
366
- params.cursor = cursor;
367
- }
368
- return params;
369
- }
370
-
371
- function buildTurnInputPayload(text) {
372
- const normalizedText = normalizeNonEmptyString(text);
373
- return normalizedText ? [{ type: "text", text: normalizedText }] : [];
374
- }
375
-
376
- function buildTurnStartParams({ threadId, input, model, effort, accessMode, workspaceRoot, extraWritableRoots = [] }) {
377
- const params = { threadId, input };
378
- const normalizedWorkspaceRoot = normalizeNonEmptyString(workspaceRoot);
379
- const normalizedModel = normalizeNonEmptyString(model);
380
- const normalizedEffort = normalizeNonEmptyString(effort);
381
- const normalizedAccessMode = normalizeAccessMode(accessMode);
382
- const executionPolicies = buildExecutionPolicies(normalizedAccessMode, workspaceRoot, extraWritableRoots);
383
- if (normalizedWorkspaceRoot) {
384
- params.cwd = normalizedWorkspaceRoot;
385
- }
386
- if (normalizedModel) {
387
- params.model = normalizedModel;
388
- }
389
- if (normalizedEffort) {
390
- params.effort = normalizedEffort;
391
- }
392
- if (normalizedAccessMode) {
393
- params.accessMode = normalizedAccessMode;
394
- }
395
- params.approvalPolicy = executionPolicies.approvalPolicy;
396
- params.sandboxPolicy = executionPolicies.sandboxPolicy;
397
- return params;
398
- }
399
-
400
- function normalizeAccessMode(value) {
401
- const normalized = normalizeNonEmptyString(value).toLowerCase();
402
- if (normalized === "default") {
403
- return "current";
404
- }
405
- return normalized === "full-access" ? normalized : "";
406
- }
407
-
408
- function buildExecutionPolicies(accessMode, workspaceRoot, extraWritableRoots = []) {
409
- if (accessMode === "full-access") {
410
- return {
411
- approvalPolicy: "never",
412
- sandboxPolicy: { type: "dangerFullAccess" },
413
- };
414
- }
415
- const normalizedWorkspaceRoot = normalizeNonEmptyString(workspaceRoot);
416
- const writableRoots = normalizeWritableRoots([
417
- normalizedWorkspaceRoot,
418
- ...extraWritableRoots,
419
- ]);
420
- const sandboxPolicy = writableRoots.length
421
- ? { type: "workspaceWrite", writableRoots, networkAccess: true }
422
- : { type: "workspaceWrite", networkAccess: true };
423
- return {
424
- approvalPolicy: "on-request",
425
- sandboxPolicy,
426
- };
427
- }
428
-
429
- function normalizeWritableRoots(values) {
430
- const roots = [];
431
- const seen = new Set();
432
- for (const value of values) {
433
- const normalized = normalizeNonEmptyString(value);
434
- if (!normalized || seen.has(normalized)) {
435
- continue;
436
- }
437
- seen.add(normalized);
438
- roots.push(normalized);
439
- }
440
- return roots;
441
- }
442
-
443
- module.exports = { CodexRpcClient };
319
+
320
+ function buildCodexCommandCandidates(configuredCommand) {
321
+ const explicit = normalizeNonEmptyString(configuredCommand);
322
+ if (explicit) {
323
+ if (!IS_WINDOWS) {
324
+ return [explicit];
325
+ }
326
+ const candidates = [explicit];
327
+ if (!WINDOWS_EXECUTABLE_SUFFIX_RE.test(explicit)) {
328
+ candidates.push(`${explicit}.cmd`, `${explicit}.exe`, `${explicit}.bat`);
329
+ }
330
+ return [...new Set(candidates)];
331
+ }
332
+ if (IS_WINDOWS) {
333
+ return [DEFAULT_CODEX_COMMAND, `${DEFAULT_CODEX_COMMAND}.cmd`, `${DEFAULT_CODEX_COMMAND}.exe`, `${DEFAULT_CODEX_COMMAND}.bat`];
334
+ }
335
+ return [DEFAULT_CODEX_COMMAND];
336
+ }
337
+
338
+ function buildSpawnSpec(command) {
339
+ if (IS_WINDOWS) {
340
+ return {
341
+ command: "cmd.exe",
342
+ args: ["/c", command, "app-server"],
343
+ };
344
+ }
345
+ return {
346
+ command,
347
+ args: ["app-server"],
348
+ };
349
+ }
350
+
351
+ function normalizeNonEmptyString(value) {
352
+ return typeof value === "string" && value.trim() ? value.trim() : "";
353
+ }
354
+
355
+ function buildStartThreadParams(cwd) {
356
+ const normalizedCwd = normalizeNonEmptyString(cwd);
357
+ return normalizedCwd ? { cwd: normalizedCwd } : {};
358
+ }
359
+
360
+ function buildListThreadsParams({ cursor, limit, sortKey }) {
361
+ const params = { limit, sortKey };
362
+ const normalizedCursor = normalizeNonEmptyString(cursor);
363
+ if (normalizedCursor) {
364
+ params.cursor = normalizedCursor;
365
+ } else if (cursor != null) {
366
+ params.cursor = cursor;
367
+ }
368
+ return params;
369
+ }
370
+
371
+ function buildTurnInputPayload(text) {
372
+ const normalizedText = normalizeNonEmptyString(text);
373
+ return normalizedText ? [{ type: "text", text: normalizedText }] : [];
374
+ }
375
+
376
+ function buildTurnStartParams({ threadId, input, model, effort, accessMode, workspaceRoot, extraWritableRoots = [] }) {
377
+ const params = { threadId, input };
378
+ const normalizedWorkspaceRoot = normalizeNonEmptyString(workspaceRoot);
379
+ const normalizedModel = normalizeNonEmptyString(model);
380
+ const normalizedEffort = normalizeNonEmptyString(effort);
381
+ const normalizedAccessMode = normalizeAccessMode(accessMode);
382
+ const executionPolicies = buildExecutionPolicies(normalizedAccessMode, workspaceRoot, extraWritableRoots);
383
+ if (normalizedWorkspaceRoot) {
384
+ params.cwd = normalizedWorkspaceRoot;
385
+ }
386
+ if (normalizedModel) {
387
+ params.model = normalizedModel;
388
+ }
389
+ if (normalizedEffort) {
390
+ params.effort = normalizedEffort;
391
+ }
392
+ if (normalizedAccessMode) {
393
+ params.accessMode = normalizedAccessMode;
394
+ }
395
+ params.approvalPolicy = executionPolicies.approvalPolicy;
396
+ params.sandboxPolicy = executionPolicies.sandboxPolicy;
397
+ return params;
398
+ }
399
+
400
+ function normalizeAccessMode(value) {
401
+ const normalized = normalizeNonEmptyString(value).toLowerCase();
402
+ if (normalized === "default") {
403
+ return "current";
404
+ }
405
+ return normalized === "full-access" ? normalized : "";
406
+ }
407
+
408
+ function buildExecutionPolicies(accessMode, workspaceRoot, extraWritableRoots = []) {
409
+ if (accessMode === "full-access") {
410
+ return {
411
+ approvalPolicy: "never",
412
+ sandboxPolicy: { type: "dangerFullAccess" },
413
+ };
414
+ }
415
+ const normalizedWorkspaceRoot = normalizeNonEmptyString(workspaceRoot);
416
+ const writableRoots = normalizeWritableRoots([
417
+ normalizedWorkspaceRoot,
418
+ ...extraWritableRoots,
419
+ ]);
420
+ const sandboxPolicy = writableRoots.length
421
+ ? { type: "workspaceWrite", writableRoots, networkAccess: true }
422
+ : { type: "workspaceWrite", networkAccess: true };
423
+ return {
424
+ approvalPolicy: "on-request",
425
+ sandboxPolicy,
426
+ };
427
+ }
428
+
429
+ function normalizeWritableRoots(values) {
430
+ const roots = [];
431
+ const seen = new Set();
432
+ for (const value of values) {
433
+ const normalized = normalizeNonEmptyString(value);
434
+ if (!normalized || seen.has(normalized)) {
435
+ continue;
436
+ }
437
+ seen.add(normalized);
438
+ roots.push(normalized);
439
+ }
440
+ return roots;
441
+ }
442
+
443
+ module.exports = { CodexRpcClient };