@mars-sea/dsh-commandcode-provider 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +8 -4
- package/README.zh-CN.md +8 -4
- package/lib/client.js +519 -144
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +145 -31
- package/lib/index.js +991 -432
- package/lib/index.js.map +1 -1
- package/package.json +8 -1
package/lib/index.js
CHANGED
|
@@ -173,12 +173,18 @@ var CommandCodeAccountPool = class {
|
|
|
173
173
|
if (accounts.length === 0) return;
|
|
174
174
|
const routed = selectAccountForModel(accounts, options?.model ?? "", this.deps.modelAccountRules?.());
|
|
175
175
|
if (routed !== void 0) return this.pick(routed);
|
|
176
|
-
const
|
|
176
|
+
const preferred = this.deps.preferredId?.();
|
|
177
|
+
const chosen = selectActiveAccount(accounts, preferred);
|
|
177
178
|
if (chosen !== void 0) return this.pick(chosen);
|
|
178
179
|
await Promise.all(accounts.map(async (account) => {
|
|
179
180
|
if (account.state?.kind === "disabled") return;
|
|
180
181
|
if (options?.exclude !== void 0 && account.key === options.exclude) return;
|
|
181
|
-
|
|
182
|
+
let probe;
|
|
183
|
+
try {
|
|
184
|
+
probe = await this.deps.probeWindow(account.key);
|
|
185
|
+
} catch {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
182
188
|
if (probe === void 0) return;
|
|
183
189
|
if (!probe.exceeded) this.states.delete(account.key);
|
|
184
190
|
else this.states.set(account.key, {
|
|
@@ -187,9 +193,9 @@ var CommandCodeAccountPool = class {
|
|
|
187
193
|
until: probe.resetAt
|
|
188
194
|
});
|
|
189
195
|
}));
|
|
190
|
-
const revived = selectActiveAccount(await this.resolvedAccounts(), this.deps.preferredId?.());
|
|
191
|
-
if (revived !== void 0) return this.pick(revived);
|
|
192
196
|
const latest = await this.resolvedAccounts();
|
|
197
|
+
const revived = selectActiveAccount(latest, preferred);
|
|
198
|
+
if (revived !== void 0) return this.pick(revived);
|
|
193
199
|
if (latest.filter((account) => account.state?.kind === "disabled").length === latest.length) throw new LlmError(`llm-commandcode: every configured Command Code account (${latest.length}) was rejected with 401 — check the stored API keys (Models page / settings) or the auth file;已配置的 ${latest.length} 个 Command Code 账户密钥均被拒绝(401)——请在设置页检查存储的 API 密钥,或重新运行 command-code login`, "INVALID_CREDENTIAL");
|
|
194
200
|
const resets = latest.map((account) => account.state).filter((state) => state !== void 0 && state.kind === "cooldown" && state.until > 0).map((state) => state.until);
|
|
195
201
|
const earliest = resets.length > 0 ? Math.min(...resets) : 0;
|
|
@@ -464,13 +470,21 @@ const KNOWN_EFFORTS = {
|
|
|
464
470
|
"low",
|
|
465
471
|
"medium",
|
|
466
472
|
"high",
|
|
467
|
-
"xhigh"
|
|
473
|
+
"xhigh",
|
|
474
|
+
"max"
|
|
468
475
|
],
|
|
469
476
|
"meta/muse-spark-1.3-contributor": [
|
|
470
477
|
"low",
|
|
471
478
|
"medium",
|
|
472
479
|
"high",
|
|
473
480
|
"xhigh"
|
|
481
|
+
],
|
|
482
|
+
"gpt-6-astra": [
|
|
483
|
+
"low",
|
|
484
|
+
"medium",
|
|
485
|
+
"high",
|
|
486
|
+
"xhigh",
|
|
487
|
+
"max"
|
|
474
488
|
]
|
|
475
489
|
};
|
|
476
490
|
/**
|
|
@@ -519,12 +533,12 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
519
533
|
"gpt-5.6-luna",
|
|
520
534
|
"gpt-5.6-sol",
|
|
521
535
|
"gpt-5.6-terra",
|
|
536
|
+
"gpt-6-astra",
|
|
522
537
|
"meta/muse-spark-1.1",
|
|
523
538
|
"meta/muse-spark-1.2",
|
|
524
539
|
"meta/muse-spark-1.2-contributor",
|
|
525
540
|
"meta/muse-spark-1.3",
|
|
526
541
|
"meta/muse-spark-1.3-contributor",
|
|
527
|
-
"minimax/minimax-m3-free",
|
|
528
542
|
"moonshotai/Kimi-K2.5",
|
|
529
543
|
"moonshotai/Kimi-K2.6",
|
|
530
544
|
"moonshotai/Kimi-K2.7-Code",
|
|
@@ -540,7 +554,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
540
554
|
"z-ai/glm-5.3-flash"
|
|
541
555
|
]);
|
|
542
556
|
/**
|
|
543
|
-
* Models the official CLI's model table (command-code@1.
|
|
557
|
+
* Models the official CLI's model table (command-code@1.50.0) marks
|
|
544
558
|
* `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
|
|
545
559
|
* think automatically, with Command Code driving the depth. This is the
|
|
546
560
|
* authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
|
|
@@ -548,7 +562,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
548
562
|
* effort levels, and this snapshot is not surfaced in the picker's compact
|
|
549
563
|
* description — it exists for programmatic consumers.
|
|
550
564
|
*
|
|
551
|
-
* Source: the command-code@1.
|
|
565
|
+
* Source: the command-code@1.50.0 bundled model table (dist/cli.mjs),
|
|
552
566
|
* cross-checked with https://commandcode.ai/docs/reference/cli/models.
|
|
553
567
|
* (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
|
|
554
568
|
* selectable `['low', 'high', 'max']` efforts; the preview then ended in
|
|
@@ -571,7 +585,6 @@ const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
|
|
|
571
585
|
"Qwen/Qwen3.7-Flash",
|
|
572
586
|
"Qwen/Qwen3.7-Max",
|
|
573
587
|
"Qwen/Qwen3.7-Plus",
|
|
574
|
-
"minimax/minimax-m3-free",
|
|
575
588
|
"moonshotai/Kimi-K2.7-Code",
|
|
576
589
|
"moonshotai/Kimi-K2.7-Code-Highspeed",
|
|
577
590
|
"stepfun/Step-3.5-Flash",
|
|
@@ -624,12 +637,9 @@ const KNOWN_PLANS = {
|
|
|
624
637
|
"deepseek/deepseek-v4-flash-vision-exp": "go",
|
|
625
638
|
"deepseek/deepseek-v4-pro": "go",
|
|
626
639
|
"gpt-5.6-luna": "go",
|
|
627
|
-
"inclusionai/ling-3.0-flash-free": "go",
|
|
628
640
|
"meituan/LongCat-2.0:free": "go",
|
|
629
641
|
"meta/muse-spark-1.2-contributor": "go",
|
|
630
642
|
"meta/muse-spark-1.3-contributor": "go",
|
|
631
|
-
"minimax/minimax-m2.7-free": "go",
|
|
632
|
-
"minimax/minimax-m3-free": "go",
|
|
633
643
|
"moonshotai/Kimi-K2.5": "go",
|
|
634
644
|
"moonshotai/Kimi-K2.6": "go",
|
|
635
645
|
"moonshotai/Kimi-K2.7-Code": "go",
|
|
@@ -639,7 +649,6 @@ const KNOWN_PLANS = {
|
|
|
639
649
|
"poolside/laguna-s-2.1-free": "go",
|
|
640
650
|
"stepfun/Step-3.5-Flash": "go",
|
|
641
651
|
"stepfun/Step-3.7-Flash": "go",
|
|
642
|
-
"tencent/Hy3": "go",
|
|
643
652
|
"tencent/hy3-paid": "go",
|
|
644
653
|
"tencent/hy4-preview": "go",
|
|
645
654
|
"thinkingmachines/inkling": "go",
|
|
@@ -677,6 +686,7 @@ const KNOWN_PLANS = {
|
|
|
677
686
|
"claude-opus-4-7": "provider",
|
|
678
687
|
"claude-opus-4-8": "provider",
|
|
679
688
|
"claude-opus-5": "provider",
|
|
689
|
+
"gpt-6-astra": "provider",
|
|
680
690
|
"sakana/fugu-ultra": "provider"
|
|
681
691
|
};
|
|
682
692
|
/** Official display labels for each plan tier. */
|
|
@@ -724,8 +734,8 @@ function compareByPlan(a, b) {
|
|
|
724
734
|
}
|
|
725
735
|
/**
|
|
726
736
|
* Subscription plan table, synced from the official CLI bundle's plan maps
|
|
727
|
-
* (`
|
|
728
|
-
*
|
|
737
|
+
* (located by the `"individual-go"` key in command-code@1.50.0 `dist/cli.mjs`,
|
|
738
|
+
* re-verified unchanged through 1.50.0): subscription `planId`
|
|
729
739
|
* prefix → display name and the plan's monthly credit total. This is the
|
|
730
740
|
* account's own subscription (from `/alpha/billing/subscriptions`) — distinct
|
|
731
741
|
* from {@link KNOWN_PLANS}, which maps catalog models to their minimum tier.
|
|
@@ -793,13 +803,14 @@ function subscriptionPlanInfo(planId) {
|
|
|
793
803
|
/**
|
|
794
804
|
* Whether the picker lists `modelId` for an account with the given billing
|
|
795
805
|
* access. Fails open at every uncertainty: no billing data, an unknown plan,
|
|
796
|
-
*
|
|
797
|
-
*
|
|
806
|
+
* a non-finite weight (corrupt billing fact), or a model outside
|
|
807
|
+
* {@link KNOWN_PLANS} all keep the model visible — the server remains the
|
|
808
|
+
* final gate (`403 MODEL_NOT_IN_PLAN`).
|
|
798
809
|
*/
|
|
799
810
|
function modelVisibleInPlan(modelId, access) {
|
|
800
811
|
if (access === void 0) return true;
|
|
801
812
|
if (access.onDemandCredits > 0) return true;
|
|
802
|
-
if (access.tierWeight === void 0) return true;
|
|
813
|
+
if (access.tierWeight === void 0 || !Number.isFinite(access.tierWeight)) return true;
|
|
803
814
|
const tier = KNOWN_PLANS[modelId];
|
|
804
815
|
if (tier === void 0) return true;
|
|
805
816
|
const weight = PLAN_ORDER[tier];
|
|
@@ -893,8 +904,8 @@ function dealLabel(modelId, now = Date.now()) {
|
|
|
893
904
|
}
|
|
894
905
|
/**
|
|
895
906
|
* Compact human-readable context window, e.g. `1_000_000 -> "1M"`,
|
|
896
|
-
* `256_000 -> "256K"`, `262_144 -> "
|
|
897
|
-
* Returns undefined for unknown/absent sizes.
|
|
907
|
+
* `256_000 -> "256K"`, `262_144 -> "262K"` (floor to the nearest K).
|
|
908
|
+
* Returns undefined for unknown/absent sizes; values under 1K render raw.
|
|
898
909
|
*/
|
|
899
910
|
function formatContext(contextWindow) {
|
|
900
911
|
if (contextWindow === void 0 || !Number.isFinite(contextWindow) || contextWindow <= 0) return;
|
|
@@ -903,6 +914,7 @@ function formatContext(contextWindow) {
|
|
|
903
914
|
const rounded = Math.round(m * 10) / 10;
|
|
904
915
|
return `${Number.isInteger(rounded) ? rounded : rounded.toFixed(1)}M`;
|
|
905
916
|
}
|
|
917
|
+
if (contextWindow < 1e3) return String(Math.floor(contextWindow));
|
|
906
918
|
return `${Math.floor(contextWindow / 1e3)}K`;
|
|
907
919
|
}
|
|
908
920
|
/**
|
|
@@ -935,7 +947,7 @@ function capabilityDescription(modelId, contextWindow, now = Date.now()) {
|
|
|
935
947
|
* and API key or subscription, and Command Code's terms apply.
|
|
936
948
|
*
|
|
937
949
|
* Wire protocol (reverse-engineered by the pi plugin, command-code@1.28.4;
|
|
938
|
-
* re-verified against command-code@1.
|
|
950
|
+
* re-verified against command-code@1.50.0 — endpoints, request shape, and
|
|
939
951
|
* stream events unchanged):
|
|
940
952
|
* POST {apiBase}/alpha/generate
|
|
941
953
|
* body: { config, memory, taste, skills, params: { model, messages, tools,
|
|
@@ -948,13 +960,17 @@ function capabilityDescription(modelId, contextWindow, now = Date.now()) {
|
|
|
948
960
|
* per-request options thunk and an API-key resolver from the plugin entry
|
|
949
961
|
* (src/index.ts), so a settings change reaches the very next request.
|
|
950
962
|
*/
|
|
951
|
-
const COMMAND_CODE_CLI_VERSION = "1.
|
|
963
|
+
const COMMAND_CODE_CLI_VERSION = "1.50.0";
|
|
952
964
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
953
965
|
const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
|
|
954
966
|
const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
955
967
|
const MODELS_TIMEOUT_MS = 1e4;
|
|
956
968
|
/** How long the picker's plan-filter billing facts stay cached before refetching. */
|
|
957
969
|
const BILLING_ACCESS_TTL_MS = 3e5;
|
|
970
|
+
/** The entry-plan tier weight (`individual-go` in KNOWN_SUBSCRIPTION_PLANS). */
|
|
971
|
+
const GO_TIER_WEIGHT = 0;
|
|
972
|
+
/** Hard cap on account rotations within one request (one attempt per distinct key). */
|
|
973
|
+
const MAX_ACCOUNT_ROTATIONS = 16;
|
|
958
974
|
/**
|
|
959
975
|
* Subscription statuses the CLI treats as live (`Mr` in command-code's
|
|
960
976
|
* cli.mjs): the plan gate applies only under one of these.
|
|
@@ -1109,6 +1125,40 @@ function pairedToolCalls(messages) {
|
|
|
1109
1125
|
names
|
|
1110
1126
|
};
|
|
1111
1127
|
}
|
|
1128
|
+
/**
|
|
1129
|
+
* The Command Code gateway rejects tool call ids longer than 64 characters
|
|
1130
|
+
* (`input[N].call_id` must be `<= 64`, issue #23). Cross-provider histories
|
|
1131
|
+
* can carry longer ids — e.g. switching to Command Code mid-session after
|
|
1132
|
+
* another provider issued the call — so overlong paired ids are remapped to
|
|
1133
|
+
* short per-request aliases. Correlation only needs to hold within one
|
|
1134
|
+
* request (each call travels with its result in the same body), so a
|
|
1135
|
+
* sequential alias is enough: no durable state, no cross-request stability,
|
|
1136
|
+
* and the harness log keeps the original ids.
|
|
1137
|
+
*/
|
|
1138
|
+
const MAX_WIRE_TOOL_CALL_ID_LENGTH = 64;
|
|
1139
|
+
/**
|
|
1140
|
+
* Map each paired tool-call id to its wire id: ids within the gateway limit
|
|
1141
|
+
* pass through verbatim, overlong ids get a collision-free `cc-<n>` alias.
|
|
1142
|
+
* Callers must resolve BOTH the tool-call and its paired tool-result through
|
|
1143
|
+
* the returned map so the pair stays correlated.
|
|
1144
|
+
*/
|
|
1145
|
+
function wireToolCallIds(paired) {
|
|
1146
|
+
const wire = /* @__PURE__ */ new Map();
|
|
1147
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1148
|
+
for (const id of paired) if (id.length <= MAX_WIRE_TOOL_CALL_ID_LENGTH) {
|
|
1149
|
+
wire.set(id, id);
|
|
1150
|
+
taken.add(id);
|
|
1151
|
+
}
|
|
1152
|
+
let seq = 1;
|
|
1153
|
+
for (const id of paired) {
|
|
1154
|
+
if (wire.has(id)) continue;
|
|
1155
|
+
let alias = `cc-${seq++}`;
|
|
1156
|
+
while (taken.has(alias)) alias = `cc-${seq++}`;
|
|
1157
|
+
wire.set(id, alias);
|
|
1158
|
+
taken.add(alias);
|
|
1159
|
+
}
|
|
1160
|
+
return wire;
|
|
1161
|
+
}
|
|
1112
1162
|
function blockText(block) {
|
|
1113
1163
|
return block.type === "text" || block.type === "reasoning" ? block.text : "";
|
|
1114
1164
|
}
|
|
@@ -1139,6 +1189,7 @@ async function imageToCommandCode(ref, readImage) {
|
|
|
1139
1189
|
async function messagesToCC(messages, readImage) {
|
|
1140
1190
|
const out = [];
|
|
1141
1191
|
const { ids: paired, names: toolNames } = pairedToolCalls(messages);
|
|
1192
|
+
const wireIds = wireToolCallIds(paired);
|
|
1142
1193
|
for (const message of messages) {
|
|
1143
1194
|
if (message.role === "system") continue;
|
|
1144
1195
|
if (message.role === "user" && message.source.kind !== "tool") {
|
|
@@ -1167,7 +1218,7 @@ async function messagesToCC(messages, readImage) {
|
|
|
1167
1218
|
});
|
|
1168
1219
|
else if (block.type === "tool-call" && paired.has(block.id)) parts.push({
|
|
1169
1220
|
type: "tool-call",
|
|
1170
|
-
toolCallId: block.id,
|
|
1221
|
+
toolCallId: wireIds.get(block.id) ?? block.id,
|
|
1171
1222
|
toolName: block.name,
|
|
1172
1223
|
input: recordOrEmpty(block.arguments)
|
|
1173
1224
|
});
|
|
@@ -1184,7 +1235,7 @@ async function messagesToCC(messages, readImage) {
|
|
|
1184
1235
|
role: "tool",
|
|
1185
1236
|
content: [{
|
|
1186
1237
|
type: "tool-result",
|
|
1187
|
-
toolCallId: block.toolCallId,
|
|
1238
|
+
toolCallId: wireIds.get(block.toolCallId) ?? block.toolCallId,
|
|
1188
1239
|
toolName: toolNames.get(block.toolCallId) || "unknown",
|
|
1189
1240
|
output: block.isError ? {
|
|
1190
1241
|
type: "error-text",
|
|
@@ -1199,8 +1250,373 @@ async function messagesToCC(messages, readImage) {
|
|
|
1199
1250
|
}
|
|
1200
1251
|
return out;
|
|
1201
1252
|
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Convert one image reference to the OpenAI Chat Completions wire format:
|
|
1255
|
+
* `{ type: 'image_url', image_url: { url: 'data:...;base64,...' } }`.
|
|
1256
|
+
*/
|
|
1257
|
+
async function imageToOpenAI(ref, readImage) {
|
|
1258
|
+
const data = await readImage(ref);
|
|
1259
|
+
return {
|
|
1260
|
+
type: "image_url",
|
|
1261
|
+
image_url: { url: `data:${ref.mediaType};base64,${Buffer.from(data).toString("base64")}` }
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Convert harness messages to the OpenAI Chat Completions request shape.
|
|
1266
|
+
*
|
|
1267
|
+
* Unlike the Command Code CLI transport, this transport is the documented
|
|
1268
|
+
* Provider API surface. DeepSeek's thinking-mode contract requires historical
|
|
1269
|
+
* `reasoning_content` to be passed back whenever tools are in play, so this
|
|
1270
|
+
* converter intentionally DOES replay reasoning blocks as `reasoning_content`
|
|
1271
|
+
* on assistant messages. Only tool calls with a paired tool result are
|
|
1272
|
+
* replayed (same policy as the CLI path).
|
|
1273
|
+
*/
|
|
1274
|
+
async function messagesToOpenAI(messages, readImage) {
|
|
1275
|
+
const out = [];
|
|
1276
|
+
const { ids: paired } = pairedToolCalls(messages);
|
|
1277
|
+
const wireIds = wireToolCallIds(paired);
|
|
1278
|
+
for (const message of messages) {
|
|
1279
|
+
if (message.role === "system") continue;
|
|
1280
|
+
if (message.role === "user" && message.source.kind !== "tool") {
|
|
1281
|
+
const parts = [];
|
|
1282
|
+
for (const block of message.content) {
|
|
1283
|
+
if (block.type === "text") parts.push({
|
|
1284
|
+
type: "text",
|
|
1285
|
+
text: block.text
|
|
1286
|
+
});
|
|
1287
|
+
if (block.type === "image") {
|
|
1288
|
+
if (!readImage) throw new LlmError("Image input requires the durable attachment service", "UNSUPPORTED_CONTENT");
|
|
1289
|
+
parts.push(await imageToOpenAI(block.attachment, readImage));
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
if (parts.length === 0) continue;
|
|
1293
|
+
if (!parts.some((part) => part.type === "image_url") && parts.length === 1) out.push({
|
|
1294
|
+
role: "user",
|
|
1295
|
+
content: parts[0].text
|
|
1296
|
+
});
|
|
1297
|
+
else out.push({
|
|
1298
|
+
role: "user",
|
|
1299
|
+
content: parts
|
|
1300
|
+
});
|
|
1301
|
+
continue;
|
|
1302
|
+
}
|
|
1303
|
+
if (message.role === "assistant") {
|
|
1304
|
+
const text = message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
1305
|
+
const reasoning = message.content.filter((block) => block.type === "reasoning").map((block) => block.text).join("");
|
|
1306
|
+
const toolCalls = message.content.filter((block) => block.type === "tool-call" && paired.has(block.id)).map((block) => ({
|
|
1307
|
+
id: wireIds.get(block.id) ?? block.id,
|
|
1308
|
+
type: "function",
|
|
1309
|
+
function: {
|
|
1310
|
+
name: block.name,
|
|
1311
|
+
arguments: block.arguments
|
|
1312
|
+
}
|
|
1313
|
+
}));
|
|
1314
|
+
if (text === "" && reasoning === "" && toolCalls.length === 0) continue;
|
|
1315
|
+
const assistant = {
|
|
1316
|
+
role: "assistant",
|
|
1317
|
+
content: text === "" ? null : text
|
|
1318
|
+
};
|
|
1319
|
+
if (reasoning !== "") assistant.reasoning_content = reasoning;
|
|
1320
|
+
if (toolCalls.length > 0) assistant.tool_calls = toolCalls;
|
|
1321
|
+
out.push(assistant);
|
|
1322
|
+
continue;
|
|
1323
|
+
}
|
|
1324
|
+
if (message.role === "user" && message.source.kind === "tool") {
|
|
1325
|
+
const block = message.content[0];
|
|
1326
|
+
if (!block || block.type !== "tool-result" || !paired.has(block.toolCallId)) continue;
|
|
1327
|
+
out.push({
|
|
1328
|
+
role: "tool",
|
|
1329
|
+
tool_call_id: wireIds.get(block.toolCallId) ?? block.toolCallId,
|
|
1330
|
+
content: toolResultText(block)
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
return out;
|
|
1335
|
+
}
|
|
1202
1336
|
/** Account endpoints fetched by one `getUsage()` run (see the classification there). */
|
|
1203
1337
|
const USAGE_ENDPOINT_COUNT = 4;
|
|
1338
|
+
/**
|
|
1339
|
+
* Parse one usage/summary payload into the report's usage section.
|
|
1340
|
+
* Returns undefined when the endpoint answered nothing usable.
|
|
1341
|
+
*/
|
|
1342
|
+
function parseUsageTotals(usage) {
|
|
1343
|
+
if (usage === void 0) return void 0;
|
|
1344
|
+
return {
|
|
1345
|
+
totalCount: numberValue(usage.totalCount) ?? 0,
|
|
1346
|
+
totalCost: numberValue(usage.totalCost) ?? 0,
|
|
1347
|
+
successRate: numberValue(usage.successRate) ?? 0,
|
|
1348
|
+
completedCount: numberValue(usage.completedCount) ?? 0,
|
|
1349
|
+
failedCount: numberValue(usage.failedCount) ?? 0,
|
|
1350
|
+
totalTokensIn: numberValue(usage.totalTokensIn) ?? 0,
|
|
1351
|
+
totalTokensOut: numberValue(usage.totalTokensOut) ?? 0,
|
|
1352
|
+
totalCredits: numberValue(usage.totalCredits) ?? 0,
|
|
1353
|
+
periodBasis: stringValue(usage.periodBasis) ?? "billing-period"
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
/** Parse one window-limit block (`fiveHour` / `weekly`). */
|
|
1357
|
+
function parseWindowLimit(value) {
|
|
1358
|
+
const block = isRecord(value) ? value : void 0;
|
|
1359
|
+
return {
|
|
1360
|
+
used: numberValue(block?.used) ?? 0,
|
|
1361
|
+
cap: numberValue(block?.cap) ?? 0,
|
|
1362
|
+
exceeded: block?.exceeded === true,
|
|
1363
|
+
resetAt: numberValue(block?.resetAt) ?? 0
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Parse one billing/credits payload into the report's credits section.
|
|
1368
|
+
* Returns undefined when the endpoint answered nothing usable.
|
|
1369
|
+
*/
|
|
1370
|
+
function parseCreditLimits(credits) {
|
|
1371
|
+
if (credits === void 0) return void 0;
|
|
1372
|
+
const creditsData = isRecord(credits.credits) ? credits.credits : void 0;
|
|
1373
|
+
const windowLimits = isRecord(credits.windowLimits) ? credits.windowLimits : void 0;
|
|
1374
|
+
const fiveHour = windowLimits !== void 0 && isRecord(windowLimits.fiveHour) ? windowLimits.fiveHour : void 0;
|
|
1375
|
+
const weekly = windowLimits !== void 0 && isRecord(windowLimits.weekly) ? windowLimits.weekly : void 0;
|
|
1376
|
+
if (creditsData === void 0 && fiveHour === void 0 && weekly === void 0) return void 0;
|
|
1377
|
+
return {
|
|
1378
|
+
monthlyCredits: numberValue(creditsData?.monthlyCredits) ?? 0,
|
|
1379
|
+
purchasedCredits: numberValue(creditsData?.purchasedCredits) ?? 0,
|
|
1380
|
+
freeCredits: numberValue(creditsData?.freeCredits) ?? 0,
|
|
1381
|
+
fiveHour: parseWindowLimit(fiveHour),
|
|
1382
|
+
weekly: parseWindowLimit(weekly)
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Parse the account identity from whoami plus the plan identity from the
|
|
1387
|
+
* subscriptions/credits payloads. Returns the org id for the subscriptions
|
|
1388
|
+
* query alongside, so getUsage fetches whoami first and the rest in parallel.
|
|
1389
|
+
*/
|
|
1390
|
+
function parseAccountIdentity(whoami) {
|
|
1391
|
+
const whoamiData = whoami !== void 0 && isRecord(whoami.user) ? whoami.user : void 0;
|
|
1392
|
+
const orgData = whoami !== void 0 && isRecord(whoami.org) ? whoami.org : void 0;
|
|
1393
|
+
return {
|
|
1394
|
+
account: whoamiData === void 0 ? void 0 : {
|
|
1395
|
+
id: stringValue(whoamiData.id) ?? "",
|
|
1396
|
+
name: stringValue(whoamiData.name) ?? "",
|
|
1397
|
+
userName: stringValue(whoamiData.userName) ?? ""
|
|
1398
|
+
},
|
|
1399
|
+
orgId: orgData === void 0 ? void 0 : stringValue(orgData.id)
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Classify a TOTAL failure: when every endpoint failed with one class of
|
|
1404
|
+
* error, the degraded per-endpoint view would hide the root cause behind
|
|
1405
|
+
* a generic "partial data" note — name it instead.
|
|
1406
|
+
*/
|
|
1407
|
+
function classifyTotalFailure(failures, failedStatuses) {
|
|
1408
|
+
if (failures.length !== USAGE_ENDPOINT_COUNT) return void 0;
|
|
1409
|
+
const codes = failedStatuses.filter((status) => status !== void 0);
|
|
1410
|
+
if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code === 401)) return "invalid-key";
|
|
1411
|
+
if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code >= 500)) return "service-unavailable";
|
|
1412
|
+
if (codes.length === 0) return "network";
|
|
1413
|
+
}
|
|
1414
|
+
/** Build the legacy CLI (`/alpha/generate`) request body for one call. */
|
|
1415
|
+
async function buildCliBody(options, connection, facts) {
|
|
1416
|
+
return {
|
|
1417
|
+
config: {
|
|
1418
|
+
workingDir: connection.workingDir,
|
|
1419
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
1420
|
+
environment: `${process.platform}-${process.arch}, Node.js ${process.version}`,
|
|
1421
|
+
structure: [],
|
|
1422
|
+
isGitRepo: false,
|
|
1423
|
+
currentBranch: "",
|
|
1424
|
+
mainBranch: "",
|
|
1425
|
+
gitStatus: "",
|
|
1426
|
+
recentCommits: []
|
|
1427
|
+
},
|
|
1428
|
+
memory: null,
|
|
1429
|
+
taste: null,
|
|
1430
|
+
skills: null,
|
|
1431
|
+
params: {
|
|
1432
|
+
model: options.model,
|
|
1433
|
+
messages: await messagesToCC(options.messages, facts.readImage),
|
|
1434
|
+
tools: (options.tools ?? []).map((tool) => ({
|
|
1435
|
+
type: "function",
|
|
1436
|
+
name: tool.name,
|
|
1437
|
+
description: tool.description,
|
|
1438
|
+
input_schema: tool.parameters
|
|
1439
|
+
})),
|
|
1440
|
+
system: facts.systemText,
|
|
1441
|
+
max_tokens: facts.maxTokens,
|
|
1442
|
+
temperature: options.temperature ?? .3,
|
|
1443
|
+
stream: true,
|
|
1444
|
+
...facts.reasoningEffort ? { reasoning_effort: facts.reasoningEffort } : {}
|
|
1445
|
+
},
|
|
1446
|
+
threadId: randomUUID()
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
/** Build the documented Provider Chat Completions request body for one call. */
|
|
1450
|
+
async function buildOpenAIBody(options, facts) {
|
|
1451
|
+
const openAiMessages = [...facts.systemText ? [{
|
|
1452
|
+
role: "system",
|
|
1453
|
+
content: facts.systemText
|
|
1454
|
+
}] : [], ...await messagesToOpenAI(options.messages, facts.readImage)];
|
|
1455
|
+
const openAiTools = (options.tools ?? []).map((tool) => ({
|
|
1456
|
+
type: "function",
|
|
1457
|
+
function: {
|
|
1458
|
+
name: tool.name,
|
|
1459
|
+
description: tool.description,
|
|
1460
|
+
parameters: tool.parameters
|
|
1461
|
+
}
|
|
1462
|
+
}));
|
|
1463
|
+
return {
|
|
1464
|
+
model: options.model,
|
|
1465
|
+
messages: openAiMessages,
|
|
1466
|
+
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
1467
|
+
max_tokens: facts.maxTokens,
|
|
1468
|
+
temperature: options.temperature ?? .3,
|
|
1469
|
+
stream: true,
|
|
1470
|
+
...facts.reasoningEffort ? { reasoning_effort: facts.reasoningEffort } : {}
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* One pre-stream connect attempt: POST the body and wait for response
|
|
1475
|
+
* headers only (`requestTimeoutMs` must never bound the body stream — see
|
|
1476
|
+
* the caller). Returns the live response plus its cleanup, or the rejection
|
|
1477
|
+
* facts for the rotation loop to classify. Every failure path cleans up
|
|
1478
|
+
* before returning or throwing; on success the caller-abort listener
|
|
1479
|
+
* outlives the connect phase (it aborts a stalled body read), so the
|
|
1480
|
+
* streaming tail calls cleanup.
|
|
1481
|
+
*/
|
|
1482
|
+
async function connectGenerate(deps, key, protocol, body) {
|
|
1483
|
+
const { options, connection, fetchImpl } = deps;
|
|
1484
|
+
const connectAbort = new AbortController();
|
|
1485
|
+
let connectTimedOut = false;
|
|
1486
|
+
const endpoint = protocol === "cli" ? `${connection.apiBase}/alpha/generate` : `${connection.apiBase}/provider/v1/chat/completions`;
|
|
1487
|
+
const connectTimer = setTimeout(() => {
|
|
1488
|
+
connectTimedOut = true;
|
|
1489
|
+
connectAbort.abort(new DOMException(`Command Code API request to ${endpoint} did not respond within ${connection.requestTimeoutMs}ms`, "TimeoutError"));
|
|
1490
|
+
}, connection.requestTimeoutMs);
|
|
1491
|
+
const onCallerAbort = () => {
|
|
1492
|
+
connectAbort.abort(options.signal?.reason);
|
|
1493
|
+
};
|
|
1494
|
+
if (options.signal) {
|
|
1495
|
+
if (options.signal.aborted) onCallerAbort();
|
|
1496
|
+
else options.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
1497
|
+
}
|
|
1498
|
+
const cleanup = () => {
|
|
1499
|
+
clearTimeout(connectTimer);
|
|
1500
|
+
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
1501
|
+
};
|
|
1502
|
+
let response;
|
|
1503
|
+
const headers = protocol === "cli" ? {
|
|
1504
|
+
"Content-Type": "application/json",
|
|
1505
|
+
Authorization: `Bearer ${key}`,
|
|
1506
|
+
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
1507
|
+
"x-cli-environment": "production",
|
|
1508
|
+
"x-project-slug": projectSlugFromPath(connection.workingDir),
|
|
1509
|
+
"x-taste-learning": "true",
|
|
1510
|
+
"x-co-flag": "false",
|
|
1511
|
+
...attributionHeaders()
|
|
1512
|
+
} : {
|
|
1513
|
+
"Content-Type": "application/json",
|
|
1514
|
+
Authorization: `Bearer ${key}`,
|
|
1515
|
+
Accept: "text/event-stream",
|
|
1516
|
+
...attributionHeaders()
|
|
1517
|
+
};
|
|
1518
|
+
try {
|
|
1519
|
+
response = await fetchImpl(endpoint, {
|
|
1520
|
+
method: "POST",
|
|
1521
|
+
headers,
|
|
1522
|
+
body: JSON.stringify(body),
|
|
1523
|
+
signal: connectAbort.signal
|
|
1524
|
+
});
|
|
1525
|
+
clearTimeout(connectTimer);
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
cleanup();
|
|
1528
|
+
if (options.signal?.aborted) throw error;
|
|
1529
|
+
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 });
|
|
1530
|
+
throw new LlmError(`Command Code API request to ${endpoint} failed: ${errorChain(error)};Command Code API 请求连接失败——通常是网络或代理问题,请检查网络或代理设置后重试`, "TRANSPORT", { cause: error });
|
|
1531
|
+
}
|
|
1532
|
+
if (!response.ok) {
|
|
1533
|
+
const errText = await response.text().catch(() => "");
|
|
1534
|
+
cleanup();
|
|
1535
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
1536
|
+
return retryAfterMs === void 0 ? {
|
|
1537
|
+
status: response.status,
|
|
1538
|
+
errText
|
|
1539
|
+
} : {
|
|
1540
|
+
status: response.status,
|
|
1541
|
+
errText,
|
|
1542
|
+
retryAfterMs
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
return {
|
|
1546
|
+
response,
|
|
1547
|
+
cleanup
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
/** Fresh block-assembly state for one stream. */
|
|
1551
|
+
function createBlockAssembler() {
|
|
1552
|
+
return {
|
|
1553
|
+
nextIndex: 0,
|
|
1554
|
+
textIndex: -1,
|
|
1555
|
+
textContent: "",
|
|
1556
|
+
reasoningIndex: -1,
|
|
1557
|
+
reasoningContent: "",
|
|
1558
|
+
sawContent: false,
|
|
1559
|
+
openAiToolCalls: []
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
function* closeText(asm) {
|
|
1563
|
+
if (asm.textIndex < 0) return;
|
|
1564
|
+
yield {
|
|
1565
|
+
type: "block-end",
|
|
1566
|
+
index: asm.textIndex,
|
|
1567
|
+
block: {
|
|
1568
|
+
type: "text",
|
|
1569
|
+
text: asm.textContent
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
asm.textIndex = -1;
|
|
1573
|
+
asm.textContent = "";
|
|
1574
|
+
}
|
|
1575
|
+
function* closeReasoning(asm) {
|
|
1576
|
+
if (asm.reasoningIndex < 0) return;
|
|
1577
|
+
yield {
|
|
1578
|
+
type: "block-end",
|
|
1579
|
+
index: asm.reasoningIndex,
|
|
1580
|
+
block: {
|
|
1581
|
+
type: "reasoning",
|
|
1582
|
+
text: asm.reasoningContent
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
asm.reasoningIndex = -1;
|
|
1586
|
+
asm.reasoningContent = "";
|
|
1587
|
+
}
|
|
1588
|
+
function* emitOpenAiToolCalls(asm) {
|
|
1589
|
+
for (const call of asm.openAiToolCalls) {
|
|
1590
|
+
const id = call.id ?? randomUUID();
|
|
1591
|
+
const name = call.name ?? "";
|
|
1592
|
+
const args = call.arguments || "{}";
|
|
1593
|
+
const index = asm.nextIndex++;
|
|
1594
|
+
asm.sawContent = true;
|
|
1595
|
+
yield {
|
|
1596
|
+
type: "block-start",
|
|
1597
|
+
index,
|
|
1598
|
+
blockType: "tool-call"
|
|
1599
|
+
};
|
|
1600
|
+
yield {
|
|
1601
|
+
type: "tool-call-delta",
|
|
1602
|
+
index,
|
|
1603
|
+
id: ToolCallId(id),
|
|
1604
|
+
name,
|
|
1605
|
+
argumentsDelta: args
|
|
1606
|
+
};
|
|
1607
|
+
yield {
|
|
1608
|
+
type: "block-end",
|
|
1609
|
+
index,
|
|
1610
|
+
block: {
|
|
1611
|
+
type: "tool-call",
|
|
1612
|
+
id: ToolCallId(id),
|
|
1613
|
+
name,
|
|
1614
|
+
arguments: args
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
asm.openAiToolCalls.length = 0;
|
|
1619
|
+
}
|
|
1204
1620
|
var CommandCodeAdapter = class extends LlmAdapter {
|
|
1205
1621
|
deps;
|
|
1206
1622
|
catalog = [];
|
|
@@ -1208,6 +1624,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1208
1624
|
resolveAttachments;
|
|
1209
1625
|
billingAccess = /* @__PURE__ */ new Map();
|
|
1210
1626
|
billingAccessInflight = /* @__PURE__ */ new Map();
|
|
1627
|
+
protocolCache = /* @__PURE__ */ new Map();
|
|
1211
1628
|
constructor(deps) {
|
|
1212
1629
|
super();
|
|
1213
1630
|
this.deps = deps;
|
|
@@ -1282,10 +1699,9 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1282
1699
|
}
|
|
1283
1700
|
return this.catalog;
|
|
1284
1701
|
}
|
|
1285
|
-
async listModels(provider) {
|
|
1702
|
+
async listModels(provider, opts) {
|
|
1286
1703
|
const catalog = await this.loadCatalog();
|
|
1287
|
-
const
|
|
1288
|
-
return catalog.filter((model) => modelVisibleInPlan(model.id, access)).map((model) => {
|
|
1704
|
+
const toInfo = (model) => {
|
|
1289
1705
|
const vision = KNOWN_IMAGE_MODELS.has(model.id);
|
|
1290
1706
|
return {
|
|
1291
1707
|
provider,
|
|
@@ -1294,7 +1710,12 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1294
1710
|
description: capabilityDescription(model.id, model.contextWindow),
|
|
1295
1711
|
inputModalities: vision ? ["text", "image"] : ["text"]
|
|
1296
1712
|
};
|
|
1297
|
-
}
|
|
1713
|
+
};
|
|
1714
|
+
if (opts?.unfiltered === true) return catalog.map(toInfo).sort(compareByPlan);
|
|
1715
|
+
const access = this.deps.options().filterModelsByPlan === false ? void 0 : await this.loadBillingAccess();
|
|
1716
|
+
const visible = this.deps.options().visibleModels;
|
|
1717
|
+
const allow = Array.isArray(visible) && visible.length > 0 ? new Set(visible.filter((id) => typeof id === "string" && id !== "")) : void 0;
|
|
1718
|
+
return catalog.filter((model) => modelVisibleInPlan(model.id, access)).filter((model) => allow === void 0 || allow.has(model.id)).map(toInfo).sort(compareByPlan);
|
|
1298
1719
|
}
|
|
1299
1720
|
async resolveModel(provider, model, signal) {
|
|
1300
1721
|
const entry = this.catalog.find((m) => m.id === model) ?? (await this.loadCatalog(signal)).find((m) => m.id === model);
|
|
@@ -1327,6 +1748,25 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1327
1748
|
};
|
|
1328
1749
|
}
|
|
1329
1750
|
/**
|
|
1751
|
+
* Fetch one account endpoint and parse its JSON body. Returns the HTTP
|
|
1752
|
+
* status alongside the parsed record so each caller applies its own
|
|
1753
|
+
* failure accounting: the billing probe fails open silently, the usage
|
|
1754
|
+
* report books failures per endpoint. Non-2xx and non-record bodies come
|
|
1755
|
+
* back without a record; only a transport throw propagates to the caller.
|
|
1756
|
+
*/
|
|
1757
|
+
async fetchEndpointJson(url, headers) {
|
|
1758
|
+
const response = await this.fetchImpl(url, {
|
|
1759
|
+
headers,
|
|
1760
|
+
signal: AbortSignal.timeout(MODELS_TIMEOUT_MS)
|
|
1761
|
+
});
|
|
1762
|
+
if (!response.ok) return { status: response.status };
|
|
1763
|
+
const parsed = await response.json();
|
|
1764
|
+
return {
|
|
1765
|
+
status: response.status,
|
|
1766
|
+
...isRecord(parsed) ? { record: parsed } : {}
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1330
1770
|
* The billing facts behind the picker's plan filter, cached for
|
|
1331
1771
|
* {@link BILLING_ACCESS_TTL_MS} and shared across concurrent callers.
|
|
1332
1772
|
* `undefined` means "unknown — show everything" (fail-open).
|
|
@@ -1368,15 +1808,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1368
1808
|
const connection = this.deps.options();
|
|
1369
1809
|
const headers = await this.accountHeaders(apiKey);
|
|
1370
1810
|
const base = connection.apiBase;
|
|
1371
|
-
const getJson = async (path) => {
|
|
1372
|
-
const response = await this.fetchImpl(`${base}${path}`, {
|
|
1373
|
-
headers,
|
|
1374
|
-
signal: AbortSignal.timeout(MODELS_TIMEOUT_MS)
|
|
1375
|
-
});
|
|
1376
|
-
if (!response.ok) return void 0;
|
|
1377
|
-
const parsed = await response.json();
|
|
1378
|
-
return isRecord(parsed) ? parsed : void 0;
|
|
1379
|
-
};
|
|
1811
|
+
const getJson = async (path) => (await this.fetchEndpointJson(`${base}${path}`, headers)).record;
|
|
1380
1812
|
const whoami = await getJson("/alpha/whoami");
|
|
1381
1813
|
const orgData = whoami && isRecord(whoami.org) ? whoami.org : void 0;
|
|
1382
1814
|
const orgId = orgData === void 0 ? void 0 : stringValue(orgData.id);
|
|
@@ -1397,6 +1829,41 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1397
1829
|
return;
|
|
1398
1830
|
}
|
|
1399
1831
|
}
|
|
1832
|
+
/** Fresh cached billing tier weight for a key, or undefined when not known. */
|
|
1833
|
+
cachedBillingTierWeight(apiKey) {
|
|
1834
|
+
const hit = this.billingAccess.get(apiKey);
|
|
1835
|
+
if (hit === void 0 || Date.now() - hit.at >= 3e5) return void 0;
|
|
1836
|
+
return hit.value?.tierWeight;
|
|
1837
|
+
}
|
|
1838
|
+
/** Cached protocol decision for a key, or undefined when expired/unknown. */
|
|
1839
|
+
cachedProtocolUseCli(apiKey) {
|
|
1840
|
+
const hit = this.protocolCache.get(apiKey);
|
|
1841
|
+
if (hit === void 0 || Date.now() - hit.at >= 9e5) return void 0;
|
|
1842
|
+
return hit.useCli;
|
|
1843
|
+
}
|
|
1844
|
+
rememberProtocol(apiKey, useCli) {
|
|
1845
|
+
this.protocolCache.set(apiKey, {
|
|
1846
|
+
useCli,
|
|
1847
|
+
at: Date.now()
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
/**
|
|
1851
|
+
* Choose the initial protocol for one request. A fresh protocol cache entry
|
|
1852
|
+
* wins; otherwise a cached (not network-fetched) billing tier of Go is
|
|
1853
|
+
* treated as CLI-only. Unknown accounts default to Provider API and fall
|
|
1854
|
+
* back only after an `upgrade_required` rejection.
|
|
1855
|
+
*/
|
|
1856
|
+
resolveProtocol(apiKey) {
|
|
1857
|
+
const forced = this.deps.options().protocol;
|
|
1858
|
+
if (forced === "cli" || forced === "openai") return forced;
|
|
1859
|
+
const cached = this.cachedProtocolUseCli(apiKey);
|
|
1860
|
+
if (cached !== void 0) return cached ? "cli" : "openai";
|
|
1861
|
+
if (this.cachedBillingTierWeight(apiKey) === GO_TIER_WEIGHT) {
|
|
1862
|
+
this.rememberProtocol(apiKey, true);
|
|
1863
|
+
return "cli";
|
|
1864
|
+
}
|
|
1865
|
+
return "openai";
|
|
1866
|
+
}
|
|
1400
1867
|
/**
|
|
1401
1868
|
* Fetch account, usage, credit, and subscription state from the Command
|
|
1402
1869
|
* Code account endpoints (`/alpha/whoami`, `/alpha/usage/summary`,
|
|
@@ -1414,17 +1881,13 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1414
1881
|
const failedStatuses = [];
|
|
1415
1882
|
const getJson = async (path) => {
|
|
1416
1883
|
try {
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
if (!response.ok) {
|
|
1422
|
-
failures.push(`${path}: HTTP ${response.status}`);
|
|
1423
|
-
failedStatuses.push(response.status);
|
|
1884
|
+
const { status, record } = await this.fetchEndpointJson(`${base}${path}`, headers);
|
|
1885
|
+
if (record === void 0) {
|
|
1886
|
+
failures.push(`${path}: HTTP ${status}`);
|
|
1887
|
+
failedStatuses.push(status);
|
|
1424
1888
|
return;
|
|
1425
1889
|
}
|
|
1426
|
-
|
|
1427
|
-
return isRecord(parsed) ? parsed : void 0;
|
|
1890
|
+
return record;
|
|
1428
1891
|
} catch (error) {
|
|
1429
1892
|
failures.push(`${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1430
1893
|
failedStatuses.push(void 0);
|
|
@@ -1432,51 +1895,19 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1432
1895
|
}
|
|
1433
1896
|
};
|
|
1434
1897
|
const report = { failures };
|
|
1435
|
-
const
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
const
|
|
1445
|
-
if (
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
successRate: numberValue(usage.successRate) ?? 0,
|
|
1449
|
-
completedCount: numberValue(usage.completedCount) ?? 0,
|
|
1450
|
-
failedCount: numberValue(usage.failedCount) ?? 0,
|
|
1451
|
-
totalTokensIn: numberValue(usage.totalTokensIn) ?? 0,
|
|
1452
|
-
totalTokensOut: numberValue(usage.totalTokensOut) ?? 0,
|
|
1453
|
-
totalCredits: numberValue(usage.totalCredits) ?? 0,
|
|
1454
|
-
periodBasis: stringValue(usage.periodBasis) ?? "billing-period"
|
|
1455
|
-
};
|
|
1456
|
-
const credits = await getJson("/alpha/billing/credits");
|
|
1457
|
-
const creditsData = credits && isRecord(credits.credits) ? credits.credits : void 0;
|
|
1458
|
-
const windowLimits = credits && isRecord(credits.windowLimits) ? credits.windowLimits : void 0;
|
|
1459
|
-
const fiveHour = windowLimits && isRecord(windowLimits.fiveHour) ? windowLimits.fiveHour : void 0;
|
|
1460
|
-
const weekly = windowLimits && isRecord(windowLimits.weekly) ? windowLimits.weekly : void 0;
|
|
1461
|
-
if (creditsData || fiveHour || weekly) report.credits = {
|
|
1462
|
-
monthlyCredits: numberValue(creditsData?.monthlyCredits) ?? 0,
|
|
1463
|
-
purchasedCredits: numberValue(creditsData?.purchasedCredits) ?? 0,
|
|
1464
|
-
freeCredits: numberValue(creditsData?.freeCredits) ?? 0,
|
|
1465
|
-
fiveHour: {
|
|
1466
|
-
used: numberValue(fiveHour?.used) ?? 0,
|
|
1467
|
-
cap: numberValue(fiveHour?.cap) ?? 0,
|
|
1468
|
-
exceeded: fiveHour?.exceeded === true,
|
|
1469
|
-
resetAt: numberValue(fiveHour?.resetAt) ?? 0
|
|
1470
|
-
},
|
|
1471
|
-
weekly: {
|
|
1472
|
-
used: numberValue(weekly?.used) ?? 0,
|
|
1473
|
-
cap: numberValue(weekly?.cap) ?? 0,
|
|
1474
|
-
exceeded: weekly?.exceeded === true,
|
|
1475
|
-
resetAt: numberValue(weekly?.resetAt) ?? 0
|
|
1476
|
-
}
|
|
1477
|
-
};
|
|
1478
|
-
const subscription = await getJson(orgId === void 0 ? "/alpha/billing/subscriptions" : `/alpha/billing/subscriptions?orgId=${encodeURIComponent(orgId)}`);
|
|
1479
|
-
const subData = subscription && isRecord(subscription.data) ? subscription.data : void 0;
|
|
1898
|
+
const { account, orgId } = parseAccountIdentity(await getJson("/alpha/whoami"));
|
|
1899
|
+
if (account !== void 0) report.account = account;
|
|
1900
|
+
const [usage, credits, subscription] = await Promise.all([
|
|
1901
|
+
getJson("/alpha/usage/summary"),
|
|
1902
|
+
getJson("/alpha/billing/credits"),
|
|
1903
|
+
getJson(orgId === void 0 ? "/alpha/billing/subscriptions" : `/alpha/billing/subscriptions?orgId=${encodeURIComponent(orgId)}`)
|
|
1904
|
+
]);
|
|
1905
|
+
const totals = parseUsageTotals(usage);
|
|
1906
|
+
if (totals !== void 0) report.usage = totals;
|
|
1907
|
+
const limits = parseCreditLimits(credits);
|
|
1908
|
+
if (limits !== void 0) report.credits = limits;
|
|
1909
|
+
const subData = subscription !== void 0 && isRecord(subscription.data) ? subscription.data : void 0;
|
|
1910
|
+
const creditsData = credits !== void 0 && isRecord(credits.credits) ? credits.credits : void 0;
|
|
1480
1911
|
const planId = stringValue(subData?.planId) ?? stringValue(creditsData?.planId);
|
|
1481
1912
|
if (subData !== void 0 || planId !== void 0) {
|
|
1482
1913
|
const info = planId === void 0 ? void 0 : subscriptionPlanInfo(planId);
|
|
@@ -1488,12 +1919,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1488
1919
|
currentPeriodEnd: periodEndValue(subData?.currentPeriodEnd)
|
|
1489
1920
|
};
|
|
1490
1921
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code === 401)) report.blocked = "invalid-key";
|
|
1494
|
-
else if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code >= 500)) report.blocked = "service-unavailable";
|
|
1495
|
-
else if (codes.length === 0) report.blocked = "network";
|
|
1496
|
-
}
|
|
1922
|
+
const blocked = classifyTotalFailure(failures, failedStatuses);
|
|
1923
|
+
if (blocked !== void 0) report.blocked = blocked;
|
|
1497
1924
|
return report;
|
|
1498
1925
|
}
|
|
1499
1926
|
/**
|
|
@@ -1543,107 +1970,39 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1543
1970
|
const supported = KNOWN_EFFORTS[options.model];
|
|
1544
1971
|
const reasoningEffort = effort && effort !== "off" && supported?.includes(effort) ? effort : void 0;
|
|
1545
1972
|
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");
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
taste: null,
|
|
1560
|
-
skills: null,
|
|
1561
|
-
params: {
|
|
1562
|
-
model: options.model,
|
|
1563
|
-
messages: await messagesToCC(options.messages, readImage),
|
|
1564
|
-
tools: (options.tools ?? []).map((tool) => ({
|
|
1565
|
-
type: "function",
|
|
1566
|
-
name: tool.name,
|
|
1567
|
-
description: tool.description,
|
|
1568
|
-
input_schema: tool.parameters
|
|
1569
|
-
})),
|
|
1570
|
-
system: systemText,
|
|
1571
|
-
max_tokens: maxTokens,
|
|
1572
|
-
temperature: options.temperature ?? .3,
|
|
1573
|
-
stream: true,
|
|
1574
|
-
...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
|
|
1575
|
-
},
|
|
1576
|
-
threadId: randomUUID()
|
|
1577
|
-
};
|
|
1578
|
-
const connect = async (key) => {
|
|
1579
|
-
const connectAbort = new AbortController();
|
|
1580
|
-
let connectTimedOut = false;
|
|
1581
|
-
const connectTimer = setTimeout(() => {
|
|
1582
|
-
connectTimedOut = true;
|
|
1583
|
-
connectAbort.abort(new DOMException(`Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms`, "TimeoutError"));
|
|
1584
|
-
}, connection.requestTimeoutMs);
|
|
1585
|
-
const onCallerAbort = () => {
|
|
1586
|
-
connectAbort.abort(options.signal?.reason);
|
|
1587
|
-
};
|
|
1588
|
-
if (options.signal) {
|
|
1589
|
-
if (options.signal.aborted) onCallerAbort();
|
|
1590
|
-
else options.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
1591
|
-
}
|
|
1592
|
-
const cleanup = () => {
|
|
1593
|
-
clearTimeout(connectTimer);
|
|
1594
|
-
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
1595
|
-
};
|
|
1596
|
-
let response;
|
|
1597
|
-
try {
|
|
1598
|
-
response = await this.fetchImpl(`${connection.apiBase}/alpha/generate`, {
|
|
1599
|
-
method: "POST",
|
|
1600
|
-
headers: {
|
|
1601
|
-
"Content-Type": "application/json",
|
|
1602
|
-
Authorization: `Bearer ${key}`,
|
|
1603
|
-
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
1604
|
-
"x-cli-environment": "production",
|
|
1605
|
-
"x-project-slug": projectSlugFromPath(connection.workingDir),
|
|
1606
|
-
"x-taste-learning": "true",
|
|
1607
|
-
"x-co-flag": "false",
|
|
1608
|
-
...attributionHeaders()
|
|
1609
|
-
},
|
|
1610
|
-
body: JSON.stringify(body),
|
|
1611
|
-
signal: connectAbort.signal
|
|
1612
|
-
});
|
|
1613
|
-
clearTimeout(connectTimer);
|
|
1614
|
-
} catch (error) {
|
|
1615
|
-
cleanup();
|
|
1616
|
-
if (options.signal?.aborted) throw error;
|
|
1617
|
-
if (connectTimedOut || error instanceof DOMException && error.name === "TimeoutError") throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms: ${errorChain(error)};Command Code API 请求在 ${connection.requestTimeoutMs} 毫秒内未收到响应——通常是网络或代理问题,请检查后重试`, "TIMEOUT", { cause: error });
|
|
1618
|
-
throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate failed: ${errorChain(error)};Command Code API 请求连接失败——通常是网络或代理问题,请检查网络或代理设置后重试`, "TRANSPORT", { cause: error });
|
|
1619
|
-
}
|
|
1620
|
-
if (!response.ok) {
|
|
1621
|
-
const errText = await response.text().catch(() => "");
|
|
1622
|
-
cleanup();
|
|
1623
|
-
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
1624
|
-
return retryAfterMs === void 0 ? {
|
|
1625
|
-
status: response.status,
|
|
1626
|
-
errText
|
|
1627
|
-
} : {
|
|
1628
|
-
status: response.status,
|
|
1629
|
-
errText,
|
|
1630
|
-
retryAfterMs
|
|
1631
|
-
};
|
|
1632
|
-
}
|
|
1633
|
-
return {
|
|
1634
|
-
response,
|
|
1635
|
-
cleanup
|
|
1636
|
-
};
|
|
1637
|
-
};
|
|
1973
|
+
let protocol = this.resolveProtocol(apiKey);
|
|
1974
|
+
const buildBody = async (target) => target === "cli" ? buildCliBody(options, connection, {
|
|
1975
|
+
maxTokens,
|
|
1976
|
+
reasoningEffort,
|
|
1977
|
+
systemText,
|
|
1978
|
+
readImage
|
|
1979
|
+
}) : buildOpenAIBody(options, {
|
|
1980
|
+
maxTokens,
|
|
1981
|
+
reasoningEffort,
|
|
1982
|
+
systemText,
|
|
1983
|
+
readImage
|
|
1984
|
+
});
|
|
1985
|
+
let body = await buildBody(protocol);
|
|
1638
1986
|
const tried = /* @__PURE__ */ new Set();
|
|
1987
|
+
const connectDeps = {
|
|
1988
|
+
options,
|
|
1989
|
+
connection,
|
|
1990
|
+
fetchImpl: this.fetchImpl
|
|
1991
|
+
};
|
|
1639
1992
|
let connected;
|
|
1640
1993
|
for (;;) {
|
|
1641
1994
|
tried.add(apiKey);
|
|
1642
|
-
const attempt = await
|
|
1995
|
+
const attempt = await connectGenerate(connectDeps, apiKey, protocol, body);
|
|
1643
1996
|
if ("response" in attempt) {
|
|
1644
1997
|
connected = attempt;
|
|
1645
1998
|
break;
|
|
1646
1999
|
}
|
|
2000
|
+
if (protocol === "openai" && isUpgradeRequiredError(attempt.status, attempt.errText)) {
|
|
2001
|
+
protocol = "cli";
|
|
2002
|
+
this.rememberProtocol(apiKey, true);
|
|
2003
|
+
body = await buildBody("cli");
|
|
2004
|
+
continue;
|
|
2005
|
+
}
|
|
1647
2006
|
const rotate = this.deps.rotateApiKey;
|
|
1648
2007
|
if ((attempt.status === 429 || attempt.status === 401) && rotate !== void 0 && options.signal?.aborted !== true && tried.size < MAX_ACCOUNT_ROTATIONS) {
|
|
1649
2008
|
const next = await rotate(apiKey, attempt.status === 429 ? "rate-limit" : "invalid-credential", connection, options.model);
|
|
@@ -1654,6 +2013,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1654
2013
|
}
|
|
1655
2014
|
throw generateHttpError(attempt.status, attempt.errText, attempt.retryAfterMs);
|
|
1656
2015
|
}
|
|
2016
|
+
if (connected === void 0) throw new LlmError("Command Code API connection failed without a response", "TRANSPORT");
|
|
1657
2017
|
const { response, cleanup } = connected;
|
|
1658
2018
|
if (!response.body) {
|
|
1659
2019
|
cleanup();
|
|
@@ -1677,154 +2037,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1677
2037
|
idleTimer = void 0;
|
|
1678
2038
|
}
|
|
1679
2039
|
};
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
let textContent = "";
|
|
1683
|
-
let reasoningIndex = -1;
|
|
1684
|
-
let reasoningContent = "";
|
|
1685
|
-
let sawContent = false;
|
|
1686
|
-
const closeText = function* () {
|
|
1687
|
-
if (textIndex < 0) return;
|
|
1688
|
-
yield {
|
|
1689
|
-
type: "block-end",
|
|
1690
|
-
index: textIndex,
|
|
1691
|
-
block: {
|
|
1692
|
-
type: "text",
|
|
1693
|
-
text: textContent
|
|
1694
|
-
}
|
|
1695
|
-
};
|
|
1696
|
-
textIndex = -1;
|
|
1697
|
-
textContent = "";
|
|
1698
|
-
};
|
|
1699
|
-
const closeReasoning = function* () {
|
|
1700
|
-
if (reasoningIndex < 0) return;
|
|
1701
|
-
yield {
|
|
1702
|
-
type: "block-end",
|
|
1703
|
-
index: reasoningIndex,
|
|
1704
|
-
block: {
|
|
1705
|
-
type: "reasoning",
|
|
1706
|
-
text: reasoningContent
|
|
1707
|
-
}
|
|
1708
|
-
};
|
|
1709
|
-
reasoningIndex = -1;
|
|
1710
|
-
reasoningContent = "";
|
|
1711
|
-
};
|
|
1712
|
-
const handleEvent = (event) => {
|
|
1713
|
-
const chunks = [];
|
|
1714
|
-
if (!isRecord(event)) return chunks;
|
|
1715
|
-
switch (event.type) {
|
|
1716
|
-
case "text-delta": {
|
|
1717
|
-
chunks.push(...closeReasoning());
|
|
1718
|
-
if (textIndex < 0) {
|
|
1719
|
-
textIndex = nextIndex++;
|
|
1720
|
-
chunks.push({
|
|
1721
|
-
type: "block-start",
|
|
1722
|
-
index: textIndex,
|
|
1723
|
-
blockType: "text"
|
|
1724
|
-
});
|
|
1725
|
-
}
|
|
1726
|
-
const delta = stringValue(event.text) ?? "";
|
|
1727
|
-
textContent += delta;
|
|
1728
|
-
sawContent = true;
|
|
1729
|
-
chunks.push({
|
|
1730
|
-
type: "text-delta",
|
|
1731
|
-
index: textIndex,
|
|
1732
|
-
text: delta
|
|
1733
|
-
});
|
|
1734
|
-
break;
|
|
1735
|
-
}
|
|
1736
|
-
case "reasoning-delta": {
|
|
1737
|
-
chunks.push(...closeText());
|
|
1738
|
-
if (reasoningIndex < 0) {
|
|
1739
|
-
reasoningIndex = nextIndex++;
|
|
1740
|
-
chunks.push({
|
|
1741
|
-
type: "block-start",
|
|
1742
|
-
index: reasoningIndex,
|
|
1743
|
-
blockType: "reasoning"
|
|
1744
|
-
});
|
|
1745
|
-
}
|
|
1746
|
-
const delta = stringValue(event.text) ?? "";
|
|
1747
|
-
reasoningContent += delta;
|
|
1748
|
-
chunks.push({
|
|
1749
|
-
type: "reasoning-delta",
|
|
1750
|
-
index: reasoningIndex,
|
|
1751
|
-
text: delta
|
|
1752
|
-
});
|
|
1753
|
-
break;
|
|
1754
|
-
}
|
|
1755
|
-
case "reasoning-start":
|
|
1756
|
-
chunks.push(...closeText());
|
|
1757
|
-
break;
|
|
1758
|
-
case "reasoning-end":
|
|
1759
|
-
chunks.push(...closeReasoning());
|
|
1760
|
-
break;
|
|
1761
|
-
case "tool-call": {
|
|
1762
|
-
chunks.push(...closeText(), ...closeReasoning());
|
|
1763
|
-
const id = stringValue(event.toolCallId) ?? randomUUID();
|
|
1764
|
-
const name = stringValue(event.toolName) ?? "";
|
|
1765
|
-
const args = JSON.stringify(recordOrEmpty(event.input ?? event.args ?? event.arguments));
|
|
1766
|
-
const index = nextIndex++;
|
|
1767
|
-
sawContent = true;
|
|
1768
|
-
chunks.push({
|
|
1769
|
-
type: "block-start",
|
|
1770
|
-
index,
|
|
1771
|
-
blockType: "tool-call"
|
|
1772
|
-
}, {
|
|
1773
|
-
type: "tool-call-delta",
|
|
1774
|
-
index,
|
|
1775
|
-
id: ToolCallId(id),
|
|
1776
|
-
name,
|
|
1777
|
-
argumentsDelta: args
|
|
1778
|
-
}, {
|
|
1779
|
-
type: "block-end",
|
|
1780
|
-
index,
|
|
1781
|
-
block: {
|
|
1782
|
-
type: "tool-call",
|
|
1783
|
-
id: ToolCallId(id),
|
|
1784
|
-
name,
|
|
1785
|
-
arguments: args
|
|
1786
|
-
}
|
|
1787
|
-
});
|
|
1788
|
-
break;
|
|
1789
|
-
}
|
|
1790
|
-
case "finish": {
|
|
1791
|
-
chunks.push(...closeText(), ...closeReasoning());
|
|
1792
|
-
const usage = isRecord(event.totalUsage) ? event.totalUsage : void 0;
|
|
1793
|
-
if (usage) {
|
|
1794
|
-
const details = isRecord(usage.inputTokenDetails) ? usage.inputTokenDetails : void 0;
|
|
1795
|
-
const totalInput = numberValue(usage.inputTokens) ?? 0;
|
|
1796
|
-
const cacheRead = numberValue(details?.cacheReadTokens) ?? 0;
|
|
1797
|
-
const cacheWrite = numberValue(details?.cacheWriteTokens) ?? 0;
|
|
1798
|
-
const tokenUsage = {
|
|
1799
|
-
inputTokens: numberValue(details?.noCacheTokens) ?? Math.max(0, totalInput - cacheRead - cacheWrite),
|
|
1800
|
-
outputTokens: numberValue(usage.outputTokens) ?? 0,
|
|
1801
|
-
cacheReadTokens: cacheRead,
|
|
1802
|
-
cacheWriteTokens: cacheWrite
|
|
1803
|
-
};
|
|
1804
|
-
chunks.push({
|
|
1805
|
-
type: "usage",
|
|
1806
|
-
usage: tokenUsage
|
|
1807
|
-
});
|
|
1808
|
-
}
|
|
1809
|
-
chunks.push({
|
|
1810
|
-
type: "finish",
|
|
1811
|
-
reason: mapFinishReason(event.finishReason)
|
|
1812
|
-
});
|
|
1813
|
-
break;
|
|
1814
|
-
}
|
|
1815
|
-
case "error": {
|
|
1816
|
-
const err = isRecord(event.error) ? event.error : void 0;
|
|
1817
|
-
const detail = isRecord(event.error) ? stringValue(event.error.message) ?? JSON.stringify(event.error) : stringValue(event.error) ?? stringValue(event.message) ?? "Stream error";
|
|
1818
|
-
const statusCode = err ? numberValue(err.statusCode) : void 0;
|
|
1819
|
-
const isRetryable = err ? booleanValue(err.isRetryable) : void 0;
|
|
1820
|
-
const retryableStatus = statusCode !== void 0 && (statusCode === 429 || statusCode >= 500);
|
|
1821
|
-
const terminal = hasTerminalStreamMarker(detail);
|
|
1822
|
-
if (!(isRetryable === true || (statusCode !== void 0 ? retryableStatus : isRetryable !== false && !terminal))) throw new LlmError(`Command Code stream error: ${detail}`, "PROVIDER_STREAM_ERROR", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
1823
|
-
throw new LlmError(`Command Code stream error: ${detail}`, "SERVER", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
return chunks;
|
|
1827
|
-
};
|
|
2040
|
+
const asm = createBlockAssembler();
|
|
2041
|
+
const handle = (event) => handleEvent(asm, protocol, event);
|
|
1828
2042
|
try {
|
|
1829
2043
|
let finished = false;
|
|
1830
2044
|
for (;;) {
|
|
@@ -1841,14 +2055,17 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1841
2055
|
const { done, value } = read;
|
|
1842
2056
|
if (done) {
|
|
1843
2057
|
if (idleFired) throw new LlmError(`Command Code API stream from ${connection.apiBase} was idle for ${connection.streamIdleTimeoutMs}ms (no events) and was treated as a dead connection;Command Code API 流式响应已 ${connection.streamIdleTimeoutMs} 毫秒无任何事件,被判定为死连接——长思考模型可在设置中调大流空闲超时`, "TIMEOUT");
|
|
1844
|
-
if (buffer.trim()) for (const chunk of
|
|
2058
|
+
if (buffer.trim()) for (const chunk of handle(parseStreamEventLine(buffer))) {
|
|
2059
|
+
yield chunk;
|
|
2060
|
+
if (chunk.type === "finish") finished = true;
|
|
2061
|
+
}
|
|
1845
2062
|
break;
|
|
1846
2063
|
}
|
|
1847
2064
|
buffer += decoder.decode(value, { stream: true });
|
|
1848
2065
|
const lines = buffer.split("\n");
|
|
1849
2066
|
buffer = lines.pop() ?? "";
|
|
1850
2067
|
for (const line of lines) {
|
|
1851
|
-
const chunks =
|
|
2068
|
+
const chunks = handle(parseStreamEventLine(line));
|
|
1852
2069
|
for (const chunk of chunks) {
|
|
1853
2070
|
yield chunk;
|
|
1854
2071
|
if (chunk.type === "finish") finished = true;
|
|
@@ -1857,9 +2074,10 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1857
2074
|
if (finished) break;
|
|
1858
2075
|
}
|
|
1859
2076
|
if (!finished) {
|
|
1860
|
-
yield* closeText();
|
|
1861
|
-
yield* closeReasoning();
|
|
1862
|
-
if (
|
|
2077
|
+
yield* closeText(asm);
|
|
2078
|
+
yield* closeReasoning(asm);
|
|
2079
|
+
if (protocol === "openai") yield* emitOpenAiToolCalls(asm);
|
|
2080
|
+
if (!asm.sawContent) throw new LlmError("Command Code returned an empty response;Command Code 返回了空响应,重试通常可恢复", "EMPTY_RESPONSE");
|
|
1863
2081
|
yield {
|
|
1864
2082
|
type: "finish",
|
|
1865
2083
|
reason: { kind: "stop" }
|
|
@@ -1873,8 +2091,6 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1873
2091
|
}
|
|
1874
2092
|
}
|
|
1875
2093
|
};
|
|
1876
|
-
/** Hard cap on account rotations within one request (one attempt per distinct key). */
|
|
1877
|
-
const MAX_ACCOUNT_ROTATIONS = 16;
|
|
1878
2094
|
/**
|
|
1879
2095
|
* Map a pre-stream generate HTTP failure onto a stable LlmError. Command
|
|
1880
2096
|
* Code folds several business rejections into 403 (plan limits, CLI version,
|
|
@@ -1899,6 +2115,223 @@ function generateHttpError(status, errText, retryAfterMs) {
|
|
|
1899
2115
|
});
|
|
1900
2116
|
}
|
|
1901
2117
|
/**
|
|
2118
|
+
* Handle one CLI-transport (`/alpha/generate`) stream event, appending
|
|
2119
|
+
* harness StreamChunks. Pure over the passed assembler — no stream() locals.
|
|
2120
|
+
*/
|
|
2121
|
+
function handleCliEvent(asm, event) {
|
|
2122
|
+
const chunks = [];
|
|
2123
|
+
if (!isRecord(event)) return chunks;
|
|
2124
|
+
switch (event.type) {
|
|
2125
|
+
case "text-delta": {
|
|
2126
|
+
chunks.push(...closeReasoning(asm));
|
|
2127
|
+
if (asm.textIndex < 0) {
|
|
2128
|
+
asm.textIndex = asm.nextIndex++;
|
|
2129
|
+
chunks.push({
|
|
2130
|
+
type: "block-start",
|
|
2131
|
+
index: asm.textIndex,
|
|
2132
|
+
blockType: "text"
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
const delta = stringValue(event.text) ?? "";
|
|
2136
|
+
asm.textContent += delta;
|
|
2137
|
+
asm.sawContent = true;
|
|
2138
|
+
chunks.push({
|
|
2139
|
+
type: "text-delta",
|
|
2140
|
+
index: asm.textIndex,
|
|
2141
|
+
text: delta
|
|
2142
|
+
});
|
|
2143
|
+
break;
|
|
2144
|
+
}
|
|
2145
|
+
case "reasoning-delta": {
|
|
2146
|
+
chunks.push(...closeText(asm));
|
|
2147
|
+
if (asm.reasoningIndex < 0) {
|
|
2148
|
+
asm.reasoningIndex = asm.nextIndex++;
|
|
2149
|
+
chunks.push({
|
|
2150
|
+
type: "block-start",
|
|
2151
|
+
index: asm.reasoningIndex,
|
|
2152
|
+
blockType: "reasoning"
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
const delta = stringValue(event.text) ?? "";
|
|
2156
|
+
asm.reasoningContent += delta;
|
|
2157
|
+
chunks.push({
|
|
2158
|
+
type: "reasoning-delta",
|
|
2159
|
+
index: asm.reasoningIndex,
|
|
2160
|
+
text: delta
|
|
2161
|
+
});
|
|
2162
|
+
break;
|
|
2163
|
+
}
|
|
2164
|
+
case "reasoning-start":
|
|
2165
|
+
chunks.push(...closeText(asm));
|
|
2166
|
+
break;
|
|
2167
|
+
case "reasoning-end":
|
|
2168
|
+
chunks.push(...closeReasoning(asm));
|
|
2169
|
+
break;
|
|
2170
|
+
case "tool-call": {
|
|
2171
|
+
chunks.push(...closeText(asm), ...closeReasoning(asm));
|
|
2172
|
+
const id = stringValue(event.toolCallId) ?? randomUUID();
|
|
2173
|
+
const name = stringValue(event.toolName) ?? "";
|
|
2174
|
+
const args = JSON.stringify(recordOrEmpty(event.input ?? event.args ?? event.arguments));
|
|
2175
|
+
const index = asm.nextIndex++;
|
|
2176
|
+
asm.sawContent = true;
|
|
2177
|
+
chunks.push({
|
|
2178
|
+
type: "block-start",
|
|
2179
|
+
index,
|
|
2180
|
+
blockType: "tool-call"
|
|
2181
|
+
}, {
|
|
2182
|
+
type: "tool-call-delta",
|
|
2183
|
+
index,
|
|
2184
|
+
id: ToolCallId(id),
|
|
2185
|
+
name,
|
|
2186
|
+
argumentsDelta: args
|
|
2187
|
+
}, {
|
|
2188
|
+
type: "block-end",
|
|
2189
|
+
index,
|
|
2190
|
+
block: {
|
|
2191
|
+
type: "tool-call",
|
|
2192
|
+
id: ToolCallId(id),
|
|
2193
|
+
name,
|
|
2194
|
+
arguments: args
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
break;
|
|
2198
|
+
}
|
|
2199
|
+
case "finish": {
|
|
2200
|
+
chunks.push(...closeText(asm), ...closeReasoning(asm));
|
|
2201
|
+
const usage = isRecord(event.totalUsage) ? event.totalUsage : void 0;
|
|
2202
|
+
if (usage) {
|
|
2203
|
+
const details = isRecord(usage.inputTokenDetails) ? usage.inputTokenDetails : void 0;
|
|
2204
|
+
const totalInput = numberValue(usage.inputTokens) ?? 0;
|
|
2205
|
+
const cacheRead = numberValue(details?.cacheReadTokens) ?? 0;
|
|
2206
|
+
const cacheWrite = numberValue(details?.cacheWriteTokens) ?? 0;
|
|
2207
|
+
const tokenUsage = {
|
|
2208
|
+
inputTokens: numberValue(details?.noCacheTokens) ?? Math.max(0, totalInput - cacheRead - cacheWrite),
|
|
2209
|
+
outputTokens: numberValue(usage.outputTokens) ?? 0,
|
|
2210
|
+
cacheReadTokens: cacheRead,
|
|
2211
|
+
cacheWriteTokens: cacheWrite
|
|
2212
|
+
};
|
|
2213
|
+
chunks.push({
|
|
2214
|
+
type: "usage",
|
|
2215
|
+
usage: tokenUsage
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
chunks.push({
|
|
2219
|
+
type: "finish",
|
|
2220
|
+
reason: mapFinishReason(event.finishReason)
|
|
2221
|
+
});
|
|
2222
|
+
break;
|
|
2223
|
+
}
|
|
2224
|
+
case "error": {
|
|
2225
|
+
const err = isRecord(event.error) ? event.error : void 0;
|
|
2226
|
+
const detail = isRecord(event.error) ? stringValue(event.error.message) ?? JSON.stringify(event.error) : stringValue(event.error) ?? stringValue(event.message) ?? "Stream error";
|
|
2227
|
+
const statusCode = err ? numberValue(err.statusCode) : void 0;
|
|
2228
|
+
const isRetryable = err ? booleanValue(err.isRetryable) : void 0;
|
|
2229
|
+
const retryableStatus = statusCode !== void 0 && (statusCode === 429 || statusCode >= 500);
|
|
2230
|
+
const terminal = hasTerminalStreamMarker(detail);
|
|
2231
|
+
if (!(isRetryable === true || (statusCode !== void 0 ? retryableStatus : isRetryable !== false && !terminal))) throw new LlmError(`Command Code stream error: ${detail}`, "PROVIDER_STREAM_ERROR", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
2232
|
+
throw new LlmError(`Command Code stream error: ${detail}`, "SERVER", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
return chunks;
|
|
2236
|
+
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Handle one OpenAI-transport (`/provider/v1/chat/completions`) SSE event.
|
|
2239
|
+
* Same assembler contract as {@link handleCliEvent}; tool-call fragments
|
|
2240
|
+
* buffer on the assembler and flush at finish.
|
|
2241
|
+
*/
|
|
2242
|
+
function handleOpenAIEvent(asm, event) {
|
|
2243
|
+
const chunks = [];
|
|
2244
|
+
if (!isRecord(event)) return chunks;
|
|
2245
|
+
const choices = event.choices;
|
|
2246
|
+
if (!Array.isArray(choices) || choices.length === 0) {
|
|
2247
|
+
if (event.usage !== void 0) chunks.push({
|
|
2248
|
+
type: "usage",
|
|
2249
|
+
usage: mapOpenAIUsage(event.usage)
|
|
2250
|
+
});
|
|
2251
|
+
return chunks;
|
|
2252
|
+
}
|
|
2253
|
+
const choice = isRecord(choices[0]) ? choices[0] : {};
|
|
2254
|
+
const delta = isRecord(choice.delta) ? choice.delta : {};
|
|
2255
|
+
const reasoningDelta = stringValue(delta.reasoning) ?? stringValue(delta.reasoning_content) ?? "";
|
|
2256
|
+
if (reasoningDelta !== "") {
|
|
2257
|
+
chunks.push(...closeText(asm));
|
|
2258
|
+
if (asm.reasoningIndex < 0) {
|
|
2259
|
+
asm.reasoningIndex = asm.nextIndex++;
|
|
2260
|
+
chunks.push({
|
|
2261
|
+
type: "block-start",
|
|
2262
|
+
index: asm.reasoningIndex,
|
|
2263
|
+
blockType: "reasoning"
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
asm.reasoningContent += reasoningDelta;
|
|
2267
|
+
chunks.push({
|
|
2268
|
+
type: "reasoning-delta",
|
|
2269
|
+
index: asm.reasoningIndex,
|
|
2270
|
+
text: reasoningDelta
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
const contentDelta = stringValue(delta.content) ?? "";
|
|
2274
|
+
if (contentDelta !== "") {
|
|
2275
|
+
chunks.push(...closeReasoning(asm));
|
|
2276
|
+
if (asm.textIndex < 0) {
|
|
2277
|
+
asm.textIndex = asm.nextIndex++;
|
|
2278
|
+
chunks.push({
|
|
2279
|
+
type: "block-start",
|
|
2280
|
+
index: asm.textIndex,
|
|
2281
|
+
blockType: "text"
|
|
2282
|
+
});
|
|
2283
|
+
}
|
|
2284
|
+
asm.textContent += contentDelta;
|
|
2285
|
+
asm.sawContent = true;
|
|
2286
|
+
chunks.push({
|
|
2287
|
+
type: "text-delta",
|
|
2288
|
+
index: asm.textIndex,
|
|
2289
|
+
text: contentDelta
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
2293
|
+
asm.sawContent = true;
|
|
2294
|
+
for (const rawCall of delta.tool_calls) {
|
|
2295
|
+
if (!isRecord(rawCall)) continue;
|
|
2296
|
+
const callIndex = numberValue(rawCall.index) ?? 0;
|
|
2297
|
+
let existing = asm.openAiToolCalls.find((call) => call.index === callIndex);
|
|
2298
|
+
const fn = isRecord(rawCall.function) ? rawCall.function : void 0;
|
|
2299
|
+
const id = stringValue(rawCall.id);
|
|
2300
|
+
const name = fn === void 0 ? void 0 : stringValue(fn.name);
|
|
2301
|
+
const argDelta = fn === void 0 ? void 0 : stringValue(fn.arguments) ?? (fn.arguments === void 0 ? "" : JSON.stringify(fn.arguments));
|
|
2302
|
+
if (!existing) {
|
|
2303
|
+
existing = {
|
|
2304
|
+
index: callIndex,
|
|
2305
|
+
name: name ?? "",
|
|
2306
|
+
arguments: argDelta ?? ""
|
|
2307
|
+
};
|
|
2308
|
+
if (id !== void 0) existing.id = id;
|
|
2309
|
+
asm.openAiToolCalls.push(existing);
|
|
2310
|
+
} else {
|
|
2311
|
+
if (id !== void 0 && existing.id === void 0) existing.id = id;
|
|
2312
|
+
if (name !== void 0 && existing.name === "") existing.name = name;
|
|
2313
|
+
if (argDelta !== void 0) existing.arguments += argDelta;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
if (choice.finish_reason !== void 0 && choice.finish_reason !== null) {
|
|
2318
|
+
chunks.push(...closeText(asm), ...closeReasoning(asm), ...emitOpenAiToolCalls(asm));
|
|
2319
|
+
if (event.usage !== void 0) chunks.push({
|
|
2320
|
+
type: "usage",
|
|
2321
|
+
usage: mapOpenAIUsage(event.usage)
|
|
2322
|
+
});
|
|
2323
|
+
chunks.push({
|
|
2324
|
+
type: "finish",
|
|
2325
|
+
reason: mapFinishReason(choice.finish_reason)
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
return chunks;
|
|
2329
|
+
}
|
|
2330
|
+
/** Dispatch one parsed stream event to the active transport's handler. */
|
|
2331
|
+
function handleEvent(asm, protocol, event) {
|
|
2332
|
+
return protocol === "openai" ? handleOpenAIEvent(asm, event) : handleCliEvent(asm, event);
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
1902
2335
|
* Parse an HTTP `Retry-After` value (delay-seconds or an HTTP-date) into
|
|
1903
2336
|
* milliseconds; undefined when absent or unparseable. An HTTP-date in the
|
|
1904
2337
|
* past yields 0, which the caller drops (LlmError wants a positive delay).
|
|
@@ -1918,11 +2351,61 @@ function parseRetryAfterMs(value, now = Date.now()) {
|
|
|
1918
2351
|
const date = Date.parse(trimmed);
|
|
1919
2352
|
if (!Number.isNaN(date)) return Math.max(0, date - now);
|
|
1920
2353
|
}
|
|
2354
|
+
/**
|
|
2355
|
+
* Map a stream finish reason onto the harness taxonomy. The two transports
|
|
2356
|
+
* spell tool-calls differently (`tool-calls` on the CLI transport,
|
|
2357
|
+
* `tool_calls` on the OpenAI transport) but share every other reason, so
|
|
2358
|
+
* one function serves both — a new reason cannot drift between them.
|
|
2359
|
+
*/
|
|
1921
2360
|
function mapFinishReason(reason) {
|
|
1922
|
-
if (reason === "tool-calls") return { kind: "tool-calls" };
|
|
2361
|
+
if (reason === "tool-calls" || reason === "tool_calls") return { kind: "tool-calls" };
|
|
1923
2362
|
if (reason === "length" || reason === "max_tokens" || reason === "max-tokens" || reason === "max_output_tokens") return { kind: "max-tokens" };
|
|
1924
2363
|
return { kind: "stop" };
|
|
1925
2364
|
}
|
|
2365
|
+
/**
|
|
2366
|
+
* True when a Provider API pre-stream rejection is Command Code's Go-plan
|
|
2367
|
+
* gate (`upgrade_required`). Only this exact class of rejection should fall
|
|
2368
|
+
* back to the CLI /alpha/generate transport; other 4xx/5xx must surface as
|
|
2369
|
+
* ordinary errors so real account/model problems are not masked.
|
|
2370
|
+
*/
|
|
2371
|
+
function isUpgradeRequiredError(status, errText) {
|
|
2372
|
+
if (status !== 403) return false;
|
|
2373
|
+
const lower = errText.toLowerCase();
|
|
2374
|
+
if (lower.includes("upgrade_required")) return true;
|
|
2375
|
+
if (lower.includes("go plan") && lower.includes("api access")) return true;
|
|
2376
|
+
if (lower.includes("only plan without api access")) return true;
|
|
2377
|
+
if (lower.includes("upgrade to goat or higher")) return true;
|
|
2378
|
+
try {
|
|
2379
|
+
const parsed = JSON.parse(errText);
|
|
2380
|
+
if (isRecord(parsed)) {
|
|
2381
|
+
const error = isRecord(parsed.error) ? parsed.error : parsed;
|
|
2382
|
+
if ((stringValue(error.code) ?? stringValue(error.type))?.toLowerCase() === "upgrade_required") return true;
|
|
2383
|
+
const message = stringValue(error.message) ?? "";
|
|
2384
|
+
if (message.toLowerCase().includes("go plan") && message.toLowerCase().includes("api access")) return true;
|
|
2385
|
+
}
|
|
2386
|
+
} catch {}
|
|
2387
|
+
return false;
|
|
2388
|
+
}
|
|
2389
|
+
/** Map an OpenAI Chat Completions usage payload to harness disjoint counts. */
|
|
2390
|
+
function mapOpenAIUsage(usage) {
|
|
2391
|
+
const source = isRecord(usage) ? usage : {};
|
|
2392
|
+
const promptTokens = numberValue(source.prompt_tokens) ?? 0;
|
|
2393
|
+
const outputTokens = numberValue(source.completion_tokens) ?? 0;
|
|
2394
|
+
const totalTokens = numberValue(source.total_tokens);
|
|
2395
|
+
const promptDetails = isRecord(source.prompt_tokens_details) ? source.prompt_tokens_details : void 0;
|
|
2396
|
+
const cacheRead = numberValue(promptDetails?.cached_tokens) ?? 0;
|
|
2397
|
+
const cacheWrite = numberValue(promptDetails?.cache_creation_input_tokens) ?? 0;
|
|
2398
|
+
const reasoningTokens = numberValue((isRecord(source.completion_tokens_details) ? source.completion_tokens_details : void 0)?.reasoning_tokens);
|
|
2399
|
+
const usageOut = {
|
|
2400
|
+
inputTokens: Math.max(0, promptTokens - cacheRead - cacheWrite),
|
|
2401
|
+
outputTokens,
|
|
2402
|
+
cacheReadTokens: cacheRead
|
|
2403
|
+
};
|
|
2404
|
+
if (cacheWrite > 0) usageOut.cacheWriteTokens = cacheWrite;
|
|
2405
|
+
if (reasoningTokens !== void 0) usageOut.reasoningTokens = reasoningTokens;
|
|
2406
|
+
if (totalTokens !== void 0) usageOut.totalTokens = totalTokens;
|
|
2407
|
+
return usageOut;
|
|
2408
|
+
}
|
|
1926
2409
|
//#endregion
|
|
1927
2410
|
//#region src/command-locales.ts
|
|
1928
2411
|
const commandcodeCommand = {
|
|
@@ -1954,8 +2437,8 @@ const commandcodeCommand = {
|
|
|
1954
2437
|
exceededWarning: " ⚠️ 超限!",
|
|
1955
2438
|
resetSuffix: "重置 {when}",
|
|
1956
2439
|
partialFailures: "⚠️ 部分端点失败: {list}",
|
|
1957
|
-
noData: "
|
|
1958
|
-
errorText: "
|
|
2440
|
+
noData: "(无数据 — 请检查 API 密钥)",
|
|
2441
|
+
errorText: "获取 Command Code 用量失败:{message}",
|
|
1959
2442
|
imageGate: "当前会话已包含图片,而模型 {model} 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。"
|
|
1960
2443
|
},
|
|
1961
2444
|
en: {
|
|
@@ -2129,7 +2612,7 @@ const REMOTE_SERVICE = "commandcodeUsage";
|
|
|
2129
2612
|
/** The wire namespace all Command Code endpoints share. */
|
|
2130
2613
|
const REMOTE_NAMESPACE = "commandcode";
|
|
2131
2614
|
/**
|
|
2132
|
-
* Build the validator helpers one endpoint uses
|
|
2615
|
+
* Build the validator helpers one endpoint uses. `prefix` names the
|
|
2133
2616
|
* endpoint in the rejection message (e.g. `commandcode/report result:`), so
|
|
2134
2617
|
* each wire file keeps its own diagnostic phrasing while sharing the helper
|
|
2135
2618
|
* bodies.
|
|
@@ -2185,14 +2668,13 @@ const USAGE_REMOTE_PACKAGE = REMOTE_PACKAGE;
|
|
|
2185
2668
|
/** Canonical `<namespace>/<method>` endpoint of the usage report Remote. */
|
|
2186
2669
|
const USAGE_REPORT_ENDPOINT = "commandcode/report";
|
|
2187
2670
|
/**
|
|
2188
|
-
* The shared read/validate helpers
|
|
2189
|
-
* rejection messages name the offending boundary
|
|
2190
|
-
* shares this prefix, matching the historical behavior).
|
|
2671
|
+
* The shared read/validate helpers for the usage report endpoint, prefixed
|
|
2672
|
+
* so rejection messages name the offending boundary.
|
|
2191
2673
|
*/
|
|
2192
|
-
const { reject: reject$1, record, stringField, numberField, booleanField } = makeBoundaryValidator("commandcode/report result:");
|
|
2674
|
+
const { reject: reject$1, record: record$1, stringField: stringField$1, numberField, booleanField } = makeBoundaryValidator("commandcode/report result:");
|
|
2193
2675
|
/** Validate one window-limit block (`fiveHour` / `weekly`). */
|
|
2194
2676
|
function windowLimit(value, field) {
|
|
2195
|
-
const source = record(value, field);
|
|
2677
|
+
const source = record$1(value, field);
|
|
2196
2678
|
return {
|
|
2197
2679
|
used: numberField(source, "used", `${field}.used`),
|
|
2198
2680
|
cap: numberField(source, "cap", `${field}.cap`),
|
|
@@ -2206,7 +2688,7 @@ function windowLimit(value, field) {
|
|
|
2206
2688
|
* malformed frame fails the boundary instead of rendering garbage.
|
|
2207
2689
|
*/
|
|
2208
2690
|
function parseUsageReport(value) {
|
|
2209
|
-
const source = record(value, "report");
|
|
2691
|
+
const source = record$1(value, "report");
|
|
2210
2692
|
const failures = source.failures;
|
|
2211
2693
|
if (!Array.isArray(failures) || failures.some((entry) => typeof entry !== "string")) reject$1("failures");
|
|
2212
2694
|
const report = { failures };
|
|
@@ -2216,15 +2698,15 @@ function parseUsageReport(value) {
|
|
|
2216
2698
|
else reject$1("blocked");
|
|
2217
2699
|
}
|
|
2218
2700
|
if (source.account !== void 0) {
|
|
2219
|
-
const account = record(source.account, "account");
|
|
2701
|
+
const account = record$1(source.account, "account");
|
|
2220
2702
|
report.account = {
|
|
2221
|
-
id: stringField(account, "id", "account.id"),
|
|
2222
|
-
name: stringField(account, "name", "account.name"),
|
|
2223
|
-
userName: stringField(account, "userName", "account.userName")
|
|
2703
|
+
id: stringField$1(account, "id", "account.id"),
|
|
2704
|
+
name: stringField$1(account, "name", "account.name"),
|
|
2705
|
+
userName: stringField$1(account, "userName", "account.userName")
|
|
2224
2706
|
};
|
|
2225
2707
|
}
|
|
2226
2708
|
if (source.usage !== void 0) {
|
|
2227
|
-
const usage = record(source.usage, "usage");
|
|
2709
|
+
const usage = record$1(source.usage, "usage");
|
|
2228
2710
|
report.usage = {
|
|
2229
2711
|
totalCount: numberField(usage, "totalCount", "usage.totalCount"),
|
|
2230
2712
|
totalCost: numberField(usage, "totalCost", "usage.totalCost"),
|
|
@@ -2234,11 +2716,11 @@ function parseUsageReport(value) {
|
|
|
2234
2716
|
totalTokensIn: numberField(usage, "totalTokensIn", "usage.totalTokensIn"),
|
|
2235
2717
|
totalTokensOut: numberField(usage, "totalTokensOut", "usage.totalTokensOut"),
|
|
2236
2718
|
totalCredits: numberField(usage, "totalCredits", "usage.totalCredits"),
|
|
2237
|
-
periodBasis: stringField(usage, "periodBasis", "usage.periodBasis")
|
|
2719
|
+
periodBasis: stringField$1(usage, "periodBasis", "usage.periodBasis")
|
|
2238
2720
|
};
|
|
2239
2721
|
}
|
|
2240
2722
|
if (source.credits !== void 0) {
|
|
2241
|
-
const credits = record(source.credits, "credits");
|
|
2723
|
+
const credits = record$1(source.credits, "credits");
|
|
2242
2724
|
report.credits = {
|
|
2243
2725
|
monthlyCredits: numberField(credits, "monthlyCredits", "credits.monthlyCredits"),
|
|
2244
2726
|
purchasedCredits: numberField(credits, "purchasedCredits", "credits.purchasedCredits"),
|
|
@@ -2248,13 +2730,13 @@ function parseUsageReport(value) {
|
|
|
2248
2730
|
};
|
|
2249
2731
|
}
|
|
2250
2732
|
if (source.plan !== void 0) {
|
|
2251
|
-
const plan = record(source.plan, "plan");
|
|
2733
|
+
const plan = record$1(source.plan, "plan");
|
|
2252
2734
|
const monthly = plan.monthlyCredits;
|
|
2253
2735
|
if (monthly !== null && (typeof monthly !== "number" || !Number.isFinite(monthly))) reject$1("plan.monthlyCredits");
|
|
2254
2736
|
report.plan = {
|
|
2255
|
-
planId: stringField(plan, "planId", "plan.planId"),
|
|
2256
|
-
name: stringField(plan, "name", "plan.name"),
|
|
2257
|
-
status: stringField(plan, "status", "plan.status"),
|
|
2737
|
+
planId: stringField$1(plan, "planId", "plan.planId"),
|
|
2738
|
+
name: stringField$1(plan, "name", "plan.name"),
|
|
2739
|
+
status: stringField$1(plan, "status", "plan.status"),
|
|
2258
2740
|
monthlyCredits: monthly,
|
|
2259
2741
|
currentPeriodEnd: numberField(plan, "currentPeriodEnd", "plan.currentPeriodEnd")
|
|
2260
2742
|
};
|
|
@@ -2263,20 +2745,20 @@ function parseUsageReport(value) {
|
|
|
2263
2745
|
}
|
|
2264
2746
|
/** Parse one untrusted boundary value into a {@link CommandCodeAccountUsage}. */
|
|
2265
2747
|
function parseAccountUsage(value) {
|
|
2266
|
-
const source = record(value, "account");
|
|
2748
|
+
const source = record$1(value, "account");
|
|
2267
2749
|
return {
|
|
2268
|
-
id: stringField(source, "id", "account.id"),
|
|
2269
|
-
label: stringField(source, "label", "account.label"),
|
|
2750
|
+
id: stringField$1(source, "id", "account.id"),
|
|
2751
|
+
label: stringField$1(source, "label", "account.label"),
|
|
2270
2752
|
configured: booleanField(source, "configured", "account.configured"),
|
|
2271
2753
|
active: booleanField(source, "active", "account.active"),
|
|
2272
|
-
mark: stringField(source, "mark", "account.mark"),
|
|
2754
|
+
mark: stringField$1(source, "mark", "account.mark"),
|
|
2273
2755
|
cooldownUntil: numberField(source, "cooldownUntil", "account.cooldownUntil"),
|
|
2274
2756
|
report: parseUsageReport(source.report)
|
|
2275
2757
|
};
|
|
2276
2758
|
}
|
|
2277
2759
|
/** Parse the wire result into a {@link CommandCodeAccountsReport}. */
|
|
2278
2760
|
function parseAccountsReport(value) {
|
|
2279
|
-
const accounts = record(value, "result").accounts;
|
|
2761
|
+
const accounts = record$1(value, "result").accounts;
|
|
2280
2762
|
if (Array.isArray(accounts)) return { accounts: accounts.map(parseAccountUsage) };
|
|
2281
2763
|
return reject$1("accounts");
|
|
2282
2764
|
}
|
|
@@ -2300,19 +2782,27 @@ const USAGE_HOST_CONTRIBUTION = {
|
|
|
2300
2782
|
};
|
|
2301
2783
|
/** Canonical `<namespace>/<method>` endpoint of the model-catalog Remote. */
|
|
2302
2784
|
const MODELS_ENDPOINT = "commandcode/models";
|
|
2785
|
+
/**
|
|
2786
|
+
* The shared read/validate helpers for the model-catalog endpoint — a
|
|
2787
|
+
* separate instance so catalog boundary errors name `commandcode/models`,
|
|
2788
|
+
* not the report endpoint.
|
|
2789
|
+
*/
|
|
2790
|
+
const { record: catalogRecord, stringField: catalogString } = makeBoundaryValidator("commandcode/models result:");
|
|
2303
2791
|
/** Parse one untrusted boundary value into a {@link CommandCodeCatalogModel}. */
|
|
2304
2792
|
function parseCatalogModel(value) {
|
|
2305
|
-
const source =
|
|
2306
|
-
|
|
2307
|
-
id:
|
|
2308
|
-
name:
|
|
2793
|
+
const source = catalogRecord(value, "model");
|
|
2794
|
+
const model = {
|
|
2795
|
+
id: catalogString(source, "id", "model.id"),
|
|
2796
|
+
name: catalogString(source, "name", "model.name")
|
|
2309
2797
|
};
|
|
2798
|
+
if (source.tier !== void 0) model.tier = catalogString(source, "tier", "model.tier");
|
|
2799
|
+
return model;
|
|
2310
2800
|
}
|
|
2311
2801
|
/** Parse the wire result into a {@link CommandCodeCatalog}. */
|
|
2312
2802
|
function parseCatalog(value) {
|
|
2313
|
-
const models =
|
|
2803
|
+
const models = catalogRecord(value, "result").models;
|
|
2314
2804
|
if (Array.isArray(models)) return { models: models.map(parseCatalogModel) };
|
|
2315
|
-
|
|
2805
|
+
throw new TypeError("commandcode/models result: invalid models");
|
|
2316
2806
|
}
|
|
2317
2807
|
/**
|
|
2318
2808
|
* The model-catalog invocation descriptor, sharing the same `commandcodeUsage`
|
|
@@ -2336,45 +2826,29 @@ const REASONS = [
|
|
|
2336
2826
|
];
|
|
2337
2827
|
/** The shared read/validate helpers, prefixed with the login endpoint so
|
|
2338
2828
|
* rejection messages name the offending boundary. */
|
|
2339
|
-
|
|
2340
|
-
* function declaration (not the factory's destructured arrow) so TypeScript's
|
|
2341
|
-
* control-flow analysis recognizes it as never-returning and narrows `state`
|
|
2342
|
-
* after the guard below. */
|
|
2343
|
-
function reject(field) {
|
|
2344
|
-
throw new TypeError(`commandcode/login result: invalid ${field}`);
|
|
2345
|
-
}
|
|
2829
|
+
const { reject, record, stringField } = makeBoundaryValidator("commandcode/login result:");
|
|
2346
2830
|
/**
|
|
2347
2831
|
* Parse one untrusted boundary value into a {@link CommandCodeLoginStatus}.
|
|
2348
2832
|
* Every field is shape-checked so a malformed frame fails the boundary
|
|
2349
2833
|
* instead of leaking into the page.
|
|
2350
2834
|
*/
|
|
2351
2835
|
function parseLoginStatus(value) {
|
|
2352
|
-
|
|
2353
|
-
const source = value;
|
|
2836
|
+
const source = record(value, "status");
|
|
2354
2837
|
const state = source.state;
|
|
2355
|
-
if (state
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
if (
|
|
2359
|
-
status.
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
status.keyName = source.keyName;
|
|
2368
|
-
}
|
|
2369
|
-
if (source.reason !== void 0) {
|
|
2370
|
-
if (!REASONS.includes(source.reason)) reject("reason");
|
|
2371
|
-
status.reason = source.reason;
|
|
2372
|
-
}
|
|
2373
|
-
if (source.message !== void 0) {
|
|
2374
|
-
if (typeof source.message !== "string") reject("message");
|
|
2375
|
-
status.message = source.message;
|
|
2838
|
+
if (state === "idle" || state === "waiting" || state === "success" || state === "failed") {
|
|
2839
|
+
const status = { state };
|
|
2840
|
+
if (source.authUrl !== void 0) status.authUrl = stringField(source, "authUrl", "authUrl");
|
|
2841
|
+
if (source.userName !== void 0) status.userName = stringField(source, "userName", "userName");
|
|
2842
|
+
if (source.keyName !== void 0) status.keyName = stringField(source, "keyName", "keyName");
|
|
2843
|
+
if (source.reason !== void 0) {
|
|
2844
|
+
const reason = source.reason;
|
|
2845
|
+
if (typeof reason === "string" && REASONS.includes(reason)) status.reason = reason;
|
|
2846
|
+
else reject("reason");
|
|
2847
|
+
}
|
|
2848
|
+
if (source.message !== void 0) status.message = stringField(source, "message", "message");
|
|
2849
|
+
return status;
|
|
2376
2850
|
}
|
|
2377
|
-
return
|
|
2851
|
+
return reject("state");
|
|
2378
2852
|
}
|
|
2379
2853
|
/** The strict result codec shared by all three login endpoints. */
|
|
2380
2854
|
const loginStatusSchema = { parse: parseLoginStatus };
|
|
@@ -2424,10 +2898,11 @@ var CommandCodeUsageService = class extends TypertRemoteService {
|
|
|
2424
2898
|
}] };
|
|
2425
2899
|
}
|
|
2426
2900
|
/**
|
|
2427
|
-
* The model catalog for the settings page's
|
|
2428
|
-
*
|
|
2429
|
-
*
|
|
2430
|
-
*
|
|
2901
|
+
* The full model catalog for the settings page's model editors (the
|
|
2902
|
+
* routing-rule editor and the visible-models filter). The browser never
|
|
2903
|
+
* calls the Command Code API directly — the Host serves the catalog
|
|
2904
|
+
* (already fetched/cached by the adapter) so models can be picked from
|
|
2905
|
+
* the live list instead of typed by hand.
|
|
2431
2906
|
*/
|
|
2432
2907
|
async models() {
|
|
2433
2908
|
return this.deps.listModels?.() ?? { models: [] };
|
|
@@ -2739,12 +3214,15 @@ var CommandCodeLoginFlow = class {
|
|
|
2739
3214
|
});
|
|
2740
3215
|
return;
|
|
2741
3216
|
}
|
|
3217
|
+
let bodyBytes = 0;
|
|
2742
3218
|
let body = "";
|
|
2743
3219
|
request.on("data", (chunk) => {
|
|
3220
|
+
bodyBytes += chunk.length;
|
|
2744
3221
|
body += chunk.toString();
|
|
2745
|
-
if (
|
|
3222
|
+
if (bodyBytes > 1e4) request.destroy();
|
|
2746
3223
|
});
|
|
2747
3224
|
request.on("end", () => {
|
|
3225
|
+
if (request.destroyed) return;
|
|
2748
3226
|
let payload;
|
|
2749
3227
|
try {
|
|
2750
3228
|
payload = JSON.parse(body);
|
|
@@ -2880,21 +3358,86 @@ function corsOrigin(origin) {
|
|
|
2880
3358
|
const COMMANDCODE_SEARCH_PROVIDER_ID = "commandcode";
|
|
2881
3359
|
/**
|
|
2882
3360
|
* The factory-declared search provider id dsh ships by default (from
|
|
2883
|
-
* `dsh-base`'s cordis patch `web.config.searchProvider`).
|
|
2884
|
-
*
|
|
2885
|
-
*
|
|
3361
|
+
* `dsh-base`'s cordis patch `web.config.searchProvider`). Kept as a
|
|
3362
|
+
* documented reference only: disabling this plugin's `webSearch` toggle
|
|
3363
|
+
* restores the previously selected backend (see
|
|
3364
|
+
* {@link applyCommandCodeSearchSelection}) — it never forces this default,
|
|
3365
|
+
* because forcing it is what used to silence sibling search plugins such as
|
|
3366
|
+
* modsearch even with Command Code search turned off (issue #26).
|
|
2886
3367
|
*/
|
|
2887
3368
|
const DEFAULT_WEB_SEARCH_PROVIDER_ID = "deepseek-official";
|
|
2888
3369
|
/**
|
|
2889
3370
|
* Point the web seam's search selection at this plugin's provider (`commandcode`).
|
|
2890
3371
|
* Sets the runtime field; the next search call honours it because `search()`
|
|
2891
3372
|
* re-reads `searchProviderId` each time. Returns the prior id (or undefined).
|
|
3373
|
+
* Never throws: a hardened/frozen runtime shape must not break the
|
|
3374
|
+
* settings-save path that calls this — the provider simply stays
|
|
3375
|
+
* registered-but-unselected (the boot-time `searchProvider: commandcode`
|
|
3376
|
+
* cordis patch is the durable alternative).
|
|
3377
|
+
*
|
|
3378
|
+
* @deprecated Prefer {@link applyCommandCodeSearchSelection}: this overload
|
|
3379
|
+
* always overwrites the displaced backend with the factory default on
|
|
3380
|
+
* disable, so turning Command Code search off silences whichever provider
|
|
3381
|
+
* was selected before (e.g. modsearch) instead of restoring it (issue #26).
|
|
2892
3382
|
*/
|
|
2893
3383
|
function selectCommandCodeSearchProvider(web, enable) {
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
3384
|
+
try {
|
|
3385
|
+
const field = web;
|
|
3386
|
+
const prior = field.searchProviderId;
|
|
3387
|
+
field.searchProviderId = enable ? COMMANDCODE_SEARCH_PROVIDER_ID : DEFAULT_WEB_SEARCH_PROVIDER_ID;
|
|
3388
|
+
return prior;
|
|
3389
|
+
} catch {
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
/** Fresh selection state: the plugin starts out not owning the selection. */
|
|
3394
|
+
function commandCodeSearchSelection() {
|
|
3395
|
+
return {
|
|
3396
|
+
owner: false,
|
|
3397
|
+
displaced: void 0
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
/**
|
|
3401
|
+
* Reach one end of the `webSearch` toggle without trampling sibling search
|
|
3402
|
+
* providers (issue #26).
|
|
3403
|
+
*
|
|
3404
|
+
* - Enabling writes `commandcode` and remembers whatever it displaced. When
|
|
3405
|
+
* the plugin already owns the selection (e.g. a settings save while still
|
|
3406
|
+
* on), the original `displaced` value is kept — the field currently holds
|
|
3407
|
+
* our own id, which must never be mistaken for the user's backend.
|
|
3408
|
+
* - Disabling hands the selection back to the remembered backend. When the
|
|
3409
|
+
* state holds no memory (a fresh boot straight into `webSearch: false`),
|
|
3410
|
+
* the field is left alone: the runtime's current value — a sibling's
|
|
3411
|
+
* cordis pin such as `searchProvider: modsearch`, or unset for
|
|
3412
|
+
* auto-select — already says what the user wants.
|
|
3413
|
+
* - When the field already reads `commandcode` at first touch (e.g. a
|
|
3414
|
+
* surviving runtime the plugin did not set, or a manual
|
|
3415
|
+
* `searchProvider: commandcode` pin), `displaced` stays undefined so the
|
|
3416
|
+
* later disable is a no-op rather than a guess at the factory default.
|
|
3417
|
+
*
|
|
3418
|
+
* Never throws: like the low-level rewrite, a hardened runtime shape degrades
|
|
3419
|
+
* to registered-but-unselected.
|
|
3420
|
+
*/
|
|
3421
|
+
function applyCommandCodeSearchSelection(web, state, enable) {
|
|
3422
|
+
try {
|
|
3423
|
+
const field = web;
|
|
3424
|
+
if (enable) {
|
|
3425
|
+
if (state.owner) {
|
|
3426
|
+
field.searchProviderId = COMMANDCODE_SEARCH_PROVIDER_ID;
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
const prior = field.searchProviderId;
|
|
3430
|
+
state.displaced = prior === "commandcode" ? void 0 : prior;
|
|
3431
|
+
field.searchProviderId = COMMANDCODE_SEARCH_PROVIDER_ID;
|
|
3432
|
+
state.owner = true;
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3435
|
+
if (state.owner) {
|
|
3436
|
+
state.owner = false;
|
|
3437
|
+
field.searchProviderId = state.displaced;
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
} catch {}
|
|
2898
3441
|
}
|
|
2899
3442
|
/** Command Code's lower/upper bound on `numResults` (from the CLI's `web_search` schema). */
|
|
2900
3443
|
const MIN_NUM_RESULTS = 1;
|
|
@@ -2945,7 +3488,7 @@ var CommandCodeSearchProvider = class {
|
|
|
2945
3488
|
constructor(deps) {
|
|
2946
3489
|
this.deps = deps;
|
|
2947
3490
|
}
|
|
2948
|
-
/** Cheap local check; must not make network calls. Presence of a
|
|
3491
|
+
/** Cheap local check; must not make network calls. Presence of a parseable base is enough. */
|
|
2949
3492
|
available() {
|
|
2950
3493
|
const base = this.deps.apiBase();
|
|
2951
3494
|
return base.length > 0 && URL.canParse(base);
|
|
@@ -2992,6 +3535,8 @@ var CommandCodeSearchProvider = class {
|
|
|
2992
3535
|
}
|
|
2993
3536
|
} catch (error) {
|
|
2994
3537
|
if (signal?.aborted === true || isAbortError(error)) throw searchAborted(signal, error);
|
|
3538
|
+
const slice = (await response.text().catch(() => "")).trim().slice(0, 200);
|
|
3539
|
+
if (slice !== "") message += `: ${slice}`;
|
|
2995
3540
|
}
|
|
2996
3541
|
throw new WebError(message, "WEB_PROVIDER_ERROR");
|
|
2997
3542
|
}
|
|
@@ -3000,7 +3545,7 @@ var CommandCodeSearchProvider = class {
|
|
|
3000
3545
|
payload = await response.json();
|
|
3001
3546
|
} catch (error) {
|
|
3002
3547
|
if (signal?.aborted === true || isAbortError(error)) throw searchAborted(signal, error);
|
|
3003
|
-
throw new WebError("Command Code web search returned an unparseable response body", "WEB_PROVIDER_ERROR");
|
|
3548
|
+
throw new WebError("Command Code web search returned an unparseable response body", "WEB_PROVIDER_ERROR", { cause: error });
|
|
3004
3549
|
}
|
|
3005
3550
|
const results = payload?.results;
|
|
3006
3551
|
if (!Array.isArray(results)) throw new WebError("Command Code web search returned no results array (the server may have rejected the query)", "WEB_PROVIDER_ERROR");
|
|
@@ -3081,6 +3626,7 @@ const Config = z.object({
|
|
|
3081
3626
|
requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
3082
3627
|
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
3083
3628
|
filterModelsByPlan: z.boolean(),
|
|
3629
|
+
visibleModels: z.array(z.string()),
|
|
3084
3630
|
webSearch: z.boolean().default(true),
|
|
3085
3631
|
accounts: z.array(z.object({
|
|
3086
3632
|
label: z.string(),
|
|
@@ -3108,7 +3654,8 @@ function resolveAdapterOptions(config) {
|
|
|
3108
3654
|
modelsCachePath: config.modelsCachePath ?? DEFAULT_MODELS_CACHE_PATH,
|
|
3109
3655
|
requestTimeoutMs: config.requestTimeoutMs ?? 6e4,
|
|
3110
3656
|
streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? 3e5,
|
|
3111
|
-
filterModelsByPlan: config.filterModelsByPlan ?? true
|
|
3657
|
+
filterModelsByPlan: config.filterModelsByPlan ?? true,
|
|
3658
|
+
visibleModels: Array.isArray(config.visibleModels) ? config.visibleModels.filter((id) => typeof id === "string" && id !== "") : void 0
|
|
3112
3659
|
};
|
|
3113
3660
|
}
|
|
3114
3661
|
function apply(ctx, config) {
|
|
@@ -3238,18 +3785,26 @@ function apply(ctx, config) {
|
|
|
3238
3785
|
}
|
|
3239
3786
|
});
|
|
3240
3787
|
ctx.effect(() => () => loginFlow.dispose(), "dsh-commandcode-provider: login flow");
|
|
3241
|
-
const
|
|
3242
|
-
return { models: (await adapter.listModels(PROVIDER)).map((model) =>
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3788
|
+
const catalogForEditors = async () => {
|
|
3789
|
+
return { models: (await adapter.listModels(PROVIDER, { unfiltered: true })).map((model) => {
|
|
3790
|
+
const tier = KNOWN_PLANS[model.id];
|
|
3791
|
+
return {
|
|
3792
|
+
id: model.id,
|
|
3793
|
+
name: model.name.replace(/\s*\(CC\)$/, ""),
|
|
3794
|
+
...tier === void 0 ? {} : { tier }
|
|
3795
|
+
};
|
|
3796
|
+
}) };
|
|
3246
3797
|
};
|
|
3247
3798
|
applyUsageRemote(ctx, {
|
|
3248
3799
|
adapter,
|
|
3249
3800
|
reports: usageReports,
|
|
3250
3801
|
login: loginFlow,
|
|
3251
|
-
listModels:
|
|
3802
|
+
listModels: catalogForEditors
|
|
3252
3803
|
});
|
|
3804
|
+
const searchSelection = commandCodeSearchSelection();
|
|
3805
|
+
const applySearchSelection = (enabled) => {
|
|
3806
|
+
if (webRuntime !== void 0) applyCommandCodeSearchSelection(webRuntime, searchSelection, enabled);
|
|
3807
|
+
};
|
|
3253
3808
|
let webRuntime;
|
|
3254
3809
|
ctx.inject(["web"], (webCtx) => {
|
|
3255
3810
|
webRuntime = webCtx.web;
|
|
@@ -3260,7 +3815,11 @@ function apply(ctx, config) {
|
|
|
3260
3815
|
},
|
|
3261
3816
|
apiBase: () => options().apiBase
|
|
3262
3817
|
}));
|
|
3263
|
-
|
|
3818
|
+
applySearchSelection(current().webSearch ?? true);
|
|
3819
|
+
webCtx.effect(() => () => {
|
|
3820
|
+
webRuntime = void 0;
|
|
3821
|
+
applyCommandCodeSearchSelection(webCtx.web, searchSelection, false);
|
|
3822
|
+
}, "dsh-commandcode-provider: web search selection");
|
|
3264
3823
|
});
|
|
3265
3824
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
3266
3825
|
settingsCtx.settings.installSection(ctx, NS, Config, config, {
|
|
@@ -3268,12 +3827,12 @@ function apply(ctx, config) {
|
|
|
3268
3827
|
current = source;
|
|
3269
3828
|
},
|
|
3270
3829
|
onChange: () => {
|
|
3271
|
-
|
|
3830
|
+
applySearchSelection(current().webSearch ?? true);
|
|
3272
3831
|
}
|
|
3273
3832
|
});
|
|
3274
3833
|
});
|
|
3275
3834
|
}
|
|
3276
3835
|
//#endregion
|
|
3277
|
-
export { BILLING_ACCESS_TTL_MS, COMMANDCODE_SEARCH_PROVIDER_ID, COMMAND_CODE_CLI_VERSION, CommandCodeAccountPool, CommandCodeAdapter, CommandCodeLoginFlow, CommandCodeSearchProvider, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_WEB_SEARCH_PROVIDER_ID, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, LOGIN_ALLOWED_ORIGINS, LOGIN_BEGIN_ENDPOINT, LOGIN_BODY_LIMIT_BYTES, LOGIN_CANCEL_ENDPOINT, LOGIN_MAX_PORT_ATTEMPTS, LOGIN_START_PORT, LOGIN_STATUS_ENDPOINT, LOGIN_TIMEOUT_MS, PLAN_LABELS, PLAN_ORDER, PROVIDER, USAGE_REPORT_ENDPOINT, accountUsable, apply, applyCommands, applyUsageRemote, buildCommandAuthUrl, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, loginStatusSchema, matchModelRule, modelVisibleInPlan, name, parseLoginStatus, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, selectAccountForModel, selectActiveAccount, selectCommandCodeSearchProvider, studioBaseForApiBase, subscriptionPlanInfo, usageReportSchema, validateCommandApiKey };
|
|
3836
|
+
export { BILLING_ACCESS_TTL_MS, COMMANDCODE_SEARCH_PROVIDER_ID, COMMAND_CODE_CLI_VERSION, CommandCodeAccountPool, CommandCodeAdapter, CommandCodeLoginFlow, CommandCodeSearchProvider, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_WEB_SEARCH_PROVIDER_ID, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, LOGIN_ALLOWED_ORIGINS, LOGIN_BEGIN_ENDPOINT, LOGIN_BODY_LIMIT_BYTES, LOGIN_CANCEL_ENDPOINT, LOGIN_MAX_PORT_ATTEMPTS, LOGIN_START_PORT, LOGIN_STATUS_ENDPOINT, LOGIN_TIMEOUT_MS, PLAN_LABELS, PLAN_ORDER, PROVIDER, USAGE_REPORT_ENDPOINT, accountUsable, apply, applyCommandCodeSearchSelection, applyCommands, applyUsageRemote, buildCommandAuthUrl, capabilityDescription, commandCodeSearchSelection, commandDefinition, compareByPlan, dealLabel, formatContext, inject, loginStatusSchema, matchModelRule, modelVisibleInPlan, name, parseLoginStatus, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, selectAccountForModel, selectActiveAccount, selectCommandCodeSearchProvider, studioBaseForApiBase, subscriptionPlanInfo, usageReportSchema, validateCommandApiKey };
|
|
3278
3837
|
|
|
3279
3838
|
//# sourceMappingURL=index.js.map
|