@mars-sea/dsh-commandcode-provider 0.10.0-alpha.7 → 0.10.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/CHANGELOG.md +30 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/lib/client.js +1 -1
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +27 -5
- package/lib/index.js +424 -55
- package/lib/index.js.map +1 -1
- package/package.json +35 -35
package/lib/index.js
CHANGED
|
@@ -464,13 +464,21 @@ const KNOWN_EFFORTS = {
|
|
|
464
464
|
"low",
|
|
465
465
|
"medium",
|
|
466
466
|
"high",
|
|
467
|
-
"xhigh"
|
|
467
|
+
"xhigh",
|
|
468
|
+
"max"
|
|
468
469
|
],
|
|
469
470
|
"meta/muse-spark-1.3-contributor": [
|
|
470
471
|
"low",
|
|
471
472
|
"medium",
|
|
472
473
|
"high",
|
|
473
474
|
"xhigh"
|
|
475
|
+
],
|
|
476
|
+
"gpt-6-astra": [
|
|
477
|
+
"low",
|
|
478
|
+
"medium",
|
|
479
|
+
"high",
|
|
480
|
+
"xhigh",
|
|
481
|
+
"max"
|
|
474
482
|
]
|
|
475
483
|
};
|
|
476
484
|
/**
|
|
@@ -519,12 +527,12 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
519
527
|
"gpt-5.6-luna",
|
|
520
528
|
"gpt-5.6-sol",
|
|
521
529
|
"gpt-5.6-terra",
|
|
530
|
+
"gpt-6-astra",
|
|
522
531
|
"meta/muse-spark-1.1",
|
|
523
532
|
"meta/muse-spark-1.2",
|
|
524
533
|
"meta/muse-spark-1.2-contributor",
|
|
525
534
|
"meta/muse-spark-1.3",
|
|
526
535
|
"meta/muse-spark-1.3-contributor",
|
|
527
|
-
"minimax/minimax-m3-free",
|
|
528
536
|
"moonshotai/Kimi-K2.5",
|
|
529
537
|
"moonshotai/Kimi-K2.6",
|
|
530
538
|
"moonshotai/Kimi-K2.7-Code",
|
|
@@ -535,11 +543,12 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
535
543
|
"thinkingmachines/inkling",
|
|
536
544
|
"thinkingmachines/inkling-small",
|
|
537
545
|
"xai/grok-4.5",
|
|
546
|
+
"xai/grok-4.6",
|
|
538
547
|
"xiaomi/mimo-v2.5",
|
|
539
548
|
"z-ai/glm-5.3-flash"
|
|
540
549
|
]);
|
|
541
550
|
/**
|
|
542
|
-
* Models the official CLI's model table (command-code@1.
|
|
551
|
+
* Models the official CLI's model table (command-code@1.50.0) marks
|
|
543
552
|
* `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
|
|
544
553
|
* think automatically, with Command Code driving the depth. This is the
|
|
545
554
|
* authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
|
|
@@ -547,7 +556,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
547
556
|
* effort levels, and this snapshot is not surfaced in the picker's compact
|
|
548
557
|
* description — it exists for programmatic consumers.
|
|
549
558
|
*
|
|
550
|
-
* Source: the command-code@1.
|
|
559
|
+
* Source: the command-code@1.50.0 bundled model table (dist/cli.mjs),
|
|
551
560
|
* cross-checked with https://commandcode.ai/docs/reference/cli/models.
|
|
552
561
|
* (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
|
|
553
562
|
* selectable `['low', 'high', 'max']` efforts; the preview then ended in
|
|
@@ -570,7 +579,6 @@ const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
|
|
|
570
579
|
"Qwen/Qwen3.7-Flash",
|
|
571
580
|
"Qwen/Qwen3.7-Max",
|
|
572
581
|
"Qwen/Qwen3.7-Plus",
|
|
573
|
-
"minimax/minimax-m3-free",
|
|
574
582
|
"moonshotai/Kimi-K2.7-Code",
|
|
575
583
|
"moonshotai/Kimi-K2.7-Code-Highspeed",
|
|
576
584
|
"stepfun/Step-3.5-Flash",
|
|
@@ -623,12 +631,9 @@ const KNOWN_PLANS = {
|
|
|
623
631
|
"deepseek/deepseek-v4-flash-vision-exp": "go",
|
|
624
632
|
"deepseek/deepseek-v4-pro": "go",
|
|
625
633
|
"gpt-5.6-luna": "go",
|
|
626
|
-
"inclusionai/ling-3.0-flash-free": "go",
|
|
627
634
|
"meituan/LongCat-2.0:free": "go",
|
|
628
635
|
"meta/muse-spark-1.2-contributor": "go",
|
|
629
636
|
"meta/muse-spark-1.3-contributor": "go",
|
|
630
|
-
"minimax/minimax-m2.7-free": "go",
|
|
631
|
-
"minimax/minimax-m3-free": "go",
|
|
632
637
|
"moonshotai/Kimi-K2.5": "go",
|
|
633
638
|
"moonshotai/Kimi-K2.6": "go",
|
|
634
639
|
"moonshotai/Kimi-K2.7-Code": "go",
|
|
@@ -638,7 +643,6 @@ const KNOWN_PLANS = {
|
|
|
638
643
|
"poolside/laguna-s-2.1-free": "go",
|
|
639
644
|
"stepfun/Step-3.5-Flash": "go",
|
|
640
645
|
"stepfun/Step-3.7-Flash": "go",
|
|
641
|
-
"tencent/Hy3": "go",
|
|
642
646
|
"tencent/hy3-paid": "go",
|
|
643
647
|
"tencent/hy4-preview": "go",
|
|
644
648
|
"thinkingmachines/inkling": "go",
|
|
@@ -676,6 +680,7 @@ const KNOWN_PLANS = {
|
|
|
676
680
|
"claude-opus-4-7": "provider",
|
|
677
681
|
"claude-opus-4-8": "provider",
|
|
678
682
|
"claude-opus-5": "provider",
|
|
683
|
+
"gpt-6-astra": "provider",
|
|
679
684
|
"sakana/fugu-ultra": "provider"
|
|
680
685
|
};
|
|
681
686
|
/** Official display labels for each plan tier. */
|
|
@@ -723,8 +728,8 @@ function compareByPlan(a, b) {
|
|
|
723
728
|
}
|
|
724
729
|
/**
|
|
725
730
|
* Subscription plan table, synced from the official CLI bundle's plan maps
|
|
726
|
-
* (`
|
|
727
|
-
*
|
|
731
|
+
* (located by the `"individual-go"` key in command-code@1.50.0 `dist/cli.mjs`,
|
|
732
|
+
* re-verified unchanged through 1.50.0): subscription `planId`
|
|
728
733
|
* prefix → display name and the plan's monthly credit total. This is the
|
|
729
734
|
* account's own subscription (from `/alpha/billing/subscriptions`) — distinct
|
|
730
735
|
* from {@link KNOWN_PLANS}, which maps catalog models to their minimum tier.
|
|
@@ -934,7 +939,7 @@ function capabilityDescription(modelId, contextWindow, now = Date.now()) {
|
|
|
934
939
|
* and API key or subscription, and Command Code's terms apply.
|
|
935
940
|
*
|
|
936
941
|
* Wire protocol (reverse-engineered by the pi plugin, command-code@1.28.4;
|
|
937
|
-
* re-verified against command-code@1.
|
|
942
|
+
* re-verified against command-code@1.50.0 — endpoints, request shape, and
|
|
938
943
|
* stream events unchanged):
|
|
939
944
|
* POST {apiBase}/alpha/generate
|
|
940
945
|
* body: { config, memory, taste, skills, params: { model, messages, tools,
|
|
@@ -947,7 +952,7 @@ function capabilityDescription(modelId, contextWindow, now = Date.now()) {
|
|
|
947
952
|
* per-request options thunk and an API-key resolver from the plugin entry
|
|
948
953
|
* (src/index.ts), so a settings change reaches the very next request.
|
|
949
954
|
*/
|
|
950
|
-
const COMMAND_CODE_CLI_VERSION = "1.
|
|
955
|
+
const COMMAND_CODE_CLI_VERSION = "1.50.0";
|
|
951
956
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
952
957
|
const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
|
|
953
958
|
const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
@@ -1108,6 +1113,40 @@ function pairedToolCalls(messages) {
|
|
|
1108
1113
|
names
|
|
1109
1114
|
};
|
|
1110
1115
|
}
|
|
1116
|
+
/**
|
|
1117
|
+
* The Command Code gateway rejects tool call ids longer than 64 characters
|
|
1118
|
+
* (`input[N].call_id` must be `<= 64`, issue #23). Cross-provider histories
|
|
1119
|
+
* can carry longer ids — e.g. switching to Command Code mid-session after
|
|
1120
|
+
* another provider issued the call — so overlong paired ids are remapped to
|
|
1121
|
+
* short per-request aliases. Correlation only needs to hold within one
|
|
1122
|
+
* request (each call travels with its result in the same body), so a
|
|
1123
|
+
* sequential alias is enough: no durable state, no cross-request stability,
|
|
1124
|
+
* and the harness log keeps the original ids.
|
|
1125
|
+
*/
|
|
1126
|
+
const MAX_WIRE_TOOL_CALL_ID_LENGTH = 64;
|
|
1127
|
+
/**
|
|
1128
|
+
* Map each paired tool-call id to its wire id: ids within the gateway limit
|
|
1129
|
+
* pass through verbatim, overlong ids get a collision-free `cc-<n>` alias.
|
|
1130
|
+
* Callers must resolve BOTH the tool-call and its paired tool-result through
|
|
1131
|
+
* the returned map so the pair stays correlated.
|
|
1132
|
+
*/
|
|
1133
|
+
function wireToolCallIds(paired) {
|
|
1134
|
+
const wire = /* @__PURE__ */ new Map();
|
|
1135
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1136
|
+
for (const id of paired) if (id.length <= MAX_WIRE_TOOL_CALL_ID_LENGTH) {
|
|
1137
|
+
wire.set(id, id);
|
|
1138
|
+
taken.add(id);
|
|
1139
|
+
}
|
|
1140
|
+
let seq = 1;
|
|
1141
|
+
for (const id of paired) {
|
|
1142
|
+
if (wire.has(id)) continue;
|
|
1143
|
+
let alias = `cc-${seq++}`;
|
|
1144
|
+
while (taken.has(alias)) alias = `cc-${seq++}`;
|
|
1145
|
+
wire.set(id, alias);
|
|
1146
|
+
taken.add(alias);
|
|
1147
|
+
}
|
|
1148
|
+
return wire;
|
|
1149
|
+
}
|
|
1111
1150
|
function blockText(block) {
|
|
1112
1151
|
return block.type === "text" || block.type === "reasoning" ? block.text : "";
|
|
1113
1152
|
}
|
|
@@ -1138,6 +1177,7 @@ async function imageToCommandCode(ref, readImage) {
|
|
|
1138
1177
|
async function messagesToCC(messages, readImage) {
|
|
1139
1178
|
const out = [];
|
|
1140
1179
|
const { ids: paired, names: toolNames } = pairedToolCalls(messages);
|
|
1180
|
+
const wireIds = wireToolCallIds(paired);
|
|
1141
1181
|
for (const message of messages) {
|
|
1142
1182
|
if (message.role === "system") continue;
|
|
1143
1183
|
if (message.role === "user" && message.source.kind !== "tool") {
|
|
@@ -1166,7 +1206,7 @@ async function messagesToCC(messages, readImage) {
|
|
|
1166
1206
|
});
|
|
1167
1207
|
else if (block.type === "tool-call" && paired.has(block.id)) parts.push({
|
|
1168
1208
|
type: "tool-call",
|
|
1169
|
-
toolCallId: block.id,
|
|
1209
|
+
toolCallId: wireIds.get(block.id) ?? block.id,
|
|
1170
1210
|
toolName: block.name,
|
|
1171
1211
|
input: recordOrEmpty(block.arguments)
|
|
1172
1212
|
});
|
|
@@ -1183,7 +1223,7 @@ async function messagesToCC(messages, readImage) {
|
|
|
1183
1223
|
role: "tool",
|
|
1184
1224
|
content: [{
|
|
1185
1225
|
type: "tool-result",
|
|
1186
|
-
toolCallId: block.toolCallId,
|
|
1226
|
+
toolCallId: wireIds.get(block.toolCallId) ?? block.toolCallId,
|
|
1187
1227
|
toolName: toolNames.get(block.toolCallId) || "unknown",
|
|
1188
1228
|
output: block.isError ? {
|
|
1189
1229
|
type: "error-text",
|
|
@@ -1198,6 +1238,89 @@ async function messagesToCC(messages, readImage) {
|
|
|
1198
1238
|
}
|
|
1199
1239
|
return out;
|
|
1200
1240
|
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Convert one image reference to the OpenAI Chat Completions wire format:
|
|
1243
|
+
* `{ type: 'image_url', image_url: { url: 'data:...;base64,...' } }`.
|
|
1244
|
+
*/
|
|
1245
|
+
async function imageToOpenAI(ref, readImage) {
|
|
1246
|
+
const data = await readImage(ref);
|
|
1247
|
+
return {
|
|
1248
|
+
type: "image_url",
|
|
1249
|
+
image_url: { url: `data:${ref.mediaType};base64,${Buffer.from(data).toString("base64")}` }
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Convert harness messages to the OpenAI Chat Completions request shape.
|
|
1254
|
+
*
|
|
1255
|
+
* Unlike the Command Code CLI transport, this transport is the documented
|
|
1256
|
+
* Provider API surface. DeepSeek's thinking-mode contract requires historical
|
|
1257
|
+
* `reasoning_content` to be passed back whenever tools are in play, so this
|
|
1258
|
+
* converter intentionally DOES replay reasoning blocks as `reasoning_content`
|
|
1259
|
+
* on assistant messages. Only tool calls with a paired tool result are
|
|
1260
|
+
* replayed (same policy as the CLI path).
|
|
1261
|
+
*/
|
|
1262
|
+
async function messagesToOpenAI(messages, readImage) {
|
|
1263
|
+
const out = [];
|
|
1264
|
+
const { ids: paired } = pairedToolCalls(messages);
|
|
1265
|
+
const wireIds = wireToolCallIds(paired);
|
|
1266
|
+
for (const message of messages) {
|
|
1267
|
+
if (message.role === "system") continue;
|
|
1268
|
+
if (message.role === "user" && message.source.kind !== "tool") {
|
|
1269
|
+
const parts = [];
|
|
1270
|
+
for (const block of message.content) {
|
|
1271
|
+
if (block.type === "text") parts.push({
|
|
1272
|
+
type: "text",
|
|
1273
|
+
text: block.text
|
|
1274
|
+
});
|
|
1275
|
+
if (block.type === "image") {
|
|
1276
|
+
if (!readImage) throw new LlmError("Image input requires the durable attachment service", "UNSUPPORTED_CONTENT");
|
|
1277
|
+
parts.push(await imageToOpenAI(block.attachment, readImage));
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (parts.length === 0) continue;
|
|
1281
|
+
if (!parts.some((part) => part.type === "image_url") && parts.length === 1) out.push({
|
|
1282
|
+
role: "user",
|
|
1283
|
+
content: parts[0].text
|
|
1284
|
+
});
|
|
1285
|
+
else out.push({
|
|
1286
|
+
role: "user",
|
|
1287
|
+
content: parts
|
|
1288
|
+
});
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
if (message.role === "assistant") {
|
|
1292
|
+
const text = message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
1293
|
+
const reasoning = message.content.filter((block) => block.type === "reasoning").map((block) => block.text).join("");
|
|
1294
|
+
const toolCalls = message.content.filter((block) => block.type === "tool-call" && paired.has(block.id)).map((block) => ({
|
|
1295
|
+
id: wireIds.get(block.id) ?? block.id,
|
|
1296
|
+
type: "function",
|
|
1297
|
+
function: {
|
|
1298
|
+
name: block.name,
|
|
1299
|
+
arguments: block.arguments
|
|
1300
|
+
}
|
|
1301
|
+
}));
|
|
1302
|
+
if (text === "" && reasoning === "" && toolCalls.length === 0) continue;
|
|
1303
|
+
const assistant = {
|
|
1304
|
+
role: "assistant",
|
|
1305
|
+
content: text === "" ? null : text
|
|
1306
|
+
};
|
|
1307
|
+
if (reasoning !== "") assistant.reasoning_content = reasoning;
|
|
1308
|
+
if (toolCalls.length > 0) assistant.tool_calls = toolCalls;
|
|
1309
|
+
out.push(assistant);
|
|
1310
|
+
continue;
|
|
1311
|
+
}
|
|
1312
|
+
if (message.role === "user" && message.source.kind === "tool") {
|
|
1313
|
+
const block = message.content[0];
|
|
1314
|
+
if (!block || block.type !== "tool-result" || !paired.has(block.toolCallId)) continue;
|
|
1315
|
+
out.push({
|
|
1316
|
+
role: "tool",
|
|
1317
|
+
tool_call_id: wireIds.get(block.toolCallId) ?? block.toolCallId,
|
|
1318
|
+
content: toolResultText(block)
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
return out;
|
|
1323
|
+
}
|
|
1201
1324
|
/** Account endpoints fetched by one `getUsage()` run (see the classification there). */
|
|
1202
1325
|
const USAGE_ENDPOINT_COUNT = 4;
|
|
1203
1326
|
var CommandCodeAdapter = class extends LlmAdapter {
|
|
@@ -1207,6 +1330,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1207
1330
|
resolveAttachments;
|
|
1208
1331
|
billingAccess = /* @__PURE__ */ new Map();
|
|
1209
1332
|
billingAccessInflight = /* @__PURE__ */ new Map();
|
|
1333
|
+
protocolCache = /* @__PURE__ */ new Map();
|
|
1210
1334
|
constructor(deps) {
|
|
1211
1335
|
super();
|
|
1212
1336
|
this.deps = deps;
|
|
@@ -1396,6 +1520,41 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1396
1520
|
return;
|
|
1397
1521
|
}
|
|
1398
1522
|
}
|
|
1523
|
+
/** Fresh cached billing tier weight for a key, or undefined when not known. */
|
|
1524
|
+
cachedBillingTierWeight(apiKey) {
|
|
1525
|
+
const hit = this.billingAccess.get(apiKey);
|
|
1526
|
+
if (hit === void 0 || Date.now() - hit.at >= 3e5) return void 0;
|
|
1527
|
+
return hit.value?.tierWeight;
|
|
1528
|
+
}
|
|
1529
|
+
/** Cached protocol decision for a key, or undefined when expired/unknown. */
|
|
1530
|
+
cachedProtocolUseCli(apiKey) {
|
|
1531
|
+
const hit = this.protocolCache.get(apiKey);
|
|
1532
|
+
if (hit === void 0 || Date.now() - hit.at >= 9e5) return void 0;
|
|
1533
|
+
return hit.useCli;
|
|
1534
|
+
}
|
|
1535
|
+
rememberProtocol(apiKey, useCli) {
|
|
1536
|
+
this.protocolCache.set(apiKey, {
|
|
1537
|
+
useCli,
|
|
1538
|
+
at: Date.now()
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Choose the initial protocol for one request. A fresh protocol cache entry
|
|
1543
|
+
* wins; otherwise a cached (not network-fetched) billing tier of Go is
|
|
1544
|
+
* treated as CLI-only. Unknown accounts default to Provider API and fall
|
|
1545
|
+
* back only after an `upgrade_required` rejection.
|
|
1546
|
+
*/
|
|
1547
|
+
resolveProtocol(apiKey) {
|
|
1548
|
+
const forced = this.deps.options().protocol;
|
|
1549
|
+
if (forced === "cli" || forced === "openai") return forced;
|
|
1550
|
+
const cached = this.cachedProtocolUseCli(apiKey);
|
|
1551
|
+
if (cached !== void 0) return cached ? "cli" : "openai";
|
|
1552
|
+
if (this.cachedBillingTierWeight(apiKey) === 0) {
|
|
1553
|
+
this.rememberProtocol(apiKey, true);
|
|
1554
|
+
return "cli";
|
|
1555
|
+
}
|
|
1556
|
+
return "openai";
|
|
1557
|
+
}
|
|
1399
1558
|
/**
|
|
1400
1559
|
* Fetch account, usage, credit, and subscription state from the Command
|
|
1401
1560
|
* Code account endpoints (`/alpha/whoami`, `/alpha/usage/summary`,
|
|
@@ -1542,44 +1701,70 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1542
1701
|
const supported = KNOWN_EFFORTS[options.model];
|
|
1543
1702
|
const reasoningEffort = effort && effort !== "off" && supported?.includes(effort) ? effort : void 0;
|
|
1544
1703
|
const systemText = [options.system ?? "", ...options.messages.filter((m) => m.role === "system").map((m) => m.content.map(blockText).filter(Boolean).join("\n"))].filter(Boolean).join("\n\n");
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1704
|
+
let protocol = this.resolveProtocol(apiKey);
|
|
1705
|
+
const buildBody = async (target) => {
|
|
1706
|
+
if (target === "cli") return {
|
|
1707
|
+
config: {
|
|
1708
|
+
workingDir: connection.workingDir,
|
|
1709
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
1710
|
+
environment: `${process.platform}-${process.arch}, Node.js ${process.version}`,
|
|
1711
|
+
structure: [],
|
|
1712
|
+
isGitRepo: false,
|
|
1713
|
+
currentBranch: "",
|
|
1714
|
+
mainBranch: "",
|
|
1715
|
+
gitStatus: "",
|
|
1716
|
+
recentCommits: []
|
|
1717
|
+
},
|
|
1718
|
+
memory: null,
|
|
1719
|
+
taste: null,
|
|
1720
|
+
skills: null,
|
|
1721
|
+
params: {
|
|
1722
|
+
model: options.model,
|
|
1723
|
+
messages: await messagesToCC(options.messages, readImage),
|
|
1724
|
+
tools: (options.tools ?? []).map((tool) => ({
|
|
1725
|
+
type: "function",
|
|
1726
|
+
name: tool.name,
|
|
1727
|
+
description: tool.description,
|
|
1728
|
+
input_schema: tool.parameters
|
|
1729
|
+
})),
|
|
1730
|
+
system: systemText,
|
|
1731
|
+
max_tokens: maxTokens,
|
|
1732
|
+
temperature: options.temperature ?? .3,
|
|
1733
|
+
stream: true,
|
|
1734
|
+
...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
|
|
1735
|
+
},
|
|
1736
|
+
threadId: randomUUID()
|
|
1737
|
+
};
|
|
1738
|
+
const openAiMessages = [...systemText ? [{
|
|
1739
|
+
role: "system",
|
|
1740
|
+
content: systemText
|
|
1741
|
+
}] : [], ...await messagesToOpenAI(options.messages, readImage)];
|
|
1742
|
+
const openAiTools = (options.tools ?? []).map((tool) => ({
|
|
1743
|
+
type: "function",
|
|
1744
|
+
function: {
|
|
1565
1745
|
name: tool.name,
|
|
1566
1746
|
description: tool.description,
|
|
1567
|
-
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1747
|
+
parameters: tool.parameters
|
|
1748
|
+
}
|
|
1749
|
+
}));
|
|
1750
|
+
return {
|
|
1751
|
+
model: options.model,
|
|
1752
|
+
messages: openAiMessages,
|
|
1753
|
+
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
1570
1754
|
max_tokens: maxTokens,
|
|
1571
1755
|
temperature: options.temperature ?? .3,
|
|
1572
1756
|
stream: true,
|
|
1573
1757
|
...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
|
|
1574
|
-
}
|
|
1575
|
-
threadId: randomUUID()
|
|
1758
|
+
};
|
|
1576
1759
|
};
|
|
1760
|
+
let body = await buildBody(protocol);
|
|
1577
1761
|
const connect = async (key) => {
|
|
1578
1762
|
const connectAbort = new AbortController();
|
|
1579
1763
|
let connectTimedOut = false;
|
|
1764
|
+
const endpoint = protocol === "cli" ? `${connection.apiBase}/alpha/generate` : `${connection.apiBase}/provider/v1/chat/completions`;
|
|
1580
1765
|
const connectTimer = setTimeout(() => {
|
|
1581
1766
|
connectTimedOut = true;
|
|
1582
|
-
connectAbort.abort(new DOMException(`Command Code API request to ${
|
|
1767
|
+
connectAbort.abort(new DOMException(`Command Code API request to ${endpoint} did not respond within ${connection.requestTimeoutMs}ms`, "TimeoutError"));
|
|
1583
1768
|
}, connection.requestTimeoutMs);
|
|
1584
1769
|
const onCallerAbort = () => {
|
|
1585
1770
|
connectAbort.abort(options.signal?.reason);
|
|
@@ -1593,19 +1778,25 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1593
1778
|
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
1594
1779
|
};
|
|
1595
1780
|
let response;
|
|
1781
|
+
const headers = protocol === "cli" ? {
|
|
1782
|
+
"Content-Type": "application/json",
|
|
1783
|
+
Authorization: `Bearer ${key}`,
|
|
1784
|
+
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
1785
|
+
"x-cli-environment": "production",
|
|
1786
|
+
"x-project-slug": projectSlugFromPath(connection.workingDir),
|
|
1787
|
+
"x-taste-learning": "true",
|
|
1788
|
+
"x-co-flag": "false",
|
|
1789
|
+
...attributionHeaders()
|
|
1790
|
+
} : {
|
|
1791
|
+
"Content-Type": "application/json",
|
|
1792
|
+
Authorization: `Bearer ${key}`,
|
|
1793
|
+
Accept: "text/event-stream",
|
|
1794
|
+
...attributionHeaders()
|
|
1795
|
+
};
|
|
1596
1796
|
try {
|
|
1597
|
-
response = await this.fetchImpl(
|
|
1797
|
+
response = await this.fetchImpl(endpoint, {
|
|
1598
1798
|
method: "POST",
|
|
1599
|
-
headers
|
|
1600
|
-
"Content-Type": "application/json",
|
|
1601
|
-
Authorization: `Bearer ${key}`,
|
|
1602
|
-
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
1603
|
-
"x-cli-environment": "production",
|
|
1604
|
-
"x-project-slug": projectSlugFromPath(connection.workingDir),
|
|
1605
|
-
"x-taste-learning": "true",
|
|
1606
|
-
"x-co-flag": "false",
|
|
1607
|
-
...attributionHeaders()
|
|
1608
|
-
},
|
|
1799
|
+
headers,
|
|
1609
1800
|
body: JSON.stringify(body),
|
|
1610
1801
|
signal: connectAbort.signal
|
|
1611
1802
|
});
|
|
@@ -1613,8 +1804,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1613
1804
|
} catch (error) {
|
|
1614
1805
|
cleanup();
|
|
1615
1806
|
if (options.signal?.aborted) throw error;
|
|
1616
|
-
if (connectTimedOut || error instanceof DOMException && error.name === "TimeoutError") throw new LlmError(`Command Code API request to ${
|
|
1617
|
-
throw new LlmError(`Command Code API request to ${
|
|
1807
|
+
if (connectTimedOut || error instanceof DOMException && error.name === "TimeoutError") throw new LlmError(`Command Code API request to ${endpoint} did not respond within ${connection.requestTimeoutMs}ms: ${errorChain(error)};Command Code API 请求在 ${connection.requestTimeoutMs} 毫秒内未收到响应——通常是网络或代理问题,请检查后重试`, "TIMEOUT", { cause: error });
|
|
1808
|
+
throw new LlmError(`Command Code API request to ${endpoint} failed: ${errorChain(error)};Command Code API 请求连接失败——通常是网络或代理问题,请检查网络或代理设置后重试`, "TRANSPORT", { cause: error });
|
|
1618
1809
|
}
|
|
1619
1810
|
if (!response.ok) {
|
|
1620
1811
|
const errText = await response.text().catch(() => "");
|
|
@@ -1643,6 +1834,12 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1643
1834
|
connected = attempt;
|
|
1644
1835
|
break;
|
|
1645
1836
|
}
|
|
1837
|
+
if (protocol === "openai" && isUpgradeRequiredError(attempt.status, attempt.errText)) {
|
|
1838
|
+
protocol = "cli";
|
|
1839
|
+
this.rememberProtocol(apiKey, true);
|
|
1840
|
+
body = await buildBody("cli");
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1646
1843
|
const rotate = this.deps.rotateApiKey;
|
|
1647
1844
|
if ((attempt.status === 429 || attempt.status === 401) && rotate !== void 0 && options.signal?.aborted !== true && tried.size < MAX_ACCOUNT_ROTATIONS) {
|
|
1648
1845
|
const next = await rotate(apiKey, attempt.status === 429 ? "rate-limit" : "invalid-credential", connection, options.model);
|
|
@@ -1708,7 +1905,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1708
1905
|
reasoningIndex = -1;
|
|
1709
1906
|
reasoningContent = "";
|
|
1710
1907
|
};
|
|
1711
|
-
const
|
|
1908
|
+
const handleCliEvent = (event) => {
|
|
1712
1909
|
const chunks = [];
|
|
1713
1910
|
if (!isRecord(event)) return chunks;
|
|
1714
1911
|
switch (event.type) {
|
|
@@ -1824,6 +2021,128 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1824
2021
|
}
|
|
1825
2022
|
return chunks;
|
|
1826
2023
|
};
|
|
2024
|
+
const openAiToolCalls = [];
|
|
2025
|
+
const emitOpenAiToolCalls = function* () {
|
|
2026
|
+
for (const call of openAiToolCalls) {
|
|
2027
|
+
const id = call.id ?? randomUUID();
|
|
2028
|
+
const name = call.name ?? "";
|
|
2029
|
+
const args = call.arguments || "{}";
|
|
2030
|
+
const index = nextIndex++;
|
|
2031
|
+
sawContent = true;
|
|
2032
|
+
yield {
|
|
2033
|
+
type: "block-start",
|
|
2034
|
+
index,
|
|
2035
|
+
blockType: "tool-call"
|
|
2036
|
+
};
|
|
2037
|
+
yield {
|
|
2038
|
+
type: "tool-call-delta",
|
|
2039
|
+
index,
|
|
2040
|
+
id: ToolCallId(id),
|
|
2041
|
+
name,
|
|
2042
|
+
argumentsDelta: args
|
|
2043
|
+
};
|
|
2044
|
+
yield {
|
|
2045
|
+
type: "block-end",
|
|
2046
|
+
index,
|
|
2047
|
+
block: {
|
|
2048
|
+
type: "tool-call",
|
|
2049
|
+
id: ToolCallId(id),
|
|
2050
|
+
name,
|
|
2051
|
+
arguments: args
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
openAiToolCalls.length = 0;
|
|
2056
|
+
};
|
|
2057
|
+
const handleOpenAIEvent = (event) => {
|
|
2058
|
+
const chunks = [];
|
|
2059
|
+
if (!isRecord(event)) return chunks;
|
|
2060
|
+
const choices = event.choices;
|
|
2061
|
+
if (!Array.isArray(choices) || choices.length === 0) {
|
|
2062
|
+
if (event.usage !== void 0) chunks.push({
|
|
2063
|
+
type: "usage",
|
|
2064
|
+
usage: mapOpenAIUsage(event.usage)
|
|
2065
|
+
});
|
|
2066
|
+
return chunks;
|
|
2067
|
+
}
|
|
2068
|
+
const choice = isRecord(choices[0]) ? choices[0] : {};
|
|
2069
|
+
const delta = isRecord(choice.delta) ? choice.delta : {};
|
|
2070
|
+
const reasoningDelta = stringValue(delta.reasoning) ?? stringValue(delta.reasoning_content) ?? "";
|
|
2071
|
+
if (reasoningDelta !== "") {
|
|
2072
|
+
chunks.push(...closeText());
|
|
2073
|
+
if (reasoningIndex < 0) {
|
|
2074
|
+
reasoningIndex = nextIndex++;
|
|
2075
|
+
chunks.push({
|
|
2076
|
+
type: "block-start",
|
|
2077
|
+
index: reasoningIndex,
|
|
2078
|
+
blockType: "reasoning"
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
reasoningContent += reasoningDelta;
|
|
2082
|
+
chunks.push({
|
|
2083
|
+
type: "reasoning-delta",
|
|
2084
|
+
index: reasoningIndex,
|
|
2085
|
+
text: reasoningDelta
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
const contentDelta = stringValue(delta.content) ?? "";
|
|
2089
|
+
if (contentDelta !== "") {
|
|
2090
|
+
chunks.push(...closeReasoning());
|
|
2091
|
+
if (textIndex < 0) {
|
|
2092
|
+
textIndex = nextIndex++;
|
|
2093
|
+
chunks.push({
|
|
2094
|
+
type: "block-start",
|
|
2095
|
+
index: textIndex,
|
|
2096
|
+
blockType: "text"
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
textContent += contentDelta;
|
|
2100
|
+
sawContent = true;
|
|
2101
|
+
chunks.push({
|
|
2102
|
+
type: "text-delta",
|
|
2103
|
+
index: textIndex,
|
|
2104
|
+
text: contentDelta
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
2108
|
+
sawContent = true;
|
|
2109
|
+
for (const rawCall of delta.tool_calls) {
|
|
2110
|
+
if (!isRecord(rawCall)) continue;
|
|
2111
|
+
const callIndex = numberValue(rawCall.index) ?? 0;
|
|
2112
|
+
let existing = openAiToolCalls.find((call) => call.index === callIndex);
|
|
2113
|
+
const fn = isRecord(rawCall.function) ? rawCall.function : void 0;
|
|
2114
|
+
const id = stringValue(rawCall.id);
|
|
2115
|
+
const name = fn === void 0 ? void 0 : stringValue(fn.name);
|
|
2116
|
+
const argDelta = fn === void 0 ? void 0 : stringValue(fn.arguments) ?? (fn.arguments === void 0 ? "" : JSON.stringify(fn.arguments));
|
|
2117
|
+
if (!existing) {
|
|
2118
|
+
existing = {
|
|
2119
|
+
index: callIndex,
|
|
2120
|
+
name: name ?? "",
|
|
2121
|
+
arguments: argDelta ?? ""
|
|
2122
|
+
};
|
|
2123
|
+
if (id !== void 0) existing.id = id;
|
|
2124
|
+
openAiToolCalls.push(existing);
|
|
2125
|
+
} else {
|
|
2126
|
+
if (id !== void 0 && existing.id === void 0) existing.id = id;
|
|
2127
|
+
if (name !== void 0 && existing.name === "") existing.name = name;
|
|
2128
|
+
if (argDelta !== void 0) existing.arguments += argDelta;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
if (choice.finish_reason !== void 0 && choice.finish_reason !== null) {
|
|
2133
|
+
chunks.push(...closeText(), ...closeReasoning(), ...emitOpenAiToolCalls());
|
|
2134
|
+
if (event.usage !== void 0) chunks.push({
|
|
2135
|
+
type: "usage",
|
|
2136
|
+
usage: mapOpenAIUsage(event.usage)
|
|
2137
|
+
});
|
|
2138
|
+
chunks.push({
|
|
2139
|
+
type: "finish",
|
|
2140
|
+
reason: mapOpenAIFinishReason(choice.finish_reason)
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
return chunks;
|
|
2144
|
+
};
|
|
2145
|
+
const handleEvent = (event) => protocol === "openai" ? handleOpenAIEvent(event) : handleCliEvent(event);
|
|
1827
2146
|
try {
|
|
1828
2147
|
let finished = false;
|
|
1829
2148
|
for (;;) {
|
|
@@ -1858,6 +2177,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1858
2177
|
if (!finished) {
|
|
1859
2178
|
yield* closeText();
|
|
1860
2179
|
yield* closeReasoning();
|
|
2180
|
+
if (protocol === "openai") yield* emitOpenAiToolCalls();
|
|
1861
2181
|
if (!sawContent) throw new LlmError("Command Code returned an empty response;Command Code 返回了空响应,重试通常可恢复", "EMPTY_RESPONSE");
|
|
1862
2182
|
yield {
|
|
1863
2183
|
type: "finish",
|
|
@@ -1922,6 +2242,55 @@ function mapFinishReason(reason) {
|
|
|
1922
2242
|
if (reason === "length" || reason === "max_tokens" || reason === "max-tokens" || reason === "max_output_tokens") return { kind: "max-tokens" };
|
|
1923
2243
|
return { kind: "stop" };
|
|
1924
2244
|
}
|
|
2245
|
+
function mapOpenAIFinishReason(reason) {
|
|
2246
|
+
if (reason === "tool_calls" || reason === "tool-calls") return { kind: "tool-calls" };
|
|
2247
|
+
if (reason === "length" || reason === "max_tokens" || reason === "max-tokens" || reason === "max_output_tokens") return { kind: "max-tokens" };
|
|
2248
|
+
return { kind: "stop" };
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* True when a Provider API pre-stream rejection is Command Code's Go-plan
|
|
2252
|
+
* gate (`upgrade_required`). Only this exact class of rejection should fall
|
|
2253
|
+
* back to the CLI /alpha/generate transport; other 4xx/5xx must surface as
|
|
2254
|
+
* ordinary errors so real account/model problems are not masked.
|
|
2255
|
+
*/
|
|
2256
|
+
function isUpgradeRequiredError(status, errText) {
|
|
2257
|
+
if (status !== 403) return false;
|
|
2258
|
+
const lower = errText.toLowerCase();
|
|
2259
|
+
if (lower.includes("upgrade_required")) return true;
|
|
2260
|
+
if (lower.includes("go plan") && lower.includes("api access")) return true;
|
|
2261
|
+
if (lower.includes("only plan without api access")) return true;
|
|
2262
|
+
if (lower.includes("upgrade to goat or higher")) return true;
|
|
2263
|
+
try {
|
|
2264
|
+
const parsed = JSON.parse(errText);
|
|
2265
|
+
if (isRecord(parsed)) {
|
|
2266
|
+
const error = isRecord(parsed.error) ? parsed.error : parsed;
|
|
2267
|
+
if ((stringValue(error.code) ?? stringValue(error.type))?.toLowerCase() === "upgrade_required") return true;
|
|
2268
|
+
const message = stringValue(error.message) ?? "";
|
|
2269
|
+
if (message.toLowerCase().includes("go plan") && message.toLowerCase().includes("api access")) return true;
|
|
2270
|
+
}
|
|
2271
|
+
} catch {}
|
|
2272
|
+
return false;
|
|
2273
|
+
}
|
|
2274
|
+
/** Map an OpenAI Chat Completions usage payload to harness disjoint counts. */
|
|
2275
|
+
function mapOpenAIUsage(usage) {
|
|
2276
|
+
const source = isRecord(usage) ? usage : {};
|
|
2277
|
+
const promptTokens = numberValue(source.prompt_tokens) ?? 0;
|
|
2278
|
+
const outputTokens = numberValue(source.completion_tokens) ?? 0;
|
|
2279
|
+
const totalTokens = numberValue(source.total_tokens);
|
|
2280
|
+
const promptDetails = isRecord(source.prompt_tokens_details) ? source.prompt_tokens_details : void 0;
|
|
2281
|
+
const cacheRead = numberValue(promptDetails?.cached_tokens) ?? 0;
|
|
2282
|
+
const cacheWrite = numberValue(promptDetails?.cache_creation_input_tokens) ?? 0;
|
|
2283
|
+
const reasoningTokens = numberValue((isRecord(source.completion_tokens_details) ? source.completion_tokens_details : void 0)?.reasoning_tokens);
|
|
2284
|
+
const usageOut = {
|
|
2285
|
+
inputTokens: Math.max(0, promptTokens - cacheRead - cacheWrite),
|
|
2286
|
+
outputTokens,
|
|
2287
|
+
cacheReadTokens: cacheRead
|
|
2288
|
+
};
|
|
2289
|
+
if (cacheWrite > 0) usageOut.cacheWriteTokens = cacheWrite;
|
|
2290
|
+
if (reasoningTokens !== void 0) usageOut.reasoningTokens = reasoningTokens;
|
|
2291
|
+
if (totalTokens !== void 0) usageOut.totalTokens = totalTokens;
|
|
2292
|
+
return usageOut;
|
|
2293
|
+
}
|
|
1925
2294
|
//#endregion
|
|
1926
2295
|
//#region src/command-locales.ts
|
|
1927
2296
|
const commandcodeCommand = {
|