@mars-sea/dsh-commandcode-provider 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +8 -3
- package/README.zh-CN.md +8 -3
- package/assets/screenshots/settings-page.png +0 -0
- package/lib/client.js +669 -8
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +149 -6
- package/lib/index.js +384 -21
- package/lib/index.js.map +1 -1
- package/package.json +3 -1
package/lib/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-sett
|
|
|
9
9
|
import { existsSync, readFileSync } from "node:fs";
|
|
10
10
|
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
11
11
|
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
12
13
|
//#region src/adapter.ts
|
|
13
14
|
/**
|
|
14
15
|
* DeepSeek Harness LLM adapter for the Command Code Provider API.
|
|
@@ -17,7 +18,7 @@ import { randomUUID } from "node:crypto";
|
|
|
17
18
|
* community-maintained integration; you need your own Command Code account
|
|
18
19
|
* and API key or subscription, and Command Code's terms apply.
|
|
19
20
|
*
|
|
20
|
-
* Wire protocol (reverse-engineered by the pi plugin, command-code@1.
|
|
21
|
+
* Wire protocol (reverse-engineered by the pi plugin, command-code@1.27.1):
|
|
21
22
|
* POST {apiBase}/alpha/generate
|
|
22
23
|
* body: { config, memory, taste, skills, params: { model, messages, tools,
|
|
23
24
|
* system, max_tokens, temperature, stream, reasoning_effort? }, threadId }
|
|
@@ -223,7 +224,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
223
224
|
"xiaomi/mimo-v2.5"
|
|
224
225
|
]);
|
|
225
226
|
/**
|
|
226
|
-
* Models the official CLI's model table (`ZA` in command-code@1.
|
|
227
|
+
* Models the official CLI's model table (`ZA` in command-code@1.27.1) marks
|
|
227
228
|
* `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
|
|
228
229
|
* think automatically, with Command Code driving the depth. This is the
|
|
229
230
|
* authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
|
|
@@ -231,7 +232,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
231
232
|
* effort levels, and this snapshot is not surfaced in the picker's compact
|
|
232
233
|
* description — it exists for programmatic consumers.
|
|
233
234
|
*
|
|
234
|
-
* Source: the command-code@1.
|
|
235
|
+
* Source: the command-code@1.27.1 bundled model table (dist/cli.mjs, the `ZA`
|
|
235
236
|
* object), cross-checked with https://commandcode.ai/docs/reference/cli/models.
|
|
236
237
|
* Keep in sync via the dsh-commandcode-upstream skill.
|
|
237
238
|
*/
|
|
@@ -306,6 +307,7 @@ const KNOWN_PLANS = {
|
|
|
306
307
|
"zai-org/GLM-5.2-Fast": "go",
|
|
307
308
|
"zai-org/GLM-5.3": "go",
|
|
308
309
|
"google/gemini-3.7-flash": "goat",
|
|
310
|
+
"gpt-5.6-sol": "goat",
|
|
309
311
|
"meta/muse-spark-1.2": "goat",
|
|
310
312
|
"xai/grok-4.6": "goat",
|
|
311
313
|
"claude-haiku-4-5-20251001": "pro",
|
|
@@ -319,7 +321,6 @@ const KNOWN_PLANS = {
|
|
|
319
321
|
"gpt-5.4": "pro",
|
|
320
322
|
"gpt-5.4-mini": "pro",
|
|
321
323
|
"gpt-5.5": "pro",
|
|
322
|
-
"gpt-5.6-sol": "pro",
|
|
323
324
|
"gpt-5.6-terra": "pro",
|
|
324
325
|
"meta/muse-spark-1.1": "pro",
|
|
325
326
|
"claude-fable-5": "provider",
|
|
@@ -359,6 +360,89 @@ function compareByPlan(a, b) {
|
|
|
359
360
|
if (nameDiff !== 0) return nameDiff;
|
|
360
361
|
return a.id.localeCompare(b.id);
|
|
361
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Subscription plan table, synced from the official CLI bundle's plan maps
|
|
365
|
+
* (`Nn`/`$n` in command-code@1.27.1 `dist/cli.mjs`): subscription `planId`
|
|
366
|
+
* prefix → display name and the plan's monthly credit total. This is the
|
|
367
|
+
* account's own subscription (from `/alpha/billing/subscriptions`) — distinct
|
|
368
|
+
* from {@link KNOWN_PLANS}, which maps catalog models to their minimum tier.
|
|
369
|
+
*
|
|
370
|
+
* `tierWeight` is plugin-added (not from the CLI maps): the plan's rank on
|
|
371
|
+
* the {@link PLAN_ORDER} scale, used by the picker's plan filter
|
|
372
|
+
* ({@link modelVisibleInPlan}) to hide models above the account's tier.
|
|
373
|
+
*/
|
|
374
|
+
const KNOWN_SUBSCRIPTION_PLANS = {
|
|
375
|
+
"individual-go": {
|
|
376
|
+
name: "Go",
|
|
377
|
+
monthlyCredits: 10,
|
|
378
|
+
tierWeight: 0
|
|
379
|
+
},
|
|
380
|
+
"individual-goat": {
|
|
381
|
+
name: "GOAT",
|
|
382
|
+
monthlyCredits: 70,
|
|
383
|
+
tierWeight: 1
|
|
384
|
+
},
|
|
385
|
+
"individual-pro": {
|
|
386
|
+
name: "Pro",
|
|
387
|
+
monthlyCredits: 30,
|
|
388
|
+
tierWeight: 2
|
|
389
|
+
},
|
|
390
|
+
"individual-pro-v1": {
|
|
391
|
+
name: "Pro",
|
|
392
|
+
monthlyCredits: 80,
|
|
393
|
+
tierWeight: 2
|
|
394
|
+
},
|
|
395
|
+
"individual-provider": {
|
|
396
|
+
name: "Provider",
|
|
397
|
+
monthlyCredits: 15,
|
|
398
|
+
tierWeight: 3
|
|
399
|
+
},
|
|
400
|
+
"individual-max": {
|
|
401
|
+
name: "Max",
|
|
402
|
+
monthlyCredits: 150,
|
|
403
|
+
tierWeight: 4
|
|
404
|
+
},
|
|
405
|
+
"individual-ultra": {
|
|
406
|
+
name: "Ultra",
|
|
407
|
+
monthlyCredits: 300,
|
|
408
|
+
tierWeight: 4
|
|
409
|
+
},
|
|
410
|
+
"teams-pro": {
|
|
411
|
+
name: "Teams Pro",
|
|
412
|
+
monthlyCredits: 40,
|
|
413
|
+
tierWeight: 2
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
/** Plan-id prefixes, longest first — the CLI's prefix-match order. */
|
|
417
|
+
const SUBSCRIPTION_PLAN_PREFIXES = Object.keys(KNOWN_SUBSCRIPTION_PLANS).sort((a, b) => b.length - a.length);
|
|
418
|
+
/**
|
|
419
|
+
* Resolve a subscription `planId` (e.g. `individual-pro-v1`) to its display
|
|
420
|
+
* name and monthly credit total, mirroring the CLI's `getPlanInfo`:
|
|
421
|
+
* normalize (lowercase, `_` → `-`), then longest-prefix match so
|
|
422
|
+
* `individual-pro-v1` wins over `individual-pro`. Unknown ids return
|
|
423
|
+
* `undefined`.
|
|
424
|
+
*/
|
|
425
|
+
function subscriptionPlanInfo(planId) {
|
|
426
|
+
const normalized = planId.toLowerCase().replace(/_/g, "-");
|
|
427
|
+
const prefix = SUBSCRIPTION_PLAN_PREFIXES.find((candidate) => normalized.startsWith(candidate));
|
|
428
|
+
return prefix === void 0 ? void 0 : KNOWN_SUBSCRIPTION_PLANS[prefix];
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Whether the picker lists `modelId` for an account with the given billing
|
|
432
|
+
* access. Fails open at every uncertainty: no billing data, an unknown plan,
|
|
433
|
+
* or a model outside {@link KNOWN_PLANS} all keep the model visible — the
|
|
434
|
+
* server remains the final gate (`403 MODEL_NOT_IN_PLAN`).
|
|
435
|
+
*/
|
|
436
|
+
function modelVisibleInPlan(modelId, access) {
|
|
437
|
+
if (access === void 0) return true;
|
|
438
|
+
if (access.onDemandCredits > 0) return true;
|
|
439
|
+
if (access.tierWeight === void 0) return true;
|
|
440
|
+
const tier = KNOWN_PLANS[modelId];
|
|
441
|
+
if (tier === void 0) return true;
|
|
442
|
+
const weight = PLAN_ORDER[tier];
|
|
443
|
+
if (weight === void 0) return true;
|
|
444
|
+
return weight <= access.tierWeight;
|
|
445
|
+
}
|
|
362
446
|
const KNOWN_DEALS = {
|
|
363
447
|
"google/gemini-3.7-flash": {
|
|
364
448
|
label: "50% off",
|
|
@@ -408,10 +492,22 @@ function peakPricingLabel(modelId, now = Date.now()) {
|
|
|
408
492
|
if (state === void 0) return void 0;
|
|
409
493
|
return state === "peak" ? "Peak" : "Half";
|
|
410
494
|
}
|
|
411
|
-
const COMMAND_CODE_CLI_VERSION = "1.
|
|
495
|
+
const COMMAND_CODE_CLI_VERSION = "1.27.1";
|
|
412
496
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
413
497
|
const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
|
|
414
498
|
const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
499
|
+
const MODELS_TIMEOUT_MS = 1e4;
|
|
500
|
+
/** How long the picker's plan-filter billing facts stay cached before refetching. */
|
|
501
|
+
const BILLING_ACCESS_TTL_MS = 3e5;
|
|
502
|
+
/**
|
|
503
|
+
* Subscription statuses the CLI treats as live (`Mr` in command-code's
|
|
504
|
+
* cli.mjs): the plan gate applies only under one of these.
|
|
505
|
+
*/
|
|
506
|
+
const ACTIVE_SUBSCRIPTION_STATUSES = /* @__PURE__ */ new Set([
|
|
507
|
+
"active",
|
|
508
|
+
"trialing",
|
|
509
|
+
"past_due"
|
|
510
|
+
]);
|
|
415
511
|
/** Head-of-request timeout: how long to wait for the first response byte. */
|
|
416
512
|
const DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
417
513
|
/** Stream idle timeout: a generation that stalls this long is a dead connection. */
|
|
@@ -484,6 +580,15 @@ function numberValue(value) {
|
|
|
484
580
|
function booleanValue(value) {
|
|
485
581
|
return typeof value === "boolean" ? value : void 0;
|
|
486
582
|
}
|
|
583
|
+
/** Parse a billing-period timestamp (ISO string or millis) into millis; 0 when absent/invalid. */
|
|
584
|
+
function periodEndValue(value) {
|
|
585
|
+
const asNumber = numberValue(value);
|
|
586
|
+
if (asNumber !== void 0) return asNumber;
|
|
587
|
+
const asString = stringValue(value);
|
|
588
|
+
if (asString === void 0) return 0;
|
|
589
|
+
const parsed = Date.parse(asString);
|
|
590
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
591
|
+
}
|
|
487
592
|
/**
|
|
488
593
|
* Terminal stream-error markers from the official CLI (`Xw` in command-code's
|
|
489
594
|
* cli.mjs): these always mean "retrying cannot succeed", so the adapter must
|
|
@@ -684,6 +789,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
684
789
|
catalog = [];
|
|
685
790
|
fetchImpl;
|
|
686
791
|
resolveAttachments;
|
|
792
|
+
billingAccess;
|
|
793
|
+
billingAccessInflight;
|
|
687
794
|
constructor(deps) {
|
|
688
795
|
super();
|
|
689
796
|
this.deps = deps;
|
|
@@ -722,7 +829,9 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
722
829
|
return this.catalog;
|
|
723
830
|
}
|
|
724
831
|
async listModels(provider) {
|
|
725
|
-
|
|
832
|
+
const catalog = await this.loadCatalog();
|
|
833
|
+
const access = this.deps.options().filterModelsByPlan === false ? void 0 : await this.loadBillingAccess();
|
|
834
|
+
return catalog.filter((model) => modelVisibleInPlan(model.id, access)).map((model) => {
|
|
726
835
|
const vision = KNOWN_IMAGE_MODELS.has(model.id);
|
|
727
836
|
return {
|
|
728
837
|
provider,
|
|
@@ -753,27 +862,96 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
753
862
|
})) } } : {}
|
|
754
863
|
};
|
|
755
864
|
}
|
|
865
|
+
/** The headers every authenticated account endpoint shares. */
|
|
866
|
+
async accountHeaders() {
|
|
867
|
+
const connection = this.deps.options();
|
|
868
|
+
return {
|
|
869
|
+
Authorization: `Bearer ${await this.deps.resolveApiKey(connection)}`,
|
|
870
|
+
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
871
|
+
"x-cli-environment": "production",
|
|
872
|
+
...attributionHeaders()
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* The billing facts behind the picker's plan filter, cached for
|
|
877
|
+
* {@link BILLING_ACCESS_TTL_MS} and shared across concurrent callers.
|
|
878
|
+
* `undefined` means "unknown — show everything" (fail-open).
|
|
879
|
+
*/
|
|
880
|
+
async loadBillingAccess() {
|
|
881
|
+
const cached = this.billingAccess;
|
|
882
|
+
if (cached !== void 0 && Date.now() - cached.at < 3e5) return cached.value;
|
|
883
|
+
this.billingAccessInflight ??= this.fetchBillingAccess().then((value) => {
|
|
884
|
+
this.billingAccess = {
|
|
885
|
+
value,
|
|
886
|
+
at: Date.now()
|
|
887
|
+
};
|
|
888
|
+
return value;
|
|
889
|
+
}).finally(() => {
|
|
890
|
+
this.billingAccessInflight = void 0;
|
|
891
|
+
});
|
|
892
|
+
return this.billingAccessInflight;
|
|
893
|
+
}
|
|
756
894
|
/**
|
|
757
|
-
*
|
|
758
|
-
*
|
|
895
|
+
* The billing facts behind the picker's plan filter, mirroring the CLI's
|
|
896
|
+
* `createBilling` flow: whoami yields the org id, then the subscriptions
|
|
897
|
+
* and credits endpoints answer in parallel. The plan id is honored only
|
|
898
|
+
* when the subscription reports an active-ish status (the CLI's rule); when
|
|
899
|
+
* the subscriptions endpoint fails entirely, `credits.planId` is the
|
|
900
|
+
* fallback (the CLI stamps plan identity from it too). Any failure resolves
|
|
901
|
+
* to `undefined` (fail-open) rather than breaking the picker.
|
|
902
|
+
*/
|
|
903
|
+
async fetchBillingAccess() {
|
|
904
|
+
try {
|
|
905
|
+
const connection = this.deps.options();
|
|
906
|
+
const headers = await this.accountHeaders();
|
|
907
|
+
const base = connection.apiBase;
|
|
908
|
+
const getJson = async (path) => {
|
|
909
|
+
const response = await this.fetchImpl(`${base}${path}`, {
|
|
910
|
+
headers,
|
|
911
|
+
signal: AbortSignal.timeout(MODELS_TIMEOUT_MS)
|
|
912
|
+
});
|
|
913
|
+
if (!response.ok) return void 0;
|
|
914
|
+
const parsed = await response.json();
|
|
915
|
+
return isRecord(parsed) ? parsed : void 0;
|
|
916
|
+
};
|
|
917
|
+
const whoami = await getJson("/alpha/whoami");
|
|
918
|
+
const orgData = whoami && isRecord(whoami.org) ? whoami.org : void 0;
|
|
919
|
+
const orgId = orgData === void 0 ? void 0 : stringValue(orgData.id);
|
|
920
|
+
const [subscription, credits] = await Promise.all([getJson(orgId === void 0 ? "/alpha/billing/subscriptions" : `/alpha/billing/subscriptions?orgId=${encodeURIComponent(orgId)}`), getJson("/alpha/billing/credits")]);
|
|
921
|
+
const subData = subscription && isRecord(subscription.data) ? subscription.data : void 0;
|
|
922
|
+
const creditsData = credits && isRecord(credits.credits) ? credits.credits : void 0;
|
|
923
|
+
if (subData === void 0 && creditsData === void 0) return void 0;
|
|
924
|
+
let planId;
|
|
925
|
+
if (subData !== void 0) {
|
|
926
|
+
const status = stringValue(subData.status);
|
|
927
|
+
if (status !== void 0 && ACTIVE_SUBSCRIPTION_STATUSES.has(status)) planId = stringValue(subData.planId);
|
|
928
|
+
} else planId = stringValue(creditsData?.planId);
|
|
929
|
+
return {
|
|
930
|
+
tierWeight: planId === void 0 ? void 0 : subscriptionPlanInfo(planId)?.tierWeight,
|
|
931
|
+
onDemandCredits: (numberValue(creditsData?.purchasedCredits) ?? 0) + (numberValue(creditsData?.freeCredits) ?? 0)
|
|
932
|
+
};
|
|
933
|
+
} catch {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Fetch account, usage, credit, and subscription state from the Command
|
|
939
|
+
* Code account endpoints (`/alpha/whoami`, `/alpha/usage/summary`,
|
|
940
|
+
* `/alpha/billing/credits`, `/alpha/billing/subscriptions`).
|
|
759
941
|
* Each endpoint degrades independently: a failed one lands in `failures`
|
|
760
942
|
* while the rest still report, so a transient outage never blanks the whole
|
|
761
943
|
* view. Requires a usable API key (throws `MISSING_CREDENTIAL` otherwise).
|
|
762
944
|
*/
|
|
763
945
|
async getUsage() {
|
|
764
|
-
const
|
|
765
|
-
const
|
|
766
|
-
const base = connection.apiBase;
|
|
767
|
-
const headers = {
|
|
768
|
-
Authorization: `Bearer ${apiKey}`,
|
|
769
|
-
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
770
|
-
"x-cli-environment": "production",
|
|
771
|
-
...attributionHeaders()
|
|
772
|
-
};
|
|
946
|
+
const base = this.deps.options().apiBase;
|
|
947
|
+
const headers = await this.accountHeaders();
|
|
773
948
|
const failures = [];
|
|
774
949
|
const getJson = async (path) => {
|
|
775
950
|
try {
|
|
776
|
-
const response = await this.fetchImpl(`${base}${path}`, {
|
|
951
|
+
const response = await this.fetchImpl(`${base}${path}`, {
|
|
952
|
+
headers,
|
|
953
|
+
signal: AbortSignal.timeout(MODELS_TIMEOUT_MS)
|
|
954
|
+
});
|
|
777
955
|
if (!response.ok) {
|
|
778
956
|
failures.push(`${path}: HTTP ${response.status}`);
|
|
779
957
|
return;
|
|
@@ -793,6 +971,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
793
971
|
name: stringValue(whoamiData.name) ?? "",
|
|
794
972
|
userName: stringValue(whoamiData.userName) ?? ""
|
|
795
973
|
};
|
|
974
|
+
const orgData = whoami && isRecord(whoami.org) ? whoami.org : void 0;
|
|
975
|
+
const orgId = orgData === void 0 ? void 0 : stringValue(orgData.id);
|
|
796
976
|
const usage = await getJson("/alpha/usage/summary");
|
|
797
977
|
if (usage) report.usage = {
|
|
798
978
|
totalCount: numberValue(usage.totalCount) ?? 0,
|
|
@@ -827,6 +1007,19 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
827
1007
|
resetAt: numberValue(weekly?.resetAt) ?? 0
|
|
828
1008
|
}
|
|
829
1009
|
};
|
|
1010
|
+
const subscription = await getJson(orgId === void 0 ? "/alpha/billing/subscriptions" : `/alpha/billing/subscriptions?orgId=${encodeURIComponent(orgId)}`);
|
|
1011
|
+
const subData = subscription && isRecord(subscription.data) ? subscription.data : void 0;
|
|
1012
|
+
const planId = stringValue(subData?.planId) ?? stringValue(creditsData?.planId);
|
|
1013
|
+
if (subData !== void 0 || planId !== void 0) {
|
|
1014
|
+
const info = planId === void 0 ? void 0 : subscriptionPlanInfo(planId);
|
|
1015
|
+
report.plan = {
|
|
1016
|
+
planId: planId ?? "",
|
|
1017
|
+
name: info?.name ?? planId ?? "",
|
|
1018
|
+
status: stringValue(subData?.status) ?? "",
|
|
1019
|
+
monthlyCredits: info?.monthlyCredits ?? null,
|
|
1020
|
+
currentPeriodEnd: periodEndValue(subData?.currentPeriodEnd)
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
830
1023
|
return report;
|
|
831
1024
|
}
|
|
832
1025
|
async *stream(options) {
|
|
@@ -1190,6 +1383,12 @@ function renderReport(report) {
|
|
|
1190
1383
|
const lines = [];
|
|
1191
1384
|
const account = report.account ? ` (${report.account.userName || report.account.name})` : "";
|
|
1192
1385
|
lines.push(`📊 Command Code 用量${account}`, "");
|
|
1386
|
+
if (report.plan && report.plan.name !== "") {
|
|
1387
|
+
const p = report.plan;
|
|
1388
|
+
const status = p.status !== "" && p.status !== "active" ? ` (${p.status})` : "";
|
|
1389
|
+
const period = p.currentPeriodEnd > 0 ? ` · 账期截止 ${new Date(p.currentPeriodEnd).toLocaleDateString()}` : "";
|
|
1390
|
+
lines.push(` 📦 套餐 ${p.name}${status}${period}`, "");
|
|
1391
|
+
}
|
|
1193
1392
|
if (report.usage) {
|
|
1194
1393
|
const u = report.usage;
|
|
1195
1394
|
lines.push("── 请求 ──────────────────────────────", ` 💬 请求 ${u.completedCount} 次 / 失败 ${u.failedCount} 成功率 ${u.successRate}%`, ` 💰 花费 ${money(u.totalCost)} (${moneyShort(u.totalCredits)} credits)`, ` 🔤 Token ${tokensCompact(u.totalTokensIn)} 入 / ${tokensCompact(u.totalTokensOut)} 出`, "");
|
|
@@ -1230,6 +1429,167 @@ function applyCommands(ctx, deps) {
|
|
|
1230
1429
|
ctx.commands.register(commandDefinition(deps));
|
|
1231
1430
|
}
|
|
1232
1431
|
//#endregion
|
|
1432
|
+
//#region src/usage-wire.ts
|
|
1433
|
+
/** The npm package identity both contribution registrations claim. */
|
|
1434
|
+
const USAGE_REMOTE_PACKAGE = "@mars-sea/dsh-commandcode-provider";
|
|
1435
|
+
/** Canonical `<namespace>/<method>` endpoint of the usage report Remote. */
|
|
1436
|
+
const USAGE_REPORT_ENDPOINT = "commandcode/report";
|
|
1437
|
+
/** Reject one boundary value with a field-naming error. */
|
|
1438
|
+
function reject(field) {
|
|
1439
|
+
throw new TypeError(`commandcode/report result: invalid ${field}`);
|
|
1440
|
+
}
|
|
1441
|
+
/** Read one required finite number field (`field` is the dotted error label). */
|
|
1442
|
+
function numberField(source, key, field) {
|
|
1443
|
+
const value = source[key];
|
|
1444
|
+
if (typeof value !== "number" || !Number.isFinite(value)) reject(field);
|
|
1445
|
+
return value;
|
|
1446
|
+
}
|
|
1447
|
+
/** Read one required string field (`field` is the dotted error label). */
|
|
1448
|
+
function stringField(source, key, field) {
|
|
1449
|
+
const value = source[key];
|
|
1450
|
+
if (typeof value !== "string") reject(field);
|
|
1451
|
+
return value;
|
|
1452
|
+
}
|
|
1453
|
+
/** Read one required boolean field (`field` is the dotted error label). */
|
|
1454
|
+
function booleanField(source, key, field) {
|
|
1455
|
+
const value = source[key];
|
|
1456
|
+
if (typeof value !== "boolean") reject(field);
|
|
1457
|
+
return value;
|
|
1458
|
+
}
|
|
1459
|
+
/** Narrow an unknown value to a plain record, or reject. */
|
|
1460
|
+
function record(value, field) {
|
|
1461
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) reject(field);
|
|
1462
|
+
return value;
|
|
1463
|
+
}
|
|
1464
|
+
/** Validate one window-limit block (`fiveHour` / `weekly`). */
|
|
1465
|
+
function windowLimit(value, field) {
|
|
1466
|
+
const source = record(value, field);
|
|
1467
|
+
return {
|
|
1468
|
+
used: numberField(source, "used", `${field}.used`),
|
|
1469
|
+
cap: numberField(source, "cap", `${field}.cap`),
|
|
1470
|
+
exceeded: booleanField(source, "exceeded", `${field}.exceeded`),
|
|
1471
|
+
resetAt: numberField(source, "resetAt", `${field}.resetAt`)
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Parse one untrusted boundary value into a {@link CommandCodeUsageReport}.
|
|
1476
|
+
* Optional sections stay optional; every present field is shape-checked so a
|
|
1477
|
+
* malformed frame fails the boundary instead of rendering garbage.
|
|
1478
|
+
*/
|
|
1479
|
+
function parseUsageReport(value) {
|
|
1480
|
+
const source = record(value, "report");
|
|
1481
|
+
const failures = source.failures;
|
|
1482
|
+
if (!Array.isArray(failures) || failures.some((entry) => typeof entry !== "string")) reject("failures");
|
|
1483
|
+
const report = { failures };
|
|
1484
|
+
if (source.account !== void 0) {
|
|
1485
|
+
const account = record(source.account, "account");
|
|
1486
|
+
report.account = {
|
|
1487
|
+
id: stringField(account, "id", "account.id"),
|
|
1488
|
+
name: stringField(account, "name", "account.name"),
|
|
1489
|
+
userName: stringField(account, "userName", "account.userName")
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
if (source.usage !== void 0) {
|
|
1493
|
+
const usage = record(source.usage, "usage");
|
|
1494
|
+
report.usage = {
|
|
1495
|
+
totalCount: numberField(usage, "totalCount", "usage.totalCount"),
|
|
1496
|
+
totalCost: numberField(usage, "totalCost", "usage.totalCost"),
|
|
1497
|
+
successRate: numberField(usage, "successRate", "usage.successRate"),
|
|
1498
|
+
completedCount: numberField(usage, "completedCount", "usage.completedCount"),
|
|
1499
|
+
failedCount: numberField(usage, "failedCount", "usage.failedCount"),
|
|
1500
|
+
totalTokensIn: numberField(usage, "totalTokensIn", "usage.totalTokensIn"),
|
|
1501
|
+
totalTokensOut: numberField(usage, "totalTokensOut", "usage.totalTokensOut"),
|
|
1502
|
+
totalCredits: numberField(usage, "totalCredits", "usage.totalCredits"),
|
|
1503
|
+
periodBasis: stringField(usage, "periodBasis", "usage.periodBasis")
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
if (source.credits !== void 0) {
|
|
1507
|
+
const credits = record(source.credits, "credits");
|
|
1508
|
+
report.credits = {
|
|
1509
|
+
monthlyCredits: numberField(credits, "monthlyCredits", "credits.monthlyCredits"),
|
|
1510
|
+
purchasedCredits: numberField(credits, "purchasedCredits", "credits.purchasedCredits"),
|
|
1511
|
+
freeCredits: numberField(credits, "freeCredits", "credits.freeCredits"),
|
|
1512
|
+
fiveHour: windowLimit(credits.fiveHour, "credits.fiveHour"),
|
|
1513
|
+
weekly: windowLimit(credits.weekly, "credits.weekly")
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
if (source.plan !== void 0) {
|
|
1517
|
+
const plan = record(source.plan, "plan");
|
|
1518
|
+
const monthly = plan.monthlyCredits;
|
|
1519
|
+
if (monthly !== null && (typeof monthly !== "number" || !Number.isFinite(monthly))) reject("plan.monthlyCredits");
|
|
1520
|
+
report.plan = {
|
|
1521
|
+
planId: stringField(plan, "planId", "plan.planId"),
|
|
1522
|
+
name: stringField(plan, "name", "plan.name"),
|
|
1523
|
+
status: stringField(plan, "status", "plan.status"),
|
|
1524
|
+
monthlyCredits: monthly,
|
|
1525
|
+
currentPeriodEnd: numberField(plan, "currentPeriodEnd", "plan.currentPeriodEnd")
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
return report;
|
|
1529
|
+
}
|
|
1530
|
+
/**
|
|
1531
|
+
* The strict result codec both halves attach to the descriptor. Hand-rolled:
|
|
1532
|
+
* the client bundle may not require a schema library, and `TypertSchema` is
|
|
1533
|
+
* deliberately minimal so one `parse` function satisfies it.
|
|
1534
|
+
*/
|
|
1535
|
+
const usageReportSchema = { parse: parseUsageReport };
|
|
1536
|
+
/** The Host-face contribution registered on `ctx.typert`. */
|
|
1537
|
+
const USAGE_HOST_CONTRIBUTION = {
|
|
1538
|
+
package: USAGE_REMOTE_PACKAGE,
|
|
1539
|
+
face: "host",
|
|
1540
|
+
schemas: [],
|
|
1541
|
+
invocations: [{
|
|
1542
|
+
id: `${USAGE_REMOTE_PACKAGE}#${USAGE_REPORT_ENDPOINT}`,
|
|
1543
|
+
service: "commandcodeUsage",
|
|
1544
|
+
namespace: "commandcode",
|
|
1545
|
+
method: "report",
|
|
1546
|
+
invocation: { kind: "direct" },
|
|
1547
|
+
parameters: [],
|
|
1548
|
+
result: {
|
|
1549
|
+
mode: "strict",
|
|
1550
|
+
typeSymbol: `${USAGE_REMOTE_PACKAGE}#CommandCodeUsageReport`,
|
|
1551
|
+
schema: usageReportSchema
|
|
1552
|
+
}
|
|
1553
|
+
}]
|
|
1554
|
+
};
|
|
1555
|
+
//#endregion
|
|
1556
|
+
//#region src/usage-remote.ts
|
|
1557
|
+
/**
|
|
1558
|
+
* The Remote receiver: a Cordis service the Gateway resolves by key
|
|
1559
|
+
* (`commandcodeUsage`) and binds to the wire namespace (`commandcode`). The
|
|
1560
|
+
* base class stamps the `typertRemote` binding the Gateway validates on every
|
|
1561
|
+
* dispatch; no decorators are needed because the descriptor is registered
|
|
1562
|
+
* explicitly (strict path) rather than discovered from source markers.
|
|
1563
|
+
*/
|
|
1564
|
+
var CommandCodeUsageService = class extends TypertRemoteService {
|
|
1565
|
+
deps;
|
|
1566
|
+
constructor(ctx, deps) {
|
|
1567
|
+
super(ctx, "commandcodeUsage", { namespace: "commandcode" });
|
|
1568
|
+
this.deps = deps;
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Account, usage, and credit state for the settings page's account card.
|
|
1572
|
+
* Degrades per endpoint like the `/commandcode` command (failures land in
|
|
1573
|
+
* `report.failures`); throws `MISSING_CREDENTIAL` when no key resolves, which
|
|
1574
|
+
* the Gateway folds into the failure branch the page renders as a hint.
|
|
1575
|
+
*/
|
|
1576
|
+
async report() {
|
|
1577
|
+
return this.deps.adapter.getUsage();
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
/**
|
|
1581
|
+
* Provide the usage service and register its Remote descriptor. The registry
|
|
1582
|
+
* contribution is tied to this fiber's lifetime: the registry's own
|
|
1583
|
+
* `register()` effect would otherwise outlive the plugin.
|
|
1584
|
+
*/
|
|
1585
|
+
function applyUsageRemote(ctx, deps) {
|
|
1586
|
+
ctx.inject(["typert"], (remoteCtx) => {
|
|
1587
|
+
new CommandCodeUsageService(remoteCtx, deps);
|
|
1588
|
+
const unregister = remoteCtx.typert.register(USAGE_HOST_CONTRIBUTION);
|
|
1589
|
+
remoteCtx.effect(() => () => void unregister(), "dsh-commandcode-provider: usage remote");
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
//#endregion
|
|
1233
1593
|
//#region src/index.ts
|
|
1234
1594
|
/**
|
|
1235
1595
|
* dsh-commandcode-provider — DeepSeek Harness LLM provider plugin for Command
|
|
@@ -1273,7 +1633,8 @@ const Config = z.object({
|
|
|
1273
1633
|
workingDir: z.string(),
|
|
1274
1634
|
modelsCachePath: z.string(),
|
|
1275
1635
|
requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
1276
|
-
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS)
|
|
1636
|
+
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
1637
|
+
filterModelsByPlan: z.boolean()
|
|
1277
1638
|
});
|
|
1278
1639
|
/**
|
|
1279
1640
|
* The one explicit resolve step from raw config to validated connection
|
|
@@ -1288,7 +1649,8 @@ function resolveAdapterOptions(config) {
|
|
|
1288
1649
|
workingDir: config.workingDir ?? process.cwd(),
|
|
1289
1650
|
modelsCachePath: config.modelsCachePath ?? DEFAULT_MODELS_CACHE_PATH,
|
|
1290
1651
|
requestTimeoutMs: config.requestTimeoutMs ?? 6e4,
|
|
1291
|
-
streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? 3e5
|
|
1652
|
+
streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? 3e5,
|
|
1653
|
+
filterModelsByPlan: config.filterModelsByPlan ?? true
|
|
1292
1654
|
};
|
|
1293
1655
|
}
|
|
1294
1656
|
function apply(ctx, config) {
|
|
@@ -1338,6 +1700,7 @@ function apply(ctx, config) {
|
|
|
1338
1700
|
ctx.inject(["commands"], (commandCtx) => {
|
|
1339
1701
|
applyCommands(commandCtx, { adapter });
|
|
1340
1702
|
});
|
|
1703
|
+
applyUsageRemote(ctx, { adapter });
|
|
1341
1704
|
installSettingsSection(ctx, NS, Config, config, {
|
|
1342
1705
|
setSource: (source) => {
|
|
1343
1706
|
current = source;
|
|
@@ -1346,6 +1709,6 @@ function apply(ctx, config) {
|
|
|
1346
1709
|
});
|
|
1347
1710
|
}
|
|
1348
1711
|
//#endregion
|
|
1349
|
-
export { COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, 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, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_THINKING_MODELS, PLAN_LABELS, PLAN_ORDER, PROVIDER, apply, applyCommands, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, name, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey };
|
|
1712
|
+
export { BILLING_ACCESS_TTL_MS, COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, 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, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, PLAN_LABELS, PLAN_ORDER, PROVIDER, USAGE_REPORT_ENDPOINT, apply, applyCommands, applyUsageRemote, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, modelVisibleInPlan, name, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, subscriptionPlanInfo, usageReportSchema };
|
|
1350
1713
|
|
|
1351
1714
|
//# sourceMappingURL=index.js.map
|