@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk 0.1.23 → 0.1.25
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/dist/index.d.ts +37 -2
- package/dist/index.js +97 -119
- package/openclaw.plugin.json +11 -0
- package/package.json +4 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Config } from '@nextclaw/core';
|
|
2
1
|
import { NcpAgentRuntime } from '@nextclaw/ncp';
|
|
3
2
|
import { RuntimeFactoryParams } from '@nextclaw/ncp-toolkit';
|
|
4
3
|
|
|
@@ -15,9 +14,45 @@ type SessionTypeDescriptor = {
|
|
|
15
14
|
} | null;
|
|
16
15
|
};
|
|
17
16
|
|
|
17
|
+
type ResolvedProviderSpec = {
|
|
18
|
+
isGateway?: boolean;
|
|
19
|
+
isLocal?: boolean;
|
|
20
|
+
supportsResponsesApi?: boolean;
|
|
21
|
+
};
|
|
22
|
+
type ResolvedProviderConfig = {
|
|
23
|
+
wireApi?: string | null;
|
|
24
|
+
extraHeaders?: Record<string, string> | null;
|
|
25
|
+
};
|
|
26
|
+
type ResolvedProviderRuntime = {
|
|
27
|
+
resolvedModel: string;
|
|
28
|
+
providerLocalModel: string;
|
|
29
|
+
provider: ResolvedProviderConfig | null;
|
|
30
|
+
providerName: string | null;
|
|
31
|
+
providerDisplayName: string | null;
|
|
32
|
+
providerSpec: ResolvedProviderSpec | null;
|
|
33
|
+
apiKey: string | null;
|
|
34
|
+
apiBase: string | null;
|
|
35
|
+
};
|
|
36
|
+
type AgentRuntimeApi = {
|
|
37
|
+
defaults: {
|
|
38
|
+
workspace?: string;
|
|
39
|
+
model?: string;
|
|
40
|
+
};
|
|
41
|
+
resolveWorkspacePath: (workspace?: string) => string;
|
|
42
|
+
resolveProviderRuntime: (model?: string) => ResolvedProviderRuntime;
|
|
43
|
+
buildRuntimeUserPrompt: (params: {
|
|
44
|
+
workspace?: string;
|
|
45
|
+
sessionKey?: string;
|
|
46
|
+
metadata?: Record<string, unknown>;
|
|
47
|
+
userMessage: string;
|
|
48
|
+
}) => string;
|
|
49
|
+
};
|
|
18
50
|
type PluginApi = {
|
|
19
|
-
config
|
|
51
|
+
config?: Record<string, unknown>;
|
|
20
52
|
pluginConfig?: Record<string, unknown>;
|
|
53
|
+
runtime: {
|
|
54
|
+
agent: AgentRuntimeApi;
|
|
55
|
+
};
|
|
21
56
|
registerNcpAgentRuntime: (registration: {
|
|
22
57
|
kind: string;
|
|
23
58
|
label?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
3
|
-
findProviderByModel,
|
|
4
|
-
findProviderByName,
|
|
5
|
-
resolveProviderRuntime,
|
|
6
|
-
getWorkspacePath
|
|
7
|
-
} from "@nextclaw/core";
|
|
8
2
|
import {
|
|
9
3
|
CodexSdkNcpAgentRuntime
|
|
10
4
|
} from "@nextclaw/nextclaw-ncp-runtime-codex-sdk";
|
|
@@ -97,24 +91,6 @@ function resolveCodexAccessMode(pluginConfig) {
|
|
|
97
91
|
}
|
|
98
92
|
|
|
99
93
|
// src/codex-input-builder.ts
|
|
100
|
-
import {
|
|
101
|
-
buildRequestedSkillsUserPrompt,
|
|
102
|
-
SkillsLoader
|
|
103
|
-
} from "@nextclaw/core";
|
|
104
|
-
function readString(value) {
|
|
105
|
-
if (typeof value !== "string") {
|
|
106
|
-
return void 0;
|
|
107
|
-
}
|
|
108
|
-
const trimmed = value.trim();
|
|
109
|
-
return trimmed || void 0;
|
|
110
|
-
}
|
|
111
|
-
function readRequestedSkills(metadata) {
|
|
112
|
-
const raw = metadata.requested_skills ?? metadata.requestedSkills;
|
|
113
|
-
if (!Array.isArray(raw)) {
|
|
114
|
-
return [];
|
|
115
|
-
}
|
|
116
|
-
return raw.map((entry) => readString(entry)).filter((entry) => Boolean(entry)).slice(0, 8);
|
|
117
|
-
}
|
|
118
94
|
function readUserText(input) {
|
|
119
95
|
for (let index = input.messages.length - 1; index >= 0; index -= 1) {
|
|
120
96
|
const message = input.messages[index];
|
|
@@ -128,13 +104,16 @@ function readUserText(input) {
|
|
|
128
104
|
}
|
|
129
105
|
return "";
|
|
130
106
|
}
|
|
131
|
-
function buildCodexInputBuilder(workspace) {
|
|
132
|
-
const skillsLoader = new SkillsLoader(workspace);
|
|
107
|
+
function buildCodexInputBuilder(runtimeAgent, workspace) {
|
|
133
108
|
return async (input) => {
|
|
134
109
|
const userText = readUserText(input);
|
|
135
110
|
const metadata = input.metadata && typeof input.metadata === "object" && !Array.isArray(input.metadata) ? input.metadata : {};
|
|
136
|
-
|
|
137
|
-
|
|
111
|
+
return runtimeAgent.buildRuntimeUserPrompt({
|
|
112
|
+
workspace,
|
|
113
|
+
sessionKey: input.sessionId,
|
|
114
|
+
metadata,
|
|
115
|
+
userMessage: userText
|
|
116
|
+
});
|
|
138
117
|
};
|
|
139
118
|
}
|
|
140
119
|
|
|
@@ -143,7 +122,7 @@ import { randomUUID } from "crypto";
|
|
|
143
122
|
import { createServer } from "http";
|
|
144
123
|
|
|
145
124
|
// src/codex-openai-responses-bridge-shared.ts
|
|
146
|
-
function
|
|
125
|
+
function readString(value) {
|
|
147
126
|
if (typeof value !== "string") {
|
|
148
127
|
return void 0;
|
|
149
128
|
}
|
|
@@ -201,7 +180,7 @@ function stripModelPrefix(model, prefixes) {
|
|
|
201
180
|
}
|
|
202
181
|
function resolveUpstreamModel(requestedModel, config) {
|
|
203
182
|
const prefixes = (config.modelPrefixes ?? []).filter((value) => value.trim().length > 0);
|
|
204
|
-
const model = stripModelPrefix(
|
|
183
|
+
const model = stripModelPrefix(readString(requestedModel) ?? "", prefixes) || stripModelPrefix(config.defaultModel ?? "", prefixes);
|
|
205
184
|
if (!model) {
|
|
206
185
|
throw new Error("Codex bridge could not resolve an upstream model.");
|
|
207
186
|
}
|
|
@@ -212,27 +191,27 @@ function normalizeTextPart(value) {
|
|
|
212
191
|
if (!record) {
|
|
213
192
|
return "";
|
|
214
193
|
}
|
|
215
|
-
const type =
|
|
194
|
+
const type = readString(record.type);
|
|
216
195
|
if (type !== "input_text" && type !== "output_text") {
|
|
217
196
|
return "";
|
|
218
197
|
}
|
|
219
|
-
return
|
|
198
|
+
return readString(record.text) ?? "";
|
|
220
199
|
}
|
|
221
200
|
function normalizeImageUrl(value) {
|
|
222
201
|
const record = readRecord(value);
|
|
223
|
-
if (!record ||
|
|
202
|
+
if (!record || readString(record.type) !== "input_image") {
|
|
224
203
|
return null;
|
|
225
204
|
}
|
|
226
205
|
const source = readRecord(record.source);
|
|
227
206
|
if (!source) {
|
|
228
207
|
return null;
|
|
229
208
|
}
|
|
230
|
-
if (
|
|
231
|
-
return
|
|
209
|
+
if (readString(source.type) === "url") {
|
|
210
|
+
return readString(source.url) ?? null;
|
|
232
211
|
}
|
|
233
|
-
if (
|
|
234
|
-
const mediaType =
|
|
235
|
-
const data =
|
|
212
|
+
if (readString(source.type) === "base64") {
|
|
213
|
+
const mediaType = readString(source.media_type) ?? "application/octet-stream";
|
|
214
|
+
const data = readString(source.data);
|
|
236
215
|
if (!data) {
|
|
237
216
|
return null;
|
|
238
217
|
}
|
|
@@ -288,7 +267,7 @@ function buildChatContent(content) {
|
|
|
288
267
|
}
|
|
289
268
|
const textOnly = chatContent.every((entry) => entry.type === "text");
|
|
290
269
|
if (textOnly) {
|
|
291
|
-
return chatContent.map((entry) =>
|
|
270
|
+
return chatContent.map((entry) => readString(entry.text) ?? "").join("\n");
|
|
292
271
|
}
|
|
293
272
|
return chatContent;
|
|
294
273
|
}
|
|
@@ -299,10 +278,10 @@ function readAssistantMessageText(content) {
|
|
|
299
278
|
if (!Array.isArray(content)) {
|
|
300
279
|
return "";
|
|
301
280
|
}
|
|
302
|
-
return content.filter((entry) => entry.type === "text").map((entry) =>
|
|
281
|
+
return content.filter((entry) => entry.type === "text").map((entry) => readString(entry.text) ?? "").join("\n");
|
|
303
282
|
}
|
|
304
283
|
function appendMessageInputItem(params) {
|
|
305
|
-
const role =
|
|
284
|
+
const role = readString(params.item.role);
|
|
306
285
|
const content = buildChatContent(params.item.content);
|
|
307
286
|
if (role === "assistant") {
|
|
308
287
|
const text = readAssistantMessageText(content);
|
|
@@ -321,12 +300,12 @@ function appendMessageInputItem(params) {
|
|
|
321
300
|
}
|
|
322
301
|
}
|
|
323
302
|
function appendFunctionCallItem(params) {
|
|
324
|
-
const name =
|
|
325
|
-
const argumentsText =
|
|
303
|
+
const name = readString(params.item.name);
|
|
304
|
+
const argumentsText = readString(params.item.arguments) ?? "{}";
|
|
326
305
|
if (!name) {
|
|
327
306
|
return;
|
|
328
307
|
}
|
|
329
|
-
const callId =
|
|
308
|
+
const callId = readString(params.item.call_id) ?? readString(params.item.id) ?? `call_${params.assistantToolCalls.length}`;
|
|
330
309
|
params.assistantToolCalls.push({
|
|
331
310
|
id: callId,
|
|
332
311
|
type: "function",
|
|
@@ -338,7 +317,7 @@ function appendFunctionCallItem(params) {
|
|
|
338
317
|
}
|
|
339
318
|
function appendFunctionCallOutputItem(params) {
|
|
340
319
|
params.flushAssistant();
|
|
341
|
-
const callId =
|
|
320
|
+
const callId = readString(params.item.call_id);
|
|
342
321
|
if (!callId) {
|
|
343
322
|
return;
|
|
344
323
|
}
|
|
@@ -350,7 +329,7 @@ function appendFunctionCallOutputItem(params) {
|
|
|
350
329
|
}
|
|
351
330
|
function buildOpenAiMessages(input, instructions) {
|
|
352
331
|
const messages = [];
|
|
353
|
-
const instructionText =
|
|
332
|
+
const instructionText = readString(instructions);
|
|
354
333
|
if (instructionText) {
|
|
355
334
|
messages.push({
|
|
356
335
|
role: "system",
|
|
@@ -387,7 +366,7 @@ function buildOpenAiMessages(input, instructions) {
|
|
|
387
366
|
if (!item) {
|
|
388
367
|
continue;
|
|
389
368
|
}
|
|
390
|
-
const type =
|
|
369
|
+
const type = readString(item.type);
|
|
391
370
|
if (type === "message") {
|
|
392
371
|
appendMessageInputItem({
|
|
393
372
|
messages,
|
|
@@ -420,13 +399,13 @@ function toOpenAiTools(value) {
|
|
|
420
399
|
const tools = [];
|
|
421
400
|
for (const entry of readArray(value)) {
|
|
422
401
|
const tool = readRecord(entry);
|
|
423
|
-
const type =
|
|
402
|
+
const type = readString(tool?.type);
|
|
424
403
|
const fn = readRecord(tool?.function);
|
|
425
|
-
const name =
|
|
404
|
+
const name = readString(fn?.name) ?? readString(tool?.name);
|
|
426
405
|
if (type !== "function" || !name) {
|
|
427
406
|
continue;
|
|
428
407
|
}
|
|
429
|
-
const description = (fn ?
|
|
408
|
+
const description = (fn ? readString(fn.description) : void 0) ?? readString(tool?.description);
|
|
430
409
|
const parameters = (fn ? readRecord(fn.parameters) : void 0) ?? readRecord(tool?.parameters);
|
|
431
410
|
const strict = (fn ? readBoolean(fn.strict) : void 0) ?? readBoolean(tool?.strict);
|
|
432
411
|
tools.push({
|
|
@@ -450,8 +429,8 @@ function toOpenAiToolChoice(value) {
|
|
|
450
429
|
}
|
|
451
430
|
const record = readRecord(value);
|
|
452
431
|
const fn = readRecord(record?.function);
|
|
453
|
-
const name =
|
|
454
|
-
if (
|
|
432
|
+
const name = readString(fn?.name) ?? readString(record?.name);
|
|
433
|
+
if (readString(record?.type) === "function" && name) {
|
|
455
434
|
return {
|
|
456
435
|
type: "function",
|
|
457
436
|
function: {
|
|
@@ -500,7 +479,7 @@ async function callOpenAiCompatibleUpstream(params) {
|
|
|
500
479
|
}
|
|
501
480
|
if (!upstreamResponse.ok) {
|
|
502
481
|
throw new Error(
|
|
503
|
-
|
|
482
|
+
readString(parsed.error?.message) ?? rawText.slice(0, 240) ?? `HTTP ${upstreamResponse.status}`
|
|
504
483
|
);
|
|
505
484
|
}
|
|
506
485
|
return {
|
|
@@ -522,9 +501,9 @@ function extractAssistantText(content) {
|
|
|
522
501
|
if (!record) {
|
|
523
502
|
return "";
|
|
524
503
|
}
|
|
525
|
-
const type =
|
|
504
|
+
const type = readString(record.type);
|
|
526
505
|
if (type === "text" || type === "output_text") {
|
|
527
|
-
return
|
|
506
|
+
return readString(record.text) ?? "";
|
|
528
507
|
}
|
|
529
508
|
return "";
|
|
530
509
|
}).filter(Boolean).join("");
|
|
@@ -555,12 +534,12 @@ function buildOpenResponsesOutputItems(response, responseId) {
|
|
|
555
534
|
toolCalls.forEach((entry, index) => {
|
|
556
535
|
const toolCall = readRecord(entry);
|
|
557
536
|
const fn = readRecord(toolCall?.function);
|
|
558
|
-
const name =
|
|
559
|
-
const argumentsText =
|
|
537
|
+
const name = readString(fn?.name);
|
|
538
|
+
const argumentsText = readString(fn?.arguments) ?? "{}";
|
|
560
539
|
if (!name) {
|
|
561
540
|
return;
|
|
562
541
|
}
|
|
563
|
-
const callId =
|
|
542
|
+
const callId = readString(toolCall?.id) ?? `${responseId}:call:${index}`;
|
|
564
543
|
outputItems.push({
|
|
565
544
|
type: "function_call",
|
|
566
545
|
id: `${responseId}:function:${index}`,
|
|
@@ -606,7 +585,7 @@ function cloneRecord(value) {
|
|
|
606
585
|
return structuredClone(value);
|
|
607
586
|
}
|
|
608
587
|
function buildInProgressOutputItem(item) {
|
|
609
|
-
const type =
|
|
588
|
+
const type = readString(item.type);
|
|
610
589
|
if (type === "message") {
|
|
611
590
|
return {
|
|
612
591
|
...cloneRecord(item),
|
|
@@ -624,10 +603,10 @@ function buildInProgressOutputItem(item) {
|
|
|
624
603
|
return cloneRecord(item);
|
|
625
604
|
}
|
|
626
605
|
function writeMessageOutputItemEvents(params) {
|
|
627
|
-
const itemId =
|
|
606
|
+
const itemId = readString(params.item.id);
|
|
628
607
|
const content = readArray(params.item.content);
|
|
629
|
-
const textPart = content.find((entry) =>
|
|
630
|
-
const text =
|
|
608
|
+
const textPart = content.find((entry) => readString(readRecord(entry)?.type) === "output_text");
|
|
609
|
+
const text = readString(readRecord(textPart)?.text) ?? "";
|
|
631
610
|
writeSseEvent(params.response, "response.output_item.added", {
|
|
632
611
|
type: "response.output_item.added",
|
|
633
612
|
sequence_number: nextSequenceNumber(params.sequenceState),
|
|
@@ -686,8 +665,8 @@ function writeMessageOutputItemEvents(params) {
|
|
|
686
665
|
});
|
|
687
666
|
}
|
|
688
667
|
function writeFunctionCallOutputItemEvents(params) {
|
|
689
|
-
const itemId =
|
|
690
|
-
const argumentsText =
|
|
668
|
+
const itemId = readString(params.item.id);
|
|
669
|
+
const argumentsText = readString(params.item.arguments) ?? "";
|
|
691
670
|
writeSseEvent(params.response, "response.output_item.added", {
|
|
692
671
|
type: "response.output_item.added",
|
|
693
672
|
sequence_number: nextSequenceNumber(params.sequenceState),
|
|
@@ -721,7 +700,7 @@ function writeFunctionCallOutputItemEvents(params) {
|
|
|
721
700
|
}
|
|
722
701
|
function writeResponseOutputItemEvents(params) {
|
|
723
702
|
params.outputItems.forEach((item, outputIndex) => {
|
|
724
|
-
const type =
|
|
703
|
+
const type = readString(item.type);
|
|
725
704
|
if (type === "message") {
|
|
726
705
|
writeMessageOutputItemEvents({
|
|
727
706
|
response: params.response,
|
|
@@ -950,7 +929,7 @@ function readErrorMessage(value) {
|
|
|
950
929
|
return "";
|
|
951
930
|
}
|
|
952
931
|
const record = value;
|
|
953
|
-
return
|
|
932
|
+
return readString(record.message) ?? readString(record.error) ?? "";
|
|
954
933
|
}
|
|
955
934
|
function shouldTreatResponsesProbeFailureAsUnsupported(params) {
|
|
956
935
|
const normalizedMessage = params.message.trim().toLowerCase();
|
|
@@ -1005,7 +984,7 @@ async function probeCodexResponsesApiSupport(params) {
|
|
|
1005
984
|
}
|
|
1006
985
|
const parsedRecord = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
1007
986
|
const message = readErrorMessage(parsedRecord?.error) || readErrorMessage(parsed) || rawText.slice(0, 240);
|
|
1008
|
-
const responseFailed =
|
|
987
|
+
const responseFailed = readString(parsedRecord?.status)?.toLowerCase() === "failed" || Boolean(parsedRecord?.error);
|
|
1009
988
|
if (response.ok && !responseFailed) {
|
|
1010
989
|
return true;
|
|
1011
990
|
}
|
|
@@ -1029,7 +1008,7 @@ async function resolveCodexResponsesApiSupport(params) {
|
|
|
1029
1008
|
if (params.capabilitySpec?.supportsResponsesApi === false) {
|
|
1030
1009
|
return false;
|
|
1031
1010
|
}
|
|
1032
|
-
const explicitWireApi =
|
|
1011
|
+
const explicitWireApi = readString(params.wireApi)?.toLowerCase();
|
|
1033
1012
|
if (explicitWireApi === "chat") {
|
|
1034
1013
|
return false;
|
|
1035
1014
|
}
|
|
@@ -1045,7 +1024,7 @@ async function resolveCodexResponsesApiSupport(params) {
|
|
|
1045
1024
|
}
|
|
1046
1025
|
|
|
1047
1026
|
// src/codex-session-type.ts
|
|
1048
|
-
function
|
|
1027
|
+
function readString2(value) {
|
|
1049
1028
|
if (typeof value !== "string") {
|
|
1050
1029
|
return void 0;
|
|
1051
1030
|
}
|
|
@@ -1056,47 +1035,51 @@ function readStringArray(value) {
|
|
|
1056
1035
|
if (!Array.isArray(value)) {
|
|
1057
1036
|
return void 0;
|
|
1058
1037
|
}
|
|
1059
|
-
const values = value.map((entry) =>
|
|
1038
|
+
const values = value.map((entry) => readString2(entry)).filter((entry) => Boolean(entry));
|
|
1060
1039
|
return values.length > 0 ? values : void 0;
|
|
1061
1040
|
}
|
|
1062
1041
|
function dedupeStrings(values) {
|
|
1063
1042
|
return Array.from(new Set(values));
|
|
1064
1043
|
}
|
|
1065
|
-
function resolveConfiguredCodexModels(
|
|
1066
|
-
const explicitSupportedModels = readStringArray(
|
|
1067
|
-
if (explicitSupportedModels) {
|
|
1068
|
-
return
|
|
1044
|
+
function resolveConfiguredCodexModels(pluginConfig) {
|
|
1045
|
+
const explicitSupportedModels = readStringArray(pluginConfig.supportedModels);
|
|
1046
|
+
if (!explicitSupportedModels) {
|
|
1047
|
+
return void 0;
|
|
1069
1048
|
}
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
const fallbackModels = fallbackModel ? [fallbackModel] : [];
|
|
1076
|
-
return dedupeStrings(configuredModels.length > 0 ? configuredModels : fallbackModels);
|
|
1049
|
+
const normalizedModels = dedupeStrings(explicitSupportedModels);
|
|
1050
|
+
if (normalizedModels.includes("*")) {
|
|
1051
|
+
return void 0;
|
|
1052
|
+
}
|
|
1053
|
+
return normalizedModels;
|
|
1077
1054
|
}
|
|
1078
1055
|
function resolveRecommendedCodexModel(params) {
|
|
1079
|
-
const configuredModel =
|
|
1080
|
-
if (
|
|
1056
|
+
const configuredModel = readString2(params.pluginConfig.model) ?? params.defaultModel;
|
|
1057
|
+
if (!configuredModel) {
|
|
1058
|
+
return params.supportedModels?.[0] ?? null;
|
|
1059
|
+
}
|
|
1060
|
+
if (!params.supportedModels || params.supportedModels.includes(configuredModel)) {
|
|
1081
1061
|
return configuredModel;
|
|
1082
1062
|
}
|
|
1083
1063
|
return params.supportedModels[0] ?? configuredModel ?? null;
|
|
1084
1064
|
}
|
|
1085
1065
|
function createDescribeCodexSessionType(params) {
|
|
1086
1066
|
return () => {
|
|
1087
|
-
const supportedModels = resolveConfiguredCodexModels(params);
|
|
1088
|
-
|
|
1067
|
+
const supportedModels = resolveConfiguredCodexModels(params.pluginConfig);
|
|
1068
|
+
const descriptor = {
|
|
1089
1069
|
ready: true,
|
|
1090
1070
|
reason: null,
|
|
1091
1071
|
reasonMessage: null,
|
|
1092
|
-
supportedModels,
|
|
1093
1072
|
recommendedModel: resolveRecommendedCodexModel({
|
|
1094
|
-
|
|
1073
|
+
defaultModel: params.defaultModel,
|
|
1095
1074
|
pluginConfig: params.pluginConfig,
|
|
1096
1075
|
supportedModels
|
|
1097
1076
|
}),
|
|
1098
1077
|
cta: null
|
|
1099
1078
|
};
|
|
1079
|
+
if (supportedModels) {
|
|
1080
|
+
descriptor.supportedModels = supportedModels;
|
|
1081
|
+
}
|
|
1082
|
+
return descriptor;
|
|
1100
1083
|
};
|
|
1101
1084
|
}
|
|
1102
1085
|
|
|
@@ -1122,7 +1105,7 @@ var DeferredCodexSdkNcpAgentRuntime = class {
|
|
|
1122
1105
|
return stream();
|
|
1123
1106
|
}
|
|
1124
1107
|
};
|
|
1125
|
-
function
|
|
1108
|
+
function readString3(value) {
|
|
1126
1109
|
if (typeof value !== "string") {
|
|
1127
1110
|
return void 0;
|
|
1128
1111
|
}
|
|
@@ -1142,14 +1125,9 @@ function readStringArray2(value) {
|
|
|
1142
1125
|
if (!Array.isArray(value)) {
|
|
1143
1126
|
return void 0;
|
|
1144
1127
|
}
|
|
1145
|
-
const values = value.map((entry) =>
|
|
1128
|
+
const values = value.map((entry) => readString3(entry)).filter((entry) => Boolean(entry));
|
|
1146
1129
|
return values.length > 0 ? values : void 0;
|
|
1147
1130
|
}
|
|
1148
|
-
function resolveCodexCapabilitySpec(params) {
|
|
1149
|
-
const providerSpec = params.providerName ? findProviderByName(params.providerName) : void 0;
|
|
1150
|
-
const modelSpec = params.model ? findProviderByModel(params.model) : void 0;
|
|
1151
|
-
return providerSpec?.isGateway ? modelSpec ?? providerSpec : providerSpec ?? modelSpec;
|
|
1152
|
-
}
|
|
1153
1131
|
function readStringRecord(value) {
|
|
1154
1132
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1155
1133
|
return void 0;
|
|
@@ -1178,9 +1156,9 @@ function readThinkingLevel(value) {
|
|
|
1178
1156
|
return void 0;
|
|
1179
1157
|
}
|
|
1180
1158
|
function resolveCodexExecutionOptions(params) {
|
|
1181
|
-
const configuredWorkingDirectory =
|
|
1182
|
-
const workspace =
|
|
1183
|
-
configuredWorkingDirectory ?? params.
|
|
1159
|
+
const configuredWorkingDirectory = readString3(params.pluginConfig.workingDirectory);
|
|
1160
|
+
const workspace = params.runtimeAgent.resolveWorkspacePath(
|
|
1161
|
+
configuredWorkingDirectory ?? params.runtimeAgent.defaults.workspace
|
|
1184
1162
|
);
|
|
1185
1163
|
return {
|
|
1186
1164
|
workingDirectory: workspace,
|
|
@@ -1189,14 +1167,14 @@ function resolveCodexExecutionOptions(params) {
|
|
|
1189
1167
|
}
|
|
1190
1168
|
function resolveCodexCliConfig(params) {
|
|
1191
1169
|
const explicitConfig = readRecord2(params.pluginConfig.config);
|
|
1192
|
-
const modelProvider =
|
|
1170
|
+
const modelProvider = readString3(params.modelProviderOverride) ?? resolveExternalModelProvider({
|
|
1193
1171
|
explicitModelProvider: params.pluginConfig.modelProvider,
|
|
1194
1172
|
providerName: params.providerName,
|
|
1195
1173
|
providerDisplayName: params.providerDisplayName,
|
|
1196
1174
|
pluginId: PLUGIN_ID
|
|
1197
1175
|
});
|
|
1198
|
-
const preferredAuthMethod =
|
|
1199
|
-
const apiBase =
|
|
1176
|
+
const preferredAuthMethod = readString3(params.pluginConfig.preferredAuthMethod) ?? "apikey";
|
|
1177
|
+
const apiBase = readString3(params.pluginConfig.apiBase) ?? readString3(params.apiBase);
|
|
1200
1178
|
const config = {
|
|
1201
1179
|
model_provider: modelProvider,
|
|
1202
1180
|
preferred_auth_method: preferredAuthMethod
|
|
@@ -1217,7 +1195,7 @@ function resolveCodexCliConfig(params) {
|
|
|
1217
1195
|
};
|
|
1218
1196
|
}
|
|
1219
1197
|
function resolveCodexModel(params) {
|
|
1220
|
-
return
|
|
1198
|
+
return readString3(params.sessionMetadata.preferred_model) ?? readString3(params.sessionMetadata.model) ?? readString3(params.pluginConfig.model) ?? params.runtimeAgent.defaults.model ?? "";
|
|
1221
1199
|
}
|
|
1222
1200
|
var plugin = {
|
|
1223
1201
|
id: PLUGIN_ID,
|
|
@@ -1231,7 +1209,7 @@ var plugin = {
|
|
|
1231
1209
|
register(api) {
|
|
1232
1210
|
const pluginConfig = readRecord2(api.pluginConfig) ?? {};
|
|
1233
1211
|
const describeCodexSessionType = createDescribeCodexSessionType({
|
|
1234
|
-
|
|
1212
|
+
defaultModel: readString3(api.runtime.agent.defaults.model),
|
|
1235
1213
|
pluginConfig
|
|
1236
1214
|
});
|
|
1237
1215
|
api.registerNcpAgentRuntime({
|
|
@@ -1240,22 +1218,19 @@ var plugin = {
|
|
|
1240
1218
|
describeSessionType: describeCodexSessionType,
|
|
1241
1219
|
createRuntime: (runtimeParams) => {
|
|
1242
1220
|
return new DeferredCodexSdkNcpAgentRuntime(async () => {
|
|
1243
|
-
const nextConfig = api.config;
|
|
1244
1221
|
const model = resolveCodexModel({
|
|
1245
|
-
|
|
1222
|
+
runtimeAgent: api.runtime.agent,
|
|
1246
1223
|
pluginConfig,
|
|
1247
1224
|
sessionMetadata: runtimeParams.sessionMetadata
|
|
1248
1225
|
});
|
|
1249
|
-
const resolvedProviderRuntime = resolveProviderRuntime(
|
|
1226
|
+
const resolvedProviderRuntime = api.runtime.agent.resolveProviderRuntime(model);
|
|
1250
1227
|
const providerName = resolvedProviderRuntime.providerName;
|
|
1251
|
-
const capabilitySpec =
|
|
1252
|
-
|
|
1253
|
-
providerName
|
|
1254
|
-
});
|
|
1228
|
+
const capabilitySpec = resolvedProviderRuntime.providerSpec ?? null;
|
|
1229
|
+
const providerDisplayName = resolvedProviderRuntime.providerDisplayName;
|
|
1255
1230
|
const externalModelProvider = resolveExternalModelProvider({
|
|
1256
1231
|
explicitModelProvider: pluginConfig.modelProvider,
|
|
1257
1232
|
providerName,
|
|
1258
|
-
providerDisplayName
|
|
1233
|
+
providerDisplayName,
|
|
1259
1234
|
pluginId: PLUGIN_ID
|
|
1260
1235
|
});
|
|
1261
1236
|
const userFacingModelRoute = buildUserFacingModelRoute({
|
|
@@ -1263,8 +1238,8 @@ var plugin = {
|
|
|
1263
1238
|
providerLocalModel: resolvedProviderRuntime.providerLocalModel,
|
|
1264
1239
|
resolvedModel: resolvedProviderRuntime.resolvedModel
|
|
1265
1240
|
});
|
|
1266
|
-
const upstreamApiBase =
|
|
1267
|
-
const apiKey =
|
|
1241
|
+
const upstreamApiBase = readString3(pluginConfig.apiBase) ?? resolvedProviderRuntime.apiBase ?? void 0;
|
|
1242
|
+
const apiKey = readString3(pluginConfig.apiKey) ?? resolvedProviderRuntime.apiKey ?? void 0;
|
|
1268
1243
|
if (!apiKey) {
|
|
1269
1244
|
throw new Error(
|
|
1270
1245
|
`[codex] missing apiKey. Set plugins.entries.${PLUGIN_ID}.config.apiKey or providers.*.apiKey for model "${userFacingModelRoute}".`
|
|
@@ -1279,7 +1254,7 @@ var plugin = {
|
|
|
1279
1254
|
let codexModelProviderOverride;
|
|
1280
1255
|
const supportsResponsesApi = await resolveCodexResponsesApiSupport({
|
|
1281
1256
|
capabilitySpec,
|
|
1282
|
-
wireApi:
|
|
1257
|
+
wireApi: readString3(resolvedProviderRuntime.provider?.wireApi),
|
|
1283
1258
|
apiBase: upstreamApiBase,
|
|
1284
1259
|
apiKey,
|
|
1285
1260
|
extraHeaders: resolvedProviderRuntime.provider?.extraHeaders ?? null,
|
|
@@ -1303,7 +1278,7 @@ var plugin = {
|
|
|
1303
1278
|
);
|
|
1304
1279
|
}
|
|
1305
1280
|
const executionOptions = resolveCodexExecutionOptions({
|
|
1306
|
-
|
|
1281
|
+
runtimeAgent: api.runtime.agent,
|
|
1307
1282
|
pluginConfig
|
|
1308
1283
|
});
|
|
1309
1284
|
const accessMode = resolveCodexAccessMode(pluginConfig);
|
|
@@ -1313,20 +1288,23 @@ var plugin = {
|
|
|
1313
1288
|
apiKey,
|
|
1314
1289
|
apiBase: codexApiBase,
|
|
1315
1290
|
model: resolvedProviderRuntime.providerLocalModel,
|
|
1316
|
-
threadId:
|
|
1317
|
-
codexPathOverride:
|
|
1291
|
+
threadId: readString3(runtimeParams.sessionMetadata.codex_thread_id) ?? null,
|
|
1292
|
+
codexPathOverride: readString3(pluginConfig.codexPathOverride),
|
|
1318
1293
|
env: readStringRecord(pluginConfig.env),
|
|
1319
1294
|
cliConfig: resolveCodexCliConfig({
|
|
1320
1295
|
pluginConfig,
|
|
1321
1296
|
providerName,
|
|
1322
|
-
providerDisplayName
|
|
1297
|
+
providerDisplayName,
|
|
1323
1298
|
apiBase: codexApiBase,
|
|
1324
1299
|
modelProviderOverride: codexModelProviderOverride
|
|
1325
1300
|
}),
|
|
1326
1301
|
stateManager: runtimeParams.stateManager,
|
|
1327
1302
|
sessionMetadata: runtimeParams.sessionMetadata,
|
|
1328
1303
|
setSessionMetadata: runtimeParams.setSessionMetadata,
|
|
1329
|
-
inputBuilder: buildCodexInputBuilder(
|
|
1304
|
+
inputBuilder: buildCodexInputBuilder(
|
|
1305
|
+
api.runtime.agent,
|
|
1306
|
+
executionOptions.workingDirectory
|
|
1307
|
+
),
|
|
1330
1308
|
threadOptions: {
|
|
1331
1309
|
model,
|
|
1332
1310
|
sandboxMode: mapAccessModeToSandboxMode(accessMode),
|
|
@@ -1334,7 +1312,7 @@ var plugin = {
|
|
|
1334
1312
|
skipGitRepoCheck: executionOptions.skipGitRepoCheck,
|
|
1335
1313
|
modelReasoningEffort: thinkingLevel,
|
|
1336
1314
|
networkAccessEnabled: readBoolean2(pluginConfig.networkAccessEnabled),
|
|
1337
|
-
webSearchMode:
|
|
1315
|
+
webSearchMode: readString3(pluginConfig.webSearchMode),
|
|
1338
1316
|
webSearchEnabled: readBoolean2(pluginConfig.webSearchEnabled),
|
|
1339
1317
|
approvalPolicy: CODEX_APPROVAL_POLICY,
|
|
1340
1318
|
additionalDirectories: readStringArray2(pluginConfig.additionalDirectories)
|
package/openclaw.plugin.json
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
"model": {
|
|
18
18
|
"type": "string"
|
|
19
19
|
},
|
|
20
|
+
"supportedModels": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "string"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
20
26
|
"modelProvider": {
|
|
21
27
|
"type": "string"
|
|
22
28
|
},
|
|
@@ -74,6 +80,11 @@
|
|
|
74
80
|
"model": {
|
|
75
81
|
"label": "Default Model"
|
|
76
82
|
},
|
|
83
|
+
"supportedModels": {
|
|
84
|
+
"label": "Supported Models",
|
|
85
|
+
"help": "Optional advanced allowlist. Leave empty for no model restriction. Use [\"*\"] to explicitly allow any model.",
|
|
86
|
+
"advanced": true
|
|
87
|
+
},
|
|
77
88
|
"modelProvider": {
|
|
78
89
|
"label": "External Model Provider",
|
|
79
90
|
"help": "Optional override for the provider id exposed to Codex SDK. Recommended for custom providers whose display name is not the actual upstream provider id.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "NextClaw plugin that registers Codex SDK as an optional NCP runtime.",
|
|
6
6
|
"type": "module",
|
|
@@ -21,10 +21,9 @@
|
|
|
21
21
|
]
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@nextclaw/
|
|
25
|
-
"@nextclaw/ncp": "0.
|
|
26
|
-
"@nextclaw/ncp-
|
|
27
|
-
"@nextclaw/nextclaw-ncp-runtime-codex-sdk": "0.1.4"
|
|
24
|
+
"@nextclaw/ncp": "0.4.0",
|
|
25
|
+
"@nextclaw/ncp-toolkit": "0.4.4",
|
|
26
|
+
"@nextclaw/nextclaw-ncp-runtime-codex-sdk": "0.1.5"
|
|
28
27
|
},
|
|
29
28
|
"devDependencies": {
|
|
30
29
|
"@types/node": "^20.17.6",
|