@mars-sea/dsh-commandcode-provider 0.10.1 → 0.10.3
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 +41 -1
- package/README.md +7 -3
- package/README.zh-CN.md +7 -3
- package/lib/client.js +530 -145
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +165 -51
- package/lib/index.js +848 -604
- package/lib/index.js.map +1 -1
- package/package.json +9 -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;
|
|
@@ -332,6 +338,11 @@ const KNOWN_EFFORTS = {
|
|
|
332
338
|
"high",
|
|
333
339
|
"max"
|
|
334
340
|
],
|
|
341
|
+
"deepseek/deepseek-v4.1-flash": [
|
|
342
|
+
"low",
|
|
343
|
+
"high",
|
|
344
|
+
"max"
|
|
345
|
+
],
|
|
335
346
|
"deepseek/deepseek-v4-flash": ["high", "max"],
|
|
336
347
|
"deepseek/deepseek-v4-flash-vision-exp": ["high", "max"],
|
|
337
348
|
"deepseek/deepseek-v4-pro": ["high", "max"],
|
|
@@ -479,6 +490,11 @@ const KNOWN_EFFORTS = {
|
|
|
479
490
|
"high",
|
|
480
491
|
"xhigh",
|
|
481
492
|
"max"
|
|
493
|
+
],
|
|
494
|
+
"MiniMaxAI/MiniMax-M3": [
|
|
495
|
+
"low",
|
|
496
|
+
"medium",
|
|
497
|
+
"high"
|
|
482
498
|
]
|
|
483
499
|
};
|
|
484
500
|
/**
|
|
@@ -514,6 +530,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
514
530
|
"claude-sonnet-4-6",
|
|
515
531
|
"claude-sonnet-5",
|
|
516
532
|
"deepseek/deepseek-v4-flash-vision-exp",
|
|
533
|
+
"deepseek/deepseek-v4.1-flash",
|
|
517
534
|
"google/gemini-3.1-flash-lite",
|
|
518
535
|
"google/gemini-3.5-flash",
|
|
519
536
|
"google/gemini-3.5-flash-lite",
|
|
@@ -548,7 +565,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
548
565
|
"z-ai/glm-5.3-flash"
|
|
549
566
|
]);
|
|
550
567
|
/**
|
|
551
|
-
* Models the official CLI's model table (command-code@1.
|
|
568
|
+
* Models the official CLI's model table (command-code@1.53.0) marks
|
|
552
569
|
* `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
|
|
553
570
|
* think automatically, with Command Code driving the depth. This is the
|
|
554
571
|
* authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
|
|
@@ -556,7 +573,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
556
573
|
* effort levels, and this snapshot is not surfaced in the picker's compact
|
|
557
574
|
* description — it exists for programmatic consumers.
|
|
558
575
|
*
|
|
559
|
-
* Source: the command-code@1.
|
|
576
|
+
* Source: the command-code@1.53.0 bundled model table (dist/cli.mjs),
|
|
560
577
|
* cross-checked with https://commandcode.ai/docs/reference/cli/models.
|
|
561
578
|
* (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
|
|
562
579
|
* selectable `['low', 'high', 'max']` efforts; the preview then ended in
|
|
@@ -569,11 +586,16 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
569
586
|
* to `KNOWN_EFFORTS`. command-code@1.42.0 added `meituan/LongCat-2.0:free`
|
|
570
587
|
* (reasoning:!0, no efforts). command-code@1.45.0 gave the Muse Spark family
|
|
571
588
|
* (1.1, 1.2, 1.2-contributor, 1.3, 1.3-contributor) selectable
|
|
572
|
-
* `['low', 'medium', 'high', 'xhigh']` efforts — they moved to `KNOWN_EFFORTS`.
|
|
589
|
+
* `['low', 'medium', 'high', 'xhigh']` efforts — they moved to `KNOWN_EFFORTS`.
|
|
590
|
+
* command-code@1.51.3 gave `MiniMaxAI/MiniMax-M3` selectable
|
|
591
|
+
* `['low', 'medium', 'high']` efforts — it moved to `KNOWN_EFFORTS` too.
|
|
592
|
+
* command-code@1.52.0 added `inclusionai/ling-3.0-flash-sante:free`
|
|
593
|
+
* (reasoning:!0, no efforts).)
|
|
594
|
+
* command-code@1.53.0 added `deepseek/deepseek-v4.1-flash` with selectable
|
|
595
|
+
* ['low', 'high', 'max'] efforts, so it lives in `KNOWN_EFFORTS`, not here.)
|
|
573
596
|
* Keep in sync via the dsh-commandcode-upstream skill.
|
|
574
597
|
*/
|
|
575
598
|
const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
|
|
576
|
-
"MiniMaxAI/MiniMax-M3",
|
|
577
599
|
"Qwen/Qwen3.6-Max-Preview",
|
|
578
600
|
"Qwen/Qwen3.6-Plus",
|
|
579
601
|
"Qwen/Qwen3.7-Flash",
|
|
@@ -588,7 +610,8 @@ const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
|
|
|
588
610
|
"thinkingmachines/inkling",
|
|
589
611
|
"thinkingmachines/inkling-small",
|
|
590
612
|
"poolside/laguna-s-2.1-free",
|
|
591
|
-
"meituan/LongCat-2.0:free"
|
|
613
|
+
"meituan/LongCat-2.0:free",
|
|
614
|
+
"inclusionai/ling-3.0-flash-sante:free"
|
|
592
615
|
]);
|
|
593
616
|
/**
|
|
594
617
|
* The minimum subscription plan a model is included in, per the official plan
|
|
@@ -603,7 +626,9 @@ const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
|
|
|
603
626
|
* command-code@1.41.0 added `Qwen/Qwen3.8-Max-0902` (Go) and 1.42.0 added
|
|
604
627
|
* `meituan/LongCat-2.0:free` (Go, free promo); command-code@1.43.0 added
|
|
605
628
|
* `google/gemini-3.8-flash` (GOAT) and 1.44.0 added `meta/muse-spark-1.3`
|
|
606
|
-
* (GOAT) plus its Contributor sibling (Go).
|
|
629
|
+
* (GOAT) plus its Contributor sibling (Go); command-code@1.52.0 added the
|
|
630
|
+
* free `inclusionai/ling-3.0-flash-sante:free` (Go); command-code@1.53.0
|
|
631
|
+
* added `deepseek/deepseek-v4.1-flash` (Go).
|
|
607
632
|
*
|
|
608
633
|
* The Provider API exposes no plan metadata, so this snapshot is the source of
|
|
609
634
|
* truth for the picker's plan annotation — it answers "which plan do I need to
|
|
@@ -627,11 +652,13 @@ const KNOWN_PLANS = {
|
|
|
627
652
|
"Qwen/Qwen3.8-Max": "go",
|
|
628
653
|
"Qwen/Qwen3.8-Max-0902": "go",
|
|
629
654
|
"deepseek/deepseek-v4-flash-fast": "go",
|
|
655
|
+
"deepseek/deepseek-v4.1-flash": "go",
|
|
630
656
|
"deepseek/deepseek-v4-flash": "go",
|
|
631
657
|
"deepseek/deepseek-v4-flash-vision-exp": "go",
|
|
632
658
|
"deepseek/deepseek-v4-pro": "go",
|
|
633
659
|
"gpt-5.6-luna": "go",
|
|
634
660
|
"meituan/LongCat-2.0:free": "go",
|
|
661
|
+
"inclusionai/ling-3.0-flash-sante:free": "go",
|
|
635
662
|
"meta/muse-spark-1.2-contributor": "go",
|
|
636
663
|
"meta/muse-spark-1.3-contributor": "go",
|
|
637
664
|
"moonshotai/Kimi-K2.5": "go",
|
|
@@ -728,8 +755,8 @@ function compareByPlan(a, b) {
|
|
|
728
755
|
}
|
|
729
756
|
/**
|
|
730
757
|
* Subscription plan table, synced from the official CLI bundle's plan maps
|
|
731
|
-
* (located by the `"individual-go"` key in command-code@1.
|
|
732
|
-
* re-verified unchanged through 1.
|
|
758
|
+
* (located by the `"individual-go"` key in command-code@1.53.0 `dist/cli.mjs`,
|
|
759
|
+
* re-verified unchanged through 1.53.0): subscription `planId`
|
|
733
760
|
* prefix → display name and the plan's monthly credit total. This is the
|
|
734
761
|
* account's own subscription (from `/alpha/billing/subscriptions`) — distinct
|
|
735
762
|
* from {@link KNOWN_PLANS}, which maps catalog models to their minimum tier.
|
|
@@ -797,13 +824,14 @@ function subscriptionPlanInfo(planId) {
|
|
|
797
824
|
/**
|
|
798
825
|
* Whether the picker lists `modelId` for an account with the given billing
|
|
799
826
|
* access. Fails open at every uncertainty: no billing data, an unknown plan,
|
|
800
|
-
*
|
|
801
|
-
*
|
|
827
|
+
* a non-finite weight (corrupt billing fact), or a model outside
|
|
828
|
+
* {@link KNOWN_PLANS} all keep the model visible — the server remains the
|
|
829
|
+
* final gate (`403 MODEL_NOT_IN_PLAN`).
|
|
802
830
|
*/
|
|
803
831
|
function modelVisibleInPlan(modelId, access) {
|
|
804
832
|
if (access === void 0) return true;
|
|
805
833
|
if (access.onDemandCredits > 0) return true;
|
|
806
|
-
if (access.tierWeight === void 0) return true;
|
|
834
|
+
if (access.tierWeight === void 0 || !Number.isFinite(access.tierWeight)) return true;
|
|
807
835
|
const tier = KNOWN_PLANS[modelId];
|
|
808
836
|
if (tier === void 0) return true;
|
|
809
837
|
const weight = PLAN_ORDER[tier];
|
|
@@ -821,45 +849,69 @@ const KNOWN_DEALS = {
|
|
|
821
849
|
"meituan/LongCat-2.0:free": {
|
|
822
850
|
label: "FREE",
|
|
823
851
|
free: true
|
|
852
|
+
},
|
|
853
|
+
"inclusionai/ling-3.0-flash-sante:free": {
|
|
854
|
+
label: "FREE",
|
|
855
|
+
free: true
|
|
824
856
|
}
|
|
825
857
|
};
|
|
826
858
|
/**
|
|
827
859
|
* Models with time-of-day (peak/off-peak) pricing, per the official pricing
|
|
828
860
|
* page (`/docs/resources/pricing-limits`). Since 2026-08-16 16:00 UTC, DeepSeek
|
|
829
|
-
* charges by the hour: peak hours are 01:00–04:00 and 06:00–10:00 UTC (7h
|
|
830
|
-
* full price)
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
861
|
+
* charges by the hour: peak hours are 01:00–04:00 and 06:00–10:00 UTC (7h per
|
|
862
|
+
* weekday, full price) **Monday to Friday only**; the other 17 hours of a
|
|
863
|
+
* weekday and every hour of Saturday/Sunday (UTC) are off-peak at half price.
|
|
864
|
+
* The V4 Flash Vision (exp) variant (command-code@1.32.0) shares the V4 Flash
|
|
865
|
+
* windows and peak prices ($0.44/$1.32) — each row's hover annotation states
|
|
866
|
+
* exactly 2× that row's displayed off-peak prices. The picker shows the
|
|
834
867
|
* *current* state as a compact
|
|
835
868
|
* label (`Peak`/`Half`) matching the English noun style of the other markers
|
|
836
869
|
* (`Image`, `FREE`), so a developer can tell at a glance whether calling the
|
|
837
870
|
* model right now is cheap or expensive.
|
|
838
871
|
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
872
|
+
* Authoritative extraction: the pricing page embeds a model JSON array whose
|
|
873
|
+
* hourly-priced entries carry a `timeOfDay` block
|
|
874
|
+
* (`{ windows: "01–04 & 06–10 UTC, Mon–Fri", peakHoursPerDay: 7,
|
|
875
|
+
* offPeakHoursPerDay: 17, peak: {...}, offPeak: {...} }`). Exactly four models
|
|
876
|
+
* carry it: V4 Pro, V4 Flash, V4 Flash Vision (exp), and V4.1 Flash (added in
|
|
877
|
+
* command-code@1.53.0 at $0.15/$0.60 off-peak, $0.30/$1.20 peak — the same
|
|
878
|
+
* schedule as the other three).
|
|
879
|
+
*
|
|
880
|
+
* Extraction caution: the rendered HTML rows are a trap. Each annotation div
|
|
881
|
+
* sits inside its OWN row's container, immediately before the NEXT row starts,
|
|
882
|
+
* so flattening the page to text makes every annotation look like it belongs
|
|
883
|
+
* to the model printed after it — that is how `deepseek/deepseek-v4-flash-fast`
|
|
884
|
+
* was wrongly added here (its row is flat-priced at $0.28/$0.56/$0.07 and has
|
|
885
|
+
* no `timeOfDay` block). Trust the embedded JSON's `timeOfDay` membership and
|
|
886
|
+
* the 2× price relation, never the flat-text neighbor.
|
|
844
887
|
*
|
|
845
|
-
* Keep in sync with the official pricing page when the model set
|
|
846
|
-
* windows change (see the dsh-commandcode-upstream skill).
|
|
888
|
+
* Keep in sync with the official pricing page when the model set, the peak
|
|
889
|
+
* windows, or the weekday rule change (see the dsh-commandcode-upstream skill).
|
|
847
890
|
*/
|
|
848
891
|
const KNOWN_PEAK_PRICING = /* @__PURE__ */ new Set([
|
|
849
892
|
"deepseek/deepseek-v4-pro",
|
|
850
893
|
"deepseek/deepseek-v4-flash",
|
|
851
894
|
"deepseek/deepseek-v4-flash-vision-exp",
|
|
852
|
-
"deepseek/deepseek-v4-flash
|
|
895
|
+
"deepseek/deepseek-v4.1-flash"
|
|
853
896
|
]);
|
|
854
|
-
/**
|
|
897
|
+
/**
|
|
898
|
+
* Peak hours (UTC, hour-of-day range end-exclusive): 01–03 and 06–09.
|
|
899
|
+
* Weekday-only — see `peakPricingState()`; weekends are fully off-peak.
|
|
900
|
+
*/
|
|
855
901
|
const PEAK_HOUR_RANGES = [[1, 4], [6, 10]];
|
|
856
902
|
/**
|
|
857
|
-
* Whether `now` (defaults to `Date.now()`) falls in a peak-pricing
|
|
858
|
-
* time-of-day-priced models.
|
|
903
|
+
* Whether `now` (defaults to `Date.now()`) falls in a peak-pricing window for
|
|
904
|
+
* time-of-day-priced models. Peak rates apply Monday–Friday (UTC) only: the
|
|
905
|
+
* official rule charges Saturday and Sunday completely off-peak for all 24
|
|
906
|
+
* hours, so a weekend timestamp is off-peak even inside `PEAK_HOUR_RANGES`.
|
|
907
|
+
* `undefined` for models outside the snapshot.
|
|
859
908
|
*/
|
|
860
909
|
function peakPricingState(modelId, now = Date.now()) {
|
|
861
910
|
if (!KNOWN_PEAK_PRICING.has(modelId)) return void 0;
|
|
862
|
-
const
|
|
911
|
+
const at = new Date(now);
|
|
912
|
+
const day = at.getUTCDay();
|
|
913
|
+
if (day === 0 || day === 6) return "off-peak";
|
|
914
|
+
const hour = at.getUTCHours();
|
|
863
915
|
return PEAK_HOUR_RANGES.some(([start, end]) => hour >= start && hour < end) ? "peak" : "off-peak";
|
|
864
916
|
}
|
|
865
917
|
/**
|
|
@@ -897,8 +949,8 @@ function dealLabel(modelId, now = Date.now()) {
|
|
|
897
949
|
}
|
|
898
950
|
/**
|
|
899
951
|
* Compact human-readable context window, e.g. `1_000_000 -> "1M"`,
|
|
900
|
-
* `256_000 -> "256K"`, `262_144 -> "
|
|
901
|
-
* Returns undefined for unknown/absent sizes.
|
|
952
|
+
* `256_000 -> "256K"`, `262_144 -> "262K"` (floor to the nearest K).
|
|
953
|
+
* Returns undefined for unknown/absent sizes; values under 1K render raw.
|
|
902
954
|
*/
|
|
903
955
|
function formatContext(contextWindow) {
|
|
904
956
|
if (contextWindow === void 0 || !Number.isFinite(contextWindow) || contextWindow <= 0) return;
|
|
@@ -907,6 +959,7 @@ function formatContext(contextWindow) {
|
|
|
907
959
|
const rounded = Math.round(m * 10) / 10;
|
|
908
960
|
return `${Number.isInteger(rounded) ? rounded : rounded.toFixed(1)}M`;
|
|
909
961
|
}
|
|
962
|
+
if (contextWindow < 1e3) return String(Math.floor(contextWindow));
|
|
910
963
|
return `${Math.floor(contextWindow / 1e3)}K`;
|
|
911
964
|
}
|
|
912
965
|
/**
|
|
@@ -939,7 +992,7 @@ function capabilityDescription(modelId, contextWindow, now = Date.now()) {
|
|
|
939
992
|
* and API key or subscription, and Command Code's terms apply.
|
|
940
993
|
*
|
|
941
994
|
* Wire protocol (reverse-engineered by the pi plugin, command-code@1.28.4;
|
|
942
|
-
* re-verified against command-code@1.
|
|
995
|
+
* re-verified against command-code@1.53.0 — endpoints, request shape, and
|
|
943
996
|
* stream events unchanged):
|
|
944
997
|
* POST {apiBase}/alpha/generate
|
|
945
998
|
* body: { config, memory, taste, skills, params: { model, messages, tools,
|
|
@@ -952,13 +1005,17 @@ function capabilityDescription(modelId, contextWindow, now = Date.now()) {
|
|
|
952
1005
|
* per-request options thunk and an API-key resolver from the plugin entry
|
|
953
1006
|
* (src/index.ts), so a settings change reaches the very next request.
|
|
954
1007
|
*/
|
|
955
|
-
const COMMAND_CODE_CLI_VERSION = "1.
|
|
1008
|
+
const COMMAND_CODE_CLI_VERSION = "1.53.0";
|
|
956
1009
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
957
1010
|
const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
|
|
958
1011
|
const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
959
1012
|
const MODELS_TIMEOUT_MS = 1e4;
|
|
960
1013
|
/** How long the picker's plan-filter billing facts stay cached before refetching. */
|
|
961
1014
|
const BILLING_ACCESS_TTL_MS = 3e5;
|
|
1015
|
+
/** The entry-plan tier weight (`individual-go` in KNOWN_SUBSCRIPTION_PLANS). */
|
|
1016
|
+
const GO_TIER_WEIGHT = 0;
|
|
1017
|
+
/** Hard cap on account rotations within one request (one attempt per distinct key). */
|
|
1018
|
+
const MAX_ACCOUNT_ROTATIONS = 16;
|
|
962
1019
|
/**
|
|
963
1020
|
* Subscription statuses the CLI treats as live (`Mr` in command-code's
|
|
964
1021
|
* cli.mjs): the plan gate applies only under one of these.
|
|
@@ -1323,6 +1380,288 @@ async function messagesToOpenAI(messages, readImage) {
|
|
|
1323
1380
|
}
|
|
1324
1381
|
/** Account endpoints fetched by one `getUsage()` run (see the classification there). */
|
|
1325
1382
|
const USAGE_ENDPOINT_COUNT = 4;
|
|
1383
|
+
/**
|
|
1384
|
+
* Parse one usage/summary payload into the report's usage section.
|
|
1385
|
+
* Returns undefined when the endpoint answered nothing usable.
|
|
1386
|
+
*/
|
|
1387
|
+
function parseUsageTotals(usage) {
|
|
1388
|
+
if (usage === void 0) return void 0;
|
|
1389
|
+
return {
|
|
1390
|
+
totalCount: numberValue(usage.totalCount) ?? 0,
|
|
1391
|
+
totalCost: numberValue(usage.totalCost) ?? 0,
|
|
1392
|
+
successRate: numberValue(usage.successRate) ?? 0,
|
|
1393
|
+
completedCount: numberValue(usage.completedCount) ?? 0,
|
|
1394
|
+
failedCount: numberValue(usage.failedCount) ?? 0,
|
|
1395
|
+
totalTokensIn: numberValue(usage.totalTokensIn) ?? 0,
|
|
1396
|
+
totalTokensOut: numberValue(usage.totalTokensOut) ?? 0,
|
|
1397
|
+
totalCredits: numberValue(usage.totalCredits) ?? 0,
|
|
1398
|
+
periodBasis: stringValue(usage.periodBasis) ?? "billing-period"
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
/** Parse one window-limit block (`fiveHour` / `weekly`). */
|
|
1402
|
+
function parseWindowLimit(value) {
|
|
1403
|
+
const block = isRecord(value) ? value : void 0;
|
|
1404
|
+
return {
|
|
1405
|
+
used: numberValue(block?.used) ?? 0,
|
|
1406
|
+
cap: numberValue(block?.cap) ?? 0,
|
|
1407
|
+
exceeded: block?.exceeded === true,
|
|
1408
|
+
resetAt: numberValue(block?.resetAt) ?? 0
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Parse one billing/credits payload into the report's credits section.
|
|
1413
|
+
* Returns undefined when the endpoint answered nothing usable.
|
|
1414
|
+
*/
|
|
1415
|
+
function parseCreditLimits(credits) {
|
|
1416
|
+
if (credits === void 0) return void 0;
|
|
1417
|
+
const creditsData = isRecord(credits.credits) ? credits.credits : void 0;
|
|
1418
|
+
const windowLimits = isRecord(credits.windowLimits) ? credits.windowLimits : void 0;
|
|
1419
|
+
const fiveHour = windowLimits !== void 0 && isRecord(windowLimits.fiveHour) ? windowLimits.fiveHour : void 0;
|
|
1420
|
+
const weekly = windowLimits !== void 0 && isRecord(windowLimits.weekly) ? windowLimits.weekly : void 0;
|
|
1421
|
+
if (creditsData === void 0 && fiveHour === void 0 && weekly === void 0) return void 0;
|
|
1422
|
+
return {
|
|
1423
|
+
monthlyCredits: numberValue(creditsData?.monthlyCredits) ?? 0,
|
|
1424
|
+
purchasedCredits: numberValue(creditsData?.purchasedCredits) ?? 0,
|
|
1425
|
+
freeCredits: numberValue(creditsData?.freeCredits) ?? 0,
|
|
1426
|
+
fiveHour: parseWindowLimit(fiveHour),
|
|
1427
|
+
weekly: parseWindowLimit(weekly)
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Parse the account identity from whoami plus the plan identity from the
|
|
1432
|
+
* subscriptions/credits payloads. Returns the org id for the subscriptions
|
|
1433
|
+
* query alongside, so getUsage fetches whoami first and the rest in parallel.
|
|
1434
|
+
*/
|
|
1435
|
+
function parseAccountIdentity(whoami) {
|
|
1436
|
+
const whoamiData = whoami !== void 0 && isRecord(whoami.user) ? whoami.user : void 0;
|
|
1437
|
+
const orgData = whoami !== void 0 && isRecord(whoami.org) ? whoami.org : void 0;
|
|
1438
|
+
return {
|
|
1439
|
+
account: whoamiData === void 0 ? void 0 : {
|
|
1440
|
+
id: stringValue(whoamiData.id) ?? "",
|
|
1441
|
+
name: stringValue(whoamiData.name) ?? "",
|
|
1442
|
+
userName: stringValue(whoamiData.userName) ?? ""
|
|
1443
|
+
},
|
|
1444
|
+
orgId: orgData === void 0 ? void 0 : stringValue(orgData.id)
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Classify a TOTAL failure: when every endpoint failed with one class of
|
|
1449
|
+
* error, the degraded per-endpoint view would hide the root cause behind
|
|
1450
|
+
* a generic "partial data" note — name it instead.
|
|
1451
|
+
*/
|
|
1452
|
+
function classifyTotalFailure(failures, failedStatuses) {
|
|
1453
|
+
if (failures.length !== USAGE_ENDPOINT_COUNT) return void 0;
|
|
1454
|
+
const codes = failedStatuses.filter((status) => status !== void 0);
|
|
1455
|
+
if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code === 401)) return "invalid-key";
|
|
1456
|
+
if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code >= 500)) return "service-unavailable";
|
|
1457
|
+
if (codes.length === 0) return "network";
|
|
1458
|
+
}
|
|
1459
|
+
/** Build the legacy CLI (`/alpha/generate`) request body for one call. */
|
|
1460
|
+
async function buildCliBody(options, connection, facts) {
|
|
1461
|
+
return {
|
|
1462
|
+
config: {
|
|
1463
|
+
workingDir: connection.workingDir,
|
|
1464
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
1465
|
+
environment: `${process.platform}-${process.arch}, Node.js ${process.version}`,
|
|
1466
|
+
structure: [],
|
|
1467
|
+
isGitRepo: false,
|
|
1468
|
+
currentBranch: "",
|
|
1469
|
+
mainBranch: "",
|
|
1470
|
+
gitStatus: "",
|
|
1471
|
+
recentCommits: []
|
|
1472
|
+
},
|
|
1473
|
+
memory: null,
|
|
1474
|
+
taste: null,
|
|
1475
|
+
skills: null,
|
|
1476
|
+
params: {
|
|
1477
|
+
model: options.model,
|
|
1478
|
+
messages: await messagesToCC(options.messages, facts.readImage),
|
|
1479
|
+
tools: (options.tools ?? []).map((tool) => ({
|
|
1480
|
+
type: "function",
|
|
1481
|
+
name: tool.name,
|
|
1482
|
+
description: tool.description,
|
|
1483
|
+
input_schema: tool.parameters
|
|
1484
|
+
})),
|
|
1485
|
+
system: facts.systemText,
|
|
1486
|
+
max_tokens: facts.maxTokens,
|
|
1487
|
+
temperature: options.temperature ?? .3,
|
|
1488
|
+
stream: true,
|
|
1489
|
+
...facts.reasoningEffort ? { reasoning_effort: facts.reasoningEffort } : {}
|
|
1490
|
+
},
|
|
1491
|
+
threadId: randomUUID()
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
/** Build the documented Provider Chat Completions request body for one call. */
|
|
1495
|
+
async function buildOpenAIBody(options, facts) {
|
|
1496
|
+
const openAiMessages = [...facts.systemText ? [{
|
|
1497
|
+
role: "system",
|
|
1498
|
+
content: facts.systemText
|
|
1499
|
+
}] : [], ...await messagesToOpenAI(options.messages, facts.readImage)];
|
|
1500
|
+
const openAiTools = (options.tools ?? []).map((tool) => ({
|
|
1501
|
+
type: "function",
|
|
1502
|
+
function: {
|
|
1503
|
+
name: tool.name,
|
|
1504
|
+
description: tool.description,
|
|
1505
|
+
parameters: tool.parameters
|
|
1506
|
+
}
|
|
1507
|
+
}));
|
|
1508
|
+
return {
|
|
1509
|
+
model: options.model,
|
|
1510
|
+
messages: openAiMessages,
|
|
1511
|
+
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
1512
|
+
max_tokens: facts.maxTokens,
|
|
1513
|
+
temperature: options.temperature ?? .3,
|
|
1514
|
+
stream: true,
|
|
1515
|
+
...facts.reasoningEffort ? { reasoning_effort: facts.reasoningEffort } : {}
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
/**
|
|
1519
|
+
* One pre-stream connect attempt: POST the body and wait for response
|
|
1520
|
+
* headers only (`requestTimeoutMs` must never bound the body stream — see
|
|
1521
|
+
* the caller). Returns the live response plus its cleanup, or the rejection
|
|
1522
|
+
* facts for the rotation loop to classify. Every failure path cleans up
|
|
1523
|
+
* before returning or throwing; on success the caller-abort listener
|
|
1524
|
+
* outlives the connect phase (it aborts a stalled body read), so the
|
|
1525
|
+
* streaming tail calls cleanup.
|
|
1526
|
+
*/
|
|
1527
|
+
async function connectGenerate(deps, key, protocol, body) {
|
|
1528
|
+
const { options, connection, fetchImpl } = deps;
|
|
1529
|
+
const connectAbort = new AbortController();
|
|
1530
|
+
let connectTimedOut = false;
|
|
1531
|
+
const endpoint = protocol === "cli" ? `${connection.apiBase}/alpha/generate` : `${connection.apiBase}/provider/v1/chat/completions`;
|
|
1532
|
+
const connectTimer = setTimeout(() => {
|
|
1533
|
+
connectTimedOut = true;
|
|
1534
|
+
connectAbort.abort(new DOMException(`Command Code API request to ${endpoint} did not respond within ${connection.requestTimeoutMs}ms`, "TimeoutError"));
|
|
1535
|
+
}, connection.requestTimeoutMs);
|
|
1536
|
+
const onCallerAbort = () => {
|
|
1537
|
+
connectAbort.abort(options.signal?.reason);
|
|
1538
|
+
};
|
|
1539
|
+
if (options.signal) {
|
|
1540
|
+
if (options.signal.aborted) onCallerAbort();
|
|
1541
|
+
else options.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
1542
|
+
}
|
|
1543
|
+
const cleanup = () => {
|
|
1544
|
+
clearTimeout(connectTimer);
|
|
1545
|
+
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
1546
|
+
};
|
|
1547
|
+
let response;
|
|
1548
|
+
const headers = protocol === "cli" ? {
|
|
1549
|
+
"Content-Type": "application/json",
|
|
1550
|
+
Authorization: `Bearer ${key}`,
|
|
1551
|
+
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
1552
|
+
"x-cli-environment": "production",
|
|
1553
|
+
"x-project-slug": projectSlugFromPath(connection.workingDir),
|
|
1554
|
+
"x-taste-learning": "true",
|
|
1555
|
+
"x-co-flag": "false",
|
|
1556
|
+
...attributionHeaders()
|
|
1557
|
+
} : {
|
|
1558
|
+
"Content-Type": "application/json",
|
|
1559
|
+
Authorization: `Bearer ${key}`,
|
|
1560
|
+
Accept: "text/event-stream",
|
|
1561
|
+
...attributionHeaders()
|
|
1562
|
+
};
|
|
1563
|
+
try {
|
|
1564
|
+
response = await fetchImpl(endpoint, {
|
|
1565
|
+
method: "POST",
|
|
1566
|
+
headers,
|
|
1567
|
+
body: JSON.stringify(body),
|
|
1568
|
+
signal: connectAbort.signal
|
|
1569
|
+
});
|
|
1570
|
+
clearTimeout(connectTimer);
|
|
1571
|
+
} catch (error) {
|
|
1572
|
+
cleanup();
|
|
1573
|
+
if (options.signal?.aborted) throw error;
|
|
1574
|
+
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 });
|
|
1575
|
+
throw new LlmError(`Command Code API request to ${endpoint} failed: ${errorChain(error)};Command Code API 请求连接失败——通常是网络或代理问题,请检查网络或代理设置后重试`, "TRANSPORT", { cause: error });
|
|
1576
|
+
}
|
|
1577
|
+
if (!response.ok) {
|
|
1578
|
+
const errText = await response.text().catch(() => "");
|
|
1579
|
+
cleanup();
|
|
1580
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
1581
|
+
return retryAfterMs === void 0 ? {
|
|
1582
|
+
status: response.status,
|
|
1583
|
+
errText
|
|
1584
|
+
} : {
|
|
1585
|
+
status: response.status,
|
|
1586
|
+
errText,
|
|
1587
|
+
retryAfterMs
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
return {
|
|
1591
|
+
response,
|
|
1592
|
+
cleanup
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
/** Fresh block-assembly state for one stream. */
|
|
1596
|
+
function createBlockAssembler() {
|
|
1597
|
+
return {
|
|
1598
|
+
nextIndex: 0,
|
|
1599
|
+
textIndex: -1,
|
|
1600
|
+
textContent: "",
|
|
1601
|
+
reasoningIndex: -1,
|
|
1602
|
+
reasoningContent: "",
|
|
1603
|
+
sawContent: false,
|
|
1604
|
+
openAiToolCalls: []
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
function* closeText(asm) {
|
|
1608
|
+
if (asm.textIndex < 0) return;
|
|
1609
|
+
yield {
|
|
1610
|
+
type: "block-end",
|
|
1611
|
+
index: asm.textIndex,
|
|
1612
|
+
block: {
|
|
1613
|
+
type: "text",
|
|
1614
|
+
text: asm.textContent
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
asm.textIndex = -1;
|
|
1618
|
+
asm.textContent = "";
|
|
1619
|
+
}
|
|
1620
|
+
function* closeReasoning(asm) {
|
|
1621
|
+
if (asm.reasoningIndex < 0) return;
|
|
1622
|
+
yield {
|
|
1623
|
+
type: "block-end",
|
|
1624
|
+
index: asm.reasoningIndex,
|
|
1625
|
+
block: {
|
|
1626
|
+
type: "reasoning",
|
|
1627
|
+
text: asm.reasoningContent
|
|
1628
|
+
}
|
|
1629
|
+
};
|
|
1630
|
+
asm.reasoningIndex = -1;
|
|
1631
|
+
asm.reasoningContent = "";
|
|
1632
|
+
}
|
|
1633
|
+
function* emitOpenAiToolCalls(asm) {
|
|
1634
|
+
for (const call of asm.openAiToolCalls) {
|
|
1635
|
+
const id = call.id ?? randomUUID();
|
|
1636
|
+
const name = call.name ?? "";
|
|
1637
|
+
const args = call.arguments || "{}";
|
|
1638
|
+
const index = asm.nextIndex++;
|
|
1639
|
+
asm.sawContent = true;
|
|
1640
|
+
yield {
|
|
1641
|
+
type: "block-start",
|
|
1642
|
+
index,
|
|
1643
|
+
blockType: "tool-call"
|
|
1644
|
+
};
|
|
1645
|
+
yield {
|
|
1646
|
+
type: "tool-call-delta",
|
|
1647
|
+
index,
|
|
1648
|
+
id: ToolCallId(id),
|
|
1649
|
+
name,
|
|
1650
|
+
argumentsDelta: args
|
|
1651
|
+
};
|
|
1652
|
+
yield {
|
|
1653
|
+
type: "block-end",
|
|
1654
|
+
index,
|
|
1655
|
+
block: {
|
|
1656
|
+
type: "tool-call",
|
|
1657
|
+
id: ToolCallId(id),
|
|
1658
|
+
name,
|
|
1659
|
+
arguments: args
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
asm.openAiToolCalls.length = 0;
|
|
1664
|
+
}
|
|
1326
1665
|
var CommandCodeAdapter = class extends LlmAdapter {
|
|
1327
1666
|
deps;
|
|
1328
1667
|
catalog = [];
|
|
@@ -1405,10 +1744,9 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1405
1744
|
}
|
|
1406
1745
|
return this.catalog;
|
|
1407
1746
|
}
|
|
1408
|
-
async listModels(provider) {
|
|
1747
|
+
async listModels(provider, opts) {
|
|
1409
1748
|
const catalog = await this.loadCatalog();
|
|
1410
|
-
const
|
|
1411
|
-
return catalog.filter((model) => modelVisibleInPlan(model.id, access)).map((model) => {
|
|
1749
|
+
const toInfo = (model) => {
|
|
1412
1750
|
const vision = KNOWN_IMAGE_MODELS.has(model.id);
|
|
1413
1751
|
return {
|
|
1414
1752
|
provider,
|
|
@@ -1417,7 +1755,12 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1417
1755
|
description: capabilityDescription(model.id, model.contextWindow),
|
|
1418
1756
|
inputModalities: vision ? ["text", "image"] : ["text"]
|
|
1419
1757
|
};
|
|
1420
|
-
}
|
|
1758
|
+
};
|
|
1759
|
+
if (opts?.unfiltered === true) return catalog.map(toInfo).sort(compareByPlan);
|
|
1760
|
+
const access = this.deps.options().filterModelsByPlan === false ? void 0 : await this.loadBillingAccess();
|
|
1761
|
+
const visible = this.deps.options().visibleModels;
|
|
1762
|
+
const allow = Array.isArray(visible) && visible.length > 0 ? new Set(visible.filter((id) => typeof id === "string" && id !== "")) : void 0;
|
|
1763
|
+
return catalog.filter((model) => modelVisibleInPlan(model.id, access)).filter((model) => allow === void 0 || allow.has(model.id)).map(toInfo).sort(compareByPlan);
|
|
1421
1764
|
}
|
|
1422
1765
|
async resolveModel(provider, model, signal) {
|
|
1423
1766
|
const entry = this.catalog.find((m) => m.id === model) ?? (await this.loadCatalog(signal)).find((m) => m.id === model);
|
|
@@ -1450,6 +1793,25 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1450
1793
|
};
|
|
1451
1794
|
}
|
|
1452
1795
|
/**
|
|
1796
|
+
* Fetch one account endpoint and parse its JSON body. Returns the HTTP
|
|
1797
|
+
* status alongside the parsed record so each caller applies its own
|
|
1798
|
+
* failure accounting: the billing probe fails open silently, the usage
|
|
1799
|
+
* report books failures per endpoint. Non-2xx and non-record bodies come
|
|
1800
|
+
* back without a record; only a transport throw propagates to the caller.
|
|
1801
|
+
*/
|
|
1802
|
+
async fetchEndpointJson(url, headers) {
|
|
1803
|
+
const response = await this.fetchImpl(url, {
|
|
1804
|
+
headers,
|
|
1805
|
+
signal: AbortSignal.timeout(MODELS_TIMEOUT_MS)
|
|
1806
|
+
});
|
|
1807
|
+
if (!response.ok) return { status: response.status };
|
|
1808
|
+
const parsed = await response.json();
|
|
1809
|
+
return {
|
|
1810
|
+
status: response.status,
|
|
1811
|
+
...isRecord(parsed) ? { record: parsed } : {}
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1453
1815
|
* The billing facts behind the picker's plan filter, cached for
|
|
1454
1816
|
* {@link BILLING_ACCESS_TTL_MS} and shared across concurrent callers.
|
|
1455
1817
|
* `undefined` means "unknown — show everything" (fail-open).
|
|
@@ -1491,15 +1853,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1491
1853
|
const connection = this.deps.options();
|
|
1492
1854
|
const headers = await this.accountHeaders(apiKey);
|
|
1493
1855
|
const base = connection.apiBase;
|
|
1494
|
-
const getJson = async (path) => {
|
|
1495
|
-
const response = await this.fetchImpl(`${base}${path}`, {
|
|
1496
|
-
headers,
|
|
1497
|
-
signal: AbortSignal.timeout(MODELS_TIMEOUT_MS)
|
|
1498
|
-
});
|
|
1499
|
-
if (!response.ok) return void 0;
|
|
1500
|
-
const parsed = await response.json();
|
|
1501
|
-
return isRecord(parsed) ? parsed : void 0;
|
|
1502
|
-
};
|
|
1856
|
+
const getJson = async (path) => (await this.fetchEndpointJson(`${base}${path}`, headers)).record;
|
|
1503
1857
|
const whoami = await getJson("/alpha/whoami");
|
|
1504
1858
|
const orgData = whoami && isRecord(whoami.org) ? whoami.org : void 0;
|
|
1505
1859
|
const orgId = orgData === void 0 ? void 0 : stringValue(orgData.id);
|
|
@@ -1549,7 +1903,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1549
1903
|
if (forced === "cli" || forced === "openai") return forced;
|
|
1550
1904
|
const cached = this.cachedProtocolUseCli(apiKey);
|
|
1551
1905
|
if (cached !== void 0) return cached ? "cli" : "openai";
|
|
1552
|
-
if (this.cachedBillingTierWeight(apiKey) ===
|
|
1906
|
+
if (this.cachedBillingTierWeight(apiKey) === GO_TIER_WEIGHT) {
|
|
1553
1907
|
this.rememberProtocol(apiKey, true);
|
|
1554
1908
|
return "cli";
|
|
1555
1909
|
}
|
|
@@ -1572,17 +1926,13 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1572
1926
|
const failedStatuses = [];
|
|
1573
1927
|
const getJson = async (path) => {
|
|
1574
1928
|
try {
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
if (!response.ok) {
|
|
1580
|
-
failures.push(`${path}: HTTP ${response.status}`);
|
|
1581
|
-
failedStatuses.push(response.status);
|
|
1929
|
+
const { status, record } = await this.fetchEndpointJson(`${base}${path}`, headers);
|
|
1930
|
+
if (record === void 0) {
|
|
1931
|
+
failures.push(`${path}: HTTP ${status}`);
|
|
1932
|
+
failedStatuses.push(status);
|
|
1582
1933
|
return;
|
|
1583
1934
|
}
|
|
1584
|
-
|
|
1585
|
-
return isRecord(parsed) ? parsed : void 0;
|
|
1935
|
+
return record;
|
|
1586
1936
|
} catch (error) {
|
|
1587
1937
|
failures.push(`${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1588
1938
|
failedStatuses.push(void 0);
|
|
@@ -1590,51 +1940,19 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1590
1940
|
}
|
|
1591
1941
|
};
|
|
1592
1942
|
const report = { failures };
|
|
1593
|
-
const
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
const
|
|
1601
|
-
|
|
1602
|
-
const
|
|
1603
|
-
if (
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
successRate: numberValue(usage.successRate) ?? 0,
|
|
1607
|
-
completedCount: numberValue(usage.completedCount) ?? 0,
|
|
1608
|
-
failedCount: numberValue(usage.failedCount) ?? 0,
|
|
1609
|
-
totalTokensIn: numberValue(usage.totalTokensIn) ?? 0,
|
|
1610
|
-
totalTokensOut: numberValue(usage.totalTokensOut) ?? 0,
|
|
1611
|
-
totalCredits: numberValue(usage.totalCredits) ?? 0,
|
|
1612
|
-
periodBasis: stringValue(usage.periodBasis) ?? "billing-period"
|
|
1613
|
-
};
|
|
1614
|
-
const credits = await getJson("/alpha/billing/credits");
|
|
1615
|
-
const creditsData = credits && isRecord(credits.credits) ? credits.credits : void 0;
|
|
1616
|
-
const windowLimits = credits && isRecord(credits.windowLimits) ? credits.windowLimits : void 0;
|
|
1617
|
-
const fiveHour = windowLimits && isRecord(windowLimits.fiveHour) ? windowLimits.fiveHour : void 0;
|
|
1618
|
-
const weekly = windowLimits && isRecord(windowLimits.weekly) ? windowLimits.weekly : void 0;
|
|
1619
|
-
if (creditsData || fiveHour || weekly) report.credits = {
|
|
1620
|
-
monthlyCredits: numberValue(creditsData?.monthlyCredits) ?? 0,
|
|
1621
|
-
purchasedCredits: numberValue(creditsData?.purchasedCredits) ?? 0,
|
|
1622
|
-
freeCredits: numberValue(creditsData?.freeCredits) ?? 0,
|
|
1623
|
-
fiveHour: {
|
|
1624
|
-
used: numberValue(fiveHour?.used) ?? 0,
|
|
1625
|
-
cap: numberValue(fiveHour?.cap) ?? 0,
|
|
1626
|
-
exceeded: fiveHour?.exceeded === true,
|
|
1627
|
-
resetAt: numberValue(fiveHour?.resetAt) ?? 0
|
|
1628
|
-
},
|
|
1629
|
-
weekly: {
|
|
1630
|
-
used: numberValue(weekly?.used) ?? 0,
|
|
1631
|
-
cap: numberValue(weekly?.cap) ?? 0,
|
|
1632
|
-
exceeded: weekly?.exceeded === true,
|
|
1633
|
-
resetAt: numberValue(weekly?.resetAt) ?? 0
|
|
1634
|
-
}
|
|
1635
|
-
};
|
|
1636
|
-
const subscription = await getJson(orgId === void 0 ? "/alpha/billing/subscriptions" : `/alpha/billing/subscriptions?orgId=${encodeURIComponent(orgId)}`);
|
|
1637
|
-
const subData = subscription && isRecord(subscription.data) ? subscription.data : void 0;
|
|
1943
|
+
const { account, orgId } = parseAccountIdentity(await getJson("/alpha/whoami"));
|
|
1944
|
+
if (account !== void 0) report.account = account;
|
|
1945
|
+
const [usage, credits, subscription] = await Promise.all([
|
|
1946
|
+
getJson("/alpha/usage/summary"),
|
|
1947
|
+
getJson("/alpha/billing/credits"),
|
|
1948
|
+
getJson(orgId === void 0 ? "/alpha/billing/subscriptions" : `/alpha/billing/subscriptions?orgId=${encodeURIComponent(orgId)}`)
|
|
1949
|
+
]);
|
|
1950
|
+
const totals = parseUsageTotals(usage);
|
|
1951
|
+
if (totals !== void 0) report.usage = totals;
|
|
1952
|
+
const limits = parseCreditLimits(credits);
|
|
1953
|
+
if (limits !== void 0) report.credits = limits;
|
|
1954
|
+
const subData = subscription !== void 0 && isRecord(subscription.data) ? subscription.data : void 0;
|
|
1955
|
+
const creditsData = credits !== void 0 && isRecord(credits.credits) ? credits.credits : void 0;
|
|
1638
1956
|
const planId = stringValue(subData?.planId) ?? stringValue(creditsData?.planId);
|
|
1639
1957
|
if (subData !== void 0 || planId !== void 0) {
|
|
1640
1958
|
const info = planId === void 0 ? void 0 : subscriptionPlanInfo(planId);
|
|
@@ -1646,12 +1964,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1646
1964
|
currentPeriodEnd: periodEndValue(subData?.currentPeriodEnd)
|
|
1647
1965
|
};
|
|
1648
1966
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code === 401)) report.blocked = "invalid-key";
|
|
1652
|
-
else if (codes.length === USAGE_ENDPOINT_COUNT && codes.every((code) => code >= 500)) report.blocked = "service-unavailable";
|
|
1653
|
-
else if (codes.length === 0) report.blocked = "network";
|
|
1654
|
-
}
|
|
1967
|
+
const blocked = classifyTotalFailure(failures, failedStatuses);
|
|
1968
|
+
if (blocked !== void 0) report.blocked = blocked;
|
|
1655
1969
|
return report;
|
|
1656
1970
|
}
|
|
1657
1971
|
/**
|
|
@@ -1702,134 +2016,28 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1702
2016
|
const reasoningEffort = effort && effort !== "off" && supported?.includes(effort) ? effort : void 0;
|
|
1703
2017
|
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");
|
|
1704
2018
|
let protocol = this.resolveProtocol(apiKey);
|
|
1705
|
-
const buildBody = async (target) => {
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
recentCommits: []
|
|
1717
|
-
},
|
|
1718
|
-
memory: null,
|
|
1719
|
-
taste: null,
|
|
1720
|
-
skills: null,
|
|
1721
|
-
params: {
|
|
1722
|
-
model: options.model,
|
|
1723
|
-
messages: await messagesToCC(options.messages, readImage),
|
|
1724
|
-
tools: (options.tools ?? []).map((tool) => ({
|
|
1725
|
-
type: "function",
|
|
1726
|
-
name: tool.name,
|
|
1727
|
-
description: tool.description,
|
|
1728
|
-
input_schema: tool.parameters
|
|
1729
|
-
})),
|
|
1730
|
-
system: systemText,
|
|
1731
|
-
max_tokens: maxTokens,
|
|
1732
|
-
temperature: options.temperature ?? .3,
|
|
1733
|
-
stream: true,
|
|
1734
|
-
...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
|
|
1735
|
-
},
|
|
1736
|
-
threadId: randomUUID()
|
|
1737
|
-
};
|
|
1738
|
-
const openAiMessages = [...systemText ? [{
|
|
1739
|
-
role: "system",
|
|
1740
|
-
content: systemText
|
|
1741
|
-
}] : [], ...await messagesToOpenAI(options.messages, readImage)];
|
|
1742
|
-
const openAiTools = (options.tools ?? []).map((tool) => ({
|
|
1743
|
-
type: "function",
|
|
1744
|
-
function: {
|
|
1745
|
-
name: tool.name,
|
|
1746
|
-
description: tool.description,
|
|
1747
|
-
parameters: tool.parameters
|
|
1748
|
-
}
|
|
1749
|
-
}));
|
|
1750
|
-
return {
|
|
1751
|
-
model: options.model,
|
|
1752
|
-
messages: openAiMessages,
|
|
1753
|
-
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
1754
|
-
max_tokens: maxTokens,
|
|
1755
|
-
temperature: options.temperature ?? .3,
|
|
1756
|
-
stream: true,
|
|
1757
|
-
...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
|
|
1758
|
-
};
|
|
1759
|
-
};
|
|
2019
|
+
const buildBody = async (target) => target === "cli" ? buildCliBody(options, connection, {
|
|
2020
|
+
maxTokens,
|
|
2021
|
+
reasoningEffort,
|
|
2022
|
+
systemText,
|
|
2023
|
+
readImage
|
|
2024
|
+
}) : buildOpenAIBody(options, {
|
|
2025
|
+
maxTokens,
|
|
2026
|
+
reasoningEffort,
|
|
2027
|
+
systemText,
|
|
2028
|
+
readImage
|
|
2029
|
+
});
|
|
1760
2030
|
let body = await buildBody(protocol);
|
|
1761
|
-
const connect = async (key) => {
|
|
1762
|
-
const connectAbort = new AbortController();
|
|
1763
|
-
let connectTimedOut = false;
|
|
1764
|
-
const endpoint = protocol === "cli" ? `${connection.apiBase}/alpha/generate` : `${connection.apiBase}/provider/v1/chat/completions`;
|
|
1765
|
-
const connectTimer = setTimeout(() => {
|
|
1766
|
-
connectTimedOut = true;
|
|
1767
|
-
connectAbort.abort(new DOMException(`Command Code API request to ${endpoint} did not respond within ${connection.requestTimeoutMs}ms`, "TimeoutError"));
|
|
1768
|
-
}, connection.requestTimeoutMs);
|
|
1769
|
-
const onCallerAbort = () => {
|
|
1770
|
-
connectAbort.abort(options.signal?.reason);
|
|
1771
|
-
};
|
|
1772
|
-
if (options.signal) {
|
|
1773
|
-
if (options.signal.aborted) onCallerAbort();
|
|
1774
|
-
else options.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
1775
|
-
}
|
|
1776
|
-
const cleanup = () => {
|
|
1777
|
-
clearTimeout(connectTimer);
|
|
1778
|
-
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
1779
|
-
};
|
|
1780
|
-
let response;
|
|
1781
|
-
const headers = protocol === "cli" ? {
|
|
1782
|
-
"Content-Type": "application/json",
|
|
1783
|
-
Authorization: `Bearer ${key}`,
|
|
1784
|
-
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
1785
|
-
"x-cli-environment": "production",
|
|
1786
|
-
"x-project-slug": projectSlugFromPath(connection.workingDir),
|
|
1787
|
-
"x-taste-learning": "true",
|
|
1788
|
-
"x-co-flag": "false",
|
|
1789
|
-
...attributionHeaders()
|
|
1790
|
-
} : {
|
|
1791
|
-
"Content-Type": "application/json",
|
|
1792
|
-
Authorization: `Bearer ${key}`,
|
|
1793
|
-
Accept: "text/event-stream",
|
|
1794
|
-
...attributionHeaders()
|
|
1795
|
-
};
|
|
1796
|
-
try {
|
|
1797
|
-
response = await this.fetchImpl(endpoint, {
|
|
1798
|
-
method: "POST",
|
|
1799
|
-
headers,
|
|
1800
|
-
body: JSON.stringify(body),
|
|
1801
|
-
signal: connectAbort.signal
|
|
1802
|
-
});
|
|
1803
|
-
clearTimeout(connectTimer);
|
|
1804
|
-
} catch (error) {
|
|
1805
|
-
cleanup();
|
|
1806
|
-
if (options.signal?.aborted) throw error;
|
|
1807
|
-
if (connectTimedOut || error instanceof DOMException && error.name === "TimeoutError") throw new LlmError(`Command Code API request to ${endpoint} did not respond within ${connection.requestTimeoutMs}ms: ${errorChain(error)};Command Code API 请求在 ${connection.requestTimeoutMs} 毫秒内未收到响应——通常是网络或代理问题,请检查后重试`, "TIMEOUT", { cause: error });
|
|
1808
|
-
throw new LlmError(`Command Code API request to ${endpoint} failed: ${errorChain(error)};Command Code API 请求连接失败——通常是网络或代理问题,请检查网络或代理设置后重试`, "TRANSPORT", { cause: error });
|
|
1809
|
-
}
|
|
1810
|
-
if (!response.ok) {
|
|
1811
|
-
const errText = await response.text().catch(() => "");
|
|
1812
|
-
cleanup();
|
|
1813
|
-
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
1814
|
-
return retryAfterMs === void 0 ? {
|
|
1815
|
-
status: response.status,
|
|
1816
|
-
errText
|
|
1817
|
-
} : {
|
|
1818
|
-
status: response.status,
|
|
1819
|
-
errText,
|
|
1820
|
-
retryAfterMs
|
|
1821
|
-
};
|
|
1822
|
-
}
|
|
1823
|
-
return {
|
|
1824
|
-
response,
|
|
1825
|
-
cleanup
|
|
1826
|
-
};
|
|
1827
|
-
};
|
|
1828
2031
|
const tried = /* @__PURE__ */ new Set();
|
|
2032
|
+
const connectDeps = {
|
|
2033
|
+
options,
|
|
2034
|
+
connection,
|
|
2035
|
+
fetchImpl: this.fetchImpl
|
|
2036
|
+
};
|
|
1829
2037
|
let connected;
|
|
1830
2038
|
for (;;) {
|
|
1831
2039
|
tried.add(apiKey);
|
|
1832
|
-
const attempt = await
|
|
2040
|
+
const attempt = await connectGenerate(connectDeps, apiKey, protocol, body);
|
|
1833
2041
|
if ("response" in attempt) {
|
|
1834
2042
|
connected = attempt;
|
|
1835
2043
|
break;
|
|
@@ -1850,6 +2058,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1850
2058
|
}
|
|
1851
2059
|
throw generateHttpError(attempt.status, attempt.errText, attempt.retryAfterMs);
|
|
1852
2060
|
}
|
|
2061
|
+
if (connected === void 0) throw new LlmError("Command Code API connection failed without a response", "TRANSPORT");
|
|
1853
2062
|
const { response, cleanup } = connected;
|
|
1854
2063
|
if (!response.body) {
|
|
1855
2064
|
cleanup();
|
|
@@ -1873,276 +2082,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1873
2082
|
idleTimer = void 0;
|
|
1874
2083
|
}
|
|
1875
2084
|
};
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
let textContent = "";
|
|
1879
|
-
let reasoningIndex = -1;
|
|
1880
|
-
let reasoningContent = "";
|
|
1881
|
-
let sawContent = false;
|
|
1882
|
-
const closeText = function* () {
|
|
1883
|
-
if (textIndex < 0) return;
|
|
1884
|
-
yield {
|
|
1885
|
-
type: "block-end",
|
|
1886
|
-
index: textIndex,
|
|
1887
|
-
block: {
|
|
1888
|
-
type: "text",
|
|
1889
|
-
text: textContent
|
|
1890
|
-
}
|
|
1891
|
-
};
|
|
1892
|
-
textIndex = -1;
|
|
1893
|
-
textContent = "";
|
|
1894
|
-
};
|
|
1895
|
-
const closeReasoning = function* () {
|
|
1896
|
-
if (reasoningIndex < 0) return;
|
|
1897
|
-
yield {
|
|
1898
|
-
type: "block-end",
|
|
1899
|
-
index: reasoningIndex,
|
|
1900
|
-
block: {
|
|
1901
|
-
type: "reasoning",
|
|
1902
|
-
text: reasoningContent
|
|
1903
|
-
}
|
|
1904
|
-
};
|
|
1905
|
-
reasoningIndex = -1;
|
|
1906
|
-
reasoningContent = "";
|
|
1907
|
-
};
|
|
1908
|
-
const handleCliEvent = (event) => {
|
|
1909
|
-
const chunks = [];
|
|
1910
|
-
if (!isRecord(event)) return chunks;
|
|
1911
|
-
switch (event.type) {
|
|
1912
|
-
case "text-delta": {
|
|
1913
|
-
chunks.push(...closeReasoning());
|
|
1914
|
-
if (textIndex < 0) {
|
|
1915
|
-
textIndex = nextIndex++;
|
|
1916
|
-
chunks.push({
|
|
1917
|
-
type: "block-start",
|
|
1918
|
-
index: textIndex,
|
|
1919
|
-
blockType: "text"
|
|
1920
|
-
});
|
|
1921
|
-
}
|
|
1922
|
-
const delta = stringValue(event.text) ?? "";
|
|
1923
|
-
textContent += delta;
|
|
1924
|
-
sawContent = true;
|
|
1925
|
-
chunks.push({
|
|
1926
|
-
type: "text-delta",
|
|
1927
|
-
index: textIndex,
|
|
1928
|
-
text: delta
|
|
1929
|
-
});
|
|
1930
|
-
break;
|
|
1931
|
-
}
|
|
1932
|
-
case "reasoning-delta": {
|
|
1933
|
-
chunks.push(...closeText());
|
|
1934
|
-
if (reasoningIndex < 0) {
|
|
1935
|
-
reasoningIndex = nextIndex++;
|
|
1936
|
-
chunks.push({
|
|
1937
|
-
type: "block-start",
|
|
1938
|
-
index: reasoningIndex,
|
|
1939
|
-
blockType: "reasoning"
|
|
1940
|
-
});
|
|
1941
|
-
}
|
|
1942
|
-
const delta = stringValue(event.text) ?? "";
|
|
1943
|
-
reasoningContent += delta;
|
|
1944
|
-
chunks.push({
|
|
1945
|
-
type: "reasoning-delta",
|
|
1946
|
-
index: reasoningIndex,
|
|
1947
|
-
text: delta
|
|
1948
|
-
});
|
|
1949
|
-
break;
|
|
1950
|
-
}
|
|
1951
|
-
case "reasoning-start":
|
|
1952
|
-
chunks.push(...closeText());
|
|
1953
|
-
break;
|
|
1954
|
-
case "reasoning-end":
|
|
1955
|
-
chunks.push(...closeReasoning());
|
|
1956
|
-
break;
|
|
1957
|
-
case "tool-call": {
|
|
1958
|
-
chunks.push(...closeText(), ...closeReasoning());
|
|
1959
|
-
const id = stringValue(event.toolCallId) ?? randomUUID();
|
|
1960
|
-
const name = stringValue(event.toolName) ?? "";
|
|
1961
|
-
const args = JSON.stringify(recordOrEmpty(event.input ?? event.args ?? event.arguments));
|
|
1962
|
-
const index = nextIndex++;
|
|
1963
|
-
sawContent = true;
|
|
1964
|
-
chunks.push({
|
|
1965
|
-
type: "block-start",
|
|
1966
|
-
index,
|
|
1967
|
-
blockType: "tool-call"
|
|
1968
|
-
}, {
|
|
1969
|
-
type: "tool-call-delta",
|
|
1970
|
-
index,
|
|
1971
|
-
id: ToolCallId(id),
|
|
1972
|
-
name,
|
|
1973
|
-
argumentsDelta: args
|
|
1974
|
-
}, {
|
|
1975
|
-
type: "block-end",
|
|
1976
|
-
index,
|
|
1977
|
-
block: {
|
|
1978
|
-
type: "tool-call",
|
|
1979
|
-
id: ToolCallId(id),
|
|
1980
|
-
name,
|
|
1981
|
-
arguments: args
|
|
1982
|
-
}
|
|
1983
|
-
});
|
|
1984
|
-
break;
|
|
1985
|
-
}
|
|
1986
|
-
case "finish": {
|
|
1987
|
-
chunks.push(...closeText(), ...closeReasoning());
|
|
1988
|
-
const usage = isRecord(event.totalUsage) ? event.totalUsage : void 0;
|
|
1989
|
-
if (usage) {
|
|
1990
|
-
const details = isRecord(usage.inputTokenDetails) ? usage.inputTokenDetails : void 0;
|
|
1991
|
-
const totalInput = numberValue(usage.inputTokens) ?? 0;
|
|
1992
|
-
const cacheRead = numberValue(details?.cacheReadTokens) ?? 0;
|
|
1993
|
-
const cacheWrite = numberValue(details?.cacheWriteTokens) ?? 0;
|
|
1994
|
-
const tokenUsage = {
|
|
1995
|
-
inputTokens: numberValue(details?.noCacheTokens) ?? Math.max(0, totalInput - cacheRead - cacheWrite),
|
|
1996
|
-
outputTokens: numberValue(usage.outputTokens) ?? 0,
|
|
1997
|
-
cacheReadTokens: cacheRead,
|
|
1998
|
-
cacheWriteTokens: cacheWrite
|
|
1999
|
-
};
|
|
2000
|
-
chunks.push({
|
|
2001
|
-
type: "usage",
|
|
2002
|
-
usage: tokenUsage
|
|
2003
|
-
});
|
|
2004
|
-
}
|
|
2005
|
-
chunks.push({
|
|
2006
|
-
type: "finish",
|
|
2007
|
-
reason: mapFinishReason(event.finishReason)
|
|
2008
|
-
});
|
|
2009
|
-
break;
|
|
2010
|
-
}
|
|
2011
|
-
case "error": {
|
|
2012
|
-
const err = isRecord(event.error) ? event.error : void 0;
|
|
2013
|
-
const detail = isRecord(event.error) ? stringValue(event.error.message) ?? JSON.stringify(event.error) : stringValue(event.error) ?? stringValue(event.message) ?? "Stream error";
|
|
2014
|
-
const statusCode = err ? numberValue(err.statusCode) : void 0;
|
|
2015
|
-
const isRetryable = err ? booleanValue(err.isRetryable) : void 0;
|
|
2016
|
-
const retryableStatus = statusCode !== void 0 && (statusCode === 429 || statusCode >= 500);
|
|
2017
|
-
const terminal = hasTerminalStreamMarker(detail);
|
|
2018
|
-
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);
|
|
2019
|
-
throw new LlmError(`Command Code stream error: ${detail}`, "SERVER", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
return chunks;
|
|
2023
|
-
};
|
|
2024
|
-
const openAiToolCalls = [];
|
|
2025
|
-
const emitOpenAiToolCalls = function* () {
|
|
2026
|
-
for (const call of openAiToolCalls) {
|
|
2027
|
-
const id = call.id ?? randomUUID();
|
|
2028
|
-
const name = call.name ?? "";
|
|
2029
|
-
const args = call.arguments || "{}";
|
|
2030
|
-
const index = nextIndex++;
|
|
2031
|
-
sawContent = true;
|
|
2032
|
-
yield {
|
|
2033
|
-
type: "block-start",
|
|
2034
|
-
index,
|
|
2035
|
-
blockType: "tool-call"
|
|
2036
|
-
};
|
|
2037
|
-
yield {
|
|
2038
|
-
type: "tool-call-delta",
|
|
2039
|
-
index,
|
|
2040
|
-
id: ToolCallId(id),
|
|
2041
|
-
name,
|
|
2042
|
-
argumentsDelta: args
|
|
2043
|
-
};
|
|
2044
|
-
yield {
|
|
2045
|
-
type: "block-end",
|
|
2046
|
-
index,
|
|
2047
|
-
block: {
|
|
2048
|
-
type: "tool-call",
|
|
2049
|
-
id: ToolCallId(id),
|
|
2050
|
-
name,
|
|
2051
|
-
arguments: args
|
|
2052
|
-
}
|
|
2053
|
-
};
|
|
2054
|
-
}
|
|
2055
|
-
openAiToolCalls.length = 0;
|
|
2056
|
-
};
|
|
2057
|
-
const handleOpenAIEvent = (event) => {
|
|
2058
|
-
const chunks = [];
|
|
2059
|
-
if (!isRecord(event)) return chunks;
|
|
2060
|
-
const choices = event.choices;
|
|
2061
|
-
if (!Array.isArray(choices) || choices.length === 0) {
|
|
2062
|
-
if (event.usage !== void 0) chunks.push({
|
|
2063
|
-
type: "usage",
|
|
2064
|
-
usage: mapOpenAIUsage(event.usage)
|
|
2065
|
-
});
|
|
2066
|
-
return chunks;
|
|
2067
|
-
}
|
|
2068
|
-
const choice = isRecord(choices[0]) ? choices[0] : {};
|
|
2069
|
-
const delta = isRecord(choice.delta) ? choice.delta : {};
|
|
2070
|
-
const reasoningDelta = stringValue(delta.reasoning) ?? stringValue(delta.reasoning_content) ?? "";
|
|
2071
|
-
if (reasoningDelta !== "") {
|
|
2072
|
-
chunks.push(...closeText());
|
|
2073
|
-
if (reasoningIndex < 0) {
|
|
2074
|
-
reasoningIndex = nextIndex++;
|
|
2075
|
-
chunks.push({
|
|
2076
|
-
type: "block-start",
|
|
2077
|
-
index: reasoningIndex,
|
|
2078
|
-
blockType: "reasoning"
|
|
2079
|
-
});
|
|
2080
|
-
}
|
|
2081
|
-
reasoningContent += reasoningDelta;
|
|
2082
|
-
chunks.push({
|
|
2083
|
-
type: "reasoning-delta",
|
|
2084
|
-
index: reasoningIndex,
|
|
2085
|
-
text: reasoningDelta
|
|
2086
|
-
});
|
|
2087
|
-
}
|
|
2088
|
-
const contentDelta = stringValue(delta.content) ?? "";
|
|
2089
|
-
if (contentDelta !== "") {
|
|
2090
|
-
chunks.push(...closeReasoning());
|
|
2091
|
-
if (textIndex < 0) {
|
|
2092
|
-
textIndex = nextIndex++;
|
|
2093
|
-
chunks.push({
|
|
2094
|
-
type: "block-start",
|
|
2095
|
-
index: textIndex,
|
|
2096
|
-
blockType: "text"
|
|
2097
|
-
});
|
|
2098
|
-
}
|
|
2099
|
-
textContent += contentDelta;
|
|
2100
|
-
sawContent = true;
|
|
2101
|
-
chunks.push({
|
|
2102
|
-
type: "text-delta",
|
|
2103
|
-
index: textIndex,
|
|
2104
|
-
text: contentDelta
|
|
2105
|
-
});
|
|
2106
|
-
}
|
|
2107
|
-
if (Array.isArray(delta.tool_calls)) {
|
|
2108
|
-
sawContent = true;
|
|
2109
|
-
for (const rawCall of delta.tool_calls) {
|
|
2110
|
-
if (!isRecord(rawCall)) continue;
|
|
2111
|
-
const callIndex = numberValue(rawCall.index) ?? 0;
|
|
2112
|
-
let existing = openAiToolCalls.find((call) => call.index === callIndex);
|
|
2113
|
-
const fn = isRecord(rawCall.function) ? rawCall.function : void 0;
|
|
2114
|
-
const id = stringValue(rawCall.id);
|
|
2115
|
-
const name = fn === void 0 ? void 0 : stringValue(fn.name);
|
|
2116
|
-
const argDelta = fn === void 0 ? void 0 : stringValue(fn.arguments) ?? (fn.arguments === void 0 ? "" : JSON.stringify(fn.arguments));
|
|
2117
|
-
if (!existing) {
|
|
2118
|
-
existing = {
|
|
2119
|
-
index: callIndex,
|
|
2120
|
-
name: name ?? "",
|
|
2121
|
-
arguments: argDelta ?? ""
|
|
2122
|
-
};
|
|
2123
|
-
if (id !== void 0) existing.id = id;
|
|
2124
|
-
openAiToolCalls.push(existing);
|
|
2125
|
-
} else {
|
|
2126
|
-
if (id !== void 0 && existing.id === void 0) existing.id = id;
|
|
2127
|
-
if (name !== void 0 && existing.name === "") existing.name = name;
|
|
2128
|
-
if (argDelta !== void 0) existing.arguments += argDelta;
|
|
2129
|
-
}
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
if (choice.finish_reason !== void 0 && choice.finish_reason !== null) {
|
|
2133
|
-
chunks.push(...closeText(), ...closeReasoning(), ...emitOpenAiToolCalls());
|
|
2134
|
-
if (event.usage !== void 0) chunks.push({
|
|
2135
|
-
type: "usage",
|
|
2136
|
-
usage: mapOpenAIUsage(event.usage)
|
|
2137
|
-
});
|
|
2138
|
-
chunks.push({
|
|
2139
|
-
type: "finish",
|
|
2140
|
-
reason: mapOpenAIFinishReason(choice.finish_reason)
|
|
2141
|
-
});
|
|
2142
|
-
}
|
|
2143
|
-
return chunks;
|
|
2144
|
-
};
|
|
2145
|
-
const handleEvent = (event) => protocol === "openai" ? handleOpenAIEvent(event) : handleCliEvent(event);
|
|
2085
|
+
const asm = createBlockAssembler();
|
|
2086
|
+
const handle = (event) => handleEvent(asm, protocol, event);
|
|
2146
2087
|
try {
|
|
2147
2088
|
let finished = false;
|
|
2148
2089
|
for (;;) {
|
|
@@ -2159,14 +2100,17 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
2159
2100
|
const { done, value } = read;
|
|
2160
2101
|
if (done) {
|
|
2161
2102
|
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");
|
|
2162
|
-
if (buffer.trim()) for (const chunk of
|
|
2103
|
+
if (buffer.trim()) for (const chunk of handle(parseStreamEventLine(buffer))) {
|
|
2104
|
+
yield chunk;
|
|
2105
|
+
if (chunk.type === "finish") finished = true;
|
|
2106
|
+
}
|
|
2163
2107
|
break;
|
|
2164
2108
|
}
|
|
2165
2109
|
buffer += decoder.decode(value, { stream: true });
|
|
2166
2110
|
const lines = buffer.split("\n");
|
|
2167
2111
|
buffer = lines.pop() ?? "";
|
|
2168
2112
|
for (const line of lines) {
|
|
2169
|
-
const chunks =
|
|
2113
|
+
const chunks = handle(parseStreamEventLine(line));
|
|
2170
2114
|
for (const chunk of chunks) {
|
|
2171
2115
|
yield chunk;
|
|
2172
2116
|
if (chunk.type === "finish") finished = true;
|
|
@@ -2175,10 +2119,10 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
2175
2119
|
if (finished) break;
|
|
2176
2120
|
}
|
|
2177
2121
|
if (!finished) {
|
|
2178
|
-
yield* closeText();
|
|
2179
|
-
yield* closeReasoning();
|
|
2180
|
-
if (protocol === "openai") yield* emitOpenAiToolCalls();
|
|
2181
|
-
if (!sawContent) throw new LlmError("Command Code returned an empty response;Command Code 返回了空响应,重试通常可恢复", "EMPTY_RESPONSE");
|
|
2122
|
+
yield* closeText(asm);
|
|
2123
|
+
yield* closeReasoning(asm);
|
|
2124
|
+
if (protocol === "openai") yield* emitOpenAiToolCalls(asm);
|
|
2125
|
+
if (!asm.sawContent) throw new LlmError("Command Code returned an empty response;Command Code 返回了空响应,重试通常可恢复", "EMPTY_RESPONSE");
|
|
2182
2126
|
yield {
|
|
2183
2127
|
type: "finish",
|
|
2184
2128
|
reason: { kind: "stop" }
|
|
@@ -2192,8 +2136,6 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
2192
2136
|
}
|
|
2193
2137
|
}
|
|
2194
2138
|
};
|
|
2195
|
-
/** Hard cap on account rotations within one request (one attempt per distinct key). */
|
|
2196
|
-
const MAX_ACCOUNT_ROTATIONS = 16;
|
|
2197
2139
|
/**
|
|
2198
2140
|
* Map a pre-stream generate HTTP failure onto a stable LlmError. Command
|
|
2199
2141
|
* Code folds several business rejections into 403 (plan limits, CLI version,
|
|
@@ -2218,6 +2160,223 @@ function generateHttpError(status, errText, retryAfterMs) {
|
|
|
2218
2160
|
});
|
|
2219
2161
|
}
|
|
2220
2162
|
/**
|
|
2163
|
+
* Handle one CLI-transport (`/alpha/generate`) stream event, appending
|
|
2164
|
+
* harness StreamChunks. Pure over the passed assembler — no stream() locals.
|
|
2165
|
+
*/
|
|
2166
|
+
function handleCliEvent(asm, event) {
|
|
2167
|
+
const chunks = [];
|
|
2168
|
+
if (!isRecord(event)) return chunks;
|
|
2169
|
+
switch (event.type) {
|
|
2170
|
+
case "text-delta": {
|
|
2171
|
+
chunks.push(...closeReasoning(asm));
|
|
2172
|
+
if (asm.textIndex < 0) {
|
|
2173
|
+
asm.textIndex = asm.nextIndex++;
|
|
2174
|
+
chunks.push({
|
|
2175
|
+
type: "block-start",
|
|
2176
|
+
index: asm.textIndex,
|
|
2177
|
+
blockType: "text"
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
const delta = stringValue(event.text) ?? "";
|
|
2181
|
+
asm.textContent += delta;
|
|
2182
|
+
asm.sawContent = true;
|
|
2183
|
+
chunks.push({
|
|
2184
|
+
type: "text-delta",
|
|
2185
|
+
index: asm.textIndex,
|
|
2186
|
+
text: delta
|
|
2187
|
+
});
|
|
2188
|
+
break;
|
|
2189
|
+
}
|
|
2190
|
+
case "reasoning-delta": {
|
|
2191
|
+
chunks.push(...closeText(asm));
|
|
2192
|
+
if (asm.reasoningIndex < 0) {
|
|
2193
|
+
asm.reasoningIndex = asm.nextIndex++;
|
|
2194
|
+
chunks.push({
|
|
2195
|
+
type: "block-start",
|
|
2196
|
+
index: asm.reasoningIndex,
|
|
2197
|
+
blockType: "reasoning"
|
|
2198
|
+
});
|
|
2199
|
+
}
|
|
2200
|
+
const delta = stringValue(event.text) ?? "";
|
|
2201
|
+
asm.reasoningContent += delta;
|
|
2202
|
+
chunks.push({
|
|
2203
|
+
type: "reasoning-delta",
|
|
2204
|
+
index: asm.reasoningIndex,
|
|
2205
|
+
text: delta
|
|
2206
|
+
});
|
|
2207
|
+
break;
|
|
2208
|
+
}
|
|
2209
|
+
case "reasoning-start":
|
|
2210
|
+
chunks.push(...closeText(asm));
|
|
2211
|
+
break;
|
|
2212
|
+
case "reasoning-end":
|
|
2213
|
+
chunks.push(...closeReasoning(asm));
|
|
2214
|
+
break;
|
|
2215
|
+
case "tool-call": {
|
|
2216
|
+
chunks.push(...closeText(asm), ...closeReasoning(asm));
|
|
2217
|
+
const id = stringValue(event.toolCallId) ?? randomUUID();
|
|
2218
|
+
const name = stringValue(event.toolName) ?? "";
|
|
2219
|
+
const args = JSON.stringify(recordOrEmpty(event.input ?? event.args ?? event.arguments));
|
|
2220
|
+
const index = asm.nextIndex++;
|
|
2221
|
+
asm.sawContent = true;
|
|
2222
|
+
chunks.push({
|
|
2223
|
+
type: "block-start",
|
|
2224
|
+
index,
|
|
2225
|
+
blockType: "tool-call"
|
|
2226
|
+
}, {
|
|
2227
|
+
type: "tool-call-delta",
|
|
2228
|
+
index,
|
|
2229
|
+
id: ToolCallId(id),
|
|
2230
|
+
name,
|
|
2231
|
+
argumentsDelta: args
|
|
2232
|
+
}, {
|
|
2233
|
+
type: "block-end",
|
|
2234
|
+
index,
|
|
2235
|
+
block: {
|
|
2236
|
+
type: "tool-call",
|
|
2237
|
+
id: ToolCallId(id),
|
|
2238
|
+
name,
|
|
2239
|
+
arguments: args
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
break;
|
|
2243
|
+
}
|
|
2244
|
+
case "finish": {
|
|
2245
|
+
chunks.push(...closeText(asm), ...closeReasoning(asm));
|
|
2246
|
+
const usage = isRecord(event.totalUsage) ? event.totalUsage : void 0;
|
|
2247
|
+
if (usage) {
|
|
2248
|
+
const details = isRecord(usage.inputTokenDetails) ? usage.inputTokenDetails : void 0;
|
|
2249
|
+
const totalInput = numberValue(usage.inputTokens) ?? 0;
|
|
2250
|
+
const cacheRead = numberValue(details?.cacheReadTokens) ?? 0;
|
|
2251
|
+
const cacheWrite = numberValue(details?.cacheWriteTokens) ?? 0;
|
|
2252
|
+
const tokenUsage = {
|
|
2253
|
+
inputTokens: numberValue(details?.noCacheTokens) ?? Math.max(0, totalInput - cacheRead - cacheWrite),
|
|
2254
|
+
outputTokens: numberValue(usage.outputTokens) ?? 0,
|
|
2255
|
+
cacheReadTokens: cacheRead,
|
|
2256
|
+
cacheWriteTokens: cacheWrite
|
|
2257
|
+
};
|
|
2258
|
+
chunks.push({
|
|
2259
|
+
type: "usage",
|
|
2260
|
+
usage: tokenUsage
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
chunks.push({
|
|
2264
|
+
type: "finish",
|
|
2265
|
+
reason: mapFinishReason(event.finishReason)
|
|
2266
|
+
});
|
|
2267
|
+
break;
|
|
2268
|
+
}
|
|
2269
|
+
case "error": {
|
|
2270
|
+
const err = isRecord(event.error) ? event.error : void 0;
|
|
2271
|
+
const detail = isRecord(event.error) ? stringValue(event.error.message) ?? JSON.stringify(event.error) : stringValue(event.error) ?? stringValue(event.message) ?? "Stream error";
|
|
2272
|
+
const statusCode = err ? numberValue(err.statusCode) : void 0;
|
|
2273
|
+
const isRetryable = err ? booleanValue(err.isRetryable) : void 0;
|
|
2274
|
+
const retryableStatus = statusCode !== void 0 && (statusCode === 429 || statusCode >= 500);
|
|
2275
|
+
const terminal = hasTerminalStreamMarker(detail);
|
|
2276
|
+
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);
|
|
2277
|
+
throw new LlmError(`Command Code stream error: ${detail}`, "SERVER", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
return chunks;
|
|
2281
|
+
}
|
|
2282
|
+
/**
|
|
2283
|
+
* Handle one OpenAI-transport (`/provider/v1/chat/completions`) SSE event.
|
|
2284
|
+
* Same assembler contract as {@link handleCliEvent}; tool-call fragments
|
|
2285
|
+
* buffer on the assembler and flush at finish.
|
|
2286
|
+
*/
|
|
2287
|
+
function handleOpenAIEvent(asm, event) {
|
|
2288
|
+
const chunks = [];
|
|
2289
|
+
if (!isRecord(event)) return chunks;
|
|
2290
|
+
const choices = event.choices;
|
|
2291
|
+
if (!Array.isArray(choices) || choices.length === 0) {
|
|
2292
|
+
if (event.usage !== void 0) chunks.push({
|
|
2293
|
+
type: "usage",
|
|
2294
|
+
usage: mapOpenAIUsage(event.usage)
|
|
2295
|
+
});
|
|
2296
|
+
return chunks;
|
|
2297
|
+
}
|
|
2298
|
+
const choice = isRecord(choices[0]) ? choices[0] : {};
|
|
2299
|
+
const delta = isRecord(choice.delta) ? choice.delta : {};
|
|
2300
|
+
const reasoningDelta = stringValue(delta.reasoning) ?? stringValue(delta.reasoning_content) ?? "";
|
|
2301
|
+
if (reasoningDelta !== "") {
|
|
2302
|
+
chunks.push(...closeText(asm));
|
|
2303
|
+
if (asm.reasoningIndex < 0) {
|
|
2304
|
+
asm.reasoningIndex = asm.nextIndex++;
|
|
2305
|
+
chunks.push({
|
|
2306
|
+
type: "block-start",
|
|
2307
|
+
index: asm.reasoningIndex,
|
|
2308
|
+
blockType: "reasoning"
|
|
2309
|
+
});
|
|
2310
|
+
}
|
|
2311
|
+
asm.reasoningContent += reasoningDelta;
|
|
2312
|
+
chunks.push({
|
|
2313
|
+
type: "reasoning-delta",
|
|
2314
|
+
index: asm.reasoningIndex,
|
|
2315
|
+
text: reasoningDelta
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
const contentDelta = stringValue(delta.content) ?? "";
|
|
2319
|
+
if (contentDelta !== "") {
|
|
2320
|
+
chunks.push(...closeReasoning(asm));
|
|
2321
|
+
if (asm.textIndex < 0) {
|
|
2322
|
+
asm.textIndex = asm.nextIndex++;
|
|
2323
|
+
chunks.push({
|
|
2324
|
+
type: "block-start",
|
|
2325
|
+
index: asm.textIndex,
|
|
2326
|
+
blockType: "text"
|
|
2327
|
+
});
|
|
2328
|
+
}
|
|
2329
|
+
asm.textContent += contentDelta;
|
|
2330
|
+
asm.sawContent = true;
|
|
2331
|
+
chunks.push({
|
|
2332
|
+
type: "text-delta",
|
|
2333
|
+
index: asm.textIndex,
|
|
2334
|
+
text: contentDelta
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
2338
|
+
asm.sawContent = true;
|
|
2339
|
+
for (const rawCall of delta.tool_calls) {
|
|
2340
|
+
if (!isRecord(rawCall)) continue;
|
|
2341
|
+
const callIndex = numberValue(rawCall.index) ?? 0;
|
|
2342
|
+
let existing = asm.openAiToolCalls.find((call) => call.index === callIndex);
|
|
2343
|
+
const fn = isRecord(rawCall.function) ? rawCall.function : void 0;
|
|
2344
|
+
const id = stringValue(rawCall.id);
|
|
2345
|
+
const name = fn === void 0 ? void 0 : stringValue(fn.name);
|
|
2346
|
+
const argDelta = fn === void 0 ? void 0 : stringValue(fn.arguments) ?? (fn.arguments === void 0 ? "" : JSON.stringify(fn.arguments));
|
|
2347
|
+
if (!existing) {
|
|
2348
|
+
existing = {
|
|
2349
|
+
index: callIndex,
|
|
2350
|
+
name: name ?? "",
|
|
2351
|
+
arguments: argDelta ?? ""
|
|
2352
|
+
};
|
|
2353
|
+
if (id !== void 0) existing.id = id;
|
|
2354
|
+
asm.openAiToolCalls.push(existing);
|
|
2355
|
+
} else {
|
|
2356
|
+
if (id !== void 0 && existing.id === void 0) existing.id = id;
|
|
2357
|
+
if (name !== void 0 && existing.name === "") existing.name = name;
|
|
2358
|
+
if (argDelta !== void 0) existing.arguments += argDelta;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
if (choice.finish_reason !== void 0 && choice.finish_reason !== null) {
|
|
2363
|
+
chunks.push(...closeText(asm), ...closeReasoning(asm), ...emitOpenAiToolCalls(asm));
|
|
2364
|
+
if (event.usage !== void 0) chunks.push({
|
|
2365
|
+
type: "usage",
|
|
2366
|
+
usage: mapOpenAIUsage(event.usage)
|
|
2367
|
+
});
|
|
2368
|
+
chunks.push({
|
|
2369
|
+
type: "finish",
|
|
2370
|
+
reason: mapFinishReason(choice.finish_reason)
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
return chunks;
|
|
2374
|
+
}
|
|
2375
|
+
/** Dispatch one parsed stream event to the active transport's handler. */
|
|
2376
|
+
function handleEvent(asm, protocol, event) {
|
|
2377
|
+
return protocol === "openai" ? handleOpenAIEvent(asm, event) : handleCliEvent(asm, event);
|
|
2378
|
+
}
|
|
2379
|
+
/**
|
|
2221
2380
|
* Parse an HTTP `Retry-After` value (delay-seconds or an HTTP-date) into
|
|
2222
2381
|
* milliseconds; undefined when absent or unparseable. An HTTP-date in the
|
|
2223
2382
|
* past yields 0, which the caller drops (LlmError wants a positive delay).
|
|
@@ -2237,13 +2396,14 @@ function parseRetryAfterMs(value, now = Date.now()) {
|
|
|
2237
2396
|
const date = Date.parse(trimmed);
|
|
2238
2397
|
if (!Number.isNaN(date)) return Math.max(0, date - now);
|
|
2239
2398
|
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Map a stream finish reason onto the harness taxonomy. The two transports
|
|
2401
|
+
* spell tool-calls differently (`tool-calls` on the CLI transport,
|
|
2402
|
+
* `tool_calls` on the OpenAI transport) but share every other reason, so
|
|
2403
|
+
* one function serves both — a new reason cannot drift between them.
|
|
2404
|
+
*/
|
|
2240
2405
|
function mapFinishReason(reason) {
|
|
2241
|
-
if (reason === "tool-calls") return { kind: "tool-calls" };
|
|
2242
|
-
if (reason === "length" || reason === "max_tokens" || reason === "max-tokens" || reason === "max_output_tokens") return { kind: "max-tokens" };
|
|
2243
|
-
return { kind: "stop" };
|
|
2244
|
-
}
|
|
2245
|
-
function mapOpenAIFinishReason(reason) {
|
|
2246
|
-
if (reason === "tool_calls" || reason === "tool-calls") return { kind: "tool-calls" };
|
|
2406
|
+
if (reason === "tool-calls" || reason === "tool_calls") return { kind: "tool-calls" };
|
|
2247
2407
|
if (reason === "length" || reason === "max_tokens" || reason === "max-tokens" || reason === "max_output_tokens") return { kind: "max-tokens" };
|
|
2248
2408
|
return { kind: "stop" };
|
|
2249
2409
|
}
|
|
@@ -2322,8 +2482,8 @@ const commandcodeCommand = {
|
|
|
2322
2482
|
exceededWarning: " ⚠️ 超限!",
|
|
2323
2483
|
resetSuffix: "重置 {when}",
|
|
2324
2484
|
partialFailures: "⚠️ 部分端点失败: {list}",
|
|
2325
|
-
noData: "
|
|
2326
|
-
errorText: "
|
|
2485
|
+
noData: "(无数据 — 请检查 API 密钥)",
|
|
2486
|
+
errorText: "获取 Command Code 用量失败:{message}",
|
|
2327
2487
|
imageGate: "当前会话已包含图片,而模型 {model} 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。"
|
|
2328
2488
|
},
|
|
2329
2489
|
en: {
|
|
@@ -2399,6 +2559,14 @@ function tokensCompact(value) {
|
|
|
2399
2559
|
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
2400
2560
|
return String(value);
|
|
2401
2561
|
}
|
|
2562
|
+
/**
|
|
2563
|
+
* Format a success-rate percentage (already in percent units): at most two
|
|
2564
|
+
* decimals, trailing zeros trimmed — mirrors `formatSuccessRate` in
|
|
2565
|
+
* `./client/usage.ts`, which the settings card uses.
|
|
2566
|
+
*/
|
|
2567
|
+
function successRateText(value) {
|
|
2568
|
+
return String(Number(value.toFixed(2)));
|
|
2569
|
+
}
|
|
2402
2570
|
/** Format a millis timestamp as a local date; `n/a` when unset. */
|
|
2403
2571
|
function resetLabel(ms) {
|
|
2404
2572
|
if (ms <= 0) return "n/a";
|
|
@@ -2437,7 +2605,7 @@ function renderReport(report, locale, title) {
|
|
|
2437
2605
|
}
|
|
2438
2606
|
if (report.usage) {
|
|
2439
2607
|
const u = report.usage;
|
|
2440
|
-
lines.push(commandCopy(locale, "usageHeader"), commandCopy(locale, "requestsLine").replace("{n}", String(u.completedCount)).replace("{f}", String(u.failedCount)).replace("{r}",
|
|
2608
|
+
lines.push(commandCopy(locale, "usageHeader"), commandCopy(locale, "requestsLine").replace("{n}", String(u.completedCount)).replace("{f}", String(u.failedCount)).replace("{r}", successRateText(u.successRate)), commandCopy(locale, "costLine").replace("{money}", money(u.totalCost)).replace("{credits}", moneyShort(u.totalCredits)), commandCopy(locale, "tokensLine").replace("{in}", tokensCompact(u.totalTokensIn)).replace("{out}", tokensCompact(u.totalTokensOut)), "");
|
|
2441
2609
|
}
|
|
2442
2610
|
if (report.credits) {
|
|
2443
2611
|
const c = report.credits;
|
|
@@ -2497,7 +2665,7 @@ const REMOTE_SERVICE = "commandcodeUsage";
|
|
|
2497
2665
|
/** The wire namespace all Command Code endpoints share. */
|
|
2498
2666
|
const REMOTE_NAMESPACE = "commandcode";
|
|
2499
2667
|
/**
|
|
2500
|
-
* Build the validator helpers one endpoint uses
|
|
2668
|
+
* Build the validator helpers one endpoint uses. `prefix` names the
|
|
2501
2669
|
* endpoint in the rejection message (e.g. `commandcode/report result:`), so
|
|
2502
2670
|
* each wire file keeps its own diagnostic phrasing while sharing the helper
|
|
2503
2671
|
* bodies.
|
|
@@ -2553,14 +2721,13 @@ const USAGE_REMOTE_PACKAGE = REMOTE_PACKAGE;
|
|
|
2553
2721
|
/** Canonical `<namespace>/<method>` endpoint of the usage report Remote. */
|
|
2554
2722
|
const USAGE_REPORT_ENDPOINT = "commandcode/report";
|
|
2555
2723
|
/**
|
|
2556
|
-
* The shared read/validate helpers
|
|
2557
|
-
* rejection messages name the offending boundary
|
|
2558
|
-
* shares this prefix, matching the historical behavior).
|
|
2724
|
+
* The shared read/validate helpers for the usage report endpoint, prefixed
|
|
2725
|
+
* so rejection messages name the offending boundary.
|
|
2559
2726
|
*/
|
|
2560
|
-
const { reject: reject$1, record, stringField, numberField, booleanField } = makeBoundaryValidator("commandcode/report result:");
|
|
2727
|
+
const { reject: reject$1, record: record$1, stringField: stringField$1, numberField, booleanField } = makeBoundaryValidator("commandcode/report result:");
|
|
2561
2728
|
/** Validate one window-limit block (`fiveHour` / `weekly`). */
|
|
2562
2729
|
function windowLimit(value, field) {
|
|
2563
|
-
const source = record(value, field);
|
|
2730
|
+
const source = record$1(value, field);
|
|
2564
2731
|
return {
|
|
2565
2732
|
used: numberField(source, "used", `${field}.used`),
|
|
2566
2733
|
cap: numberField(source, "cap", `${field}.cap`),
|
|
@@ -2574,7 +2741,7 @@ function windowLimit(value, field) {
|
|
|
2574
2741
|
* malformed frame fails the boundary instead of rendering garbage.
|
|
2575
2742
|
*/
|
|
2576
2743
|
function parseUsageReport(value) {
|
|
2577
|
-
const source = record(value, "report");
|
|
2744
|
+
const source = record$1(value, "report");
|
|
2578
2745
|
const failures = source.failures;
|
|
2579
2746
|
if (!Array.isArray(failures) || failures.some((entry) => typeof entry !== "string")) reject$1("failures");
|
|
2580
2747
|
const report = { failures };
|
|
@@ -2584,15 +2751,15 @@ function parseUsageReport(value) {
|
|
|
2584
2751
|
else reject$1("blocked");
|
|
2585
2752
|
}
|
|
2586
2753
|
if (source.account !== void 0) {
|
|
2587
|
-
const account = record(source.account, "account");
|
|
2754
|
+
const account = record$1(source.account, "account");
|
|
2588
2755
|
report.account = {
|
|
2589
|
-
id: stringField(account, "id", "account.id"),
|
|
2590
|
-
name: stringField(account, "name", "account.name"),
|
|
2591
|
-
userName: stringField(account, "userName", "account.userName")
|
|
2756
|
+
id: stringField$1(account, "id", "account.id"),
|
|
2757
|
+
name: stringField$1(account, "name", "account.name"),
|
|
2758
|
+
userName: stringField$1(account, "userName", "account.userName")
|
|
2592
2759
|
};
|
|
2593
2760
|
}
|
|
2594
2761
|
if (source.usage !== void 0) {
|
|
2595
|
-
const usage = record(source.usage, "usage");
|
|
2762
|
+
const usage = record$1(source.usage, "usage");
|
|
2596
2763
|
report.usage = {
|
|
2597
2764
|
totalCount: numberField(usage, "totalCount", "usage.totalCount"),
|
|
2598
2765
|
totalCost: numberField(usage, "totalCost", "usage.totalCost"),
|
|
@@ -2602,11 +2769,11 @@ function parseUsageReport(value) {
|
|
|
2602
2769
|
totalTokensIn: numberField(usage, "totalTokensIn", "usage.totalTokensIn"),
|
|
2603
2770
|
totalTokensOut: numberField(usage, "totalTokensOut", "usage.totalTokensOut"),
|
|
2604
2771
|
totalCredits: numberField(usage, "totalCredits", "usage.totalCredits"),
|
|
2605
|
-
periodBasis: stringField(usage, "periodBasis", "usage.periodBasis")
|
|
2772
|
+
periodBasis: stringField$1(usage, "periodBasis", "usage.periodBasis")
|
|
2606
2773
|
};
|
|
2607
2774
|
}
|
|
2608
2775
|
if (source.credits !== void 0) {
|
|
2609
|
-
const credits = record(source.credits, "credits");
|
|
2776
|
+
const credits = record$1(source.credits, "credits");
|
|
2610
2777
|
report.credits = {
|
|
2611
2778
|
monthlyCredits: numberField(credits, "monthlyCredits", "credits.monthlyCredits"),
|
|
2612
2779
|
purchasedCredits: numberField(credits, "purchasedCredits", "credits.purchasedCredits"),
|
|
@@ -2616,13 +2783,13 @@ function parseUsageReport(value) {
|
|
|
2616
2783
|
};
|
|
2617
2784
|
}
|
|
2618
2785
|
if (source.plan !== void 0) {
|
|
2619
|
-
const plan = record(source.plan, "plan");
|
|
2786
|
+
const plan = record$1(source.plan, "plan");
|
|
2620
2787
|
const monthly = plan.monthlyCredits;
|
|
2621
2788
|
if (monthly !== null && (typeof monthly !== "number" || !Number.isFinite(monthly))) reject$1("plan.monthlyCredits");
|
|
2622
2789
|
report.plan = {
|
|
2623
|
-
planId: stringField(plan, "planId", "plan.planId"),
|
|
2624
|
-
name: stringField(plan, "name", "plan.name"),
|
|
2625
|
-
status: stringField(plan, "status", "plan.status"),
|
|
2790
|
+
planId: stringField$1(plan, "planId", "plan.planId"),
|
|
2791
|
+
name: stringField$1(plan, "name", "plan.name"),
|
|
2792
|
+
status: stringField$1(plan, "status", "plan.status"),
|
|
2626
2793
|
monthlyCredits: monthly,
|
|
2627
2794
|
currentPeriodEnd: numberField(plan, "currentPeriodEnd", "plan.currentPeriodEnd")
|
|
2628
2795
|
};
|
|
@@ -2631,20 +2798,20 @@ function parseUsageReport(value) {
|
|
|
2631
2798
|
}
|
|
2632
2799
|
/** Parse one untrusted boundary value into a {@link CommandCodeAccountUsage}. */
|
|
2633
2800
|
function parseAccountUsage(value) {
|
|
2634
|
-
const source = record(value, "account");
|
|
2801
|
+
const source = record$1(value, "account");
|
|
2635
2802
|
return {
|
|
2636
|
-
id: stringField(source, "id", "account.id"),
|
|
2637
|
-
label: stringField(source, "label", "account.label"),
|
|
2803
|
+
id: stringField$1(source, "id", "account.id"),
|
|
2804
|
+
label: stringField$1(source, "label", "account.label"),
|
|
2638
2805
|
configured: booleanField(source, "configured", "account.configured"),
|
|
2639
2806
|
active: booleanField(source, "active", "account.active"),
|
|
2640
|
-
mark: stringField(source, "mark", "account.mark"),
|
|
2807
|
+
mark: stringField$1(source, "mark", "account.mark"),
|
|
2641
2808
|
cooldownUntil: numberField(source, "cooldownUntil", "account.cooldownUntil"),
|
|
2642
2809
|
report: parseUsageReport(source.report)
|
|
2643
2810
|
};
|
|
2644
2811
|
}
|
|
2645
2812
|
/** Parse the wire result into a {@link CommandCodeAccountsReport}. */
|
|
2646
2813
|
function parseAccountsReport(value) {
|
|
2647
|
-
const accounts = record(value, "result").accounts;
|
|
2814
|
+
const accounts = record$1(value, "result").accounts;
|
|
2648
2815
|
if (Array.isArray(accounts)) return { accounts: accounts.map(parseAccountUsage) };
|
|
2649
2816
|
return reject$1("accounts");
|
|
2650
2817
|
}
|
|
@@ -2668,19 +2835,27 @@ const USAGE_HOST_CONTRIBUTION = {
|
|
|
2668
2835
|
};
|
|
2669
2836
|
/** Canonical `<namespace>/<method>` endpoint of the model-catalog Remote. */
|
|
2670
2837
|
const MODELS_ENDPOINT = "commandcode/models";
|
|
2838
|
+
/**
|
|
2839
|
+
* The shared read/validate helpers for the model-catalog endpoint — a
|
|
2840
|
+
* separate instance so catalog boundary errors name `commandcode/models`,
|
|
2841
|
+
* not the report endpoint.
|
|
2842
|
+
*/
|
|
2843
|
+
const { record: catalogRecord, stringField: catalogString } = makeBoundaryValidator("commandcode/models result:");
|
|
2671
2844
|
/** Parse one untrusted boundary value into a {@link CommandCodeCatalogModel}. */
|
|
2672
2845
|
function parseCatalogModel(value) {
|
|
2673
|
-
const source =
|
|
2674
|
-
|
|
2675
|
-
id:
|
|
2676
|
-
name:
|
|
2846
|
+
const source = catalogRecord(value, "model");
|
|
2847
|
+
const model = {
|
|
2848
|
+
id: catalogString(source, "id", "model.id"),
|
|
2849
|
+
name: catalogString(source, "name", "model.name")
|
|
2677
2850
|
};
|
|
2851
|
+
if (source.tier !== void 0) model.tier = catalogString(source, "tier", "model.tier");
|
|
2852
|
+
return model;
|
|
2678
2853
|
}
|
|
2679
2854
|
/** Parse the wire result into a {@link CommandCodeCatalog}. */
|
|
2680
2855
|
function parseCatalog(value) {
|
|
2681
|
-
const models =
|
|
2856
|
+
const models = catalogRecord(value, "result").models;
|
|
2682
2857
|
if (Array.isArray(models)) return { models: models.map(parseCatalogModel) };
|
|
2683
|
-
|
|
2858
|
+
throw new TypeError("commandcode/models result: invalid models");
|
|
2684
2859
|
}
|
|
2685
2860
|
/**
|
|
2686
2861
|
* The model-catalog invocation descriptor, sharing the same `commandcodeUsage`
|
|
@@ -2704,45 +2879,29 @@ const REASONS = [
|
|
|
2704
2879
|
];
|
|
2705
2880
|
/** The shared read/validate helpers, prefixed with the login endpoint so
|
|
2706
2881
|
* rejection messages name the offending boundary. */
|
|
2707
|
-
|
|
2708
|
-
* function declaration (not the factory's destructured arrow) so TypeScript's
|
|
2709
|
-
* control-flow analysis recognizes it as never-returning and narrows `state`
|
|
2710
|
-
* after the guard below. */
|
|
2711
|
-
function reject(field) {
|
|
2712
|
-
throw new TypeError(`commandcode/login result: invalid ${field}`);
|
|
2713
|
-
}
|
|
2882
|
+
const { reject, record, stringField } = makeBoundaryValidator("commandcode/login result:");
|
|
2714
2883
|
/**
|
|
2715
2884
|
* Parse one untrusted boundary value into a {@link CommandCodeLoginStatus}.
|
|
2716
2885
|
* Every field is shape-checked so a malformed frame fails the boundary
|
|
2717
2886
|
* instead of leaking into the page.
|
|
2718
2887
|
*/
|
|
2719
2888
|
function parseLoginStatus(value) {
|
|
2720
|
-
|
|
2721
|
-
const source = value;
|
|
2889
|
+
const source = record(value, "status");
|
|
2722
2890
|
const state = source.state;
|
|
2723
|
-
if (state
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
if (
|
|
2727
|
-
status.
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
status.keyName = source.keyName;
|
|
2736
|
-
}
|
|
2737
|
-
if (source.reason !== void 0) {
|
|
2738
|
-
if (!REASONS.includes(source.reason)) reject("reason");
|
|
2739
|
-
status.reason = source.reason;
|
|
2740
|
-
}
|
|
2741
|
-
if (source.message !== void 0) {
|
|
2742
|
-
if (typeof source.message !== "string") reject("message");
|
|
2743
|
-
status.message = source.message;
|
|
2891
|
+
if (state === "idle" || state === "waiting" || state === "success" || state === "failed") {
|
|
2892
|
+
const status = { state };
|
|
2893
|
+
if (source.authUrl !== void 0) status.authUrl = stringField(source, "authUrl", "authUrl");
|
|
2894
|
+
if (source.userName !== void 0) status.userName = stringField(source, "userName", "userName");
|
|
2895
|
+
if (source.keyName !== void 0) status.keyName = stringField(source, "keyName", "keyName");
|
|
2896
|
+
if (source.reason !== void 0) {
|
|
2897
|
+
const reason = source.reason;
|
|
2898
|
+
if (typeof reason === "string" && REASONS.includes(reason)) status.reason = reason;
|
|
2899
|
+
else reject("reason");
|
|
2900
|
+
}
|
|
2901
|
+
if (source.message !== void 0) status.message = stringField(source, "message", "message");
|
|
2902
|
+
return status;
|
|
2744
2903
|
}
|
|
2745
|
-
return
|
|
2904
|
+
return reject("state");
|
|
2746
2905
|
}
|
|
2747
2906
|
/** The strict result codec shared by all three login endpoints. */
|
|
2748
2907
|
const loginStatusSchema = { parse: parseLoginStatus };
|
|
@@ -2792,10 +2951,11 @@ var CommandCodeUsageService = class extends TypertRemoteService {
|
|
|
2792
2951
|
}] };
|
|
2793
2952
|
}
|
|
2794
2953
|
/**
|
|
2795
|
-
* The model catalog for the settings page's
|
|
2796
|
-
*
|
|
2797
|
-
*
|
|
2798
|
-
*
|
|
2954
|
+
* The full model catalog for the settings page's model editors (the
|
|
2955
|
+
* routing-rule editor and the visible-models filter). The browser never
|
|
2956
|
+
* calls the Command Code API directly — the Host serves the catalog
|
|
2957
|
+
* (already fetched/cached by the adapter) so models can be picked from
|
|
2958
|
+
* the live list instead of typed by hand.
|
|
2799
2959
|
*/
|
|
2800
2960
|
async models() {
|
|
2801
2961
|
return this.deps.listModels?.() ?? { models: [] };
|
|
@@ -3107,12 +3267,15 @@ var CommandCodeLoginFlow = class {
|
|
|
3107
3267
|
});
|
|
3108
3268
|
return;
|
|
3109
3269
|
}
|
|
3270
|
+
let bodyBytes = 0;
|
|
3110
3271
|
let body = "";
|
|
3111
3272
|
request.on("data", (chunk) => {
|
|
3273
|
+
bodyBytes += chunk.length;
|
|
3112
3274
|
body += chunk.toString();
|
|
3113
|
-
if (
|
|
3275
|
+
if (bodyBytes > 1e4) request.destroy();
|
|
3114
3276
|
});
|
|
3115
3277
|
request.on("end", () => {
|
|
3278
|
+
if (request.destroyed) return;
|
|
3116
3279
|
let payload;
|
|
3117
3280
|
try {
|
|
3118
3281
|
payload = JSON.parse(body);
|
|
@@ -3248,21 +3411,86 @@ function corsOrigin(origin) {
|
|
|
3248
3411
|
const COMMANDCODE_SEARCH_PROVIDER_ID = "commandcode";
|
|
3249
3412
|
/**
|
|
3250
3413
|
* The factory-declared search provider id dsh ships by default (from
|
|
3251
|
-
* `dsh-base`'s cordis patch `web.config.searchProvider`).
|
|
3252
|
-
*
|
|
3253
|
-
*
|
|
3414
|
+
* `dsh-base`'s cordis patch `web.config.searchProvider`). Kept as a
|
|
3415
|
+
* documented reference only: disabling this plugin's `webSearch` toggle
|
|
3416
|
+
* restores the previously selected backend (see
|
|
3417
|
+
* {@link applyCommandCodeSearchSelection}) — it never forces this default,
|
|
3418
|
+
* because forcing it is what used to silence sibling search plugins such as
|
|
3419
|
+
* modsearch even with Command Code search turned off (issue #26).
|
|
3254
3420
|
*/
|
|
3255
3421
|
const DEFAULT_WEB_SEARCH_PROVIDER_ID = "deepseek-official";
|
|
3256
3422
|
/**
|
|
3257
3423
|
* Point the web seam's search selection at this plugin's provider (`commandcode`).
|
|
3258
3424
|
* Sets the runtime field; the next search call honours it because `search()`
|
|
3259
3425
|
* re-reads `searchProviderId` each time. Returns the prior id (or undefined).
|
|
3426
|
+
* Never throws: a hardened/frozen runtime shape must not break the
|
|
3427
|
+
* settings-save path that calls this — the provider simply stays
|
|
3428
|
+
* registered-but-unselected (the boot-time `searchProvider: commandcode`
|
|
3429
|
+
* cordis patch is the durable alternative).
|
|
3430
|
+
*
|
|
3431
|
+
* @deprecated Prefer {@link applyCommandCodeSearchSelection}: this overload
|
|
3432
|
+
* always overwrites the displaced backend with the factory default on
|
|
3433
|
+
* disable, so turning Command Code search off silences whichever provider
|
|
3434
|
+
* was selected before (e.g. modsearch) instead of restoring it (issue #26).
|
|
3260
3435
|
*/
|
|
3261
3436
|
function selectCommandCodeSearchProvider(web, enable) {
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3437
|
+
try {
|
|
3438
|
+
const field = web;
|
|
3439
|
+
const prior = field.searchProviderId;
|
|
3440
|
+
field.searchProviderId = enable ? COMMANDCODE_SEARCH_PROVIDER_ID : DEFAULT_WEB_SEARCH_PROVIDER_ID;
|
|
3441
|
+
return prior;
|
|
3442
|
+
} catch {
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
/** Fresh selection state: the plugin starts out not owning the selection. */
|
|
3447
|
+
function commandCodeSearchSelection() {
|
|
3448
|
+
return {
|
|
3449
|
+
owner: false,
|
|
3450
|
+
displaced: void 0
|
|
3451
|
+
};
|
|
3452
|
+
}
|
|
3453
|
+
/**
|
|
3454
|
+
* Reach one end of the `webSearch` toggle without trampling sibling search
|
|
3455
|
+
* providers (issue #26).
|
|
3456
|
+
*
|
|
3457
|
+
* - Enabling writes `commandcode` and remembers whatever it displaced. When
|
|
3458
|
+
* the plugin already owns the selection (e.g. a settings save while still
|
|
3459
|
+
* on), the original `displaced` value is kept — the field currently holds
|
|
3460
|
+
* our own id, which must never be mistaken for the user's backend.
|
|
3461
|
+
* - Disabling hands the selection back to the remembered backend. When the
|
|
3462
|
+
* state holds no memory (a fresh boot straight into `webSearch: false`),
|
|
3463
|
+
* the field is left alone: the runtime's current value — a sibling's
|
|
3464
|
+
* cordis pin such as `searchProvider: modsearch`, or unset for
|
|
3465
|
+
* auto-select — already says what the user wants.
|
|
3466
|
+
* - When the field already reads `commandcode` at first touch (e.g. a
|
|
3467
|
+
* surviving runtime the plugin did not set, or a manual
|
|
3468
|
+
* `searchProvider: commandcode` pin), `displaced` stays undefined so the
|
|
3469
|
+
* later disable is a no-op rather than a guess at the factory default.
|
|
3470
|
+
*
|
|
3471
|
+
* Never throws: like the low-level rewrite, a hardened runtime shape degrades
|
|
3472
|
+
* to registered-but-unselected.
|
|
3473
|
+
*/
|
|
3474
|
+
function applyCommandCodeSearchSelection(web, state, enable) {
|
|
3475
|
+
try {
|
|
3476
|
+
const field = web;
|
|
3477
|
+
if (enable) {
|
|
3478
|
+
if (state.owner) {
|
|
3479
|
+
field.searchProviderId = COMMANDCODE_SEARCH_PROVIDER_ID;
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3482
|
+
const prior = field.searchProviderId;
|
|
3483
|
+
state.displaced = prior === "commandcode" ? void 0 : prior;
|
|
3484
|
+
field.searchProviderId = COMMANDCODE_SEARCH_PROVIDER_ID;
|
|
3485
|
+
state.owner = true;
|
|
3486
|
+
return;
|
|
3487
|
+
}
|
|
3488
|
+
if (state.owner) {
|
|
3489
|
+
state.owner = false;
|
|
3490
|
+
field.searchProviderId = state.displaced;
|
|
3491
|
+
return;
|
|
3492
|
+
}
|
|
3493
|
+
} catch {}
|
|
3266
3494
|
}
|
|
3267
3495
|
/** Command Code's lower/upper bound on `numResults` (from the CLI's `web_search` schema). */
|
|
3268
3496
|
const MIN_NUM_RESULTS = 1;
|
|
@@ -3313,7 +3541,7 @@ var CommandCodeSearchProvider = class {
|
|
|
3313
3541
|
constructor(deps) {
|
|
3314
3542
|
this.deps = deps;
|
|
3315
3543
|
}
|
|
3316
|
-
/** Cheap local check; must not make network calls. Presence of a
|
|
3544
|
+
/** Cheap local check; must not make network calls. Presence of a parseable base is enough. */
|
|
3317
3545
|
available() {
|
|
3318
3546
|
const base = this.deps.apiBase();
|
|
3319
3547
|
return base.length > 0 && URL.canParse(base);
|
|
@@ -3360,6 +3588,8 @@ var CommandCodeSearchProvider = class {
|
|
|
3360
3588
|
}
|
|
3361
3589
|
} catch (error) {
|
|
3362
3590
|
if (signal?.aborted === true || isAbortError(error)) throw searchAborted(signal, error);
|
|
3591
|
+
const slice = (await response.text().catch(() => "")).trim().slice(0, 200);
|
|
3592
|
+
if (slice !== "") message += `: ${slice}`;
|
|
3363
3593
|
}
|
|
3364
3594
|
throw new WebError(message, "WEB_PROVIDER_ERROR");
|
|
3365
3595
|
}
|
|
@@ -3368,7 +3598,7 @@ var CommandCodeSearchProvider = class {
|
|
|
3368
3598
|
payload = await response.json();
|
|
3369
3599
|
} catch (error) {
|
|
3370
3600
|
if (signal?.aborted === true || isAbortError(error)) throw searchAborted(signal, error);
|
|
3371
|
-
throw new WebError("Command Code web search returned an unparseable response body", "WEB_PROVIDER_ERROR");
|
|
3601
|
+
throw new WebError("Command Code web search returned an unparseable response body", "WEB_PROVIDER_ERROR", { cause: error });
|
|
3372
3602
|
}
|
|
3373
3603
|
const results = payload?.results;
|
|
3374
3604
|
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");
|
|
@@ -3449,6 +3679,7 @@ const Config = z.object({
|
|
|
3449
3679
|
requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
3450
3680
|
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
3451
3681
|
filterModelsByPlan: z.boolean(),
|
|
3682
|
+
visibleModels: z.array(z.string()),
|
|
3452
3683
|
webSearch: z.boolean().default(true),
|
|
3453
3684
|
accounts: z.array(z.object({
|
|
3454
3685
|
label: z.string(),
|
|
@@ -3476,7 +3707,8 @@ function resolveAdapterOptions(config) {
|
|
|
3476
3707
|
modelsCachePath: config.modelsCachePath ?? DEFAULT_MODELS_CACHE_PATH,
|
|
3477
3708
|
requestTimeoutMs: config.requestTimeoutMs ?? 6e4,
|
|
3478
3709
|
streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? 3e5,
|
|
3479
|
-
filterModelsByPlan: config.filterModelsByPlan ?? true
|
|
3710
|
+
filterModelsByPlan: config.filterModelsByPlan ?? true,
|
|
3711
|
+
visibleModels: Array.isArray(config.visibleModels) ? config.visibleModels.filter((id) => typeof id === "string" && id !== "") : void 0
|
|
3480
3712
|
};
|
|
3481
3713
|
}
|
|
3482
3714
|
function apply(ctx, config) {
|
|
@@ -3606,18 +3838,26 @@ function apply(ctx, config) {
|
|
|
3606
3838
|
}
|
|
3607
3839
|
});
|
|
3608
3840
|
ctx.effect(() => () => loginFlow.dispose(), "dsh-commandcode-provider: login flow");
|
|
3609
|
-
const
|
|
3610
|
-
return { models: (await adapter.listModels(PROVIDER)).map((model) =>
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3841
|
+
const catalogForEditors = async () => {
|
|
3842
|
+
return { models: (await adapter.listModels(PROVIDER, { unfiltered: true })).map((model) => {
|
|
3843
|
+
const tier = KNOWN_PLANS[model.id];
|
|
3844
|
+
return {
|
|
3845
|
+
id: model.id,
|
|
3846
|
+
name: model.name.replace(/\s*\(CC\)$/, ""),
|
|
3847
|
+
...tier === void 0 ? {} : { tier }
|
|
3848
|
+
};
|
|
3849
|
+
}) };
|
|
3614
3850
|
};
|
|
3615
3851
|
applyUsageRemote(ctx, {
|
|
3616
3852
|
adapter,
|
|
3617
3853
|
reports: usageReports,
|
|
3618
3854
|
login: loginFlow,
|
|
3619
|
-
listModels:
|
|
3855
|
+
listModels: catalogForEditors
|
|
3620
3856
|
});
|
|
3857
|
+
const searchSelection = commandCodeSearchSelection();
|
|
3858
|
+
const applySearchSelection = (enabled) => {
|
|
3859
|
+
if (webRuntime !== void 0) applyCommandCodeSearchSelection(webRuntime, searchSelection, enabled);
|
|
3860
|
+
};
|
|
3621
3861
|
let webRuntime;
|
|
3622
3862
|
ctx.inject(["web"], (webCtx) => {
|
|
3623
3863
|
webRuntime = webCtx.web;
|
|
@@ -3628,7 +3868,11 @@ function apply(ctx, config) {
|
|
|
3628
3868
|
},
|
|
3629
3869
|
apiBase: () => options().apiBase
|
|
3630
3870
|
}));
|
|
3631
|
-
|
|
3871
|
+
applySearchSelection(current().webSearch ?? true);
|
|
3872
|
+
webCtx.effect(() => () => {
|
|
3873
|
+
webRuntime = void 0;
|
|
3874
|
+
applyCommandCodeSearchSelection(webCtx.web, searchSelection, false);
|
|
3875
|
+
}, "dsh-commandcode-provider: web search selection");
|
|
3632
3876
|
});
|
|
3633
3877
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
3634
3878
|
settingsCtx.settings.installSection(ctx, NS, Config, config, {
|
|
@@ -3636,12 +3880,12 @@ function apply(ctx, config) {
|
|
|
3636
3880
|
current = source;
|
|
3637
3881
|
},
|
|
3638
3882
|
onChange: () => {
|
|
3639
|
-
|
|
3883
|
+
applySearchSelection(current().webSearch ?? true);
|
|
3640
3884
|
}
|
|
3641
3885
|
});
|
|
3642
3886
|
});
|
|
3643
3887
|
}
|
|
3644
3888
|
//#endregion
|
|
3645
|
-
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 };
|
|
3889
|
+
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 };
|
|
3646
3890
|
|
|
3647
3891
|
//# sourceMappingURL=index.js.map
|