@mars-sea/dsh-commandcode-provider 0.11.10 → 0.11.12
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 +16 -0
- package/README.md +20 -17
- package/README.zh-CN.md +14 -17
- package/lib/client.js +225 -182
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +53 -84
- package/lib/index.js +307 -364
- package/lib/index.js.map +1 -1
- package/package.json +40 -48
package/lib/index.js
CHANGED
|
@@ -2,8 +2,7 @@ import { homedir, tmpdir } from "node:os";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import z from "@deepseek-ai/schemastery";
|
|
4
4
|
import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
|
|
5
|
-
import
|
|
6
|
-
import { CONTEXT_WINDOW_EXCEEDED_CODE, LlmAdapter, LlmError, ReasoningEffortId, ToolCallId, assertUsableApiKey, attributionHeaders, errorChain, isContextWindowExceededError, offloadedImageText, resolveRetryPolicy, textOnlyImageText } from "@deepseek-ai/dsh-llm";
|
|
5
|
+
import { CONTEXT_WINDOW_EXCEEDED_CODE, IMAGE_OFFLOAD_REQUIRED_CODE, LlmAdapter, LlmError, ReasoningEffortId, ToolCallId, assertUsableApiKey, attributionHeaders, errorChain, isContextWindowExceededError, offloadedImageText, projectOffloadedImages, requiredImageOffload, resolveRetryPolicy, textOnlyImageText } from "@deepseek-ai/dsh-llm";
|
|
7
6
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
8
7
|
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
9
8
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
@@ -309,6 +308,11 @@ const KNOWN_EFFORTS = {
|
|
|
309
308
|
"low",
|
|
310
309
|
"medium",
|
|
311
310
|
"high"
|
|
311
|
+
],
|
|
312
|
+
"stealth/space-bunny-alpha": [
|
|
313
|
+
"low",
|
|
314
|
+
"medium",
|
|
315
|
+
"high"
|
|
312
316
|
]
|
|
313
317
|
};
|
|
314
318
|
/**
|
|
@@ -374,6 +378,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
374
378
|
"moonshotai/Kimi-K2.7-Code-Highspeed",
|
|
375
379
|
"moonshotai/Kimi-K3",
|
|
376
380
|
"sakana/fugu-ultra",
|
|
381
|
+
"stealth/space-bunny-alpha",
|
|
377
382
|
"stepfun/Step-3.7-Flash",
|
|
378
383
|
"stepfun/Step-5-Preview",
|
|
379
384
|
"thinkingmachines/inkling",
|
|
@@ -390,7 +395,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
390
395
|
]);
|
|
391
396
|
/**
|
|
392
397
|
* Models WITHOUT a zero-data-retention upstream, per the official CLI's own
|
|
393
|
-
* registry (`command-code@1.
|
|
398
|
+
* registry (`command-code@1.65.0` `dist/cli.mjs`: `modelSupportsZdr(id)` is
|
|
394
399
|
* exactly `!nonZdrSet.has(canonicalize(id))`, and `knownModelSupportsZdr`
|
|
395
400
|
* carries the same membership in the sibling route table — the union is this
|
|
396
401
|
* set). The official docs (commandcode.ai/docs/resources/zdr) put it in prose
|
|
@@ -416,9 +421,12 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
416
421
|
* is the snapshot of the exclusion set and nothing more. The 1.62.0 → 1.64.0
|
|
417
422
|
* diff of that union is EMPTY: both anchors were extracted from both bundles
|
|
418
423
|
* during the 2026-09-23 check and each carries the same 20 members — which is
|
|
419
|
-
* how rare a change here is expected to be.
|
|
420
|
-
*
|
|
421
|
-
*
|
|
424
|
+
* how rare a change here is expected to be. The 1.64.0 → 1.65.0 diff (2026-09-24)
|
|
425
|
+
* is the counterexample that proves the check still runs: exactly one member
|
|
426
|
+
* joins (`stealth/space-bunny-alpha`), and it is in both anchors. `meituan/
|
|
427
|
+
* LongCat-2.0` is the one member the two anchors disagree about (it is in
|
|
428
|
+
* `modelSupportsZdr`'s set in both releases and in neither `knownModelSupportsZdr`
|
|
429
|
+
* set), so the union is
|
|
422
430
|
* what this table follows; reading only the sibling route table would drop it.
|
|
423
431
|
*/
|
|
424
432
|
const KNOWN_NON_ZDR_MODELS = /* @__PURE__ */ new Set([
|
|
@@ -433,6 +441,7 @@ const KNOWN_NON_ZDR_MODELS = /* @__PURE__ */ new Set([
|
|
|
433
441
|
"minimax/minimax-m3-free",
|
|
434
442
|
"poolside/laguna-s-2.1-free",
|
|
435
443
|
"sakana/fugu-ultra",
|
|
444
|
+
"stealth/space-bunny-alpha",
|
|
436
445
|
"stepfun/Step-3.7-Flash",
|
|
437
446
|
"stepfun/Step-5-Preview",
|
|
438
447
|
"xai/grok-4.5",
|
|
@@ -565,7 +574,11 @@ function requiresMessagesEndpoint(modelId) {
|
|
|
565
574
|
* and 1.60.0 added `stepfun/Step-5-Preview` (Go); command-code@1.62.0 added the
|
|
566
575
|
* MiMo V2.6 family — `xiaomi/mimo-v2.6-flash` + `xiaomi/mimo-v2.6-pro` (Go) and
|
|
567
576
|
* `xiaomi/mimo-v2.6-pro-ultraspeed` (GOAT) — so the 1.58.0 -> 1.62.0 window's
|
|
568
|
-
* only tier changes are additions and the superset chain still holds.
|
|
577
|
+
* only tier changes are additions and the superset chain still holds. The
|
|
578
|
+
* 1.62.0 -> 1.65.0 window continues the pattern: 1.64.0 added
|
|
579
|
+
* `claude-opus-5-5` (Provider/Max), `gpt-6-sol` (Pro) and `gpt-6-luna` (Go),
|
|
580
|
+
* and 1.65.0 added `stealth/space-bunny-alpha` (Go, every tier) — additions
|
|
581
|
+
* only, no tier moves.
|
|
569
582
|
*
|
|
570
583
|
* The Provider API exposes no plan metadata, so this snapshot is the source of
|
|
571
584
|
* truth for the picker's plan annotation — it answers "which plan do I need to
|
|
@@ -610,6 +623,7 @@ const KNOWN_PLANS = {
|
|
|
610
623
|
"stepfun/Step-3.5-Flash": "go",
|
|
611
624
|
"stepfun/Step-3.7-Flash": "go",
|
|
612
625
|
"stepfun/Step-5-Preview": "go",
|
|
626
|
+
"stealth/space-bunny-alpha": "go",
|
|
613
627
|
"tencent/hy3-paid": "go",
|
|
614
628
|
"tencent/hy4-preview": "go",
|
|
615
629
|
"thinkingmachines/inkling": "go",
|
|
@@ -817,6 +831,10 @@ const KNOWN_DEALS = {
|
|
|
817
831
|
"inclusionai/ling-3.0-flash-sante:free": {
|
|
818
832
|
label: "FREE",
|
|
819
833
|
free: true
|
|
834
|
+
},
|
|
835
|
+
"stealth/space-bunny-alpha": {
|
|
836
|
+
label: "FREE",
|
|
837
|
+
free: true
|
|
820
838
|
}
|
|
821
839
|
};
|
|
822
840
|
/**
|
|
@@ -2135,6 +2153,14 @@ function installCostProjection(ctx) {
|
|
|
2135
2153
|
});
|
|
2136
2154
|
}
|
|
2137
2155
|
//#endregion
|
|
2156
|
+
//#region src/response-encoding.ts
|
|
2157
|
+
/**
|
|
2158
|
+
* Ask Command Code for plain response bodies. A DSH host using an incompatible
|
|
2159
|
+
* undici global dispatcher can lose response headers and leave compressed
|
|
2160
|
+
* JSON, SSE, and pre-stream error bodies undecoded.
|
|
2161
|
+
*/
|
|
2162
|
+
const IDENTITY_ENCODING_HEADER = { "accept-encoding": "identity" };
|
|
2163
|
+
//#endregion
|
|
2138
2164
|
//#region src/accounts.ts
|
|
2139
2165
|
/**
|
|
2140
2166
|
* Multi-account pool for the Command Code provider (host side).
|
|
@@ -2592,9 +2618,11 @@ const REQUEST_IMAGE_MAX_ENCODED_BYTES = 1048576;
|
|
|
2592
2618
|
* never returns a zero dimension (a 1-px degenerate side is what the round of
|
|
2593
2619
|
* an extreme aspect ratio can produce, and a 0 would make the encoder refuse).
|
|
2594
2620
|
*
|
|
2595
|
-
*
|
|
2596
|
-
*
|
|
2597
|
-
*
|
|
2621
|
+
* `dsh-attachment` exports an equivalent `longEdgeDimensions`, but it does not
|
|
2622
|
+
* guard its inputs: a reference whose declared size is 0 or NaN — a malformed
|
|
2623
|
+
* or fabricated attachment record — would project to a zero-sized target and
|
|
2624
|
+
* make the encoder refuse the whole request. The guard is the reason this stays
|
|
2625
|
+
* local; `tests/image-tokens.test.ts` pins the degenerate cases.
|
|
2598
2626
|
*/
|
|
2599
2627
|
function longEdgeDimensions(width, height, longEdge) {
|
|
2600
2628
|
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return {
|
|
@@ -2615,16 +2643,11 @@ function longEdgeDimensions(width, height, longEdge) {
|
|
|
2615
2643
|
}
|
|
2616
2644
|
/**
|
|
2617
2645
|
* The request target for one normalized attachment: its aspect ratio under the
|
|
2618
|
-
* long-edge budget, plus the byte budget
|
|
2619
|
-
*
|
|
2620
|
-
* The area is computed from the PROJECTED dimensions rather than the source's,
|
|
2621
|
-
* so ≤0.1.5's own pixel projection lands on the same geometry instead of
|
|
2622
|
-
* re-deriving a slightly different one from the original.
|
|
2646
|
+
* long-edge budget, plus the encoded-byte budget.
|
|
2623
2647
|
*/
|
|
2624
2648
|
function requestImageTarget(ref) {
|
|
2625
2649
|
const projected = longEdgeDimensions(ref.width, ref.height, REQUEST_IMAGE_MAX_LONG_EDGE);
|
|
2626
2650
|
return {
|
|
2627
|
-
maxPixels: projected.width * projected.height,
|
|
2628
2651
|
width: projected.width,
|
|
2629
2652
|
height: projected.height,
|
|
2630
2653
|
maxBytes: REQUEST_IMAGE_MAX_ENCODED_BYTES
|
|
@@ -2994,7 +3017,7 @@ function openStreamTrace(scope, options = {}) {
|
|
|
2994
3017
|
* per-request options thunk and an API-key resolver from the plugin entry
|
|
2995
3018
|
* (src/index.ts), so a settings change reaches the very next request.
|
|
2996
3019
|
*/
|
|
2997
|
-
const COMMAND_CODE_CLI_VERSION = "1.
|
|
3020
|
+
const COMMAND_CODE_CLI_VERSION = "1.65.0";
|
|
2998
3021
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
2999
3022
|
const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
|
|
3000
3023
|
const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
@@ -3375,32 +3398,24 @@ async function writeModelsCache(cachePath, models) {
|
|
|
3375
3398
|
}
|
|
3376
3399
|
}
|
|
3377
3400
|
/**
|
|
3378
|
-
*
|
|
3379
|
-
* content blocks. Older engines wrap those fields in the first tool-result
|
|
3380
|
-
* block of a role:'user' message. Read structurally so the bundle still builds
|
|
3381
|
-
* and loads against older peers. Pairing and emission MUST use this same view:
|
|
3382
|
-
* otherwise a dropped result either drops its call too (making the model
|
|
3383
|
-
* forget completed work), or leaves an unanswered call on the wire.
|
|
3401
|
+
* The tool result one message answers, or undefined when it answers none.
|
|
3384
3402
|
*
|
|
3385
|
-
*
|
|
3386
|
-
*
|
|
3387
|
-
*
|
|
3403
|
+
* The harness models a tool result as its own `role: 'tool'` message carrying
|
|
3404
|
+
* `toolCallId`/`isError` beside raw content blocks (a closed role map:
|
|
3405
|
+
* `MessageRoleMap.tool` is the only role a tool result can arrive on). Pairing
|
|
3406
|
+
* and emission MUST agree on this same view: otherwise a dropped result either
|
|
3407
|
+
* drops its call too (making the model forget completed work), or leaves an
|
|
3408
|
+
* unanswered call on the wire.
|
|
3388
3409
|
*/
|
|
3389
3410
|
function toolResultOf(message) {
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
}
|
|
3399
|
-
if (message.role !== "user") return void 0;
|
|
3400
|
-
const kind = message.source?.kind;
|
|
3401
|
-
if (kind !== void 0 && kind !== "tool") return void 0;
|
|
3402
|
-
const block = message.content[0];
|
|
3403
|
-
return block?.type === "tool-result" ? block : void 0;
|
|
3411
|
+
if (message.role !== "tool") return void 0;
|
|
3412
|
+
const { toolCallId, content } = message;
|
|
3413
|
+
if (typeof toolCallId !== "string" || toolCallId === "") return void 0;
|
|
3414
|
+
return {
|
|
3415
|
+
toolCallId,
|
|
3416
|
+
isError: message.isError ?? false,
|
|
3417
|
+
content
|
|
3418
|
+
};
|
|
3404
3419
|
}
|
|
3405
3420
|
/**
|
|
3406
3421
|
* Collect the tool calls that have a paired tool result, plus each call's
|
|
@@ -3473,23 +3488,16 @@ function toolResultMedia(block) {
|
|
|
3473
3488
|
const chunks = [];
|
|
3474
3489
|
const images = [];
|
|
3475
3490
|
const seen = /* @__PURE__ */ new Set();
|
|
3476
|
-
const
|
|
3477
|
-
|
|
3478
|
-
if (nested.
|
|
3479
|
-
|
|
3480
|
-
continue;
|
|
3481
|
-
}
|
|
3482
|
-
if (nested.type === "image") {
|
|
3483
|
-
if (!seen.has(nested.attachment.attachmentId)) {
|
|
3484
|
-
seen.add(nested.attachment.attachmentId);
|
|
3485
|
-
images.push(nested.attachment);
|
|
3486
|
-
}
|
|
3487
|
-
continue;
|
|
3488
|
-
}
|
|
3489
|
-
if (nested.type === "tool-result") walk(nested.content);
|
|
3491
|
+
for (const nested of block.content) {
|
|
3492
|
+
if (nested.type === "text" || nested.type === "reasoning") {
|
|
3493
|
+
if (nested.text) chunks.push(nested.text);
|
|
3494
|
+
continue;
|
|
3490
3495
|
}
|
|
3491
|
-
|
|
3492
|
-
|
|
3496
|
+
if (nested.type === "image" && !seen.has(nested.attachment.attachmentId)) {
|
|
3497
|
+
seen.add(nested.attachment.attachmentId);
|
|
3498
|
+
images.push(nested.attachment);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3493
3501
|
return {
|
|
3494
3502
|
text: chunks.join("\n"),
|
|
3495
3503
|
images
|
|
@@ -3534,8 +3542,7 @@ function toolResultImageNote(media, toolCallId) {
|
|
|
3534
3542
|
return `${lead} ${media.images.length > 1 ? `${media.images.length} images, ` : ""}${dimensions}`;
|
|
3535
3543
|
}
|
|
3536
3544
|
function hasImageContent(message) {
|
|
3537
|
-
|
|
3538
|
-
return check(message.content);
|
|
3545
|
+
return message.content.some((block) => block.type === "image");
|
|
3539
3546
|
}
|
|
3540
3547
|
/** The budget ladder: the primary rung first, the 413 eviction rung second. */
|
|
3541
3548
|
const REQUEST_IMAGE_BUDGETS = [{
|
|
@@ -3549,68 +3556,13 @@ const REQUEST_IMAGE_BUDGETS = [{
|
|
|
3549
3556
|
byteQuantum: 8388608,
|
|
3550
3557
|
countQuantum: 12
|
|
3551
3558
|
}];
|
|
3559
|
+
/** The engine's own helpers: what a process that injects nothing speaks. */
|
|
3560
|
+
const ENGINE_IMAGE_POLICY = {
|
|
3561
|
+
projectOffloadedImages,
|
|
3562
|
+
requiredImageOffload
|
|
3563
|
+
};
|
|
3552
3564
|
/**
|
|
3553
|
-
*
|
|
3554
|
-
* oldest images past it become placeholder text in place, the newest keep their
|
|
3555
|
-
* bytes, and a history already inside the budget is returned as the SAME array
|
|
3556
|
-
* — the caller uses that identity to tell "nothing evicted" from "evicted".
|
|
3557
|
-
*
|
|
3558
|
-
* Nested tool-result images ride the same core walk, which is exactly right
|
|
3559
|
-
* for this adapter: an evicted `read_image` result surfaces its placeholder as
|
|
3560
|
-
* the tool message's own text, so no carrier user message is emitted for it.
|
|
3561
|
-
*/
|
|
3562
|
-
function projectRequestImages(messages, budget, policy) {
|
|
3563
|
-
const offload = policy.offloadRequestImagesWithPolicy;
|
|
3564
|
-
if (typeof offload !== "function") return messages;
|
|
3565
|
-
return offload(messages, {
|
|
3566
|
-
representation: "base64",
|
|
3567
|
-
maxBytes: budget.maxBytes,
|
|
3568
|
-
maxImages: budget.maxImages,
|
|
3569
|
-
byteQuantum: budget.byteQuantum,
|
|
3570
|
-
countQuantum: budget.countQuantum,
|
|
3571
|
-
placeholder: (ref) => offloadedImageText(ref)
|
|
3572
|
-
});
|
|
3573
|
-
}
|
|
3574
|
-
/**
|
|
3575
|
-
* The request options one budget produced: the caller's own object when the
|
|
3576
|
-
* history was already inside the budget, else a copy carrying the projection.
|
|
3577
|
-
* The next rung of the ladder is applied to the CURRENT projection, never to
|
|
3578
|
-
* the raw history — otherwise a retry could reintroduce images the first rung
|
|
3579
|
-
* had already evicted.
|
|
3580
|
-
*/
|
|
3581
|
-
function withImageBudget(options, budget, policy) {
|
|
3582
|
-
const projected = projectRequestImages(options.messages, budget, policy);
|
|
3583
|
-
return projected === options.messages ? options : {
|
|
3584
|
-
...options,
|
|
3585
|
-
messages: [...projected]
|
|
3586
|
-
};
|
|
3587
|
-
}
|
|
3588
|
-
/**
|
|
3589
|
-
* Stable provider-neutral code for "a request still carries more images than
|
|
3590
|
-
* this route will send; offload N of them and retry". Declared locally rather
|
|
3591
|
-
* than imported: the constant does not exist on the 0.1.2–0.1.5 peers this
|
|
3592
|
-
* plugin still supports, and importing it statically is the link-time failure
|
|
3593
|
-
* described above the budget constants.
|
|
3594
|
-
*/
|
|
3595
|
-
const IMAGE_OFFLOAD_REQUIRED_CODE = "IMAGE_OFFLOAD_REQUIRED";
|
|
3596
|
-
/** The installed engine's image policy; see {@link CoreImagePolicy}. */
|
|
3597
|
-
const CORE_IMAGE_POLICY = dshLlm;
|
|
3598
|
-
/**
|
|
3599
|
-
* The durable-offload contract this engine speaks, or undefined on ≤0.1.5.
|
|
3600
|
-
*
|
|
3601
|
-
* Both members are required together: a half-populated engine would either
|
|
3602
|
-
* lose the surface's marks (re-sending evicted images as pixels) or lose the
|
|
3603
|
-
* count (silently sending an over-budget body), and neither is worth a partial
|
|
3604
|
-
* opt-in. Absent, the adapter keeps its own transient projection instead.
|
|
3605
|
-
*/
|
|
3606
|
-
function surfaceImagePolicy(api = CORE_IMAGE_POLICY) {
|
|
3607
|
-
return typeof api.projectOffloadedImages === "function" && typeof api.requiredImageOffload === "function" ? {
|
|
3608
|
-
projectOffloadedImages: api.projectOffloadedImages,
|
|
3609
|
-
requiredImageOffload: api.requiredImageOffload
|
|
3610
|
-
} : void 0;
|
|
3611
|
-
}
|
|
3612
|
-
/**
|
|
3613
|
-
* The failure a route raises instead of evicting on its own (≥0.1.6). The
|
|
3565
|
+
* The failure a route raises instead of evicting on its own. The
|
|
3614
3566
|
* count is the ONE thing the harness needs: `dsh-compaction-image-offload`
|
|
3615
3567
|
* records it as an `image/offload` event, marks that many oldest retained
|
|
3616
3568
|
* occurrences, and retries the step.
|
|
@@ -3639,10 +3591,14 @@ function coreBudget(budget) {
|
|
|
3639
3591
|
*/
|
|
3640
3592
|
const imageVersionBytes = (block) => block.attachment.bytes;
|
|
3641
3593
|
/**
|
|
3642
|
-
*
|
|
3643
|
-
* and an over-budget history FAILS with the count to offload
|
|
3644
|
-
* evicted here. Throws rather than returning, so no caller can
|
|
3645
|
-
* send a body this route already knows is too large.
|
|
3594
|
+
* Request options under one budget rung: the surface's durable marks become
|
|
3595
|
+
* placeholder text, and an over-budget history FAILS with the count to offload
|
|
3596
|
+
* instead of being evicted here. Throws rather than returning, so no caller can
|
|
3597
|
+
* accidentally send a body this route already knows is too large.
|
|
3598
|
+
*
|
|
3599
|
+
* The next rung is applied to the CURRENT projection, never to the raw history —
|
|
3600
|
+
* otherwise the 413 retry could reintroduce images the first rung had already
|
|
3601
|
+
* evicted.
|
|
3646
3602
|
*/
|
|
3647
3603
|
function withSurfaceOffload(options, policy, budget) {
|
|
3648
3604
|
const marked = policy.projectOffloadedImages(options.messages, (ref) => offloadedImageText(ref));
|
|
@@ -3655,21 +3611,17 @@ function withSurfaceOffload(options, policy, budget) {
|
|
|
3655
3611
|
};
|
|
3656
3612
|
}
|
|
3657
3613
|
/**
|
|
3658
|
-
* One image's bytes for the wire: the attachment service's REQUEST VERSION
|
|
3659
|
-
* it can produce one, else the normalized original.
|
|
3614
|
+
* One image's bytes for the wire: the attachment service's REQUEST VERSION.
|
|
3660
3615
|
*
|
|
3661
3616
|
* `readImage()` answers the stored original, which is what admission accepted —
|
|
3662
3617
|
* not what a provider request should carry. `readImageRequest()` re-encodes to
|
|
3663
3618
|
* the route target in `./image-request.ts` and caches the result under it, so
|
|
3664
3619
|
* the same attachment costs one encode per target instead of one per occurrence,
|
|
3665
3620
|
* and the base64 the wire expands is bounded (both the pixel budget and, more to
|
|
3666
|
-
* the point here, the encoded bytes).
|
|
3667
|
-
* not implement the abstract method; every engine this plugin declares support
|
|
3668
|
-
* for does.
|
|
3621
|
+
* the point here, the encoded bytes).
|
|
3669
3622
|
*/
|
|
3670
3623
|
async function readRequestImage(attachments, ref) {
|
|
3671
|
-
|
|
3672
|
-
return (await attachments.readImage(ref)).data;
|
|
3624
|
+
return (await attachments.readImageRequest(ref, requestImageTarget(ref))).data;
|
|
3673
3625
|
}
|
|
3674
3626
|
/**
|
|
3675
3627
|
* Price one request occurrence.
|
|
@@ -3678,13 +3630,12 @@ async function readRequestImage(attachments, ref) {
|
|
|
3678
3630
|
* model-visible TEXT so the caller's estimator can charge that instead: a model
|
|
3679
3631
|
* that accepts text only (the harness projects those images to placeholder text
|
|
3680
3632
|
* before the request), and an occurrence the session surface has already
|
|
3681
|
-
* offloaded (
|
|
3682
|
-
*
|
|
3683
|
-
*
|
|
3633
|
+
* offloaded (the same placeholder text rides in every later request). A retained
|
|
3634
|
+
* occurrence on this route contributes no text at all — the pixels are inlined —
|
|
3635
|
+
* so its price is the family's visual-token count and nothing else.
|
|
3684
3636
|
*/
|
|
3685
|
-
function priceRequestImage(
|
|
3686
|
-
const
|
|
3687
|
-
const ref = block.attachment ?? occurrence;
|
|
3637
|
+
function priceRequestImage(block, model, imageCapable) {
|
|
3638
|
+
const ref = block.attachment;
|
|
3688
3639
|
if (!imageCapable) return {
|
|
3689
3640
|
visualTokens: 0,
|
|
3690
3641
|
text: textOnlyImageText(ref)
|
|
@@ -3993,17 +3944,12 @@ function parseAccountIdentity(whoami) {
|
|
|
3993
3944
|
orgId: orgData === void 0 ? void 0 : stringValue(orgData.id)
|
|
3994
3945
|
};
|
|
3995
3946
|
}
|
|
3996
|
-
|
|
3997
|
-
* Classify a TOTAL failure: when every endpoint failed with one class of
|
|
3998
|
-
* error, the degraded per-endpoint view would hide the root cause behind
|
|
3999
|
-
* a generic "partial data" note — name it instead.
|
|
4000
|
-
*/
|
|
4001
|
-
function classifyTotalFailure(failures, failedStatuses) {
|
|
3947
|
+
function classifyTotalFailure(failures, failed) {
|
|
4002
3948
|
if (failures.length !== USAGE_ENDPOINT_COUNT) return void 0;
|
|
4003
|
-
|
|
4004
|
-
if (
|
|
4005
|
-
if (
|
|
4006
|
-
if (
|
|
3949
|
+
if (failed.every((item) => item.kind === "http" && item.status === 401)) return "invalid-key";
|
|
3950
|
+
if (failed.every((item) => item.kind === "http" && item.status >= 500)) return "service-unavailable";
|
|
3951
|
+
if (failed.every((item) => item.kind === "invalid-response")) return "invalid-response";
|
|
3952
|
+
if (failed.every((item) => item.kind === "transport")) return "network";
|
|
4007
3953
|
}
|
|
4008
3954
|
/** Build the legacy CLI (`/alpha/generate`) request body for one call. */
|
|
4009
3955
|
async function buildCliBody(options, connection, facts) {
|
|
@@ -4097,6 +4043,7 @@ async function connectGenerate(deps, key, protocol, body) {
|
|
|
4097
4043
|
const zdr = connection.zdr === true;
|
|
4098
4044
|
const headers = protocol === "cli" ? {
|
|
4099
4045
|
"Content-Type": "application/json",
|
|
4046
|
+
...IDENTITY_ENCODING_HEADER,
|
|
4100
4047
|
Authorization: `Bearer ${key}`,
|
|
4101
4048
|
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
4102
4049
|
"x-cli-environment": "production",
|
|
@@ -4107,6 +4054,7 @@ async function connectGenerate(deps, key, protocol, body) {
|
|
|
4107
4054
|
...attributionHeaders()
|
|
4108
4055
|
} : {
|
|
4109
4056
|
"Content-Type": "application/json",
|
|
4057
|
+
...IDENTITY_ENCODING_HEADER,
|
|
4110
4058
|
Authorization: `Bearer ${key}`,
|
|
4111
4059
|
Accept: "text/event-stream",
|
|
4112
4060
|
...zdr ? { "x-cmd-zdr": "1" } : {},
|
|
@@ -4220,13 +4168,9 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4220
4168
|
catalog = [];
|
|
4221
4169
|
fetchImpl;
|
|
4222
4170
|
resolveAttachments;
|
|
4223
|
-
/** The engine's request-image policy, resolved once (tests may inject one). */
|
|
4224
|
-
imagePolicy;
|
|
4225
4171
|
/**
|
|
4226
|
-
* The
|
|
4227
|
-
*
|
|
4228
|
-
* speaks cannot change while it runs, and resolving here keeps every request
|
|
4229
|
-
* on the same branch (see {@link surfaceImagePolicy}).
|
|
4172
|
+
* The durable-offload contract, resolved once (tests may inject a stub). The
|
|
4173
|
+
* contract cannot change while the process runs.
|
|
4230
4174
|
*/
|
|
4231
4175
|
surfaceOffload;
|
|
4232
4176
|
billingAccess = /* @__PURE__ */ new Map();
|
|
@@ -4237,8 +4181,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4237
4181
|
this.deps = deps;
|
|
4238
4182
|
this.fetchImpl = deps.fetchImpl ?? fetch;
|
|
4239
4183
|
this.resolveAttachments = deps.resolveAttachments;
|
|
4240
|
-
this.
|
|
4241
|
-
this.surfaceOffload = surfaceImagePolicy(this.imagePolicy);
|
|
4184
|
+
this.surfaceOffload = deps.imageOffload ?? ENGINE_IMAGE_POLICY;
|
|
4242
4185
|
}
|
|
4243
4186
|
/**
|
|
4244
4187
|
* Display metadata for the picker's provider group header. The base class
|
|
@@ -4314,6 +4257,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4314
4257
|
const response = await this.fetchImpl(`${apiBase}/provider/v1/models`, {
|
|
4315
4258
|
headers: {
|
|
4316
4259
|
accept: "application/json",
|
|
4260
|
+
...IDENTITY_ENCODING_HEADER,
|
|
4317
4261
|
...attributionHeaders()
|
|
4318
4262
|
},
|
|
4319
4263
|
signal: signal ?? AbortSignal.timeout(1e4)
|
|
@@ -4377,6 +4321,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4377
4321
|
const named = connection.apiKeyEnv;
|
|
4378
4322
|
return {
|
|
4379
4323
|
Authorization: `Bearer ${assertUsableApiKey(raw, "llm-commandcode", typeof named === "string" && named !== "" ? named : "the stored credential")}`,
|
|
4324
|
+
...IDENTITY_ENCODING_HEADER,
|
|
4380
4325
|
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
4381
4326
|
"x-cli-environment": "production",
|
|
4382
4327
|
...attributionHeaders()
|
|
@@ -4386,8 +4331,8 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4386
4331
|
* Fetch one account endpoint and parse its JSON body. Returns the HTTP
|
|
4387
4332
|
* status alongside the parsed record so each caller applies its own
|
|
4388
4333
|
* failure accounting: the billing probe fails open silently, the usage
|
|
4389
|
-
* report books failures per endpoint. Non-2xx and
|
|
4390
|
-
* back without a record; only a
|
|
4334
|
+
* report books failures per endpoint. Non-2xx and invalid JSON bodies come
|
|
4335
|
+
* back without a record; only a fetch failure propagates to the caller.
|
|
4391
4336
|
*
|
|
4392
4337
|
* `timeoutMs` is the caller's budget for this one request. The default is the
|
|
4393
4338
|
* CATALOG probe's short cap, which fits the picker's fail-open reads; the
|
|
@@ -4400,10 +4345,21 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4400
4345
|
signal: AbortSignal.timeout(timeoutMs)
|
|
4401
4346
|
});
|
|
4402
4347
|
if (!response.ok) return { status: response.status };
|
|
4403
|
-
|
|
4404
|
-
|
|
4348
|
+
let parsed;
|
|
4349
|
+
try {
|
|
4350
|
+
parsed = await response.json();
|
|
4351
|
+
} catch {
|
|
4352
|
+
return {
|
|
4353
|
+
status: response.status,
|
|
4354
|
+
invalidResponse: true
|
|
4355
|
+
};
|
|
4356
|
+
}
|
|
4357
|
+
return isRecord(parsed) ? {
|
|
4358
|
+
status: response.status,
|
|
4359
|
+
record: parsed
|
|
4360
|
+
} : {
|
|
4405
4361
|
status: response.status,
|
|
4406
|
-
|
|
4362
|
+
invalidResponse: true
|
|
4407
4363
|
};
|
|
4408
4364
|
}
|
|
4409
4365
|
/**
|
|
@@ -4584,19 +4540,25 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4584
4540
|
throw error;
|
|
4585
4541
|
}
|
|
4586
4542
|
const failures = [];
|
|
4587
|
-
const
|
|
4543
|
+
const failed = [];
|
|
4588
4544
|
const getJson = async (path) => {
|
|
4589
4545
|
try {
|
|
4590
|
-
const { status, record } = await this.fetchEndpointJson(`${base}${path}`, headers, connection.requestTimeoutMs);
|
|
4546
|
+
const { status, record, invalidResponse } = await this.fetchEndpointJson(`${base}${path}`, headers, connection.requestTimeoutMs);
|
|
4591
4547
|
if (record === void 0) {
|
|
4592
|
-
failures.push(`${path}: HTTP ${status}`);
|
|
4593
|
-
|
|
4548
|
+
failures.push(`${path}: HTTP ${status}${invalidResponse ? " returned an unreadable or invalid JSON body" : ""}`);
|
|
4549
|
+
failed.push(invalidResponse ? {
|
|
4550
|
+
kind: "invalid-response",
|
|
4551
|
+
status
|
|
4552
|
+
} : {
|
|
4553
|
+
kind: "http",
|
|
4554
|
+
status
|
|
4555
|
+
});
|
|
4594
4556
|
return;
|
|
4595
4557
|
}
|
|
4596
4558
|
return record;
|
|
4597
4559
|
} catch (error) {
|
|
4598
4560
|
failures.push(`${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4599
|
-
|
|
4561
|
+
failed.push({ kind: "transport" });
|
|
4600
4562
|
return;
|
|
4601
4563
|
}
|
|
4602
4564
|
};
|
|
@@ -4625,7 +4587,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4625
4587
|
currentPeriodEnd: periodEndValue(subData?.currentPeriodEnd)
|
|
4626
4588
|
};
|
|
4627
4589
|
}
|
|
4628
|
-
const blocked = classifyTotalFailure(failures,
|
|
4590
|
+
const blocked = classifyTotalFailure(failures, failed);
|
|
4629
4591
|
if (blocked !== void 0) report.blocked = blocked;
|
|
4630
4592
|
return report;
|
|
4631
4593
|
}
|
|
@@ -4703,8 +4665,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4703
4665
|
const reasoningEffort = effort && effort !== "off" && supported?.includes(effort) ? effort : void 0;
|
|
4704
4666
|
const systemText = [options.system ?? "", ...options.messages.filter((m) => m.role === "system").map((m) => m.content.map(blockText).filter(Boolean).join("\n"))].filter(Boolean).join("\n\n");
|
|
4705
4667
|
let protocol = this.resolveProtocol(apiKey, options.model);
|
|
4706
|
-
let requestOptions =
|
|
4707
|
-
let imageBudgetIndex = 0;
|
|
4668
|
+
let requestOptions = withSurfaceOffload(options, this.surfaceOffload, REQUEST_IMAGE_BUDGETS[0]);
|
|
4708
4669
|
const buildBody = async (target) => target === "cli" ? buildCliBody(requestOptions, connection, {
|
|
4709
4670
|
maxTokens,
|
|
4710
4671
|
reasoningEffort,
|
|
@@ -4737,19 +4698,9 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4737
4698
|
body = await buildBody("cli");
|
|
4738
4699
|
continue;
|
|
4739
4700
|
}
|
|
4740
|
-
if (attempt.status === 413 &&
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
if (missing > 0) throw imageOffloadRequired(missing);
|
|
4744
|
-
} else {
|
|
4745
|
-
const tightened = withImageBudget(requestOptions, REQUEST_IMAGE_BUDGETS[imageBudgetIndex + 1], this.imagePolicy);
|
|
4746
|
-
if (tightened !== requestOptions) {
|
|
4747
|
-
imageBudgetIndex += 1;
|
|
4748
|
-
requestOptions = tightened;
|
|
4749
|
-
body = await buildBody(protocol);
|
|
4750
|
-
continue;
|
|
4751
|
-
}
|
|
4752
|
-
}
|
|
4701
|
+
if (attempt.status === 413 && REQUEST_IMAGE_BUDGETS.length > 1) {
|
|
4702
|
+
const missing = this.surfaceOffload.requiredImageOffload(requestOptions.messages, coreBudget(REQUEST_IMAGE_BUDGETS[1]), imageVersionBytes);
|
|
4703
|
+
if (missing > 0) throw imageOffloadRequired(missing);
|
|
4753
4704
|
}
|
|
4754
4705
|
const rotate = this.deps.rotateApiKey;
|
|
4755
4706
|
const rejection = classifyAccountRejection(attempt.status, attempt.errText);
|
|
@@ -5438,6 +5389,7 @@ const commandcodeCommand = {
|
|
|
5438
5389
|
unconfigured: " (未配置 API 密钥)",
|
|
5439
5390
|
blockedInvalidKey: "⛔ API 密钥无效或已过期 — 服务端拒绝了全部请求(401),请检查该账户的密钥配置",
|
|
5440
5391
|
blockedServiceUnavailable: "⚠️ Command Code 服务暂时不可用(5xx),稍后重试",
|
|
5392
|
+
blockedInvalidResponse: "⚠️ Command Code 返回了无法读取的响应 — 请检查宿主的 HTTP 代理或响应解压配置",
|
|
5441
5393
|
blockedNetwork: "⚠️ 无法连接 Command Code 服务 — 请检查网络或 API 地址",
|
|
5442
5394
|
planLine: " 📦 套餐 {name}{status}{period}",
|
|
5443
5395
|
planPeriodSuffix: " · 账期截止 {date}",
|
|
@@ -5469,6 +5421,7 @@ const commandcodeCommand = {
|
|
|
5469
5421
|
unconfigured: " (no API key configured)",
|
|
5470
5422
|
blockedInvalidKey: "⛔ API key invalid or expired — the server rejected every request (401); check the key configured for this account",
|
|
5471
5423
|
blockedServiceUnavailable: "⚠️ Command Code service temporarily unavailable (5xx); try again later",
|
|
5424
|
+
blockedInvalidResponse: "⚠️ Command Code returned an unreadable response — check the host HTTP proxy or response decoding",
|
|
5472
5425
|
blockedNetwork: "⚠️ could not reach the Command Code service — check your network or the API base setting",
|
|
5473
5426
|
planLine: " 📦 Plan {name}{status}{period}",
|
|
5474
5427
|
planPeriodSuffix: " · period ends {date}",
|
|
@@ -5567,6 +5520,7 @@ function renderReport(report, locale, title) {
|
|
|
5567
5520
|
lines.push(title ?? commandCopy(locale, "title").replace("{account}", account), "");
|
|
5568
5521
|
if (report.blocked === "invalid-key") lines.push(commandCopy(locale, "blockedInvalidKey"), "");
|
|
5569
5522
|
else if (report.blocked === "service-unavailable") lines.push(commandCopy(locale, "blockedServiceUnavailable"), "");
|
|
5523
|
+
else if (report.blocked === "invalid-response") lines.push(commandCopy(locale, "blockedInvalidResponse"), "");
|
|
5570
5524
|
else if (report.blocked === "network") lines.push(commandCopy(locale, "blockedNetwork"), "");
|
|
5571
5525
|
if (report.plan && report.plan.name !== "") {
|
|
5572
5526
|
const p = report.plan;
|
|
@@ -5668,6 +5622,14 @@ function makeBoundaryValidator(prefix) {
|
|
|
5668
5622
|
booleanField
|
|
5669
5623
|
};
|
|
5670
5624
|
}
|
|
5625
|
+
/** Use the same strict-codec shape for invocation parameters and results. */
|
|
5626
|
+
function makeStrictCodec(typeSymbol, schema) {
|
|
5627
|
+
return {
|
|
5628
|
+
mode: "strict",
|
|
5629
|
+
typeSymbol,
|
|
5630
|
+
create: () => schema
|
|
5631
|
+
};
|
|
5632
|
+
}
|
|
5671
5633
|
/**
|
|
5672
5634
|
* Build one strict invocation descriptor. Every Command Code Remote shares the
|
|
5673
5635
|
* `commandcode` namespace, the `commandcodeUsage` service, and a strict
|
|
@@ -5676,12 +5638,6 @@ function makeBoundaryValidator(prefix) {
|
|
|
5676
5638
|
* only its own facts.
|
|
5677
5639
|
*/
|
|
5678
5640
|
function makeRemoteDescriptor(endpoint, method, typeSymbol, schema) {
|
|
5679
|
-
const result = {
|
|
5680
|
-
mode: "strict",
|
|
5681
|
-
typeSymbol,
|
|
5682
|
-
schema,
|
|
5683
|
-
create: () => schema
|
|
5684
|
-
};
|
|
5685
5641
|
return {
|
|
5686
5642
|
id: `${REMOTE_PACKAGE}#${endpoint}`,
|
|
5687
5643
|
service: REMOTE_SERVICE,
|
|
@@ -5689,7 +5645,7 @@ function makeRemoteDescriptor(endpoint, method, typeSymbol, schema) {
|
|
|
5689
5645
|
method,
|
|
5690
5646
|
invocation: { kind: "direct" },
|
|
5691
5647
|
parameters: [],
|
|
5692
|
-
result
|
|
5648
|
+
result: makeStrictCodec(typeSymbol, schema)
|
|
5693
5649
|
};
|
|
5694
5650
|
}
|
|
5695
5651
|
//#endregion
|
|
@@ -5734,7 +5690,7 @@ function parseUsageReport(value) {
|
|
|
5734
5690
|
const report = { failures };
|
|
5735
5691
|
if (source.blocked !== void 0) {
|
|
5736
5692
|
const blocked = source.blocked;
|
|
5737
|
-
if (blocked === "invalid-key" || blocked === "service-unavailable" || blocked === "network") report.blocked = blocked;
|
|
5693
|
+
if (blocked === "invalid-key" || blocked === "service-unavailable" || blocked === "invalid-response" || blocked === "network") report.blocked = blocked;
|
|
5738
5694
|
else reject$1("blocked");
|
|
5739
5695
|
}
|
|
5740
5696
|
if (source.account !== void 0) {
|
|
@@ -5963,9 +5919,24 @@ function parseLoginStatus(value) {
|
|
|
5963
5919
|
}
|
|
5964
5920
|
/** The strict result codec shared by all three login endpoints. */
|
|
5965
5921
|
const loginStatusSchema = { parse: parseLoginStatus };
|
|
5966
|
-
/**
|
|
5922
|
+
/** Optional account target; the Host still checks it against saved slots. */
|
|
5923
|
+
const loginTargetSchema = { parse(value) {
|
|
5924
|
+
if (value === void 0 || typeof value === "string") return value;
|
|
5925
|
+
return reject("targetRef");
|
|
5926
|
+
} };
|
|
5927
|
+
/** Build one login invocation descriptor. Only begin accepts an account ref. */
|
|
5967
5928
|
function loginDescriptor(endpoint, method) {
|
|
5968
|
-
|
|
5929
|
+
const descriptor = makeRemoteDescriptor(endpoint, method, `${REMOTE_PACKAGE}#CommandCodeLoginStatus`, loginStatusSchema);
|
|
5930
|
+
return method === "loginBegin" ? {
|
|
5931
|
+
...descriptor,
|
|
5932
|
+
parameters: [{
|
|
5933
|
+
name: "targetRef",
|
|
5934
|
+
wire: "targetRef",
|
|
5935
|
+
source: "json",
|
|
5936
|
+
codec: makeStrictCodec(`${REMOTE_PACKAGE}#CommandCodeLoginTargetRef`, loginTargetSchema),
|
|
5937
|
+
acceptsUndefined: true
|
|
5938
|
+
}]
|
|
5939
|
+
} : descriptor;
|
|
5969
5940
|
}
|
|
5970
5941
|
/** The three login descriptors, shared verbatim by Host registration and Client mount. */
|
|
5971
5942
|
const LOGIN_DESCRIPTORS = [
|
|
@@ -6033,8 +6004,8 @@ var CommandCodeUsageService = class extends TypertRemoteService {
|
|
|
6033
6004
|
* (no free loopback port, disposed plugin); the Gateway folds the throw
|
|
6034
6005
|
* into the failure branch the page renders.
|
|
6035
6006
|
*/
|
|
6036
|
-
async loginBegin() {
|
|
6037
|
-
return this.requireLogin().begin();
|
|
6007
|
+
async loginBegin(targetRef) {
|
|
6008
|
+
return this.requireLogin().begin(targetRef);
|
|
6038
6009
|
}
|
|
6039
6010
|
/** Poll a login attempt's status. */
|
|
6040
6011
|
async loginStatus() {
|
|
@@ -6121,6 +6092,12 @@ const LOGIN_ALLOWED_ORIGINS = [
|
|
|
6121
6092
|
];
|
|
6122
6093
|
/** The Studio route that performs the browser-side login. */
|
|
6123
6094
|
const STUDIO_AUTH_PATH = "/studio/auth/cli";
|
|
6095
|
+
/** Resolve a browser-login destination without allowing arbitrary credential writes. */
|
|
6096
|
+
function loginCredentialRef(targetRef, defaultRef, accounts) {
|
|
6097
|
+
if (targetRef === void 0) return defaultRef;
|
|
6098
|
+
if (targetRef === defaultRef || !accounts.some((account) => account.apiKeyEnv === targetRef)) throw new Error("the target account must be saved before browser sign-in");
|
|
6099
|
+
return targetRef;
|
|
6100
|
+
}
|
|
6124
6101
|
/** Compose the Studio authorization URL (pure, exported for tests). */
|
|
6125
6102
|
function buildCommandAuthUrl(options) {
|
|
6126
6103
|
const callback = `http://localhost:${options.port}/callback`;
|
|
@@ -6143,6 +6120,7 @@ async function validateCommandApiKey(fetchImpl, apiBase, apiKey) {
|
|
|
6143
6120
|
method: "GET",
|
|
6144
6121
|
headers: {
|
|
6145
6122
|
"Content-Type": "application/json",
|
|
6123
|
+
...IDENTITY_ENCODING_HEADER,
|
|
6146
6124
|
Authorization: `Bearer ${apiKey}`
|
|
6147
6125
|
}
|
|
6148
6126
|
});
|
|
@@ -6210,6 +6188,8 @@ var CommandCodeLoginFlow = class {
|
|
|
6210
6188
|
* the port window so browser login dies until the Host restarts.
|
|
6211
6189
|
*/
|
|
6212
6190
|
starting;
|
|
6191
|
+
/** A live attempt's destination; different rows may not rejoin it. */
|
|
6192
|
+
targetRef;
|
|
6213
6193
|
/** A `cancel()` that arrived while a start was still binding (see {@link CommandCodeLoginFlow.cancel}). */
|
|
6214
6194
|
cancelPending = false;
|
|
6215
6195
|
disposed = false;
|
|
@@ -6230,12 +6210,20 @@ var CommandCodeLoginFlow = class {
|
|
|
6230
6210
|
* `waiting` carrying the Studio URL once the loopback server is up.
|
|
6231
6211
|
* Rejects only when the flow cannot start at all (no free port, disposed).
|
|
6232
6212
|
*/
|
|
6233
|
-
async begin() {
|
|
6213
|
+
async begin(targetRef) {
|
|
6234
6214
|
if (this.disposed) throw new Error("login flow has been disposed");
|
|
6235
|
-
|
|
6236
|
-
if (this.
|
|
6215
|
+
this.deps.validateTargetRef?.(targetRef);
|
|
6216
|
+
if (this.statusValue.state === "waiting" && this.server !== void 0) {
|
|
6217
|
+
if (this.targetRef !== targetRef) throw new Error("another account login is already in progress");
|
|
6218
|
+
return this.statusValue;
|
|
6219
|
+
}
|
|
6220
|
+
if (this.starting !== void 0) {
|
|
6221
|
+
if (this.targetRef !== targetRef) throw new Error("another account login is already in progress");
|
|
6222
|
+
return this.starting;
|
|
6223
|
+
}
|
|
6237
6224
|
this.cancelPending = false;
|
|
6238
|
-
|
|
6225
|
+
this.targetRef = targetRef;
|
|
6226
|
+
const starting = this.startAttempt(targetRef);
|
|
6239
6227
|
this.starting = starting;
|
|
6240
6228
|
try {
|
|
6241
6229
|
return await starting;
|
|
@@ -6248,7 +6236,7 @@ var CommandCodeLoginFlow = class {
|
|
|
6248
6236
|
* server, one published `waiting` status. Callers reach it only through
|
|
6249
6237
|
* `begin()`'s single-flight fence.
|
|
6250
6238
|
*/
|
|
6251
|
-
async startAttempt() {
|
|
6239
|
+
async startAttempt(targetRef) {
|
|
6252
6240
|
this.teardown();
|
|
6253
6241
|
const attempt = ++this.attemptSeq;
|
|
6254
6242
|
const port = await this.findPort();
|
|
@@ -6288,7 +6276,7 @@ var CommandCodeLoginFlow = class {
|
|
|
6288
6276
|
});
|
|
6289
6277
|
}, this.deps.timeoutMs ?? 12e4);
|
|
6290
6278
|
this.timer.unref?.();
|
|
6291
|
-
settled.then((credentials) => this.complete(attempt, credentials), (failure) => this.failFrom(attempt, failure));
|
|
6279
|
+
settled.then((credentials) => this.complete(attempt, credentials, targetRef), (failure) => this.failFrom(attempt, failure));
|
|
6292
6280
|
return this.statusValue;
|
|
6293
6281
|
}
|
|
6294
6282
|
/**
|
|
@@ -6467,7 +6455,7 @@ var CommandCodeLoginFlow = class {
|
|
|
6467
6455
|
* would report "cancelled" while the credential landed anyway, and the page
|
|
6468
6456
|
* would silently flip to success.
|
|
6469
6457
|
*/
|
|
6470
|
-
async complete(attempt, credentials) {
|
|
6458
|
+
async complete(attempt, credentials, targetRef) {
|
|
6471
6459
|
if (!this.ownsAttempt(attempt)) return;
|
|
6472
6460
|
const validation = await validateCommandApiKey(this.deps.fetchImpl ?? fetch, this.readApiBase(), credentials.apiKey);
|
|
6473
6461
|
if (!this.ownsAttempt(attempt)) return;
|
|
@@ -6482,7 +6470,8 @@ var CommandCodeLoginFlow = class {
|
|
|
6482
6470
|
}
|
|
6483
6471
|
if (!this.ownsAttempt(attempt)) return;
|
|
6484
6472
|
try {
|
|
6485
|
-
|
|
6473
|
+
this.deps.validateTargetRef?.(targetRef);
|
|
6474
|
+
await this.deps.storeKey(credentials, targetRef);
|
|
6486
6475
|
} catch (error) {
|
|
6487
6476
|
if (!this.ownsAttempt(attempt)) return;
|
|
6488
6477
|
this.setStatus({
|
|
@@ -6579,30 +6568,6 @@ const COMMANDCODE_SEARCH_PROVIDER_ID = "commandcode";
|
|
|
6579
6568
|
* modsearch even with Command Code search turned off (issue #26).
|
|
6580
6569
|
*/
|
|
6581
6570
|
const DEFAULT_WEB_SEARCH_PROVIDER_ID = "deepseek-official";
|
|
6582
|
-
/**
|
|
6583
|
-
* Point the web seam's search selection at this plugin's provider (`commandcode`).
|
|
6584
|
-
* Sets the runtime field; the next search call honours it because `search()`
|
|
6585
|
-
* re-reads `searchProviderId` each time. Returns the prior id (or undefined).
|
|
6586
|
-
* Never throws: a hardened/frozen runtime shape must not break the
|
|
6587
|
-
* settings-save path that calls this — the provider simply stays
|
|
6588
|
-
* registered-but-unselected (the boot-time `searchProvider: commandcode`
|
|
6589
|
-
* cordis patch is the durable alternative).
|
|
6590
|
-
*
|
|
6591
|
-
* @deprecated Prefer {@link applyCommandCodeSearchSelection}: this overload
|
|
6592
|
-
* always overwrites the displaced backend with the factory default on
|
|
6593
|
-
* disable, so turning Command Code search off silences whichever provider
|
|
6594
|
-
* was selected before (e.g. modsearch) instead of restoring it (issue #26).
|
|
6595
|
-
*/
|
|
6596
|
-
function selectCommandCodeSearchProvider(web, enable) {
|
|
6597
|
-
try {
|
|
6598
|
-
const field = web;
|
|
6599
|
-
const prior = field.searchProviderId;
|
|
6600
|
-
field.searchProviderId = enable ? COMMANDCODE_SEARCH_PROVIDER_ID : DEFAULT_WEB_SEARCH_PROVIDER_ID;
|
|
6601
|
-
return prior;
|
|
6602
|
-
} catch {
|
|
6603
|
-
return;
|
|
6604
|
-
}
|
|
6605
|
-
}
|
|
6606
6571
|
/** Fresh selection state: the plugin starts out not owning the selection. */
|
|
6607
6572
|
function commandCodeSearchSelection() {
|
|
6608
6573
|
return {
|
|
@@ -6732,6 +6697,7 @@ var CommandCodeSearchProvider = class {
|
|
|
6732
6697
|
method: "POST",
|
|
6733
6698
|
headers: {
|
|
6734
6699
|
"Content-Type": "application/json",
|
|
6700
|
+
...IDENTITY_ENCODING_HEADER,
|
|
6735
6701
|
Authorization: `Bearer ${key}`,
|
|
6736
6702
|
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
6737
6703
|
"x-cli-environment": "production",
|
|
@@ -7313,8 +7279,8 @@ function transportBudgetMessage(failureMessage, maxRetries) {
|
|
|
7313
7279
|
* as an internal advisor instead: the command guard in `./command-guard.ts`
|
|
7314
7280
|
* asks it whether a shell command is safe to auto-approve.
|
|
7315
7281
|
*
|
|
7316
|
-
* The three question types are the whole vocabulary (command-code@1.
|
|
7317
|
-
* `systemOneRequestSchema
|
|
7282
|
+
* The three question types are the whole vocabulary (command-code@1.65.0
|
|
7283
|
+
* `systemOneRequestSchema`, unchanged since 1.64.0):
|
|
7318
7284
|
*
|
|
7319
7285
|
* - `noul` — "is this true?", optional `criteria: { true, false }` describing
|
|
7320
7286
|
* each verdict. Answers with `noul`, the probability of *yes*.
|
|
@@ -7572,6 +7538,7 @@ async function runSystemOne(deps, request, options = {}) {
|
|
|
7572
7538
|
method: "POST",
|
|
7573
7539
|
headers: {
|
|
7574
7540
|
"Content-Type": "application/json",
|
|
7541
|
+
...IDENTITY_ENCODING_HEADER,
|
|
7575
7542
|
Authorization: `Bearer ${key}`,
|
|
7576
7543
|
"x-command-code-version": COMMAND_CODE_CLI_VERSION,
|
|
7577
7544
|
...attributionHeaders()
|
|
@@ -7648,7 +7615,7 @@ const COMMAND_GUARD_DENY_LIST = [
|
|
|
7648
7615
|
},
|
|
7649
7616
|
{
|
|
7650
7617
|
id: "device-redirect",
|
|
7651
|
-
pattern:
|
|
7618
|
+
pattern: />{1,2}\s*\/dev\/(?:sd|disk|nvme|hd)/
|
|
7652
7619
|
},
|
|
7653
7620
|
{
|
|
7654
7621
|
id: "fork-bomb",
|
|
@@ -8033,25 +8000,58 @@ function applyCommandGuard(ctx, deps) {
|
|
|
8033
8000
|
//#endregion
|
|
8034
8001
|
//#region src/config-volatile.ts
|
|
8035
8002
|
/**
|
|
8036
|
-
*
|
|
8037
|
-
*
|
|
8038
|
-
*
|
|
8039
|
-
*
|
|
8003
|
+
* Volatile-config helpers for dsh 0.1.7's schema-derived profile Config.
|
|
8004
|
+
*
|
|
8005
|
+
* 0.1.7 rewrote settings around profile Config: `settings.describe()` projects
|
|
8006
|
+
* each active entry's schema through `volatileForm()`, so a form contains ONLY
|
|
8007
|
+
* fields whose schema nodes carry `meta.volatile`, and a form edit is refused
|
|
8008
|
+
* unless its path lies beneath a marked node. On that generation the loader
|
|
8009
|
+
* resolves each marked top-level field into a readonly `Volatile<T>` reference
|
|
8010
|
+
* (`{ get() }`, frozen, written in place by the owning runtime) instead of a
|
|
8011
|
+
* plain value, commits later changes WITHOUT remounting the fiber, and
|
|
8012
|
+
* dispatches `loader/volatile-update` to the owning fiber.
|
|
8013
|
+
*
|
|
8014
|
+
* Two helpers keep the rest of the plugin reading a PLAIN `Config`:
|
|
8015
|
+
*
|
|
8016
|
+
* - `markVolatile(schema)` applies `.volatile()`, so 0.1.7's forms see every
|
|
8017
|
+
* field we mark. The declared type is preserved through the mark on purpose:
|
|
8018
|
+
* it changes what the schema PARSES to (a live reference), not the
|
|
8019
|
+
* plugin-facing `Config` shape every consumer reads.
|
|
8020
|
+
* - `unwrapVolatileConfig(config)` reads every top-level field through its
|
|
8021
|
+
* reference when one is present — returning a FRESH object per call, since
|
|
8022
|
+
* a reference's identity is stable while its value changes — and returns the
|
|
8023
|
+
* config untouched when none is.
|
|
8024
|
+
*
|
|
8025
|
+
* Only TOP-LEVEL fields are marked (form writes name top-level paths such as
|
|
8026
|
+
* `accounts` or `modelVisibility.<id>`, whose first segment is the marked
|
|
8027
|
+
* node), so one unwrap level is complete. The literal `apiKey` secret stays
|
|
8028
|
+
* unmarked on purpose: no settings surface writes it — the settings page and
|
|
8029
|
+
* the dsh-TUI section both write keys through the credentials seam — so it
|
|
8030
|
+
* keeps composition-only semantics, and a config-file edit to it reloading the
|
|
8031
|
+
* fiber is the correct behavior for a secret literal.
|
|
8032
|
+
*
|
|
8033
|
+
* @module dsh-commandcode-provider/config-volatile
|
|
8034
|
+
*/
|
|
8035
|
+
/**
|
|
8036
|
+
* Mark one schema field volatile.
|
|
8037
|
+
*
|
|
8038
|
+
* `.volatile()` is unconditional: the mark is a schemastery 3.18.3 feature and
|
|
8039
|
+
* the only engine this bundle supports (`dsh 0.1.7-rc.1`) pins `~3.18.4`, so
|
|
8040
|
+
* the method is always present. It is reached through a structural member
|
|
8041
|
+
* rather than schemastery's own typing so the function can keep the caller's
|
|
8042
|
+
* declared field type, which is what lets `Config` stay the plain shape every
|
|
8043
|
+
* consumer reads while the schema parses to live references.
|
|
8044
|
+
*
|
|
8045
|
+
* @param schema - The field schema.
|
|
8046
|
+
* @returns The schema carrying `meta.volatile`.
|
|
8040
8047
|
*/
|
|
8041
8048
|
function markVolatile(schema) {
|
|
8042
|
-
|
|
8043
|
-
if (typeof candidate.volatile !== "function") return schema;
|
|
8044
|
-
return candidate.volatile();
|
|
8049
|
+
return schema.volatile();
|
|
8045
8050
|
}
|
|
8046
8051
|
/**
|
|
8047
8052
|
* Mark every field of a Config field dict volatile, except the named
|
|
8048
8053
|
* composition-only secrets (`apiKey` here, which no settings surface writes).
|
|
8049
8054
|
*
|
|
8050
|
-
* The result is a NEW dict of schema instances: callers that also need an
|
|
8051
|
-
* UNMARKED schema for the legacy settings generation must build the fields
|
|
8052
|
-
* twice (`configFields()`), never share one dict — `.volatile()` clones per
|
|
8053
|
-
* call, and a shared instance would carry the mark into the legacy schema.
|
|
8054
|
-
*
|
|
8055
8055
|
* @param fields - Freshly built field schemas.
|
|
8056
8056
|
* @param skip - Field names that must stay unmarked.
|
|
8057
8057
|
* @returns The field dict with every non-skipped field marked.
|
|
@@ -8072,24 +8072,21 @@ function isVolatileRef(value) {
|
|
|
8072
8072
|
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.isFrozen(value) && typeof value.get === "function";
|
|
8073
8073
|
}
|
|
8074
8074
|
/**
|
|
8075
|
-
* Read one config object as PLAIN values
|
|
8075
|
+
* Read one config object as PLAIN values.
|
|
8076
8076
|
*
|
|
8077
|
-
*
|
|
8077
|
+
* Every top-level field found to be a volatile reference is read through
|
|
8078
8078
|
* `.get()` — freshly on every call, so a live update lands on the very next
|
|
8079
|
-
* read.
|
|
8080
|
-
*
|
|
8081
|
-
*
|
|
8079
|
+
* read. The loader resolves EVERY marked node into a reference, unset fields
|
|
8080
|
+
* included, so a loader-parsed config always yields a fresh object here; the
|
|
8081
|
+
* pass-through for a field that is not a reference is what keeps the unmarked
|
|
8082
|
+
* `apiKey` literal readable.
|
|
8082
8083
|
*/
|
|
8083
8084
|
function unwrapVolatileConfig(config) {
|
|
8084
8085
|
const source = config;
|
|
8085
8086
|
if (typeof source !== "object" || source === null) return config;
|
|
8086
|
-
let found = false;
|
|
8087
8087
|
const plain = {};
|
|
8088
|
-
for (const [field, value] of Object.entries(source))
|
|
8089
|
-
|
|
8090
|
-
found = true;
|
|
8091
|
-
} else plain[field] = value;
|
|
8092
|
-
return found ? plain : config;
|
|
8088
|
+
for (const [field, value] of Object.entries(source)) plain[field] = isVolatileRef(value) ? value.get() : value;
|
|
8089
|
+
return plain;
|
|
8093
8090
|
}
|
|
8094
8091
|
//#endregion
|
|
8095
8092
|
//#region src/index.ts
|
|
@@ -8101,10 +8098,9 @@ function unwrapVolatileConfig(config) {
|
|
|
8101
8098
|
* the configurable-provider directory, so the web Models page shows a
|
|
8102
8099
|
* "Command Code" card with an API-key field and the model picker lists the
|
|
8103
8100
|
* live Command Code model catalog. Connection facts resolve per request over
|
|
8104
|
-
* the plugin's live
|
|
8105
|
-
*
|
|
8106
|
-
*
|
|
8107
|
-
* reaches the next request without a restart.
|
|
8101
|
+
* the plugin's live profile Config (volatile fields, unwrapped per read) and
|
|
8102
|
+
* the credential seam, so a changed key, endpoint, or cache path reaches the
|
|
8103
|
+
* next request without a restart.
|
|
8108
8104
|
*
|
|
8109
8105
|
* ```yaml
|
|
8110
8106
|
* - id: llm-commandcode
|
|
@@ -8130,87 +8126,55 @@ const PROVIDER = "commandcode";
|
|
|
8130
8126
|
/** Default models cache path (mirrors the pi plugin's on-disk cache). */
|
|
8131
8127
|
const DEFAULT_MODELS_CACHE_PATH = join(homedir(), ".commandcode", "models-cache.json");
|
|
8132
8128
|
/**
|
|
8133
|
-
* The Config schema
|
|
8134
|
-
*
|
|
8135
|
-
* A FACTORY, not a shared dict, and that is load-bearing: the exported
|
|
8136
|
-
* {@link Config} marks every field volatile while {@link LegacySettingsSchema}
|
|
8137
|
-
* must carry no mark at all, and schemastery's schema instances are
|
|
8138
|
-
* single-purpose objects — sharing one dict would leak the marks into the
|
|
8139
|
-
* legacy registration (see `config-volatile.ts` for why that is a boot-time
|
|
8140
|
-
* `ValidationError` on ≤0.1.6 engines whose schemastery resolved to ≥3.18.3).
|
|
8141
|
-
*
|
|
8142
|
-
* @returns One fresh schema instance per Config field.
|
|
8143
|
-
*/
|
|
8144
|
-
function configFields() {
|
|
8145
|
-
return {
|
|
8146
|
-
apiKeyEnv: z.string().role("credential-ref").default(DEFAULT_API_KEY_ENV),
|
|
8147
|
-
apiKey: z.string().role("secret"),
|
|
8148
|
-
apiBase: z.string(),
|
|
8149
|
-
workingDir: z.string(),
|
|
8150
|
-
modelsCachePath: z.string(),
|
|
8151
|
-
requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
8152
|
-
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
8153
|
-
transportMaxRetries: z.number().min(0).max(50),
|
|
8154
|
-
filterModelsByPlan: z.boolean(),
|
|
8155
|
-
visibleModels: z.array(z.string()),
|
|
8156
|
-
/**
|
|
8157
|
-
* Per-model visibility overrides for the terminal settings page's checkbox
|
|
8158
|
-
* list, keyed by catalog id. dsh-TUI addresses a staged edit by its field
|
|
8159
|
-
* PATH, so two checkboxes sharing one path would overwrite each other's
|
|
8160
|
-
* draft and the section's last model would decide every write; a map gives
|
|
8161
|
-
* each checkbox a path of its own. An id listed here wins over
|
|
8162
|
-
* {@link Config.visibleModels}; ids absent here keep following it.
|
|
8163
|
-
*/
|
|
8164
|
-
modelVisibility: z.dict(z.boolean()),
|
|
8165
|
-
webSearch: z.boolean().default(true),
|
|
8166
|
-
showSidebarQuota: z.boolean().default(false),
|
|
8167
|
-
accounts: z.array(z.object({
|
|
8168
|
-
label: z.string(),
|
|
8169
|
-
apiKeyEnv: z.string().role("credential-ref"),
|
|
8170
|
-
/** Literal key for one extra slot; see the top-level `apiKey` secret note. */
|
|
8171
|
-
apiKey: z.string().role("secret")
|
|
8172
|
-
})),
|
|
8173
|
-
activeAccount: z.string(),
|
|
8174
|
-
modelAccountRules: z.array(z.object({
|
|
8175
|
-
models: z.array(z.string()),
|
|
8176
|
-
account: z.string()
|
|
8177
|
-
})),
|
|
8178
|
-
commandGuard: z.boolean().default(false),
|
|
8179
|
-
commandGuardThreshold: z.number().min(COMMAND_GUARD_MIN_THRESHOLD).max(1),
|
|
8180
|
-
commandGuardTimeoutMs: z.number().min(200).max(COMMAND_GUARD_MAX_TIMEOUT_MS),
|
|
8181
|
-
zdr: z.boolean().default(false),
|
|
8182
|
-
lang: z.string().pattern(/^(zh|en)$/).default("zh")
|
|
8183
|
-
};
|
|
8184
|
-
}
|
|
8185
|
-
/**
|
|
8186
|
-
* The 0.1.7-generation schema: every field volatile except the
|
|
8187
|
-
* composition-only `apiKey` secret.
|
|
8129
|
+
* The Config schema: every field volatile except the composition-only `apiKey`
|
|
8130
|
+
* secret.
|
|
8188
8131
|
*
|
|
8189
8132
|
* dsh 0.1.7's settings forms are projected from the schema's `meta.volatile`
|
|
8190
8133
|
* nodes, so an unmarked field would be invisible to AND unwritable from the
|
|
8191
|
-
* settings page and refused by form-edit path validation — and
|
|
8192
|
-
*
|
|
8193
|
-
*
|
|
8194
|
-
*
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
*
|
|
8209
|
-
*
|
|
8210
|
-
*
|
|
8211
|
-
*
|
|
8212
|
-
|
|
8213
|
-
|
|
8134
|
+
* settings page and refused by form-edit path validation — and the loader
|
|
8135
|
+
* hands `apply()` a live reference per marked field, committing later writes
|
|
8136
|
+
* without remounting this fiber (the `loader/volatile-update` listener at the
|
|
8137
|
+
* bottom of `apply` covers the facts that are not re-derived per read).
|
|
8138
|
+
*/
|
|
8139
|
+
const Config = z.object(markVolatileFields({
|
|
8140
|
+
apiKeyEnv: z.string().role("credential-ref").default(DEFAULT_API_KEY_ENV),
|
|
8141
|
+
apiKey: z.string().role("secret"),
|
|
8142
|
+
apiBase: z.string(),
|
|
8143
|
+
workingDir: z.string(),
|
|
8144
|
+
modelsCachePath: z.string(),
|
|
8145
|
+
requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
8146
|
+
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
8147
|
+
transportMaxRetries: z.number().min(0).max(50),
|
|
8148
|
+
filterModelsByPlan: z.boolean(),
|
|
8149
|
+
visibleModels: z.array(z.string()),
|
|
8150
|
+
/**
|
|
8151
|
+
* Per-model visibility overrides for the terminal settings page's checkbox
|
|
8152
|
+
* list, keyed by catalog id. dsh-TUI addresses a staged edit by its field
|
|
8153
|
+
* PATH, so two checkboxes sharing one path would overwrite each other's
|
|
8154
|
+
* draft and the section's last model would decide every write; a map gives
|
|
8155
|
+
* each checkbox a path of its own. An id listed here wins over
|
|
8156
|
+
* {@link Config.visibleModels}; ids absent here keep following it.
|
|
8157
|
+
*/
|
|
8158
|
+
modelVisibility: z.dict(z.boolean()),
|
|
8159
|
+
webSearch: z.boolean().default(true),
|
|
8160
|
+
showSidebarQuota: z.boolean().default(false),
|
|
8161
|
+
accounts: z.array(z.object({
|
|
8162
|
+
label: z.string(),
|
|
8163
|
+
apiKeyEnv: z.string().role("credential-ref"),
|
|
8164
|
+
/** Literal key for one extra slot; see the top-level `apiKey` secret note. */
|
|
8165
|
+
apiKey: z.string().role("secret")
|
|
8166
|
+
})),
|
|
8167
|
+
activeAccount: z.string(),
|
|
8168
|
+
modelAccountRules: z.array(z.object({
|
|
8169
|
+
models: z.array(z.string()),
|
|
8170
|
+
account: z.string()
|
|
8171
|
+
})),
|
|
8172
|
+
commandGuard: z.boolean().default(false),
|
|
8173
|
+
commandGuardThreshold: z.number().min(COMMAND_GUARD_MIN_THRESHOLD).max(1),
|
|
8174
|
+
commandGuardTimeoutMs: z.number().min(200).max(COMMAND_GUARD_MAX_TIMEOUT_MS),
|
|
8175
|
+
zdr: z.boolean().default(false),
|
|
8176
|
+
lang: z.string().pattern(/^(zh|en)$/).default("zh")
|
|
8177
|
+
}, ["apiKey"]));
|
|
8214
8178
|
/**
|
|
8215
8179
|
* The one explicit resolve step from raw config to validated connection
|
|
8216
8180
|
* facts. Programmatic construction may bypass Schemastery normalization, so
|
|
@@ -8245,19 +8209,8 @@ function readModelVisibility(raw) {
|
|
|
8245
8209
|
}
|
|
8246
8210
|
function apply(ctx, config) {
|
|
8247
8211
|
installCostProjection(ctx);
|
|
8248
|
-
|
|
8249
|
-
const
|
|
8250
|
-
let lastRaw;
|
|
8251
|
-
let lastGood;
|
|
8252
|
-
const options = () => {
|
|
8253
|
-
const raw = current();
|
|
8254
|
-
if (raw === lastRaw && lastGood !== void 0) return lastGood;
|
|
8255
|
-
const next = resolveAdapterOptions(raw);
|
|
8256
|
-
lastRaw = raw;
|
|
8257
|
-
lastGood = next;
|
|
8258
|
-
return next;
|
|
8259
|
-
};
|
|
8260
|
-
options();
|
|
8212
|
+
const current = () => unwrapVolatileConfig(config);
|
|
8213
|
+
const options = () => resolveAdapterOptions(current());
|
|
8261
8214
|
const slots = () => {
|
|
8262
8215
|
const raw = current();
|
|
8263
8216
|
const list = [{
|
|
@@ -8398,8 +8351,11 @@ function apply(ctx, config) {
|
|
|
8398
8351
|
});
|
|
8399
8352
|
const loginFlow = new CommandCodeLoginFlow({
|
|
8400
8353
|
apiBase: () => options().apiBase,
|
|
8401
|
-
|
|
8402
|
-
|
|
8354
|
+
validateTargetRef: (targetRef) => {
|
|
8355
|
+
loginCredentialRef(targetRef, current().apiKeyEnv ?? DEFAULT_API_KEY_ENV, current().accounts ?? []);
|
|
8356
|
+
},
|
|
8357
|
+
storeKey: async ({ apiKey }, targetRef) => {
|
|
8358
|
+
const ref = credentialRef(loginCredentialRef(targetRef, current().apiKeyEnv ?? DEFAULT_API_KEY_ENV, current().accounts ?? []));
|
|
8403
8359
|
const credentials = ctx.get("credentials");
|
|
8404
8360
|
if (credentials === void 0) throw new Error("the credentials service is unavailable in this profile; paste the key manually");
|
|
8405
8361
|
await credentials.set(ref, apiKey);
|
|
@@ -8479,23 +8435,10 @@ function apply(ctx, config) {
|
|
|
8479
8435
|
}, "dsh-commandcode-provider: tui settings handle");
|
|
8480
8436
|
});
|
|
8481
8437
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
service.installSection(ctx, NS, LegacySettingsSchema, unwrapVolatileConfig(config), {
|
|
8485
|
-
setSource: (next) => {
|
|
8486
|
-
source = next;
|
|
8487
|
-
},
|
|
8488
|
-
onChange: () => {
|
|
8489
|
-
applySearchSelection(current().webSearch ?? true);
|
|
8490
|
-
refreshTuiSettings?.();
|
|
8491
|
-
}
|
|
8492
|
-
});
|
|
8493
|
-
return;
|
|
8494
|
-
}
|
|
8495
|
-
if (typeof service.configure === "function") settingsCtx.effect(() => {
|
|
8496
|
-
const dispose = service.configure?.({ auto: false }, ctx.fiber);
|
|
8438
|
+
settingsCtx.effect(() => {
|
|
8439
|
+
const dispose = settingsCtx.settings.configure({ auto: false }, ctx.fiber);
|
|
8497
8440
|
return () => {
|
|
8498
|
-
dispose
|
|
8441
|
+
dispose();
|
|
8499
8442
|
};
|
|
8500
8443
|
}, "dsh-commandcode-provider: settings auto-form policy");
|
|
8501
8444
|
});
|
|
@@ -8511,6 +8454,6 @@ function apply(ctx, config) {
|
|
|
8511
8454
|
}, "dsh-commandcode-provider: volatile config updates");
|
|
8512
8455
|
}
|
|
8513
8456
|
//#endregion
|
|
8514
|
-
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_NON_ZDR_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,
|
|
8457
|
+
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_NON_ZDR_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, studioBaseForApiBase, subscriptionPlanInfo, supportsZeroDataRetention, usageReportSchema, validateCommandApiKey };
|
|
8515
8458
|
|
|
8516
8459
|
//# sourceMappingURL=index.js.map
|