@mars-sea/dsh-commandcode-provider 0.10.3 → 0.10.5
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 +37 -0
- package/README.md +63 -1
- package/README.zh-CN.md +63 -1
- package/lib/client.js +115 -28
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +219 -3
- package/lib/index.js +546 -22
- package/lib/index.js.map +1 -1
- package/package.json +4 -2
package/lib/index.js
CHANGED
|
@@ -220,16 +220,38 @@ var CommandCodeAccountPool = class {
|
|
|
220
220
|
until: 0
|
|
221
221
|
});
|
|
222
222
|
}
|
|
223
|
-
/**
|
|
223
|
+
/**
|
|
224
|
+
* One account's key: literal → credential seam → auth file (default slot).
|
|
225
|
+
*
|
|
226
|
+
* Every source is normalized here, at the single point where a slot's key
|
|
227
|
+
* enters the pool. The adapter sends the key through the harness's
|
|
228
|
+
* `assertUsableApiKey()`, which trims it — a stored key from the credentials
|
|
229
|
+
* seam, a `.env` line, or a shell export all pick up surrounding whitespace
|
|
230
|
+
* — and reports that trimmed form back to `markRejected()`. Returning the
|
|
231
|
+
* raw value would file every 429/401 mark under a key no later lookup can
|
|
232
|
+
* find: rotation would re-offer the same account, the account card would show
|
|
233
|
+
* no mark, and the usage endpoints would 401 while chat kept working.
|
|
234
|
+
* Normalizing once makes resolution, probing, marking, and the request path
|
|
235
|
+
* agree on one string.
|
|
236
|
+
*/
|
|
224
237
|
async resolveSlotKey(slot) {
|
|
225
|
-
if (slot.literal !== void 0
|
|
238
|
+
if (slot.literal !== void 0) {
|
|
239
|
+
const literal = normalizeResolvedKey(slot.literal);
|
|
240
|
+
if (literal !== void 0) return literal;
|
|
241
|
+
}
|
|
226
242
|
if (slot.ref !== void 0) {
|
|
227
243
|
const hit = await this.deps.resolveRef(slot.ref);
|
|
228
|
-
if (hit !== void 0
|
|
244
|
+
if (hit !== void 0) {
|
|
245
|
+
const resolved = normalizeResolvedKey(hit);
|
|
246
|
+
if (resolved !== void 0) return resolved;
|
|
247
|
+
}
|
|
229
248
|
}
|
|
230
249
|
if (slot.allowAuthFile) {
|
|
231
250
|
const fromFile = this.deps.authFileKey();
|
|
232
|
-
if (fromFile !== void 0
|
|
251
|
+
if (fromFile !== void 0) {
|
|
252
|
+
const fileKey = normalizeResolvedKey(fromFile);
|
|
253
|
+
if (fileKey !== void 0) return fileKey;
|
|
254
|
+
}
|
|
233
255
|
}
|
|
234
256
|
}
|
|
235
257
|
/** Hand out the chosen account's key. */
|
|
@@ -240,6 +262,16 @@ var CommandCodeAccountPool = class {
|
|
|
240
262
|
};
|
|
241
263
|
}
|
|
242
264
|
};
|
|
265
|
+
/**
|
|
266
|
+
* Normalize one resolved credential to the form every consumer sees. Trim
|
|
267
|
+
* only: a blank-after-trim value means "no key" (the slot is omitted and the
|
|
268
|
+
* caller reports `MISSING_CREDENTIAL`), while characters an HTTP header cannot
|
|
269
|
+
* carry are left for `assertUsableApiKey()` to reject with its own message.
|
|
270
|
+
*/
|
|
271
|
+
function normalizeResolvedKey(value) {
|
|
272
|
+
const trimmed = value.trim();
|
|
273
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
274
|
+
}
|
|
243
275
|
//#endregion
|
|
244
276
|
//#region src/capabilities.ts
|
|
245
277
|
/**
|
|
@@ -1207,8 +1239,65 @@ function wireToolCallIds(paired) {
|
|
|
1207
1239
|
function blockText(block) {
|
|
1208
1240
|
return block.type === "text" || block.type === "reasoning" ? block.text : "";
|
|
1209
1241
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1242
|
+
/**
|
|
1243
|
+
* Collect text and nested images of one tool result. The harness `read_image`
|
|
1244
|
+
* tool returns both, so dropping the image half is what made a tool-read image
|
|
1245
|
+
* invisible to a Vision model (issue #30). Deduplication keeps a result that
|
|
1246
|
+
* repeats one attachment from paying for the same pixels twice.
|
|
1247
|
+
*/
|
|
1248
|
+
function toolResultMedia(block) {
|
|
1249
|
+
const chunks = [];
|
|
1250
|
+
const images = [];
|
|
1251
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1252
|
+
const walk = (blocks) => {
|
|
1253
|
+
for (const nested of blocks) {
|
|
1254
|
+
if (nested.type === "text" || nested.type === "reasoning") {
|
|
1255
|
+
if (nested.text) chunks.push(nested.text);
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1258
|
+
if (nested.type === "image") {
|
|
1259
|
+
if (!seen.has(nested.attachment.attachmentId)) {
|
|
1260
|
+
seen.add(nested.attachment.attachmentId);
|
|
1261
|
+
images.push(nested.attachment);
|
|
1262
|
+
}
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
if (nested.type === "tool-result") walk(nested.content);
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
walk(block.content);
|
|
1269
|
+
return {
|
|
1270
|
+
text: chunks.join("\n"),
|
|
1271
|
+
images
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Result text for the wire's `role: 'tool'` message. A tool that returned only
|
|
1276
|
+
* an image (and possibly a nested image-only tool result) has no text at all,
|
|
1277
|
+
* and neither transport accepts an empty tool content string, so it gets a
|
|
1278
|
+
* descriptor pointing at the user message that follows with the pixels.
|
|
1279
|
+
*/
|
|
1280
|
+
function toolResultTextForWire(media) {
|
|
1281
|
+
return media.text || (media.images.length > 0 ? "(image returned; see the attached image)" : "");
|
|
1282
|
+
}
|
|
1283
|
+
/** Leading line of the user message that carries a tool result's images. */
|
|
1284
|
+
const TOOL_RESULT_IMAGE_TEXT = "Attached image(s) from tool result:";
|
|
1285
|
+
/**
|
|
1286
|
+
* The model-visible note introducing the images carried out of one tool
|
|
1287
|
+
* result. Neither transport merges them back into the tool result, so this
|
|
1288
|
+
* line is what tells the model the image it is about to see belongs to the
|
|
1289
|
+
* tool it just ran rather than to the user; it also leads the part list,
|
|
1290
|
+
* because an image-first content array is what some gateways reject. Count and
|
|
1291
|
+
* pixel dimensions are appended when the tool's own text does not already
|
|
1292
|
+
* state them (a result that returns the image and nothing else).
|
|
1293
|
+
*/
|
|
1294
|
+
function toolResultImageNote(media) {
|
|
1295
|
+
const first = media.images[0];
|
|
1296
|
+
if (first === void 0) return TOOL_RESULT_IMAGE_TEXT;
|
|
1297
|
+
const dimensions = `${first.width}x${first.height} px`;
|
|
1298
|
+
if (first.width <= 0 || first.height <= 0 || media.text.includes(dimensions)) return TOOL_RESULT_IMAGE_TEXT;
|
|
1299
|
+
const count = media.images.length > 1 ? `${media.images.length} images, ` : "";
|
|
1300
|
+
return `${TOOL_RESULT_IMAGE_TEXT} ${count}${dimensions}`;
|
|
1212
1301
|
}
|
|
1213
1302
|
function hasImageContent(message) {
|
|
1214
1303
|
const check = (blocks) => blocks.some((b) => b.type === "image" || b.type === "tool-result" && check(b.content));
|
|
@@ -1276,6 +1365,7 @@ async function messagesToCC(messages, readImage) {
|
|
|
1276
1365
|
if (message.role === "user" && message.source.kind === "tool") {
|
|
1277
1366
|
const block = message.content[0];
|
|
1278
1367
|
if (!block || block.type !== "tool-result" || !paired.has(block.toolCallId)) continue;
|
|
1368
|
+
const media = toolResultMedia(block);
|
|
1279
1369
|
out.push({
|
|
1280
1370
|
role: "tool",
|
|
1281
1371
|
content: [{
|
|
@@ -1284,13 +1374,25 @@ async function messagesToCC(messages, readImage) {
|
|
|
1284
1374
|
toolName: toolNames.get(block.toolCallId) || "unknown",
|
|
1285
1375
|
output: block.isError ? {
|
|
1286
1376
|
type: "error-text",
|
|
1287
|
-
value:
|
|
1377
|
+
value: toolResultTextForWire(media)
|
|
1288
1378
|
} : {
|
|
1289
1379
|
type: "text",
|
|
1290
|
-
value:
|
|
1380
|
+
value: toolResultTextForWire(media)
|
|
1291
1381
|
}
|
|
1292
1382
|
}]
|
|
1293
1383
|
});
|
|
1384
|
+
if (media.images.length > 0) {
|
|
1385
|
+
if (!readImage) throw new LlmError("Image input requires the durable attachment service", "UNSUPPORTED_CONTENT");
|
|
1386
|
+
const carried = [{
|
|
1387
|
+
type: "text",
|
|
1388
|
+
text: toolResultImageNote(media)
|
|
1389
|
+
}];
|
|
1390
|
+
for (const attachment of media.images) carried.push(await imageToCommandCode(attachment, readImage));
|
|
1391
|
+
out.push({
|
|
1392
|
+
role: "user",
|
|
1393
|
+
content: carried
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1294
1396
|
}
|
|
1295
1397
|
}
|
|
1296
1398
|
return out;
|
|
@@ -1369,11 +1471,24 @@ async function messagesToOpenAI(messages, readImage) {
|
|
|
1369
1471
|
if (message.role === "user" && message.source.kind === "tool") {
|
|
1370
1472
|
const block = message.content[0];
|
|
1371
1473
|
if (!block || block.type !== "tool-result" || !paired.has(block.toolCallId)) continue;
|
|
1474
|
+
const media = toolResultMedia(block);
|
|
1372
1475
|
out.push({
|
|
1373
1476
|
role: "tool",
|
|
1374
1477
|
tool_call_id: wireIds.get(block.toolCallId) ?? block.toolCallId,
|
|
1375
|
-
content:
|
|
1478
|
+
content: toolResultTextForWire(media)
|
|
1376
1479
|
});
|
|
1480
|
+
if (media.images.length > 0) {
|
|
1481
|
+
if (!readImage) throw new LlmError("Image input requires the durable attachment service", "UNSUPPORTED_CONTENT");
|
|
1482
|
+
const carried = [{
|
|
1483
|
+
type: "text",
|
|
1484
|
+
text: toolResultImageNote(media)
|
|
1485
|
+
}];
|
|
1486
|
+
for (const attachment of media.images) carried.push(await imageToOpenAI(attachment, readImage));
|
|
1487
|
+
out.push({
|
|
1488
|
+
role: "user",
|
|
1489
|
+
content: carried
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1377
1492
|
}
|
|
1378
1493
|
}
|
|
1379
1494
|
return out;
|
|
@@ -1760,7 +1875,12 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1760
1875
|
const access = this.deps.options().filterModelsByPlan === false ? void 0 : await this.loadBillingAccess();
|
|
1761
1876
|
const visible = this.deps.options().visibleModels;
|
|
1762
1877
|
const allow = Array.isArray(visible) && visible.length > 0 ? new Set(visible.filter((id) => typeof id === "string" && id !== "")) : void 0;
|
|
1763
|
-
|
|
1878
|
+
const overrides = this.deps.options().modelVisibility;
|
|
1879
|
+
return catalog.filter((model) => modelVisibleInPlan(model.id, access)).filter((model) => {
|
|
1880
|
+
const override = overrides?.[model.id];
|
|
1881
|
+
if (typeof override === "boolean") return override;
|
|
1882
|
+
return allow === void 0 || allow.has(model.id);
|
|
1883
|
+
}).map(toInfo).sort(compareByPlan);
|
|
1764
1884
|
}
|
|
1765
1885
|
async resolveModel(provider, model, signal) {
|
|
1766
1886
|
const entry = this.catalog.find((m) => m.id === model) ?? (await this.loadCatalog(signal)).find((m) => m.id === model);
|
|
@@ -3122,6 +3242,14 @@ var CommandCodeLoginFlow = class {
|
|
|
3122
3242
|
timer;
|
|
3123
3243
|
/** Settle hooks of the live attempt's callback promise. */
|
|
3124
3244
|
settle;
|
|
3245
|
+
/**
|
|
3246
|
+
* Attempt generation. A delivered callback keeps validating the key
|
|
3247
|
+
* asynchronously (`complete()`), and that window is open to a cancel or a
|
|
3248
|
+
* fresh `begin()`; the generation lets a late completion recognize that it
|
|
3249
|
+
* no longer owns the status face and stop instead of storing a credential
|
|
3250
|
+
* the user cancelled and flipping the page back to success.
|
|
3251
|
+
*/
|
|
3252
|
+
attemptSeq = 0;
|
|
3125
3253
|
disposed = false;
|
|
3126
3254
|
constructor(deps) {
|
|
3127
3255
|
this.deps = deps;
|
|
@@ -3142,8 +3270,9 @@ var CommandCodeLoginFlow = class {
|
|
|
3142
3270
|
*/
|
|
3143
3271
|
async begin() {
|
|
3144
3272
|
if (this.disposed) throw new Error("login flow has been disposed");
|
|
3145
|
-
if (this.statusValue.state === "waiting") return this.statusValue;
|
|
3273
|
+
if (this.statusValue.state === "waiting" && this.server !== void 0) return this.statusValue;
|
|
3146
3274
|
this.teardown();
|
|
3275
|
+
const attempt = ++this.attemptSeq;
|
|
3147
3276
|
const port = await this.findPort();
|
|
3148
3277
|
const expectedState = this.deps.randomToken?.(32) ?? randomBytes(32).toString("base64url");
|
|
3149
3278
|
const settled = new Promise((resolve, reject) => {
|
|
@@ -3171,7 +3300,7 @@ var CommandCodeLoginFlow = class {
|
|
|
3171
3300
|
});
|
|
3172
3301
|
}, this.deps.timeoutMs ?? 12e4);
|
|
3173
3302
|
this.timer.unref?.();
|
|
3174
|
-
settled.then((credentials) => this.complete(credentials), (failure) => this.failFrom(failure));
|
|
3303
|
+
settled.then((credentials) => this.complete(attempt, credentials), (failure) => this.failFrom(attempt, failure));
|
|
3175
3304
|
return this.statusValue;
|
|
3176
3305
|
}
|
|
3177
3306
|
/** Cancel a waiting attempt; terminal states are untouched. */
|
|
@@ -3288,6 +3417,13 @@ var CommandCodeLoginFlow = class {
|
|
|
3288
3417
|
}
|
|
3289
3418
|
if (typeof payload === "object" && payload !== null && "error" in payload) {
|
|
3290
3419
|
const denial = payload;
|
|
3420
|
+
if (denial.state !== expectedState) {
|
|
3421
|
+
json(403, {
|
|
3422
|
+
success: false,
|
|
3423
|
+
error: "Invalid state token"
|
|
3424
|
+
});
|
|
3425
|
+
return;
|
|
3426
|
+
}
|
|
3291
3427
|
const description = denial.error_description ?? denial.error;
|
|
3292
3428
|
this.settleAttempt(json, 200, { success: true }, new LoginSettleError(denial.error === "access_denied" ? "denied" : "error", typeof description === "string" && description !== "" ? description : "Authorization failed"));
|
|
3293
3429
|
return;
|
|
@@ -3319,10 +3455,20 @@ var CommandCodeLoginFlow = class {
|
|
|
3319
3455
|
if (failure !== void 0) settle.reject(failure);
|
|
3320
3456
|
else if (credentials !== void 0) settle.resolve(credentials);
|
|
3321
3457
|
}
|
|
3322
|
-
/**
|
|
3323
|
-
|
|
3324
|
-
|
|
3458
|
+
/**
|
|
3459
|
+
* Post-validation completion: whoami check, then hand-off to storage.
|
|
3460
|
+
*
|
|
3461
|
+
* Every step re-checks {@link ownsAttempt} first: the whoami round-trip and
|
|
3462
|
+
* the credential write are awaits, and the user may cancel (or start another
|
|
3463
|
+
* attempt) while one is in flight. A completion that no longer owns the
|
|
3464
|
+
* attempt must not write the key or publish a status — otherwise cancel
|
|
3465
|
+
* would report "cancelled" while the credential landed anyway, and the page
|
|
3466
|
+
* would silently flip to success.
|
|
3467
|
+
*/
|
|
3468
|
+
async complete(attempt, credentials) {
|
|
3469
|
+
if (!this.ownsAttempt(attempt)) return;
|
|
3325
3470
|
const validation = await validateCommandApiKey(this.deps.fetchImpl ?? fetch, this.readApiBase(), credentials.apiKey);
|
|
3471
|
+
if (!this.ownsAttempt(attempt)) return;
|
|
3326
3472
|
if (!validation.valid) {
|
|
3327
3473
|
const reason = validation.error === "invalid_key" ? "invalid-key" : validation.error === "network_error" ? "network" : "error";
|
|
3328
3474
|
this.setStatus({
|
|
@@ -3332,9 +3478,11 @@ var CommandCodeLoginFlow = class {
|
|
|
3332
3478
|
});
|
|
3333
3479
|
return;
|
|
3334
3480
|
}
|
|
3481
|
+
if (!this.ownsAttempt(attempt)) return;
|
|
3335
3482
|
try {
|
|
3336
3483
|
await this.deps.storeKey(credentials);
|
|
3337
3484
|
} catch (error) {
|
|
3485
|
+
if (!this.ownsAttempt(attempt)) return;
|
|
3338
3486
|
this.setStatus({
|
|
3339
3487
|
state: "failed",
|
|
3340
3488
|
reason: "unavailable",
|
|
@@ -3342,7 +3490,7 @@ var CommandCodeLoginFlow = class {
|
|
|
3342
3490
|
});
|
|
3343
3491
|
return;
|
|
3344
3492
|
}
|
|
3345
|
-
if (this.
|
|
3493
|
+
if (!this.ownsAttempt(attempt)) return;
|
|
3346
3494
|
this.clearTimer();
|
|
3347
3495
|
this.setStatus({
|
|
3348
3496
|
state: "success",
|
|
@@ -3350,10 +3498,14 @@ var CommandCodeLoginFlow = class {
|
|
|
3350
3498
|
keyName: credentials.keyName
|
|
3351
3499
|
});
|
|
3352
3500
|
}
|
|
3501
|
+
/** Whether one attempt still owns the status face (not cancelled, replaced, or disposed). */
|
|
3502
|
+
ownsAttempt(attempt) {
|
|
3503
|
+
return !this.disposed && this.attemptSeq === attempt && this.statusValue.state === "waiting";
|
|
3504
|
+
}
|
|
3353
3505
|
/** Map a tagged settle rejection onto the status face. */
|
|
3354
|
-
failFrom(failure) {
|
|
3506
|
+
failFrom(attempt, failure) {
|
|
3355
3507
|
if (!(failure instanceof LoginSettleError)) return;
|
|
3356
|
-
if (this.
|
|
3508
|
+
if (!this.ownsAttempt(attempt)) return;
|
|
3357
3509
|
this.setStatus({
|
|
3358
3510
|
state: "failed",
|
|
3359
3511
|
reason: failure.reason,
|
|
@@ -3634,6 +3786,338 @@ var CommandCodeSearchProvider = class {
|
|
|
3634
3786
|
}
|
|
3635
3787
|
};
|
|
3636
3788
|
//#endregion
|
|
3789
|
+
//#region src/tui-settings.ts
|
|
3790
|
+
/** The selector value meaning "no pinned account — follow rotation order". */
|
|
3791
|
+
const ACTIVE_ACCOUNT_AUTO = "auto";
|
|
3792
|
+
/** The selector value meaning "no language override — follow the shell locale". */
|
|
3793
|
+
const LANG_AUTO = "auto";
|
|
3794
|
+
/** Tier display names, mirroring the web dropdown's headings (`model-select.ts`). */
|
|
3795
|
+
const TIER_TITLES = {
|
|
3796
|
+
go: "Go",
|
|
3797
|
+
goat: "GOAT",
|
|
3798
|
+
pro: "Pro",
|
|
3799
|
+
provider: "Provider",
|
|
3800
|
+
max: "Max"
|
|
3801
|
+
};
|
|
3802
|
+
/** Tiers in picker order; a model outside this set joins the "Other" group. */
|
|
3803
|
+
const TIER_ORDER = [
|
|
3804
|
+
"go",
|
|
3805
|
+
"goat",
|
|
3806
|
+
"pro",
|
|
3807
|
+
"provider",
|
|
3808
|
+
"max"
|
|
3809
|
+
];
|
|
3810
|
+
/** The group holding models this build's catalog does not know. */
|
|
3811
|
+
const OTHER_GROUP_ID = "models-other";
|
|
3812
|
+
/**
|
|
3813
|
+
* Every model this build knows, in checkbox order: plan tier (Go first), then
|
|
3814
|
+
* free before paid inside a tier, then by id.
|
|
3815
|
+
*
|
|
3816
|
+
* The list is the static capability snapshot rather than a live catalog read
|
|
3817
|
+
* on purpose — a settings page must draw synchronously, and the snapshot is
|
|
3818
|
+
* synced from the same upstream table the picker's tier headings come from.
|
|
3819
|
+
* A model added upstream after this build still reaches the user: an empty
|
|
3820
|
+
* allowlist shows everything, and a model named in the allowlist but absent
|
|
3821
|
+
* here is rendered by the "Other" group instead of disappearing.
|
|
3822
|
+
*/
|
|
3823
|
+
function commandCodeTuiModelChoices() {
|
|
3824
|
+
return Object.keys(KNOWN_PLANS).map((id) => ({
|
|
3825
|
+
id,
|
|
3826
|
+
tier: KNOWN_PLANS[id] ?? "",
|
|
3827
|
+
free: isFreeModel(id),
|
|
3828
|
+
hint: capabilityDescription(id)
|
|
3829
|
+
})).sort((a, b) => {
|
|
3830
|
+
const tierDelta = tierRank(a.tier) - tierRank(b.tier);
|
|
3831
|
+
if (tierDelta !== 0) return tierDelta;
|
|
3832
|
+
if (a.free !== b.free) return a.free ? -1 : 1;
|
|
3833
|
+
return a.id.localeCompare(b.id);
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
/** Sort rank of a tier key; unknown tiers trail every known one. */
|
|
3837
|
+
function tierRank(tier) {
|
|
3838
|
+
const rank = TIER_ORDER.indexOf(tier);
|
|
3839
|
+
return rank === -1 ? TIER_ORDER.length : rank;
|
|
3840
|
+
}
|
|
3841
|
+
/** The stored allowlist as a clean id list (non-strings and blanks dropped). */
|
|
3842
|
+
function storedIds(value) {
|
|
3843
|
+
return Array.isArray(value) ? value.filter((id) => typeof id === "string" && id !== "") : [];
|
|
3844
|
+
}
|
|
3845
|
+
/**
|
|
3846
|
+
* Build the section descriptor. Pure, so tests can pin the exact fields
|
|
3847
|
+
* without a dsh-TUI host.
|
|
3848
|
+
*
|
|
3849
|
+
* Field choices worth keeping: the two option-bearing fields (`activeAccount`,
|
|
3850
|
+
* `lang`) are `text` + `options` rather than `select`, because a `select`
|
|
3851
|
+
* cannot express "unset" — cycling only ever lands on a declared option, so a
|
|
3852
|
+
* `select` would strand the user on a pinned value with no way back to
|
|
3853
|
+
* automatic. The `auto` sentinel plus a `parse` that clears the path keeps the
|
|
3854
|
+
* unset state reachable. `filterModelsByPlan` formats its EFFECTIVE default
|
|
3855
|
+
* (unset means true at the adapter), so a fresh install reads true instead of
|
|
3856
|
+
* the screen's "(empty)".
|
|
3857
|
+
*/
|
|
3858
|
+
function buildCommandCodeTuiSection(deps) {
|
|
3859
|
+
const ref = deps.apiKeyRef();
|
|
3860
|
+
const slots = deps.accountSlots();
|
|
3861
|
+
const choices = (deps.modelChoices ?? commandCodeTuiModelChoices)();
|
|
3862
|
+
const catalogIds = choices.map((choice) => choice.id);
|
|
3863
|
+
const known = new Set(catalogIds);
|
|
3864
|
+
const stored = storedIds(deps.visibleModels());
|
|
3865
|
+
const overrides = deps.modelVisibility?.() ?? {};
|
|
3866
|
+
const extras = [.../* @__PURE__ */ new Set([...stored, ...Object.keys(overrides)])].filter((id) => !known.has(id));
|
|
3867
|
+
const tierGroups = TIER_ORDER.filter((tier) => choices.some((choice) => choice.tier === tier)).map((tier) => ({
|
|
3868
|
+
id: `models-${tier}`,
|
|
3869
|
+
title: `${TIER_TITLES[tier] ?? tier} models`,
|
|
3870
|
+
descriptions: { zh: `${TIER_TITLES[tier] ?? tier} 模型` }
|
|
3871
|
+
}));
|
|
3872
|
+
const unranked = choices.filter((choice) => tierRank(choice.tier) === TIER_ORDER.length);
|
|
3873
|
+
const otherGroup = unranked.length > 0 || extras.length > 0 ? [{
|
|
3874
|
+
id: OTHER_GROUP_ID,
|
|
3875
|
+
title: "Other models",
|
|
3876
|
+
descriptions: { zh: "其他模型" }
|
|
3877
|
+
}] : [];
|
|
3878
|
+
/**
|
|
3879
|
+
* One checkbox: a boolean at a path of its OWN (`modelVisibility.<id>`).
|
|
3880
|
+
*
|
|
3881
|
+
* The path must be unique per model. dsh-TUI keys a staged draft by the
|
|
3882
|
+
* field's path (`fieldKey`), so N checkboxes sharing `visibleModels` share
|
|
3883
|
+
* ONE draft: every one of them then parses that same draft on save, all N
|
|
3884
|
+
* write ops address the same path, and only the LAST field's op survives —
|
|
3885
|
+
* which silently rewrote the allowlist from the last catalog model instead
|
|
3886
|
+
* of the one that was toggled. A per-model key is what makes a checkbox
|
|
3887
|
+
* express one model's state.
|
|
3888
|
+
*
|
|
3889
|
+
* An override equal to what the array already says is written as a CLEAR, so
|
|
3890
|
+
* toggling a model back to its inherited state leaves no residue and the
|
|
3891
|
+
* document stays minimal.
|
|
3892
|
+
*/
|
|
3893
|
+
const modelField = (id, hint, group) => ({
|
|
3894
|
+
path: ["modelVisibility", id],
|
|
3895
|
+
group,
|
|
3896
|
+
kind: "boolean",
|
|
3897
|
+
label: id,
|
|
3898
|
+
...hint === "" ? {} : { hint },
|
|
3899
|
+
format: (value) => {
|
|
3900
|
+
if (typeof value === "boolean") return String(value);
|
|
3901
|
+
const listed = storedIds(deps.visibleModels());
|
|
3902
|
+
return String(listed.length === 0 || listed.includes(id));
|
|
3903
|
+
},
|
|
3904
|
+
parse: (text) => {
|
|
3905
|
+
const on = text.trim() === "true";
|
|
3906
|
+
const listed = storedIds(deps.visibleModels());
|
|
3907
|
+
return on === (listed.length === 0 || listed.includes(id)) ? { kind: "clear" } : {
|
|
3908
|
+
kind: "set",
|
|
3909
|
+
value: on
|
|
3910
|
+
};
|
|
3911
|
+
}
|
|
3912
|
+
});
|
|
3913
|
+
return {
|
|
3914
|
+
ns: deps.ns,
|
|
3915
|
+
title: deps.title ?? "Command Code",
|
|
3916
|
+
descriptions: { zh: "Command Code(非官方)" },
|
|
3917
|
+
groups: [
|
|
3918
|
+
{
|
|
3919
|
+
id: "connection",
|
|
3920
|
+
title: "Connection",
|
|
3921
|
+
descriptions: { zh: "连接" }
|
|
3922
|
+
},
|
|
3923
|
+
{
|
|
3924
|
+
id: "models",
|
|
3925
|
+
title: "Models",
|
|
3926
|
+
descriptions: { zh: "模型" }
|
|
3927
|
+
},
|
|
3928
|
+
...tierGroups,
|
|
3929
|
+
...otherGroup,
|
|
3930
|
+
{
|
|
3931
|
+
id: "advanced",
|
|
3932
|
+
title: "Advanced",
|
|
3933
|
+
descriptions: { zh: "高级" }
|
|
3934
|
+
}
|
|
3935
|
+
],
|
|
3936
|
+
fields: [
|
|
3937
|
+
{
|
|
3938
|
+
path: ["apiKey"],
|
|
3939
|
+
group: "connection",
|
|
3940
|
+
kind: "text",
|
|
3941
|
+
label: "API key",
|
|
3942
|
+
descriptions: { zh: "API 密钥" },
|
|
3943
|
+
secret: { ref },
|
|
3944
|
+
hint: `Stored in the credential store as ${ref}, never in settings.yaml.`,
|
|
3945
|
+
hintDescriptions: { zh: `保存在凭据库(${ref}),不会写入 settings.yaml。` }
|
|
3946
|
+
},
|
|
3947
|
+
{
|
|
3948
|
+
path: ["apiBase"],
|
|
3949
|
+
group: "connection",
|
|
3950
|
+
kind: "text",
|
|
3951
|
+
label: "API base",
|
|
3952
|
+
descriptions: { zh: "API 地址" },
|
|
3953
|
+
placeholder: "https://api.commandcode.ai",
|
|
3954
|
+
hint: "Leave empty for the public Command Code Provider API.",
|
|
3955
|
+
hintDescriptions: { zh: "留空即使用官方 Command Code Provider API。" },
|
|
3956
|
+
parse: (text) => {
|
|
3957
|
+
const trimmed = text.trim();
|
|
3958
|
+
return trimmed === "" ? { kind: "clear" } : {
|
|
3959
|
+
kind: "set",
|
|
3960
|
+
value: trimmed
|
|
3961
|
+
};
|
|
3962
|
+
}
|
|
3963
|
+
},
|
|
3964
|
+
{
|
|
3965
|
+
path: ["filterModelsByPlan"],
|
|
3966
|
+
group: "models",
|
|
3967
|
+
kind: "boolean",
|
|
3968
|
+
label: "Hide out-of-plan models",
|
|
3969
|
+
descriptions: { zh: "隐藏套餐外的模型" },
|
|
3970
|
+
hint: "Keeps models above your subscription tier out of the picker. Fails open.",
|
|
3971
|
+
hintDescriptions: { zh: "在选择器里隐藏超出当前订阅档位的模型;判断不出来时全部显示。" },
|
|
3972
|
+
format: (value) => value === false ? "false" : "true",
|
|
3973
|
+
parse: (text) => ({
|
|
3974
|
+
kind: "set",
|
|
3975
|
+
value: text.trim() === "true"
|
|
3976
|
+
})
|
|
3977
|
+
},
|
|
3978
|
+
...choices.filter((choice) => tierRank(choice.tier) !== TIER_ORDER.length).map((choice) => modelField(choice.id, choice.hint, `models-${choice.tier}`)),
|
|
3979
|
+
...unranked.map((choice) => modelField(choice.id, choice.hint, OTHER_GROUP_ID)),
|
|
3980
|
+
...extras.map((id) => modelField(id, capabilityDescription(id), OTHER_GROUP_ID)),
|
|
3981
|
+
{
|
|
3982
|
+
path: ["activeAccount"],
|
|
3983
|
+
group: "advanced",
|
|
3984
|
+
kind: "text",
|
|
3985
|
+
label: "Active account",
|
|
3986
|
+
descriptions: { zh: "当前账号" },
|
|
3987
|
+
hint: "A pinned account id, or auto to follow the rotation order.",
|
|
3988
|
+
hintDescriptions: { zh: "固定使用某个账号的 id;auto 表示按轮换顺序自动选择。" },
|
|
3989
|
+
options: [{
|
|
3990
|
+
value: ACTIVE_ACCOUNT_AUTO,
|
|
3991
|
+
label: "Automatic (rotation order)",
|
|
3992
|
+
descriptions: { zh: "自动(按轮换顺序)" }
|
|
3993
|
+
}, ...slots.map((slot) => ({
|
|
3994
|
+
value: slot.id,
|
|
3995
|
+
label: slot.label,
|
|
3996
|
+
descriptions: { zh: slot.label }
|
|
3997
|
+
}))],
|
|
3998
|
+
format: (value) => typeof value === "string" && value.trim() !== "" ? value : ACTIVE_ACCOUNT_AUTO,
|
|
3999
|
+
parse: (text) => {
|
|
4000
|
+
const trimmed = text.trim();
|
|
4001
|
+
return trimmed === "" || trimmed === "auto" ? { kind: "clear" } : {
|
|
4002
|
+
kind: "set",
|
|
4003
|
+
value: trimmed
|
|
4004
|
+
};
|
|
4005
|
+
}
|
|
4006
|
+
},
|
|
4007
|
+
{
|
|
4008
|
+
path: ["lang"],
|
|
4009
|
+
group: "advanced",
|
|
4010
|
+
kind: "text",
|
|
4011
|
+
label: "Command language",
|
|
4012
|
+
descriptions: { zh: "命令语言" },
|
|
4013
|
+
hint: "Language of the /commandcode dashboard; auto follows the shell locale.",
|
|
4014
|
+
hintDescriptions: { zh: "/commandcode 用量面板的语言;auto 跟随终端 locale。" },
|
|
4015
|
+
options: [
|
|
4016
|
+
{
|
|
4017
|
+
value: LANG_AUTO,
|
|
4018
|
+
label: "Automatic (shell locale)",
|
|
4019
|
+
descriptions: { zh: "自动(跟随终端 locale)" }
|
|
4020
|
+
},
|
|
4021
|
+
{
|
|
4022
|
+
value: "zh",
|
|
4023
|
+
label: "中文"
|
|
4024
|
+
},
|
|
4025
|
+
{
|
|
4026
|
+
value: "en",
|
|
4027
|
+
label: "English"
|
|
4028
|
+
}
|
|
4029
|
+
],
|
|
4030
|
+
format: (value) => value === "zh" || value === "en" ? value : LANG_AUTO,
|
|
4031
|
+
parse: (text) => {
|
|
4032
|
+
const trimmed = text.trim();
|
|
4033
|
+
return trimmed === "" || trimmed === "auto" ? { kind: "clear" } : {
|
|
4034
|
+
kind: "set",
|
|
4035
|
+
value: trimmed
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
]
|
|
4040
|
+
};
|
|
4041
|
+
}
|
|
4042
|
+
/**
|
|
4043
|
+
* The `tuiSettingsSections` service, read defensively.
|
|
4044
|
+
*
|
|
4045
|
+
* The service name is declared by dsh-TUI's own module augmentation, which
|
|
4046
|
+
* this package deliberately does not import, so the typed `Context` has no
|
|
4047
|
+
* such property. The read goes through the REFLECTIVE `ctx.get` rather than a
|
|
4048
|
+
* bare property access: cordis refuses a property read for a service the fiber
|
|
4049
|
+
* never declared in `inject` (`cannot get property … without inject`), and an
|
|
4050
|
+
* unmeet seam must degrade to "no section" instead of throwing out of the
|
|
4051
|
+
* plugin's boot.
|
|
4052
|
+
*/
|
|
4053
|
+
function tuiSettingsService(ctx) {
|
|
4054
|
+
let candidate;
|
|
4055
|
+
try {
|
|
4056
|
+
candidate = ctx.get("tuiSettingsSections");
|
|
4057
|
+
} catch {
|
|
4058
|
+
return;
|
|
4059
|
+
}
|
|
4060
|
+
if (typeof candidate !== "object" || candidate === null) return void 0;
|
|
4061
|
+
const register = candidate.register;
|
|
4062
|
+
if (typeof register !== "function") return void 0;
|
|
4063
|
+
return { register: register.bind(candidate) };
|
|
4064
|
+
}
|
|
4065
|
+
/**
|
|
4066
|
+
* Identity of everything in the section that can change at runtime: the
|
|
4067
|
+
* credential reference behind the API-key field, the account slots behind the
|
|
4068
|
+
* active-account selector, and the stored-but-unknown allowlist entries that
|
|
4069
|
+
* get a checkbox of their own. Re-registration is skipped while this matches,
|
|
4070
|
+
* so ordinary settings writes never churn the screen's section list — the
|
|
4071
|
+
* checkboxes themselves read their state live and need no re-declaration.
|
|
4072
|
+
*/
|
|
4073
|
+
function sectionSignature(section) {
|
|
4074
|
+
const active = section.fields.find((field) => field.path.join(".") === "activeAccount");
|
|
4075
|
+
return JSON.stringify({
|
|
4076
|
+
secret: section.fields.find((field) => field.secret !== void 0)?.secret?.ref ?? "",
|
|
4077
|
+
options: active?.options?.map((option) => option.value) ?? [],
|
|
4078
|
+
other: section.fields.filter((field) => field.group === OTHER_GROUP_ID).map((field) => field.label)
|
|
4079
|
+
});
|
|
4080
|
+
}
|
|
4081
|
+
/**
|
|
4082
|
+
* Register the Command Code section on a dsh-TUI host.
|
|
4083
|
+
*
|
|
4084
|
+
* @param ctx - the context of an activated `tuiSettingsSections` injection.
|
|
4085
|
+
* @param deps - plugin-owned facts the section reads.
|
|
4086
|
+
* @returns a refresh function that re-registers the section when a fact it
|
|
4087
|
+
* renders changed (the plugin entry calls it from its settings `onChange`
|
|
4088
|
+
* hook), or `undefined` when the seam is unusable. The returned function is
|
|
4089
|
+
* inert after the fiber is torn down.
|
|
4090
|
+
*/
|
|
4091
|
+
function applyCommandCodeTuiSettings(ctx, deps) {
|
|
4092
|
+
const service = tuiSettingsService(ctx);
|
|
4093
|
+
if (service === void 0) return void 0;
|
|
4094
|
+
let disposed = false;
|
|
4095
|
+
let current;
|
|
4096
|
+
const refresh = () => {
|
|
4097
|
+
if (disposed) return;
|
|
4098
|
+
const section = buildCommandCodeTuiSection(deps);
|
|
4099
|
+
const signature = sectionSignature(section);
|
|
4100
|
+
if (current?.signature === signature) return;
|
|
4101
|
+
current?.dispose();
|
|
4102
|
+
current = void 0;
|
|
4103
|
+
try {
|
|
4104
|
+
current = {
|
|
4105
|
+
signature,
|
|
4106
|
+
dispose: service.register(section)
|
|
4107
|
+
};
|
|
4108
|
+
} catch (error) {
|
|
4109
|
+
ctx.logger?.warn(`llm-commandcode: could not register the dsh-TUI settings section: ${error instanceof Error ? error.message : String(error)}`);
|
|
4110
|
+
}
|
|
4111
|
+
};
|
|
4112
|
+
refresh();
|
|
4113
|
+
ctx.effect(() => () => {
|
|
4114
|
+
disposed = true;
|
|
4115
|
+
current?.dispose();
|
|
4116
|
+
current = void 0;
|
|
4117
|
+
}, "dsh-commandcode-provider: tui settings section");
|
|
4118
|
+
return refresh;
|
|
4119
|
+
}
|
|
4120
|
+
//#endregion
|
|
3637
4121
|
//#region src/index.ts
|
|
3638
4122
|
/**
|
|
3639
4123
|
* dsh-commandcode-provider — DeepSeek Harness LLM provider plugin for Command
|
|
@@ -3672,7 +4156,7 @@ const PROVIDER = "commandcode";
|
|
|
3672
4156
|
const DEFAULT_MODELS_CACHE_PATH = join(homedir(), ".commandcode", "models-cache.json");
|
|
3673
4157
|
const Config = z.object({
|
|
3674
4158
|
apiKeyEnv: z.string().role("credential-ref").default(DEFAULT_API_KEY_ENV),
|
|
3675
|
-
apiKey: z.string(),
|
|
4159
|
+
apiKey: z.string().role("secret"),
|
|
3676
4160
|
apiBase: z.string(),
|
|
3677
4161
|
workingDir: z.string(),
|
|
3678
4162
|
modelsCachePath: z.string(),
|
|
@@ -3680,11 +4164,21 @@ const Config = z.object({
|
|
|
3680
4164
|
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
3681
4165
|
filterModelsByPlan: z.boolean(),
|
|
3682
4166
|
visibleModels: z.array(z.string()),
|
|
4167
|
+
/**
|
|
4168
|
+
* Per-model visibility overrides for the terminal settings page's checkbox
|
|
4169
|
+
* list, keyed by catalog id. dsh-TUI addresses a staged edit by its field
|
|
4170
|
+
* PATH, so two checkboxes sharing one path would overwrite each other's
|
|
4171
|
+
* draft and the section's last model would decide every write; a map gives
|
|
4172
|
+
* each checkbox a path of its own. An id listed here wins over
|
|
4173
|
+
* {@link visibleModels}; ids absent here keep following it.
|
|
4174
|
+
*/
|
|
4175
|
+
modelVisibility: z.dict(z.boolean()),
|
|
3683
4176
|
webSearch: z.boolean().default(true),
|
|
3684
4177
|
accounts: z.array(z.object({
|
|
3685
4178
|
label: z.string(),
|
|
3686
4179
|
apiKeyEnv: z.string().role("credential-ref"),
|
|
3687
|
-
apiKey
|
|
4180
|
+
/** Literal key for one extra slot; see the top-level `apiKey` secret note. */
|
|
4181
|
+
apiKey: z.string().role("secret")
|
|
3688
4182
|
})),
|
|
3689
4183
|
activeAccount: z.string(),
|
|
3690
4184
|
modelAccountRules: z.array(z.object({
|
|
@@ -3708,9 +4202,22 @@ function resolveAdapterOptions(config) {
|
|
|
3708
4202
|
requestTimeoutMs: config.requestTimeoutMs ?? 6e4,
|
|
3709
4203
|
streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? 3e5,
|
|
3710
4204
|
filterModelsByPlan: config.filterModelsByPlan ?? true,
|
|
3711
|
-
visibleModels: Array.isArray(config.visibleModels) ? config.visibleModels.filter((id) => typeof id === "string" && id !== "") : void 0
|
|
4205
|
+
visibleModels: Array.isArray(config.visibleModels) ? config.visibleModels.filter((id) => typeof id === "string" && id !== "") : void 0,
|
|
4206
|
+
modelVisibility: readModelVisibility(config.modelVisibility)
|
|
3712
4207
|
};
|
|
3713
4208
|
}
|
|
4209
|
+
/**
|
|
4210
|
+
* Per-model visibility overrides, cleaned for the adapter. Programmatic
|
|
4211
|
+
* construction may bypass Schemastery normalization, so a non-object or a
|
|
4212
|
+
* non-boolean entry is dropped here instead of reaching the picker filter.
|
|
4213
|
+
* @param raw - The `modelVisibility` value from any config source.
|
|
4214
|
+
* @returns A frozen id → boolean map, or undefined when nothing is set.
|
|
4215
|
+
*/
|
|
4216
|
+
function readModelVisibility(raw) {
|
|
4217
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
|
|
4218
|
+
const entries = Object.entries(raw).filter((entry) => entry[0] !== "" && typeof entry[1] === "boolean");
|
|
4219
|
+
return entries.length === 0 ? void 0 : Object.fromEntries(entries);
|
|
4220
|
+
}
|
|
3714
4221
|
function apply(ctx, config) {
|
|
3715
4222
|
let current = () => config;
|
|
3716
4223
|
let lastRaw;
|
|
@@ -3874,6 +4381,22 @@ function apply(ctx, config) {
|
|
|
3874
4381
|
applyCommandCodeSearchSelection(webCtx.web, searchSelection, false);
|
|
3875
4382
|
}, "dsh-commandcode-provider: web search selection");
|
|
3876
4383
|
});
|
|
4384
|
+
let refreshTuiSettings;
|
|
4385
|
+
ctx.inject(["tuiSettingsSections"], (tuiCtx) => {
|
|
4386
|
+
refreshTuiSettings = applyCommandCodeTuiSettings(tuiCtx, {
|
|
4387
|
+
ns: NS,
|
|
4388
|
+
apiKeyRef: () => current().apiKeyEnv ?? DEFAULT_API_KEY_ENV,
|
|
4389
|
+
accountSlots: () => slots().map((slot) => ({
|
|
4390
|
+
id: slot.id,
|
|
4391
|
+
label: slot.label
|
|
4392
|
+
})),
|
|
4393
|
+
visibleModels: () => options().visibleModels ?? [],
|
|
4394
|
+
modelVisibility: () => options().modelVisibility
|
|
4395
|
+
});
|
|
4396
|
+
tuiCtx.effect(() => () => {
|
|
4397
|
+
refreshTuiSettings = void 0;
|
|
4398
|
+
}, "dsh-commandcode-provider: tui settings handle");
|
|
4399
|
+
});
|
|
3877
4400
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
3878
4401
|
settingsCtx.settings.installSection(ctx, NS, Config, config, {
|
|
3879
4402
|
setSource: (source) => {
|
|
@@ -3881,11 +4404,12 @@ function apply(ctx, config) {
|
|
|
3881
4404
|
},
|
|
3882
4405
|
onChange: () => {
|
|
3883
4406
|
applySearchSelection(current().webSearch ?? true);
|
|
4407
|
+
refreshTuiSettings?.();
|
|
3884
4408
|
}
|
|
3885
4409
|
});
|
|
3886
4410
|
});
|
|
3887
4411
|
}
|
|
3888
4412
|
//#endregion
|
|
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 };
|
|
4413
|
+
export { ACTIVE_ACCOUNT_AUTO, 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, LANG_AUTO, 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, applyCommandCodeTuiSettings, applyCommands, applyUsageRemote, buildCommandAuthUrl, buildCommandCodeTuiSection, 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 };
|
|
3890
4414
|
|
|
3891
4415
|
//# sourceMappingURL=index.js.map
|