@mars-sea/dsh-commandcode-provider 0.11.17 → 0.12.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 +117 -1
- package/README.md +16 -10
- package/README.zh-CN.md +13 -7
- package/lib/client.js +197 -123
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +267 -92
- package/lib/index.js +2502 -717
- package/lib/index.js.map +1 -1
- package/package.json +40 -37
package/lib/client.js
CHANGED
|
@@ -526,22 +526,62 @@ window.__ModuleLoader__.load({
|
|
|
526
526
|
}
|
|
527
527
|
};
|
|
528
528
|
}
|
|
529
|
-
const
|
|
529
|
+
const MAX_TIMEOUT_SECONDS = 3600;
|
|
530
|
+
/**
|
|
531
|
+
* A duration field the user edits in WHOLE SECONDS.
|
|
532
|
+
*
|
|
533
|
+
* Milliseconds are the wrong unit to show: the defaults are 300000 and the
|
|
534
|
+
* ceiling is 2147483647, so the page asked users to do arithmetic to answer
|
|
535
|
+
* "how long is a reasonable wait". `format` divides, `parse` multiplies, and
|
|
536
|
+
* the stored value never leaves milliseconds.
|
|
537
|
+
*
|
|
538
|
+
* Decimals are refused rather than rounded. "1.5" is a format the control does
|
|
539
|
+
* not accept, and silently storing 1000 or 2000 for it would make the saved
|
|
540
|
+
* profile disagree with the box it was typed in.
|
|
541
|
+
*/
|
|
542
|
+
function secondsField(field, bounds) {
|
|
543
|
+
return {
|
|
544
|
+
field,
|
|
545
|
+
format: (value) => typeof value === "number" ? String(Math.round(value / 1e3)) : "",
|
|
546
|
+
parse: (text) => {
|
|
547
|
+
const trimmed = text.trim();
|
|
548
|
+
if (trimmed === "") return { kind: "clear" };
|
|
549
|
+
const parsed = Number(trimmed);
|
|
550
|
+
if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) return {
|
|
551
|
+
kind: "invalid",
|
|
552
|
+
reason: "format"
|
|
553
|
+
};
|
|
554
|
+
if (parsed < bounds.min) return {
|
|
555
|
+
kind: "invalid",
|
|
556
|
+
reason: "tooSmall"
|
|
557
|
+
};
|
|
558
|
+
if (parsed > bounds.max) return {
|
|
559
|
+
kind: "invalid",
|
|
560
|
+
reason: "tooLarge"
|
|
561
|
+
};
|
|
562
|
+
return {
|
|
563
|
+
kind: "set",
|
|
564
|
+
value: parsed * 1e3
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
}
|
|
530
569
|
/** The fields this page edits inside the `llm-commandcode` namespace. */
|
|
531
570
|
const SECTION_FIELDS = [
|
|
532
571
|
textField("apiBase"),
|
|
533
|
-
|
|
572
|
+
secondsField("requestTimeoutMs", {
|
|
534
573
|
min: 1,
|
|
535
|
-
max:
|
|
574
|
+
max: MAX_TIMEOUT_SECONDS
|
|
536
575
|
}),
|
|
537
|
-
|
|
576
|
+
secondsField("streamIdleTimeoutMs", {
|
|
538
577
|
min: 1,
|
|
539
|
-
max:
|
|
578
|
+
max: MAX_TIMEOUT_SECONDS
|
|
540
579
|
}),
|
|
541
580
|
numberField$1("transportMaxRetries", {
|
|
542
581
|
min: 0,
|
|
543
582
|
max: 50
|
|
544
583
|
}),
|
|
584
|
+
booleanField$1("offloadSeenImagesForCache"),
|
|
545
585
|
booleanField$1("filterModelsByPlan"),
|
|
546
586
|
booleanField$1("webSearch"),
|
|
547
587
|
booleanField$1("showSidebarQuota"),
|
|
@@ -675,6 +715,7 @@ window.__ModuleLoader__.load({
|
|
|
675
715
|
requestTimeoutMs: this.field("requestTimeoutMs"),
|
|
676
716
|
streamIdleTimeoutMs: this.field("streamIdleTimeoutMs"),
|
|
677
717
|
transportMaxRetries: this.field("transportMaxRetries"),
|
|
718
|
+
offloadSeenImagesForCache: this.field("offloadSeenImagesForCache"),
|
|
678
719
|
filterModelsByPlan: this.field("filterModelsByPlan"),
|
|
679
720
|
webSearch: this.field("webSearch"),
|
|
680
721
|
showSidebarQuota: this.field("showSidebarQuota"),
|
|
@@ -1082,7 +1123,8 @@ window.__ModuleLoader__.load({
|
|
|
1082
1123
|
shaped.push({
|
|
1083
1124
|
id: entry.id,
|
|
1084
1125
|
name: entry.name,
|
|
1085
|
-
...typeof entry.tier === "string" ? { tier: entry.tier } : {}
|
|
1126
|
+
...typeof entry.tier === "string" ? { tier: entry.tier } : {},
|
|
1127
|
+
...typeof entry.allowance === "number" && Number.isFinite(entry.allowance) ? { allowance: entry.allowance } : {}
|
|
1086
1128
|
});
|
|
1087
1129
|
}
|
|
1088
1130
|
this.catalogModels = shaped;
|
|
@@ -1184,6 +1226,34 @@ window.__ModuleLoader__.load({
|
|
|
1184
1226
|
}
|
|
1185
1227
|
};
|
|
1186
1228
|
//#endregion
|
|
1229
|
+
//#region src/display-format.ts
|
|
1230
|
+
/** Shared, dependency-free formatting for Host commands and browser surfaces. */
|
|
1231
|
+
function formatMoney(value) {
|
|
1232
|
+
return `$${value.toFixed(2)}`;
|
|
1233
|
+
}
|
|
1234
|
+
function formatMoneyExact(value) {
|
|
1235
|
+
return `$${value.toFixed(4)}`;
|
|
1236
|
+
}
|
|
1237
|
+
function formatTokensCompact(value) {
|
|
1238
|
+
if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`;
|
|
1239
|
+
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
1240
|
+
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
1241
|
+
return String(value);
|
|
1242
|
+
}
|
|
1243
|
+
/** The input is already in percent units; callers add the percent sign. */
|
|
1244
|
+
function formatSuccessRate(value) {
|
|
1245
|
+
return String(Number(value.toFixed(2)));
|
|
1246
|
+
}
|
|
1247
|
+
/** One window's fill ratio in [0, 1]; zero when uncapped. */
|
|
1248
|
+
function windowRatio(used, cap) {
|
|
1249
|
+
if (cap <= 0) return 0;
|
|
1250
|
+
return Math.max(0, Math.min(1, used / cap));
|
|
1251
|
+
}
|
|
1252
|
+
/** Local date-time, with the empty state chosen by the caller. */
|
|
1253
|
+
function formatResetAt(ms) {
|
|
1254
|
+
return ms <= 0 ? "" : new Date(ms).toLocaleString();
|
|
1255
|
+
}
|
|
1256
|
+
//#endregion
|
|
1187
1257
|
//#region src/client/usage.ts
|
|
1188
1258
|
const IDLE$1 = {
|
|
1189
1259
|
status: "idle",
|
|
@@ -1266,39 +1336,6 @@ window.__ModuleLoader__.load({
|
|
|
1266
1336
|
for (const listener of this.listeners) listener();
|
|
1267
1337
|
}
|
|
1268
1338
|
};
|
|
1269
|
-
/** Format a dollar amount compactly (2 decimals). */
|
|
1270
|
-
function formatMoney(value) {
|
|
1271
|
-
return `$${value.toFixed(2)}`;
|
|
1272
|
-
}
|
|
1273
|
-
/** Format a dollar amount precisely (4 decimals) for small totals. */
|
|
1274
|
-
function formatMoneyExact(value) {
|
|
1275
|
-
return `$${value.toFixed(4)}`;
|
|
1276
|
-
}
|
|
1277
|
-
/** Format a large token count compactly (1.9M style). */
|
|
1278
|
-
function formatTokensCompact(value) {
|
|
1279
|
-
if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`;
|
|
1280
|
-
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
1281
|
-
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
1282
|
-
return String(value);
|
|
1283
|
-
}
|
|
1284
|
-
/**
|
|
1285
|
-
* Success-rate percentage (already in percent units, e.g. 99.96): at most two
|
|
1286
|
-
* decimals, trailing zeros trimmed — `100` stays `100`, not `100.00`, and
|
|
1287
|
-
* `99.965552876334` becomes `99.97`. The `%` suffix is the caller's job.
|
|
1288
|
-
*/
|
|
1289
|
-
function formatSuccessRate(value) {
|
|
1290
|
-
return String(Number(value.toFixed(2)));
|
|
1291
|
-
}
|
|
1292
|
-
/** One window's fill ratio in [0, 1]; 0 when uncapped. */
|
|
1293
|
-
function windowRatio(used, cap) {
|
|
1294
|
-
if (cap <= 0) return 0;
|
|
1295
|
-
return Math.max(0, Math.min(1, used / cap));
|
|
1296
|
-
}
|
|
1297
|
-
/** Format a millis timestamp as a local short date-time; empty when unset. */
|
|
1298
|
-
function formatResetAt(ms) {
|
|
1299
|
-
if (ms <= 0) return "";
|
|
1300
|
-
return new Date(ms).toLocaleString();
|
|
1301
|
-
}
|
|
1302
1339
|
/** Host facts drive the usage card, including keys invisible to browser credentials. */
|
|
1303
1340
|
function usageCardState(usage) {
|
|
1304
1341
|
const accounts = usage.report?.accounts ?? [];
|
|
@@ -1925,7 +1962,7 @@ window.__ModuleLoader__.load({
|
|
|
1925
1962
|
* separate instance so catalog boundary errors name `commandcode/models`,
|
|
1926
1963
|
* not the report endpoint.
|
|
1927
1964
|
*/
|
|
1928
|
-
const { record: catalogRecord, stringField: catalogString } = makeBoundaryValidator("commandcode/models result:");
|
|
1965
|
+
const { record: catalogRecord, stringField: catalogString, numberField: catalogNumber } = makeBoundaryValidator("commandcode/models result:");
|
|
1929
1966
|
/** Parse one untrusted boundary value into a {@link CommandCodeCatalogModel}. */
|
|
1930
1967
|
function parseCatalogModel(value) {
|
|
1931
1968
|
const source = catalogRecord(value, "model");
|
|
@@ -1934,6 +1971,7 @@ window.__ModuleLoader__.load({
|
|
|
1934
1971
|
name: catalogString(source, "name", "model.name")
|
|
1935
1972
|
};
|
|
1936
1973
|
if (source.tier !== void 0) model.tier = catalogString(source, "tier", "model.tier");
|
|
1974
|
+
if (source.allowance !== void 0) model.allowance = catalogNumber(source, "allowance", "model.allowance");
|
|
1937
1975
|
return model;
|
|
1938
1976
|
}
|
|
1939
1977
|
/** Parse the wire result into a {@link CommandCodeCatalog}. */
|
|
@@ -2085,6 +2123,24 @@ window.__ModuleLoader__.load({
|
|
|
2085
2123
|
]
|
|
2086
2124
|
};
|
|
2087
2125
|
//#endregion
|
|
2126
|
+
//#region src/plan-tiers.ts
|
|
2127
|
+
/** Shared plan-tier presentation facts; safe to inline into the browser bundle. */
|
|
2128
|
+
const PLAN_TIER_ORDER = [
|
|
2129
|
+
"go",
|
|
2130
|
+
"goat",
|
|
2131
|
+
"pro",
|
|
2132
|
+
"provider",
|
|
2133
|
+
"max"
|
|
2134
|
+
];
|
|
2135
|
+
const PLAN_LABELS = {
|
|
2136
|
+
go: "Go",
|
|
2137
|
+
goat: "GOAT",
|
|
2138
|
+
pro: "Pro",
|
|
2139
|
+
provider: "Provider",
|
|
2140
|
+
max: "Max"
|
|
2141
|
+
};
|
|
2142
|
+
Object.fromEntries(PLAN_TIER_ORDER.map((tier, index) => [tier, index]));
|
|
2143
|
+
//#endregion
|
|
2088
2144
|
//#region src/client/model-select.ts
|
|
2089
2145
|
/**
|
|
2090
2146
|
* Model-select helpers for the settings page's model editors (browser half).
|
|
@@ -2095,26 +2151,12 @@ window.__ModuleLoader__.load({
|
|
|
2095
2151
|
* detection, tier grouping — lives here, React-free, so node tests can drive
|
|
2096
2152
|
* it directly.
|
|
2097
2153
|
*
|
|
2098
|
-
*
|
|
2099
|
-
*
|
|
2100
|
-
* (tier key → heading label) rather than an import of src/capabilities.ts.
|
|
2101
|
-
* When upstream adds a plan tier, extend BOTH tables.
|
|
2154
|
+
* The shared tier module is dependency-free and inlined into the client bundle;
|
|
2155
|
+
* importing the Host's full capability snapshot here would bloat that bundle.
|
|
2102
2156
|
*
|
|
2103
2157
|
* @module dsh-commandcode-provider/model-select
|
|
2104
2158
|
*/
|
|
2105
2159
|
/**
|
|
2106
|
-
* Minimum plan tier → dropdown section heading. Mirrors the Host-side
|
|
2107
|
-
* `KNOWN_PLANS` tiers + `PLAN_LABELS` in src/capabilities.ts; an unknown tier
|
|
2108
|
-
* key falls back to the raw key rather than vanishing the row.
|
|
2109
|
-
*/
|
|
2110
|
-
const TIER_HEADINGS = {
|
|
2111
|
-
go: "Go",
|
|
2112
|
-
goat: "GOAT",
|
|
2113
|
-
pro: "Pro",
|
|
2114
|
-
provider: "Provider",
|
|
2115
|
-
max: "Max"
|
|
2116
|
-
};
|
|
2117
|
-
/**
|
|
2118
2160
|
* The dropdown section heading for a catalog model id, or undefined for
|
|
2119
2161
|
* models outside the known plan tiers (unmapped models and stale ids render
|
|
2120
2162
|
* unheaded). `knownPlans` is the Host-side `KNOWN_PLANS` table, threaded in
|
|
@@ -2123,7 +2165,7 @@ window.__ModuleLoader__.load({
|
|
|
2123
2165
|
function tierHeadingFor(modelId, knownPlans) {
|
|
2124
2166
|
const tier = knownPlans[modelId];
|
|
2125
2167
|
if (tier === void 0) return void 0;
|
|
2126
|
-
return
|
|
2168
|
+
return PLAN_LABELS[tier] ?? tier;
|
|
2127
2169
|
}
|
|
2128
2170
|
/**
|
|
2129
2171
|
* Whether `text` matches `query` as a case-insensitive substring over the
|
|
@@ -2146,7 +2188,8 @@ window.__ModuleLoader__.load({
|
|
|
2146
2188
|
const options = catalog.filter((model) => matchesModelQuery(model, query)).map((model) => ({
|
|
2147
2189
|
value: model.id,
|
|
2148
2190
|
label: model.name,
|
|
2149
|
-
stale: false
|
|
2191
|
+
stale: false,
|
|
2192
|
+
...model.allowance === void 0 ? {} : { allowance: model.allowance }
|
|
2150
2193
|
}));
|
|
2151
2194
|
const seen = new Set(catalogIds);
|
|
2152
2195
|
for (const id of selected) {
|
|
@@ -2219,7 +2262,7 @@ window.__ModuleLoader__.load({
|
|
|
2219
2262
|
}
|
|
2220
2263
|
//#endregion
|
|
2221
2264
|
//#region package.json
|
|
2222
|
-
var version = "0.
|
|
2265
|
+
var version = "0.12.1";
|
|
2223
2266
|
var repository = {
|
|
2224
2267
|
"type": "git",
|
|
2225
2268
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -2415,7 +2458,8 @@ window.__ModuleLoader__.load({
|
|
|
2415
2458
|
"apiBase",
|
|
2416
2459
|
"requestTimeoutMs",
|
|
2417
2460
|
"streamIdleTimeoutMs",
|
|
2418
|
-
"transportMaxRetries"
|
|
2461
|
+
"transportMaxRetries",
|
|
2462
|
+
"offloadSeenImagesForCache"
|
|
2419
2463
|
];
|
|
2420
2464
|
/** The "customized" tag and reset link a staged field shows once it differs from the default. */
|
|
2421
2465
|
function FieldOverride({ label, state, disabled, t, onReset }) {
|
|
@@ -2612,7 +2656,7 @@ window.__ModuleLoader__.load({
|
|
|
2612
2656
|
});
|
|
2613
2657
|
}
|
|
2614
2658
|
/** One pool account's facts (identity, totals, credits, window limits). */
|
|
2615
|
-
function AccountReport({ entry, fetchedAt, t,
|
|
2659
|
+
function AccountReport({ entry, fetchedAt, t, headless = false }) {
|
|
2616
2660
|
const report = entry.report;
|
|
2617
2661
|
const account = report.account;
|
|
2618
2662
|
const accountName = account === void 0 ? "" : account.userName || account.name;
|
|
@@ -2669,7 +2713,7 @@ window.__ModuleLoader__.load({
|
|
|
2669
2713
|
}) : null
|
|
2670
2714
|
]
|
|
2671
2715
|
}) : null,
|
|
2672
|
-
|
|
2716
|
+
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2673
2717
|
className: "cc-usageStats cc-usageStatsActivity",
|
|
2674
2718
|
children: [
|
|
2675
2719
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
@@ -2693,7 +2737,7 @@ window.__ModuleLoader__.load({
|
|
|
2693
2737
|
})
|
|
2694
2738
|
]
|
|
2695
2739
|
}) : null,
|
|
2696
|
-
|
|
2740
|
+
credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2697
2741
|
className: "cc-usageStats cc-usageStatsBalance",
|
|
2698
2742
|
children: [
|
|
2699
2743
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
@@ -2819,6 +2863,11 @@ window.__ModuleLoader__.load({
|
|
|
2819
2863
|
className: "cc-badge",
|
|
2820
2864
|
children: t("modelStale")
|
|
2821
2865
|
}) : null,
|
|
2866
|
+
option.allowance !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2867
|
+
className: "cc-badgeMuted",
|
|
2868
|
+
title: t("modelAllowanceTitle"),
|
|
2869
|
+
children: t("modelAllowance", { amount: option.allowance })
|
|
2870
|
+
}) : null,
|
|
2822
2871
|
!selected.includes(option.value) && ownerOf?.(option.value) !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2823
2872
|
className: "cc-badgeMuted",
|
|
2824
2873
|
children: t("accountModelOwner", { name: ownerOf(option.value) })
|
|
@@ -2917,11 +2966,13 @@ window.__ModuleLoader__.load({
|
|
|
2917
2966
|
}
|
|
2918
2967
|
/**
|
|
2919
2968
|
* One titled group of rows. No card surface: like the harness's own settings
|
|
2920
|
-
* pages, a group is a heading over
|
|
2969
|
+
* pages, a group is a heading over its rows. The hairline between sections is
|
|
2970
|
+
* `cc-groupDivided`, so it belongs to every group but the first one on the page
|
|
2971
|
+
* (accounts opens it) and never to the rows inside a group.
|
|
2921
2972
|
*/
|
|
2922
|
-
function SettingsGroup({ title, action, children }) {
|
|
2973
|
+
function SettingsGroup({ title, action, divided = false, children }) {
|
|
2923
2974
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2924
|
-
className: "cc-group",
|
|
2975
|
+
className: divided ? "cc-group cc-groupDivided" : "cc-group",
|
|
2925
2976
|
"aria-label": title,
|
|
2926
2977
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2927
2978
|
className: "cc-groupHead",
|
|
@@ -2942,6 +2993,7 @@ window.__ModuleLoader__.load({
|
|
|
2942
2993
|
function ModelsCard({ state, disabled, t, onEdit, onReset, onSelect, onClear }) {
|
|
2943
2994
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
2944
2995
|
title: t("modelsTitle"),
|
|
2996
|
+
divided: true,
|
|
2945
2997
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2946
2998
|
id: "cc-filter-models-by-plan",
|
|
2947
2999
|
label: t("filterModelsByPlan"),
|
|
@@ -2968,6 +3020,7 @@ window.__ModuleLoader__.load({
|
|
|
2968
3020
|
function PrivacyCard({ state, disabled, t, onEdit, onReset }) {
|
|
2969
3021
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsGroup, {
|
|
2970
3022
|
title: t("privacyTitle"),
|
|
3023
|
+
divided: true,
|
|
2971
3024
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2972
3025
|
id: "cc-zdr",
|
|
2973
3026
|
label: t("zdr"),
|
|
@@ -2985,6 +3038,7 @@ window.__ModuleLoader__.load({
|
|
|
2985
3038
|
function IntegrationsCard({ state, disabled, t, onEdit, onReset }) {
|
|
2986
3039
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
2987
3040
|
title: t("integrationsTitle"),
|
|
3041
|
+
divided: true,
|
|
2988
3042
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2989
3043
|
id: "cc-web-search",
|
|
2990
3044
|
label: t("webSearch"),
|
|
@@ -3018,7 +3072,7 @@ window.__ModuleLoader__.load({
|
|
|
3018
3072
|
const overridden = ADVANCED_FIELDS.filter((field) => state[field].overridden).length;
|
|
3019
3073
|
const invalid = ADVANCED_FIELDS.some((field) => state[field].invalid);
|
|
3020
3074
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3021
|
-
className: "cc-group",
|
|
3075
|
+
className: "cc-group cc-groupDivided",
|
|
3022
3076
|
"aria-label": t("advancedSettings"),
|
|
3023
3077
|
children: [
|
|
3024
3078
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -3098,6 +3152,17 @@ window.__ModuleLoader__.load({
|
|
|
3098
3152
|
onEdit: (text) => onEdit("transportMaxRetries", text),
|
|
3099
3153
|
onReset: () => onReset("transportMaxRetries"),
|
|
3100
3154
|
t
|
|
3155
|
+
}),
|
|
3156
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
3157
|
+
id: "cc-offload-seen-images-for-cache",
|
|
3158
|
+
label: t("offloadSeenImagesForCache"),
|
|
3159
|
+
hint: t("offloadSeenImagesForCacheHint"),
|
|
3160
|
+
state: state.offloadSeenImagesForCache,
|
|
3161
|
+
disabled,
|
|
3162
|
+
defaultChecked: false,
|
|
3163
|
+
onEdit: (text) => onEdit("offloadSeenImagesForCache", text),
|
|
3164
|
+
onReset: () => onReset("offloadSeenImagesForCache"),
|
|
3165
|
+
t
|
|
3101
3166
|
})
|
|
3102
3167
|
]
|
|
3103
3168
|
}) : null,
|
|
@@ -3161,12 +3226,33 @@ window.__ModuleLoader__.load({
|
|
|
3161
3226
|
}
|
|
3162
3227
|
return rows;
|
|
3163
3228
|
}
|
|
3164
|
-
/**
|
|
3229
|
+
/**
|
|
3230
|
+
* The row's status dot. Read top-down, so a problem always outranks "serving":
|
|
3231
|
+
* red for a key the Host rejected, amber while a rate limit is cooling down,
|
|
3232
|
+
* green for the account that is actually serving requests. A standby (or not
|
|
3233
|
+
* yet configured) account draws NOTHING — `cc-tabDotBlank` is an unfilled,
|
|
3234
|
+
* borderless dot that only holds the column so the account names stay aligned
|
|
3235
|
+
* with the rows that do have a verdict. Green therefore means "in use", not
|
|
3236
|
+
* "healthy": a wall of green dots would say nothing.
|
|
3237
|
+
*/
|
|
3165
3238
|
function statusDotClass(row) {
|
|
3166
3239
|
const entry = row.usage;
|
|
3167
|
-
if (
|
|
3240
|
+
if (entry?.mark === "invalid-credential") return "cc-tabDot cc-tabDotError";
|
|
3168
3241
|
if (entry !== void 0 && (entry.mark !== "" || entry.cooldownUntil > 0)) return "cc-tabDot cc-tabDotWarn";
|
|
3169
|
-
return "cc-tabDot cc-tabDotOk";
|
|
3242
|
+
if (row.configured && entry?.active === true) return "cc-tabDot cc-tabDotOk";
|
|
3243
|
+
return "cc-tabDot cc-tabDotBlank";
|
|
3244
|
+
}
|
|
3245
|
+
/**
|
|
3246
|
+
* The same verdict as a sentence, for the dot's tooltip. Undefined on a blank
|
|
3247
|
+
* dot: there is no verdict to read out, and a hover hotspot on an invisible
|
|
3248
|
+
* 8px mark would only invite the question it cannot usefully answer.
|
|
3249
|
+
*/
|
|
3250
|
+
function statusDotTitle(row, t) {
|
|
3251
|
+
const entry = row.usage;
|
|
3252
|
+
if (entry?.mark === "invalid-credential") return t("usageInvalidKey");
|
|
3253
|
+
if (entry !== void 0 && (entry.mark !== "" || entry.cooldownUntil > 0)) return t("usageCooldown");
|
|
3254
|
+
if (!row.configured) return t("apiKeyUnset");
|
|
3255
|
+
return entry?.active === true ? t("usageActive") : void 0;
|
|
3170
3256
|
}
|
|
3171
3257
|
/** One compact quota meter for a row's summary line. */
|
|
3172
3258
|
function MiniMeter({ label, limit }) {
|
|
@@ -3390,6 +3476,7 @@ window.__ModuleLoader__.load({
|
|
|
3390
3476
|
children: [
|
|
3391
3477
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3392
3478
|
className: statusDotClass(row),
|
|
3479
|
+
title: statusDotTitle(row, t),
|
|
3393
3480
|
"aria-hidden": "true"
|
|
3394
3481
|
}),
|
|
3395
3482
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -4467,8 +4554,7 @@ window.__ModuleLoader__.load({
|
|
|
4467
4554
|
const UNREPORTED = "—";
|
|
4468
4555
|
/** Local reset time; empty when the endpoint reported none. */
|
|
4469
4556
|
function resetText(ms) {
|
|
4470
|
-
|
|
4471
|
-
return new Date(ms).toLocaleString();
|
|
4557
|
+
return formatResetAt(ms);
|
|
4472
4558
|
}
|
|
4473
4559
|
/** Local short date; empty when unset. */
|
|
4474
4560
|
function dateText(ms) {
|
|
@@ -4901,14 +4987,6 @@ window.__ModuleLoader__.load({
|
|
|
4901
4987
|
]
|
|
4902
4988
|
});
|
|
4903
4989
|
}
|
|
4904
|
-
/** One usage tile. */
|
|
4905
|
-
function StatTile({ stat, label }) {
|
|
4906
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile, {
|
|
4907
|
-
label,
|
|
4908
|
-
value: stat.value,
|
|
4909
|
-
sub: stat.sub
|
|
4910
|
-
});
|
|
4911
|
-
}
|
|
4912
4990
|
/** The rotation/credential badge line for one account. */
|
|
4913
4991
|
function markText(account, text) {
|
|
4914
4992
|
if (account.mark === void 0) return "";
|
|
@@ -5055,9 +5133,10 @@ window.__ModuleLoader__.load({
|
|
|
5055
5133
|
children: text("usage")
|
|
5056
5134
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5057
5135
|
className: "ccp-tiles",
|
|
5058
|
-
children: account.stats.map((stat) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
5059
|
-
stat,
|
|
5060
|
-
|
|
5136
|
+
children: account.stats.map((stat) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile, {
|
|
5137
|
+
label: text(stat.label),
|
|
5138
|
+
value: stat.value,
|
|
5139
|
+
sub: stat.sub
|
|
5061
5140
|
}, stat.label))
|
|
5062
5141
|
})]
|
|
5063
5142
|
}) : null
|
|
@@ -6134,10 +6213,12 @@ window.__ModuleLoader__.load({
|
|
|
6134
6213
|
* The settings-page stylesheet.
|
|
6135
6214
|
*
|
|
6136
6215
|
* The metrics are the harness's own settings pages', read out of the 0.1.7
|
|
6137
|
-
* bundles so the page sits beside General and Models without looking foreign:
|
|
6138
|
-
* row is
|
|
6139
|
-
*
|
|
6140
|
-
*
|
|
6216
|
+
* bundles so the page sits beside General and Models without looking foreign:
|
|
6217
|
+
* a row is 12px of padding, its title 14/22 label-primary and its description
|
|
6218
|
+
* 12/18 label-tertiary, the control on the right. The hairline rhythm is
|
|
6219
|
+
* section-level: a 0.5px border-l2 rule separates the GROUPS (see
|
|
6220
|
+
* `cc-groupDivided`), never the rows inside one, so a group's rows read as one
|
|
6221
|
+
* list. Selectors and buttons are capsules (36px / 28px compact), inputs are
|
|
6141
6222
|
* 32px with an 8px radius and a 0.5px border-l4 edge, a card is a 0.5px
|
|
6142
6223
|
* border-l4 outline with a 16px radius, and a nested panel is a
|
|
6143
6224
|
* bg-module-platform fill with a 12px radius. Errors use state-error-primary.
|
|
@@ -6149,6 +6230,7 @@ window.__ModuleLoader__.load({
|
|
|
6149
6230
|
.cc-readOnly{margin:8px 0 0;color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-tertiary));font-size:12px;line-height:18px}
|
|
6150
6231
|
.cc-spacer{flex:1}
|
|
6151
6232
|
.cc-group{flex-direction:column;display:flex;margin-top:28px}
|
|
6233
|
+
.cc-groupDivided{margin-top:24px;padding-top:20px;border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
6152
6234
|
.cc-groupHead{align-items:center;gap:8px;display:flex;min-height:28px;padding-bottom:4px}
|
|
6153
6235
|
.cc-groupTitle{margin:0;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}
|
|
6154
6236
|
.cc-groupDesc{margin:0 0 12px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
@@ -6156,9 +6238,8 @@ window.__ModuleLoader__.load({
|
|
|
6156
6238
|
.cc-disclosure{width:100%;padding:0 0 4px;border:0;background:0 0;font:inherit;text-align:left;cursor:pointer;border-radius:6px}
|
|
6157
6239
|
.cc-disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
6158
6240
|
.cc-rows{flex-direction:column;display:flex}
|
|
6159
|
-
.cc-row{align-items:center;gap:8px;display:flex;padding:
|
|
6160
|
-
.cc-
|
|
6161
|
-
.cc-rowFlush{padding:0;border-bottom:0}
|
|
6241
|
+
.cc-row{align-items:center;gap:8px;display:flex;padding:12px 0}
|
|
6242
|
+
.cc-rowFlush{padding:0}
|
|
6162
6243
|
.cc-rowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:32px;display:flex}
|
|
6163
6244
|
.cc-rowTitleLine{align-items:center;gap:8px;display:flex;min-width:0}
|
|
6164
6245
|
.cc-rowTitle{min-width:0;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}
|
|
@@ -6174,7 +6255,7 @@ window.__ModuleLoader__.load({
|
|
|
6174
6255
|
.cc-linkButton:disabled{cursor:default;opacity:.4}
|
|
6175
6256
|
.cc-linkButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
6176
6257
|
.cc-badges{align-items:center;gap:8px;display:inline-flex}
|
|
6177
|
-
.cc-badge,.cc-badgeMuted{flex:none;align-items:center;display:inline-flex;white-space:nowrap;border-radius:999px;corner-shape:round;padding:1px 8px;font-size:
|
|
6258
|
+
.cc-badge,.cc-badgeMuted{flex:none;align-items:center;display:inline-flex;white-space:nowrap;border-radius:999px;corner-shape:round;padding:1px 8px;font-size:12px;font-weight:500;line-height:17px}
|
|
6178
6259
|
.cc-badge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
6179
6260
|
.cc-badgeMuted{border:.5px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-tertiary)}
|
|
6180
6261
|
.cc-badgeWarn{background:var(--dsw-alias-state-warn-tertiary,var(--dsw-alias-bg-module-platform));color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-secondary))}
|
|
@@ -6248,6 +6329,7 @@ window.__ModuleLoader__.load({
|
|
|
6248
6329
|
.cc-kebab{width:3px;height:3px;border-radius:50%;corner-shape:round;background:currentColor;box-shadow:0 -5px 0 currentColor,0 5px 0 currentColor}
|
|
6249
6330
|
.cc-tabDot{flex-shrink:0;width:8px;height:8px;border-radius:50%;corner-shape:round}
|
|
6250
6331
|
.cc-tabDotOk{background:var(--dsw-alias-state-success-primary)}
|
|
6332
|
+
.cc-tabDotBlank{background:0 0;border:0}
|
|
6251
6333
|
.cc-tabDotWarn{background:var(--dsw-alias-state-warn-primary,#d97706)}
|
|
6252
6334
|
.cc-tabDotError{background:var(--dsw-alias-state-error-primary)}
|
|
6253
6335
|
.cc-accountMeters{flex-wrap:wrap;gap:6px 20px;display:flex;padding-left:16px}
|
|
@@ -6513,12 +6595,14 @@ window.__ModuleLoader__.load({
|
|
|
6513
6595
|
apiKeyLocked: "密钥由只读来源提供",
|
|
6514
6596
|
apiBase: "API 地址",
|
|
6515
6597
|
apiBaseHint: "默认 https://api.commandcode.ai,一般无需修改。",
|
|
6516
|
-
requestTimeoutMs: "
|
|
6517
|
-
requestTimeoutMsHint: "等待响应首个字节的超时;默认
|
|
6518
|
-
streamIdleTimeoutMs: "
|
|
6519
|
-
streamIdleTimeoutMsHint: "生成流停滞多久视为断连;默认
|
|
6598
|
+
requestTimeoutMs: "请求超时(秒)",
|
|
6599
|
+
requestTimeoutMsHint: "等待响应首个字节的超时;默认 300 秒(与官方 CLI 一致)。Provider API 路由要等上游模型吐出第一个 token 才返回响应头,上游慢时仍可能不够;报超时时可按提示调高。",
|
|
6600
|
+
streamIdleTimeoutMs: "流空闲超时(秒)",
|
|
6601
|
+
streamIdleTimeoutMsHint: "生成流停滞多久视为断连;默认 300 秒(长思考模型可静默数分钟,默认值刻意放宽)。",
|
|
6520
6602
|
transportMaxRetries: "网络失败重试次数",
|
|
6521
6603
|
transportMaxRetriesHint: "连接失败时自动重试几次,默认 5(各次等待 0.5+1+2+4+8 秒,合计约 15 秒)。超过后直接报错,不再按指数退避长时间等待;填 0 表示不重试。限流、5xx 等由服务端要求重试的失败不受此项影响。",
|
|
6604
|
+
offloadSeenImagesForCache: "已看过的图片不再重复发送",
|
|
6605
|
+
offloadSeenImagesForCacheHint: "仅 CLI 传输。模型看过图片并回复后,永久用文字占位替代旧图片,以减少新增图片时的缓存回退。模型如需重看旧图,必须重新读取文件或让用户重新附加。默认关闭。",
|
|
6522
6606
|
advancedSettings: "高级设置",
|
|
6523
6607
|
advancedSettingsHint: "API 地址和网络参数,一般无需修改。",
|
|
6524
6608
|
advancedOverriddenOne: "已自定义 1 项",
|
|
@@ -6539,6 +6623,8 @@ window.__ModuleLoader__.load({
|
|
|
6539
6623
|
modelSearchPlaceholder: "搜索模型…",
|
|
6540
6624
|
modelSearchEmpty: "没有匹配的模型。",
|
|
6541
6625
|
modelStale: "已下架",
|
|
6626
|
+
modelAllowance: "${amount}/月",
|
|
6627
|
+
modelAllowanceTitle: "该模型每月可用的额度(美元)。套餐内所有模型共用同一个月度额度池,这不是额外赠送的额度。",
|
|
6542
6628
|
visibleModelsTitle: "模型白名单",
|
|
6543
6629
|
visibleModelsHint: "勾选要保留的模型,模型选择器就只列出这些;一个都不勾选时则显示全部模型。保存后,下次打开模型选择器生效。",
|
|
6544
6630
|
visibleModelsPick: "选择要保留的模型…",
|
|
@@ -6665,12 +6751,14 @@ window.__ModuleLoader__.load({
|
|
|
6665
6751
|
apiKeyLocked: "Key provided by a read-only source",
|
|
6666
6752
|
apiBase: "API base URL",
|
|
6667
6753
|
apiBaseHint: "Defaults to https://api.commandcode.ai; usually leave as-is.",
|
|
6668
|
-
requestTimeoutMs: "Request timeout (
|
|
6669
|
-
requestTimeoutMsHint: "Time to wait for the first response byte; default
|
|
6670
|
-
streamIdleTimeoutMs: "Stream idle timeout (
|
|
6671
|
-
streamIdleTimeoutMsHint: "How long a stalled stream is treated as dead; default
|
|
6754
|
+
requestTimeoutMs: "Request timeout (seconds)",
|
|
6755
|
+
requestTimeoutMsHint: "Time to wait for the first response byte; default 300 s (the official CLI’s). The Provider API route withholds headers until the upstream model emits its first token, so a slow upstream can still outlast the default; raise it when a timeout message suggests it.",
|
|
6756
|
+
streamIdleTimeoutMs: "Stream idle timeout (seconds)",
|
|
6757
|
+
streamIdleTimeoutMsHint: "How long a stalled stream is treated as dead; default 300 s (deliberately generous — long-thinking models can stay silent for minutes).",
|
|
6672
6758
|
transportMaxRetries: "Transport retries",
|
|
6673
6759
|
transportMaxRetriesHint: "How many times a failed connection is retried automatically; default 5 (waits of 0.5+1+2+4+8s, ~15s in total). After that the failure is reported instead of waiting on the exponential backoff. 0 disables the retries. Rate limits and 5xx answers keep their own, longer retry window.",
|
|
6760
|
+
offloadSeenImagesForCache: "Stop replaying images already seen",
|
|
6761
|
+
offloadSeenImagesForCacheHint: "CLI transport only. After this model responds to an image, durably replace older pixels with text to avoid cache rollback when another image arrives. To inspect an old image again, the model must reread its file or the user must attach it again. Off by default.",
|
|
6674
6762
|
advancedSettings: "Advanced",
|
|
6675
6763
|
advancedSettingsHint: "API base URL and network limits; usually leave as-is.",
|
|
6676
6764
|
advancedOverriddenOne: "1 customized",
|
|
@@ -6691,6 +6779,8 @@ window.__ModuleLoader__.load({
|
|
|
6691
6779
|
modelSearchPlaceholder: "Search models…",
|
|
6692
6780
|
modelSearchEmpty: "No matching models.",
|
|
6693
6781
|
modelStale: "Retired",
|
|
6782
|
+
modelAllowance: "${amount}/mo",
|
|
6783
|
+
modelAllowanceTitle: "Monthly usage this model can draw, in USD. Every model shares one monthly pool — this is not extra credit on top of the plan.",
|
|
6694
6784
|
visibleModelsTitle: "Model allowlist",
|
|
6695
6785
|
visibleModelsHint: "Check the models you want to keep, and model pickers will list only those. If nothing is checked, every model is shown. After saving, the change applies the next time you open a model picker.",
|
|
6696
6786
|
visibleModelsPick: "Select models to keep…",
|
|
@@ -6808,32 +6898,16 @@ window.__ModuleLoader__.load({
|
|
|
6808
6898
|
};
|
|
6809
6899
|
//#endregion
|
|
6810
6900
|
//#region src/client/index.ts
|
|
6811
|
-
/**
|
|
6812
|
-
function
|
|
6813
|
-
if (typeof document === "undefined") return;
|
|
6814
|
-
if (document.querySelector(`style[data-plugin-css="@mars-sea/dsh-commandcode-provider/CommandCodeSettingsPage.module.css"]`) !== null) return;
|
|
6815
|
-
const tag = document.createElement("style");
|
|
6816
|
-
tag.dataset.plugin = "@mars-sea/dsh-commandcode-provider";
|
|
6817
|
-
tag.dataset.pluginCss = PAGE_CSS_ID;
|
|
6818
|
-
tag.textContent = PAGE_CSS;
|
|
6819
|
-
document.head.appendChild(tag);
|
|
6820
|
-
}
|
|
6821
|
-
/**
|
|
6822
|
-
* Install the plans & quota panel's stylesheet and return its disposer, for
|
|
6823
|
-
* `ctx.effect` to own. Keyed by its own `data-plugin-css` id, so a second
|
|
6824
|
-
* surface asking for it later is a no-op.
|
|
6825
|
-
*/
|
|
6826
|
-
function injectPanelCss() {
|
|
6901
|
+
/** Install one stylesheet by id and return a disposer for its owning effect. */
|
|
6902
|
+
function injectCss(id, css) {
|
|
6827
6903
|
if (typeof document === "undefined") return () => {};
|
|
6828
|
-
if (document.querySelector(`style[data-plugin-css="
|
|
6904
|
+
if (document.querySelector(`style[data-plugin-css="${id}"]`) !== null) return () => {};
|
|
6829
6905
|
const tag = document.createElement("style");
|
|
6830
6906
|
tag.dataset.plugin = "@mars-sea/dsh-commandcode-provider";
|
|
6831
|
-
tag.dataset.pluginCss =
|
|
6832
|
-
tag.textContent =
|
|
6907
|
+
tag.dataset.pluginCss = id;
|
|
6908
|
+
tag.textContent = css;
|
|
6833
6909
|
document.head.appendChild(tag);
|
|
6834
|
-
return () =>
|
|
6835
|
-
tag.remove();
|
|
6836
|
-
};
|
|
6910
|
+
return () => tag.remove();
|
|
6837
6911
|
}
|
|
6838
6912
|
/**
|
|
6839
6913
|
* Plans & quota panel id. It is the layout's `MainPanelId`: one string shared
|
|
@@ -6859,7 +6933,7 @@ window.__ModuleLoader__.load({
|
|
|
6859
6933
|
* list at the bottom of this file.
|
|
6860
6934
|
*/
|
|
6861
6935
|
function apply(ctx) {
|
|
6862
|
-
|
|
6936
|
+
injectCss(PAGE_CSS_ID, PAGE_CSS);
|
|
6863
6937
|
ctx.effect(() => ctx.locale.register("settings.commandcode", {
|
|
6864
6938
|
zh,
|
|
6865
6939
|
en
|
|
@@ -7108,7 +7182,7 @@ window.__ModuleLoader__.load({
|
|
|
7108
7182
|
ctx.get("layout")?.selectPanel(null);
|
|
7109
7183
|
}
|
|
7110
7184
|
});
|
|
7111
|
-
ctx.effect(() =>
|
|
7185
|
+
ctx.effect(() => injectCss(PANEL_CSS_ID, PANEL_CSS), "dsh-commandcode-provider: panel styles");
|
|
7112
7186
|
try {
|
|
7113
7187
|
ctx.slots.inject("main", () => ctx.slots.register({
|
|
7114
7188
|
name: "main",
|