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.
- package/LICENSE +661 -661
- package/README.en.md +109 -47
- package/README.md +79 -58
- package/bin/cyberboss.js +1 -1
- package/package.json +86 -86
- package/scripts/open_shared_wechat_thread.sh +77 -77
- package/scripts/open_wechat_thread.sh +108 -108
- package/scripts/shared-common.js +144 -144
- package/scripts/shared-open.js +14 -14
- package/scripts/shared-start.js +5 -5
- package/scripts/shared-status.js +27 -27
- package/scripts/show_shared_status.sh +45 -45
- package/scripts/start_shared_app_server.sh +52 -52
- package/scripts/start_shared_wechat.sh +94 -94
- package/scripts/timeline-screenshot.sh +14 -14
- package/src/adapters/channel/weixin/account-store.js +99 -99
- package/src/adapters/channel/weixin/api-v2.js +50 -50
- package/src/adapters/channel/weixin/api.js +169 -169
- package/src/adapters/channel/weixin/context-token-store.js +84 -84
- package/src/adapters/channel/weixin/index.js +618 -604
- package/src/adapters/channel/weixin/legacy.js +579 -566
- package/src/adapters/channel/weixin/media-mime.js +22 -22
- package/src/adapters/channel/weixin/media-receive.js +370 -370
- package/src/adapters/channel/weixin/media-send.js +102 -102
- package/src/adapters/channel/weixin/message-utils-v2.js +282 -282
- package/src/adapters/channel/weixin/message-utils.js +199 -199
- package/src/adapters/channel/weixin/redact.js +41 -41
- package/src/adapters/channel/weixin/reminder-queue-store.js +101 -101
- package/src/adapters/channel/weixin/sync-buffer-store.js +35 -35
- package/src/adapters/runtime/codex/events.js +215 -215
- package/src/adapters/runtime/codex/index.js +109 -104
- package/src/adapters/runtime/codex/message-utils.js +95 -95
- package/src/adapters/runtime/codex/model-catalog.js +106 -106
- package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -75
- package/src/adapters/runtime/codex/rpc-client.js +339 -339
- package/src/adapters/runtime/codex/session-store.js +286 -286
- package/src/app/channel-send-file-cli.js +57 -57
- package/src/app/diary-write-cli.js +236 -88
- package/src/app/note-sync-cli.js +2 -2
- package/src/app/reminder-write-cli.js +215 -210
- package/src/app/review-cli.js +7 -5
- package/src/app/system-checkin-poller.js +64 -64
- package/src/app/system-send-cli.js +129 -129
- package/src/app/timeline-event-cli.js +28 -25
- package/src/app/timeline-screenshot-cli.js +103 -100
- package/src/core/app.js +1763 -1763
- package/src/core/branding.js +2 -1
- package/src/core/command-registry.js +381 -369
- package/src/core/config.js +30 -14
- package/src/core/default-targets.js +163 -163
- package/src/core/durable-note-schema.js +9 -8
- package/src/core/instructions-template.js +17 -16
- package/src/core/note-sync.js +8 -7
- package/src/core/path-utils.js +54 -0
- package/src/core/project-radar.js +11 -10
- package/src/core/review.js +48 -50
- package/src/core/stream-delivery.js +1162 -983
- package/src/core/system-message-dispatcher.js +68 -68
- package/src/core/system-message-queue-store.js +128 -128
- package/src/core/thread-state-store.js +96 -96
- package/src/core/timeline-screenshot-queue-store.js +134 -134
- package/src/core/timezone.js +436 -0
- package/src/core/workspace-bootstrap.js +9 -1
- package/src/index.js +148 -146
- package/src/integrations/timeline/index.js +130 -74
- package/src/integrations/timeline/state-sync.js +240 -0
- package/templates/weixin-instructions.md +12 -38
- 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 };
|