@mars-sea/dsh-commandcode-provider 0.13.4 → 0.14.0
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 +24 -0
- package/README.md +28 -13
- package/README.zh-CN.md +19 -6
- package/lib/client.js +273 -64
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +153 -62
- package/lib/index.js +603 -168
- package/lib/index.js.map +1 -1
- package/package.json +42 -43
package/lib/index.js
CHANGED
|
@@ -3676,6 +3676,140 @@ function effectiveContextWindow(catalog, model, options) {
|
|
|
3676
3676
|
if (isValidContextWindow(options.contextWindow)) return options.contextWindow;
|
|
3677
3677
|
return catalog;
|
|
3678
3678
|
}
|
|
3679
|
+
//#endregion
|
|
3680
|
+
//#region src/output-budget.ts
|
|
3681
|
+
/**
|
|
3682
|
+
* Output-budget bounds shared by the Host, the browser settings page and the
|
|
3683
|
+
* adapter (issue #74).
|
|
3684
|
+
*
|
|
3685
|
+
* The module stays pure (no node imports) so the client bundle can inline it,
|
|
3686
|
+
* exactly like `context-window.ts`. The report behind issue #74 asked for a
|
|
3687
|
+
* knob that stops the adapter from writing `max_tokens` at all; that is the
|
|
3688
|
+
* `0` sentinel here rather than a separate `null` spelling, because the value
|
|
3689
|
+
* travels through one numeric settings field on both surfaces.
|
|
3690
|
+
*/
|
|
3691
|
+
/**
|
|
3692
|
+
* The output budget this bundle asks for, and the ceiling it will never exceed.
|
|
3693
|
+
*
|
|
3694
|
+
* 131 072, chosen against a measured provider limit rather than a round guess.
|
|
3695
|
+
* Probing `/alpha/generate` directly on 2026-09-28: 64 000 → 200, 131 072 → 200,
|
|
3696
|
+
* 196 608 → 200, **200 000 → 200, 200 704 → 400**, 1 000 000 → 400. The server
|
|
3697
|
+
* therefore caps `max_tokens` at 200 000, and it does so identically for a
|
|
3698
|
+
* 262 144-window model (`stealth/pixel-canary`, retired by Command Code on
|
|
3699
|
+
* 2026-10-01 and named here only as the probe's subject) and a 1 000 000-window
|
|
3700
|
+
* one (`stealth/space-bunny-alpha`) — the cap is global, not per model. 131 072
|
|
3701
|
+
* keeps a 35 % margin under it while covering the real output ceiling of the
|
|
3702
|
+
* models that have one (Claude 64 K, Gemini 64 K, several at 128 K).
|
|
3703
|
+
*
|
|
3704
|
+
* The old 64 000 was never a provider requirement: it cost nothing in latency
|
|
3705
|
+
* (a 64 000 request answered headers in 1.28 s against 1.57 s for 131 072) but
|
|
3706
|
+
* it did cap what a model could ever say in one turn.
|
|
3707
|
+
*
|
|
3708
|
+
* No client-side clamp is applied on top of this: an earlier version pre-shrank
|
|
3709
|
+
* `max_tokens` from an estimate of the prompt already sent (`requestContextBudget()`,
|
|
3710
|
+
* issue #67), but that estimate over-corrected on ordinary long conversations —
|
|
3711
|
+
* exactly the "output gets cut short as the conversation grows" symptom issue #74
|
|
3712
|
+
* reported — while a live A/B probe (2026-09-30, `stealth/space-bunny-alpha`,
|
|
3713
|
+
* ~19.6% over its 1,000,000-token window with no client-side shrink) showed the
|
|
3714
|
+
* endpoint answers a genuine overflow with an unambiguous `400 … exceeds the
|
|
3715
|
+
* model's context window`, which `isContextOverflowDetail()` classifies as
|
|
3716
|
+
* `CONTEXT_WINDOW_EXCEEDED` independently of this constant.
|
|
3717
|
+
*
|
|
3718
|
+
* A user-set positive budget can only TIGHTEN this, never raise it — a figure
|
|
3719
|
+
* above the cap would be refused by the gateway anyway, so the settings field
|
|
3720
|
+
* refuses it at the input instead of spending a round trip to learn that.
|
|
3721
|
+
*
|
|
3722
|
+
* Lives here rather than in `adapter.ts` so the browser settings page can bound
|
|
3723
|
+
* its input by the same figure the Host enforces; `adapter.ts` re-exports it
|
|
3724
|
+
* under its historical name.
|
|
3725
|
+
*/
|
|
3726
|
+
const MAX_OUTPUT_TOKEN_LIMIT = 131072;
|
|
3727
|
+
/**
|
|
3728
|
+
* Whether a value can serve as a user output budget: the omit sentinel, or a
|
|
3729
|
+
* whole token count inside the gateway's own ceiling.
|
|
3730
|
+
*
|
|
3731
|
+
* Programmatically built connections can bypass schemastery normalization, so
|
|
3732
|
+
* the resolve step re-judges every value here and drops an out-of-range one
|
|
3733
|
+
* back to "unset" instead of letting the request carry a budget the gateway
|
|
3734
|
+
* refuses.
|
|
3735
|
+
*/
|
|
3736
|
+
function isValidMaxOutputTokens(value) {
|
|
3737
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= 131072;
|
|
3738
|
+
}
|
|
3739
|
+
/**
|
|
3740
|
+
* Apply a user output budget to a derived one.
|
|
3741
|
+
*
|
|
3742
|
+
* `undefined` means the user set nothing, so the derived ceiling stands
|
|
3743
|
+
* unchanged — the default path, and the one every existing profile takes.
|
|
3744
|
+
* {@link OMIT_OUTPUT_TOKENS} is a wire instruction rather than a number, so it
|
|
3745
|
+
* also leaves the derived budget alone; {@link omitOutputTokens} is what turns
|
|
3746
|
+
* it into an absent field.
|
|
3747
|
+
*/
|
|
3748
|
+
function applyMaxOutputTokens(derived, configured) {
|
|
3749
|
+
if (configured === void 0 || configured === 0) return derived;
|
|
3750
|
+
return Math.min(derived, configured);
|
|
3751
|
+
}
|
|
3752
|
+
/**
|
|
3753
|
+
* Whether this attempt must send no `max_tokens` field at all.
|
|
3754
|
+
*
|
|
3755
|
+
* Two guards narrow this beyond the user's own setting, and both are
|
|
3756
|
+
* correctness requirements rather than conveniences:
|
|
3757
|
+
*
|
|
3758
|
+
* - The Messages route is excluded deliberately: the endpoint rejects a body
|
|
3759
|
+
* without the field (`Invalid input: expected number, received undefined at
|
|
3760
|
+
* max_tokens`), so omitting it there would not lengthen an answer, it would
|
|
3761
|
+
* make the whole Claude family unusable. The CLI and OpenAI transports accept
|
|
3762
|
+
* an absent field, which is where the setting does what it promises.
|
|
3763
|
+
* - An explicit per-call budget from the host is an instruction, not a default,
|
|
3764
|
+
* so it wins over the plugin-wide setting. `session-title` is the concrete
|
|
3765
|
+
* case: it asks for 64 tokens precisely because its output must stay bounded,
|
|
3766
|
+
* and dropping the field there would let a title generation run long instead
|
|
3767
|
+
* of making a long answer fit (issue #88's fix depends on that bound).
|
|
3768
|
+
*/
|
|
3769
|
+
function omitOutputTokens(configured, protocol, hostRequestedTokens) {
|
|
3770
|
+
if (configured !== 0) return false;
|
|
3771
|
+
if (protocol === "messages") return false;
|
|
3772
|
+
return hostRequestedTokens === void 0;
|
|
3773
|
+
}
|
|
3774
|
+
//#endregion
|
|
3775
|
+
//#region src/readable-error.ts
|
|
3776
|
+
/**
|
|
3777
|
+
* Readable error text for the settings page AND the Host-side failure paths.
|
|
3778
|
+
*
|
|
3779
|
+
* @module dsh-commandcode-provider/readable-error
|
|
3780
|
+
*/
|
|
3781
|
+
/**
|
|
3782
|
+
* Turn a caught value into readable text.
|
|
3783
|
+
*
|
|
3784
|
+
* `error instanceof Error ? error.message : String(error)` degrades to the
|
|
3785
|
+
* literal string `[object Object]` for a plain object with no `Error`
|
|
3786
|
+
* prototype — which is exactly what a Remote call across the webview/host
|
|
3787
|
+
* boundary can hand back: `dsh-typert-protocol` serializes a rejection for
|
|
3788
|
+
* the wire, and a structurally-cloned value loses its prototype chain even
|
|
3789
|
+
* when the host threw a real `Error` (issue #74). Reading a `message` string
|
|
3790
|
+
* off the plain object first recovers the original text in that case; only a
|
|
3791
|
+
* value with neither an `Error` prototype nor a usable `message` field falls
|
|
3792
|
+
* through to a JSON dump.
|
|
3793
|
+
*
|
|
3794
|
+
* Shared by both halves on purpose. The browser client needs it for rejections
|
|
3795
|
+
* that crossed the RPC boundary, and the Host needs it for values that reached
|
|
3796
|
+
* it from a transport, a JSON body, or a user-thrown non-Error — the same
|
|
3797
|
+
* degradation, one owner.
|
|
3798
|
+
*/
|
|
3799
|
+
function readableErrorText(error) {
|
|
3800
|
+
if (error instanceof Error) return error.message;
|
|
3801
|
+
if (typeof error === "string") return error;
|
|
3802
|
+
if (error !== null && typeof error === "object") {
|
|
3803
|
+
const message = error.message;
|
|
3804
|
+
if (typeof message === "string" && message !== "") return message;
|
|
3805
|
+
try {
|
|
3806
|
+
return JSON.stringify(error);
|
|
3807
|
+
} catch {
|
|
3808
|
+
return Object.prototype.toString.call(error);
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
return String(error);
|
|
3812
|
+
}
|
|
3679
3813
|
const THROTTLED_CODE = "THROTTLED";
|
|
3680
3814
|
const boundedCodes = /* @__PURE__ */ new Set([
|
|
3681
3815
|
"EMPTY_RESPONSE",
|
|
@@ -6148,33 +6282,11 @@ const COMMAND_CODE_CLI_VERSION = "1.82.5";
|
|
|
6148
6282
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
6149
6283
|
/**
|
|
6150
6284
|
* The output budget this bundle asks for, and the ceiling it will never exceed.
|
|
6151
|
-
*
|
|
6152
|
-
*
|
|
6153
|
-
*
|
|
6154
|
-
* 196 608 → 200, **200 000 → 200, 200 704 → 400**, 1 000 000 → 400. The server
|
|
6155
|
-
* therefore caps `max_tokens` at 200 000, and it does so identically for a
|
|
6156
|
-
* 262 144-window model (`stealth/pixel-canary`, retired by Command Code on
|
|
6157
|
-
* 2026-10-01 and named here only as the probe's subject) and a 1 000 000-window
|
|
6158
|
-
* one (`stealth/space-bunny-alpha`) — the cap is global, not per model. 131 072
|
|
6159
|
-
* keeps a 35 % margin under it while covering the real output ceiling of the
|
|
6160
|
-
* models that have one (Claude 64 K, Gemini 64 K, several at 128 K).
|
|
6161
|
-
*
|
|
6162
|
-
* The old 64 000 was never a provider requirement: it cost nothing in latency
|
|
6163
|
-
* (a 64 000 request answered headers in 1.28 s against 1.57 s for 131 072) but
|
|
6164
|
-
* it did cap what a model could ever say in one turn.
|
|
6165
|
-
*
|
|
6166
|
-
* No client-side clamp is applied on top of this: an earlier version pre-shrank
|
|
6167
|
-
* `max_tokens` from an estimate of the prompt already sent (`requestContextBudget()`,
|
|
6168
|
-
* issue #67), but that estimate over-corrected on ordinary long conversations —
|
|
6169
|
-
* exactly the "output gets cut short as the conversation grows" symptom issue #74
|
|
6170
|
-
* reported — while a live A/B probe (2026-09-30, `stealth/space-bunny-alpha`,
|
|
6171
|
-
* ~19.6% over its 1,000,000-token window with no client-side shrink) showed the
|
|
6172
|
-
* endpoint answers a genuine overflow with an unambiguous `400 … exceeds the
|
|
6173
|
-
* model's context window`, which `isContextOverflowDetail()` below already
|
|
6174
|
-
* classifies as `CONTEXT_WINDOW_EXCEEDED` independently of this constant. See
|
|
6175
|
-
* [决策记录](../docs/决策记录.md).
|
|
6285
|
+
* The figure and its probe evidence live in `./output-budget.ts` — the module
|
|
6286
|
+
* the browser settings page also bounds its input by — and are re-exported here
|
|
6287
|
+
* under the historical name so existing importers are unaffected.
|
|
6176
6288
|
*/
|
|
6177
|
-
const DEFAULT_GENERATE_MAX_TOKENS =
|
|
6289
|
+
const DEFAULT_GENERATE_MAX_TOKENS = MAX_OUTPUT_TOKEN_LIMIT;
|
|
6178
6290
|
const MODELS_TIMEOUT_MS = 1e4;
|
|
6179
6291
|
/** How long the picker's plan-filter billing facts stay cached before refetching. */
|
|
6180
6292
|
const BILLING_ACCESS_TTL_MS = 3e5;
|
|
@@ -7405,7 +7517,7 @@ async function buildCliBody(options, connection, facts) {
|
|
|
7405
7517
|
input_schema: toolParametersSchema(tool.parameters)
|
|
7406
7518
|
})),
|
|
7407
7519
|
system: cliSystem(facts.systemText),
|
|
7408
|
-
max_tokens: facts.maxTokens,
|
|
7520
|
+
...facts.omitMaxTokens ? {} : { max_tokens: facts.maxTokens },
|
|
7409
7521
|
temperature: options.temperature ?? .3,
|
|
7410
7522
|
stream: true,
|
|
7411
7523
|
...facts.reasoningEffort ? { reasoning_effort: facts.reasoningEffort } : {}
|
|
@@ -7431,7 +7543,7 @@ async function buildOpenAIBody(options, facts) {
|
|
|
7431
7543
|
model: options.model,
|
|
7432
7544
|
messages: openAiMessages,
|
|
7433
7545
|
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
7434
|
-
max_tokens: facts.maxTokens,
|
|
7546
|
+
...facts.omitMaxTokens ? {} : { max_tokens: facts.maxTokens },
|
|
7435
7547
|
temperature: options.temperature ?? .3,
|
|
7436
7548
|
stream: true,
|
|
7437
7549
|
...facts.reasoningEffort ? { reasoning_effort: facts.reasoningEffort } : {}
|
|
@@ -7494,6 +7606,16 @@ async function buildMessagesBody(options, facts) {
|
|
|
7494
7606
|
return body;
|
|
7495
7607
|
}
|
|
7496
7608
|
/**
|
|
7609
|
+
* Write the output budget into an already-built body, each transport in its own
|
|
7610
|
+
* shape (the CLI route nests it under `params`). Used to keep a REBUILT body's
|
|
7611
|
+
* figure in step with the budget without duplicating the nesting knowledge, and
|
|
7612
|
+
* only called when the field is not being omitted (issue #74).
|
|
7613
|
+
*/
|
|
7614
|
+
function assignMaxTokens(body, target, maxTokens) {
|
|
7615
|
+
if (target === "cli") recordOrEmpty(body.params).max_tokens = maxTokens;
|
|
7616
|
+
else body.max_tokens = maxTokens;
|
|
7617
|
+
}
|
|
7618
|
+
/**
|
|
7497
7619
|
* One connect attempt's inputs: everything `connectGenerate` needs beyond
|
|
7498
7620
|
* the per-attempt key and protocol. Passed explicitly (not closed over) so
|
|
7499
7621
|
* the rotation loop's mutation of `protocol`/`body` stays visible at the
|
|
@@ -7857,7 +7979,7 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
7857
7979
|
toolUpdate: "addition-only",
|
|
7858
7980
|
...entry !== void 0 && contextWindow !== void 0 ? {
|
|
7859
7981
|
context: { contextWindow },
|
|
7860
|
-
defaultMaxTokens: Math.min(catalogModelMaxTokens(connection.apiBase, entry, contextWindow), DEFAULT_GENERATE_MAX_TOKENS)
|
|
7982
|
+
defaultMaxTokens: applyMaxOutputTokens(Math.min(catalogModelMaxTokens(connection.apiBase, entry, contextWindow), DEFAULT_GENERATE_MAX_TOKENS), connection.maxOutputTokens)
|
|
7861
7983
|
} : {},
|
|
7862
7984
|
...efforts ? { reasoning: { efforts: efforts.map((effort) => ({
|
|
7863
7985
|
id: ReasoningEffortId(effort),
|
|
@@ -8137,7 +8259,7 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8137
8259
|
}
|
|
8138
8260
|
return record;
|
|
8139
8261
|
} catch (error) {
|
|
8140
|
-
failures.push(`${path}: ${
|
|
8262
|
+
failures.push(`${path}: ${readableErrorText(error)}`);
|
|
8141
8263
|
failed.push({ kind: "transport" });
|
|
8142
8264
|
return;
|
|
8143
8265
|
}
|
|
@@ -8281,7 +8403,7 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8281
8403
|
options.signal?.throwIfAborted();
|
|
8282
8404
|
const modelEntry = catalog.find((row) => row.id === options.model);
|
|
8283
8405
|
const modelMax = modelEntry === void 0 ? facts.fallbackLimit : catalogModelMaxTokens(connection.apiBase, modelEntry, effectiveContextWindow(modelEntry.contextWindow, options.model, connection));
|
|
8284
|
-
const initialMax = Math.min(options.maxTokens ?? Infinity, modelMax, DEFAULT_GENERATE_MAX_TOKENS);
|
|
8406
|
+
const initialMax = applyMaxOutputTokens(Math.min(options.maxTokens ?? Infinity, modelMax, DEFAULT_GENERATE_MAX_TOKENS), connection.maxOutputTokens);
|
|
8285
8407
|
const budget = {
|
|
8286
8408
|
max: initialMax,
|
|
8287
8409
|
sent: initialMax
|
|
@@ -8332,17 +8454,16 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8332
8454
|
const buildBody = async (target) => {
|
|
8333
8455
|
const finishBody = timing.phase("body");
|
|
8334
8456
|
try {
|
|
8457
|
+
const omitMaxTokens = omitOutputTokens(connection.maxOutputTokens, target, options.maxTokens);
|
|
8335
8458
|
const facts = {
|
|
8336
8459
|
maxTokens,
|
|
8460
|
+
omitMaxTokens,
|
|
8337
8461
|
reasoningEffort,
|
|
8338
8462
|
selectableEffort: supported !== void 0 && supported.length > 0,
|
|
8339
8463
|
systemText,
|
|
8340
8464
|
readImage
|
|
8341
8465
|
};
|
|
8342
|
-
|
|
8343
|
-
if (target === "cli") recordOrEmpty(built.params).max_tokens = maxTokens;
|
|
8344
|
-
else built.max_tokens = maxTokens;
|
|
8345
|
-
return built;
|
|
8466
|
+
return target === "cli" ? await buildCliBody(requestOptions, connection, facts) : target === "messages" ? await buildMessagesBody(requestOptions, facts) : await buildOpenAIBody(requestOptions, facts);
|
|
8346
8467
|
} finally {
|
|
8347
8468
|
finishBody();
|
|
8348
8469
|
}
|
|
@@ -8392,8 +8513,7 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
|
|
|
8392
8513
|
if (lower < maxTokens) {
|
|
8393
8514
|
maxTokens = lower;
|
|
8394
8515
|
budget.max = Math.min(budget.max, lower);
|
|
8395
|
-
if (protocol
|
|
8396
|
-
else body.max_tokens = lower;
|
|
8516
|
+
if (!omitOutputTokens(connection.maxOutputTokens, protocol, options.maxTokens)) assignMaxTokens(body, protocol, lower);
|
|
8397
8517
|
serializedBodies.delete(body);
|
|
8398
8518
|
}
|
|
8399
8519
|
budget.sent = maxTokens;
|
|
@@ -8707,7 +8827,7 @@ function commandDefinition(deps) {
|
|
|
8707
8827
|
text: renderReport(await adapter.getUsage(), locale)
|
|
8708
8828
|
};
|
|
8709
8829
|
} catch (error) {
|
|
8710
|
-
const message =
|
|
8830
|
+
const message = readableErrorText(error);
|
|
8711
8831
|
return {
|
|
8712
8832
|
kind: "error",
|
|
8713
8833
|
text: commandCopy(locale, "errorText").replace("{message}", message)
|
|
@@ -9025,6 +9145,30 @@ const LOGIN_HOST_CONTRIBUTION = {
|
|
|
9025
9145
|
};
|
|
9026
9146
|
//#endregion
|
|
9027
9147
|
//#region src/enrollment-wire.ts
|
|
9148
|
+
const ENROLLMENT_MESSAGE_KEYS = [
|
|
9149
|
+
"enrollmentCreating",
|
|
9150
|
+
"enrollmentWaiting",
|
|
9151
|
+
"enrollmentWriting",
|
|
9152
|
+
"enrollmentNaming",
|
|
9153
|
+
"enrollmentNameFailed",
|
|
9154
|
+
"enrollmentFinished",
|
|
9155
|
+
"enrollmentCancelling",
|
|
9156
|
+
"enrollmentCancelled",
|
|
9157
|
+
"enrollmentCancelledCleaned",
|
|
9158
|
+
"enrollmentFailed",
|
|
9159
|
+
"enrollmentFailedCleaned",
|
|
9160
|
+
"enrollmentCleanupPending",
|
|
9161
|
+
"enrollmentCleanupFailed",
|
|
9162
|
+
"enrollmentRecordNaming",
|
|
9163
|
+
"enrollmentRecordCleanup",
|
|
9164
|
+
"enrollmentBeginFailed",
|
|
9165
|
+
"enrollmentCancelUnconfirmed",
|
|
9166
|
+
"enrollmentNameRequestFailed",
|
|
9167
|
+
"enrollmentCleanupRequestFailed",
|
|
9168
|
+
"enrollmentDisconnected",
|
|
9169
|
+
"enrollmentHostUnavailable",
|
|
9170
|
+
"enrollmentStatusUnavailable"
|
|
9171
|
+
];
|
|
9028
9172
|
const guard = makeBoundaryValidator("commandcode/enrollment");
|
|
9029
9173
|
const phases = [
|
|
9030
9174
|
"creating",
|
|
@@ -9041,11 +9185,13 @@ function parseEnrollmentState(value) {
|
|
|
9041
9185
|
const source = guard.record(value, "state");
|
|
9042
9186
|
const phase = source.phase;
|
|
9043
9187
|
if (!phases.includes(phase)) return guard.reject("phase");
|
|
9188
|
+
const messageKey = source.messageKey;
|
|
9189
|
+
if (!ENROLLMENT_MESSAGE_KEYS.includes(messageKey)) return guard.reject("messageKey");
|
|
9044
9190
|
const state = {
|
|
9045
9191
|
id: guard.stringField(source, "id", "id"),
|
|
9046
9192
|
ref: guard.stringField(source, "ref", "ref"),
|
|
9047
9193
|
phase,
|
|
9048
|
-
|
|
9194
|
+
messageKey
|
|
9049
9195
|
};
|
|
9050
9196
|
for (const field of ["authUrl", "suggestedName"]) if (source[field] !== void 0) state[field] = guard.stringField(source, field, field);
|
|
9051
9197
|
return state;
|
|
@@ -9737,7 +9883,7 @@ var CommandCodeLoginFlow = class {
|
|
|
9737
9883
|
this.setStatus({
|
|
9738
9884
|
state: "failed",
|
|
9739
9885
|
reason: "unavailable",
|
|
9740
|
-
message:
|
|
9886
|
+
message: readableErrorText(error)
|
|
9741
9887
|
});
|
|
9742
9888
|
return;
|
|
9743
9889
|
}
|
|
@@ -9846,7 +9992,7 @@ var AccountEnrollmentManager = class {
|
|
|
9846
9992
|
id: input.id,
|
|
9847
9993
|
ref: "",
|
|
9848
9994
|
phase: "creating",
|
|
9849
|
-
|
|
9995
|
+
messageKey: "enrollmentCreating"
|
|
9850
9996
|
}
|
|
9851
9997
|
};
|
|
9852
9998
|
this.tasks.set(input.id, task);
|
|
@@ -9854,7 +10000,7 @@ var AccountEnrollmentManager = class {
|
|
|
9854
10000
|
task.state = {
|
|
9855
10001
|
...task.state,
|
|
9856
10002
|
phase: "cleanup-needed",
|
|
9857
|
-
|
|
10003
|
+
messageKey: "enrollmentCleanupPending"
|
|
9858
10004
|
};
|
|
9859
10005
|
});
|
|
9860
10006
|
return task.state;
|
|
@@ -9884,7 +10030,7 @@ var AccountEnrollmentManager = class {
|
|
|
9884
10030
|
id,
|
|
9885
10031
|
ref: "",
|
|
9886
10032
|
phase: "cancelled",
|
|
9887
|
-
|
|
10033
|
+
messageKey: "enrollmentCancelled"
|
|
9888
10034
|
}
|
|
9889
10035
|
};
|
|
9890
10036
|
this.tasks.set(id, task);
|
|
@@ -9897,7 +10043,7 @@ var AccountEnrollmentManager = class {
|
|
|
9897
10043
|
task.state = {
|
|
9898
10044
|
...task.state,
|
|
9899
10045
|
phase: "cancelling",
|
|
9900
|
-
|
|
10046
|
+
messageKey: "enrollmentCancelling"
|
|
9901
10047
|
};
|
|
9902
10048
|
await task.flow?.cancelAndDrain();
|
|
9903
10049
|
await task.work;
|
|
@@ -9934,14 +10080,14 @@ var AccountEnrollmentManager = class {
|
|
|
9934
10080
|
id,
|
|
9935
10081
|
ref: record.ref,
|
|
9936
10082
|
phase: "finished",
|
|
9937
|
-
|
|
10083
|
+
messageKey: "enrollmentFinished"
|
|
9938
10084
|
});
|
|
9939
10085
|
} catch {
|
|
9940
10086
|
return this.publish(task, {
|
|
9941
10087
|
id,
|
|
9942
10088
|
ref: record.ref,
|
|
9943
10089
|
phase: "naming",
|
|
9944
|
-
|
|
10090
|
+
messageKey: "enrollmentNameFailed",
|
|
9945
10091
|
...name ? { suggestedName: name } : {}
|
|
9946
10092
|
});
|
|
9947
10093
|
}
|
|
@@ -9967,7 +10113,7 @@ var AccountEnrollmentManager = class {
|
|
|
9967
10113
|
task.state = {
|
|
9968
10114
|
...task.state,
|
|
9969
10115
|
phase: "cleanup-needed",
|
|
9970
|
-
|
|
10116
|
+
messageKey: "enrollmentCleanupFailed"
|
|
9971
10117
|
};
|
|
9972
10118
|
});
|
|
9973
10119
|
await task.work;
|
|
@@ -10035,7 +10181,7 @@ var AccountEnrollmentManager = class {
|
|
|
10035
10181
|
if (!task.cancelled && status.state === "waiting") task.state = {
|
|
10036
10182
|
...task.state,
|
|
10037
10183
|
phase: "waiting",
|
|
10038
|
-
|
|
10184
|
+
messageKey: "enrollmentWaiting",
|
|
10039
10185
|
...status.authUrl ? { authUrl: status.authUrl } : {}
|
|
10040
10186
|
};
|
|
10041
10187
|
if (task.cancelled) flow.cancelAndDrain();
|
|
@@ -10051,7 +10197,7 @@ var AccountEnrollmentManager = class {
|
|
|
10051
10197
|
task.state = {
|
|
10052
10198
|
...task.state,
|
|
10053
10199
|
phase: "naming",
|
|
10054
|
-
|
|
10200
|
+
messageKey: "enrollmentNaming",
|
|
10055
10201
|
...suggested ? { suggestedName: suggested } : {}
|
|
10056
10202
|
};
|
|
10057
10203
|
try {
|
|
@@ -10064,7 +10210,7 @@ var AccountEnrollmentManager = class {
|
|
|
10064
10210
|
if (task.state.phase === "finished") return;
|
|
10065
10211
|
task.state = {
|
|
10066
10212
|
...task.state,
|
|
10067
|
-
|
|
10213
|
+
messageKey: "enrollmentNameFailed"
|
|
10068
10214
|
};
|
|
10069
10215
|
return;
|
|
10070
10216
|
}
|
|
@@ -10076,7 +10222,7 @@ var AccountEnrollmentManager = class {
|
|
|
10076
10222
|
else task.state = {
|
|
10077
10223
|
...task.state,
|
|
10078
10224
|
phase: task.cancelled ? "cancelled" : "failed",
|
|
10079
|
-
|
|
10225
|
+
messageKey: task.cancelled ? "enrollmentCancelled" : "enrollmentFailed"
|
|
10080
10226
|
};
|
|
10081
10227
|
} finally {
|
|
10082
10228
|
task.flow?.dispose();
|
|
@@ -10087,7 +10233,7 @@ var AccountEnrollmentManager = class {
|
|
|
10087
10233
|
task.state = {
|
|
10088
10234
|
...task.state,
|
|
10089
10235
|
phase: "writing",
|
|
10090
|
-
|
|
10236
|
+
messageKey: "enrollmentWriting"
|
|
10091
10237
|
};
|
|
10092
10238
|
task.writeStarted = true;
|
|
10093
10239
|
await this.deps.set(task.state.ref, key);
|
|
@@ -10128,7 +10274,7 @@ var AccountEnrollmentManager = class {
|
|
|
10128
10274
|
id,
|
|
10129
10275
|
ref,
|
|
10130
10276
|
phase: task.cancelled ? "cancelled" : "failed",
|
|
10131
|
-
|
|
10277
|
+
messageKey: task.cancelled ? "enrollmentCancelledCleaned" : "enrollmentFailedCleaned"
|
|
10132
10278
|
};
|
|
10133
10279
|
}
|
|
10134
10280
|
settings() {
|
|
@@ -10177,12 +10323,134 @@ var AccountEnrollmentManager = class {
|
|
|
10177
10323
|
id: record.id,
|
|
10178
10324
|
ref: record.ref,
|
|
10179
10325
|
phase: record.phase === "naming" ? "naming" : "cleanup-needed",
|
|
10180
|
-
|
|
10326
|
+
messageKey: record.phase === "naming" ? "enrollmentRecordNaming" : "enrollmentRecordCleanup",
|
|
10181
10327
|
...record.phase === "naming" ? { suggestedName: record.label } : {}
|
|
10182
10328
|
};
|
|
10183
10329
|
}
|
|
10184
10330
|
};
|
|
10185
10331
|
//#endregion
|
|
10332
|
+
//#region src/config-volatile.ts
|
|
10333
|
+
/**
|
|
10334
|
+
* Volatile-config helpers for dsh 0.1.7's schema-derived profile Config.
|
|
10335
|
+
*
|
|
10336
|
+
* 0.1.7's `settings.describe()` projects each entry's schema through
|
|
10337
|
+
* `volatileForm()`, so a form contains ONLY nodes carrying `meta.volatile`
|
|
10338
|
+
* and a form edit is refused unless its path lies beneath a marked node. The
|
|
10339
|
+
* loader then resolves each marked field into a frozen `{ get() }` reference
|
|
10340
|
+
* that is written IN PLACE (no remount) and announced with
|
|
10341
|
+
* `loader/volatile-update`. Two helpers keep the rest of the plugin reading a
|
|
10342
|
+
* PLAIN `Config`: `markVolatile()` applies the mark, and
|
|
10343
|
+
* `unwrapVolatileConfig()` reads every top-level field through its reference
|
|
10344
|
+
* when one is present — FRESH per call, because a reference's identity is
|
|
10345
|
+
* stable while its value changes.
|
|
10346
|
+
*
|
|
10347
|
+
* Only TOP-LEVEL fields are marked (a form write names a top-level path such
|
|
10348
|
+
* as `accounts` or `modelVisibility.<id>`), so one unwrap level is complete.
|
|
10349
|
+
* The literal `apiKey` secret stays unmarked on purpose: no settings surface
|
|
10350
|
+
* writes it — page and TUI both write keys through the credentials seam — so a
|
|
10351
|
+
* config-file edit to it reloading the fiber is correct for a secret literal.
|
|
10352
|
+
*
|
|
10353
|
+
* `volatileWrite()` is the third helper and the only WRITE in this module: it
|
|
10354
|
+
* commits into a reference that belongs to ANOTHER plugin entry (`ctx.web`'s
|
|
10355
|
+
* `searchProvider`), which no host API addresses directly. It is the degraded
|
|
10356
|
+
* path behind `ctx.settings.update()`; see its own note for why the disk-backed
|
|
10357
|
+
* path is preferred.
|
|
10358
|
+
*
|
|
10359
|
+
* @module dsh-commandcode-provider/config-volatile
|
|
10360
|
+
*/
|
|
10361
|
+
/**
|
|
10362
|
+
* Mark one schema field volatile.
|
|
10363
|
+
*
|
|
10364
|
+
* `.volatile()` is unconditional: it is a schemastery 3.18.3 feature and the
|
|
10365
|
+
* supported engine ships `~3.18.5`, so the
|
|
10366
|
+
* method is always present. It is reached through a structural member so the caller's
|
|
10367
|
+
* declared field type survives — the mark changes what the schema PARSES to (a
|
|
10368
|
+
* live reference), not the plugin-facing `Config` shape.
|
|
10369
|
+
*/
|
|
10370
|
+
function markVolatile(schema) {
|
|
10371
|
+
return schema.volatile();
|
|
10372
|
+
}
|
|
10373
|
+
/**
|
|
10374
|
+
* Mark every field of a Config field dict volatile, except the named
|
|
10375
|
+
* composition-only secrets (`apiKey` here, which no settings surface writes).
|
|
10376
|
+
*/
|
|
10377
|
+
function markVolatileFields(fields, skip = []) {
|
|
10378
|
+
return Object.fromEntries(Object.entries(fields).map(([name, schema]) => [name, skip.includes(name) ? schema : markVolatile(schema)]));
|
|
10379
|
+
}
|
|
10380
|
+
/**
|
|
10381
|
+
* Whether a resolved config value is a volatile reference.
|
|
10382
|
+
*
|
|
10383
|
+
* Duck-typed on the two properties `createVolatile` produces (frozen object
|
|
10384
|
+
* with a `get` method) rather than cosmokit's private `write` symbol, because
|
|
10385
|
+
* cosmokit is not a peer of this bundle. Config values are plain parsed JSON
|
|
10386
|
+
* (or `!!jsExpr` wrappers), which are never frozen, so the test cannot
|
|
10387
|
+
* misfire on an ordinary field.
|
|
10388
|
+
*/
|
|
10389
|
+
function isVolatileRef(value) {
|
|
10390
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.isFrozen(value) && typeof value.get === "function";
|
|
10391
|
+
}
|
|
10392
|
+
/**
|
|
10393
|
+
* The commit protocol cosmokit's `createVolatile` installs on every reference.
|
|
10394
|
+
*
|
|
10395
|
+
* `Symbol.for` — not a local `Symbol()` — because cosmokit registers it
|
|
10396
|
+
* globally precisely so copies of the shared library loaded twice (ESM/CJS, two
|
|
10397
|
+
* node_modules trees) still recognize each other's references. Reaching it
|
|
10398
|
+
* through the global registry is what lets this bundle commit a value without
|
|
10399
|
+
* taking `@deepseek-ai/cosmokit` as a dependency.
|
|
10400
|
+
*/
|
|
10401
|
+
const VOLATILE_WRITE = Symbol.for("cosmokit.volatile.write");
|
|
10402
|
+
/**
|
|
10403
|
+
* Commit a value into a RUNNING volatile reference and verify it landed.
|
|
10404
|
+
*
|
|
10405
|
+
* This is the DEGRADED write path, and it is deliberately second choice: the
|
|
10406
|
+
* reference is the live config the owner reads per call, but the value never
|
|
10407
|
+
* reaches the profile document, so the next loader commit for that entry (any
|
|
10408
|
+
* `loader/volatile-update`, including one caused by an unrelated field of the
|
|
10409
|
+
* same entry) re-resolves the raw config and overwrites it. The official
|
|
10410
|
+
* `ctx.settings.update()` path exists to avoid exactly that; this one keeps the
|
|
10411
|
+
* CURRENT process coherent when settings is unavailable or refuses the write.
|
|
10412
|
+
*
|
|
10413
|
+
* Reading back is not optional: the write method is an internal protocol, so a
|
|
10414
|
+
* future cosmokit that renames the symbol, hides it, or changes the value shape
|
|
10415
|
+
* must degrade to "not applied" instead of silently pretending success.
|
|
10416
|
+
*
|
|
10417
|
+
* @param ref - Candidate volatile reference; anything else returns `false`.
|
|
10418
|
+
* @param value - Value to commit.
|
|
10419
|
+
* @returns Whether the reference now reads back as `value`.
|
|
10420
|
+
*/
|
|
10421
|
+
function volatileWrite(ref, value) {
|
|
10422
|
+
if (!isVolatileRef(ref)) return false;
|
|
10423
|
+
const write = ref[VOLATILE_WRITE];
|
|
10424
|
+
if (typeof write !== "function") return false;
|
|
10425
|
+
try {
|
|
10426
|
+
write.call(ref, value);
|
|
10427
|
+
} catch {
|
|
10428
|
+
return false;
|
|
10429
|
+
}
|
|
10430
|
+
try {
|
|
10431
|
+
return Object.is(ref.get(), value);
|
|
10432
|
+
} catch {
|
|
10433
|
+
return false;
|
|
10434
|
+
}
|
|
10435
|
+
}
|
|
10436
|
+
/**
|
|
10437
|
+
* Read one config object as PLAIN values.
|
|
10438
|
+
*
|
|
10439
|
+
* Every top-level field found to be a volatile reference is read through
|
|
10440
|
+
* `.get()` — freshly on every call, so a live update lands on the very next
|
|
10441
|
+
* read (the loader resolves EVERY marked node into a reference, unset fields
|
|
10442
|
+
* included, so a loader-parsed config always yields a fresh object here). The
|
|
10443
|
+
* pass-through for a field that is not a reference is what keeps the unmarked
|
|
10444
|
+
* `apiKey` literal readable.
|
|
10445
|
+
*/
|
|
10446
|
+
function unwrapVolatileConfig(config) {
|
|
10447
|
+
const source = config;
|
|
10448
|
+
if (typeof source !== "object" || source === null) return config;
|
|
10449
|
+
const plain = {};
|
|
10450
|
+
for (const [field, value] of Object.entries(source)) plain[field] = isVolatileRef(value) ? value.get() : value;
|
|
10451
|
+
return plain;
|
|
10452
|
+
}
|
|
10453
|
+
//#endregion
|
|
10186
10454
|
//#region src/web-search.ts
|
|
10187
10455
|
/**
|
|
10188
10456
|
* Command Code web-search provider over `ctx.web`.
|
|
@@ -10195,6 +10463,12 @@ var AccountEnrollmentManager = class {
|
|
|
10195
10463
|
* therefore reuses the plugin's existing credential chain and `apiBase`, so
|
|
10196
10464
|
* the model-facing `web_search` tool needs no separate configuration.
|
|
10197
10465
|
*
|
|
10466
|
+
* The second half of this module is the SELECTION seam: which registered search
|
|
10467
|
+
* backend `ctx.web` actually runs. dsh 0.2.1-rc.2 turned that choice into a
|
|
10468
|
+
* per-call `Volatile` field of the `web` profile entry, so it is changed
|
|
10469
|
+
* through the host settings service rather than by assigning a runtime field —
|
|
10470
|
+
* see {@link createCommandCodeSearchSelection}.
|
|
10471
|
+
*
|
|
10198
10472
|
* @module dsh-commandcode-provider/web-search
|
|
10199
10473
|
*/
|
|
10200
10474
|
/** Stable id this provider registers under in `ctx.web`. */
|
|
@@ -10206,54 +10480,273 @@ const COMMANDCODE_SEARCH_PROVIDER_ID = "commandcode";
|
|
|
10206
10480
|
* PREVIOUSLY selected backend, never this default (issue #26).
|
|
10207
10481
|
*/
|
|
10208
10482
|
const DEFAULT_WEB_SEARCH_PROVIDER_ID = "deepseek-official";
|
|
10483
|
+
/**
|
|
10484
|
+
* Profile entry id whose volatile `searchProvider` selects the search backend.
|
|
10485
|
+
*
|
|
10486
|
+
* The hosting profile declares it in `dsh-base`'s cordis patch as
|
|
10487
|
+
* `- id: web` / `name: '@deepseek-ai/dsh-web'`, and the shipped web-search
|
|
10488
|
+
* settings page addresses the very same namespace — that page and this plugin
|
|
10489
|
+
* are therefore two writers of ONE field (see
|
|
10490
|
+
* {@link createCommandCodeSearchSelection} for how they avoid fighting).
|
|
10491
|
+
*/
|
|
10492
|
+
const WEB_SELECTION_NAMESPACE = "web";
|
|
10209
10493
|
/** Fresh selection state: the plugin starts out not owning the selection. */
|
|
10210
10494
|
function commandCodeSearchSelection() {
|
|
10211
10495
|
return {
|
|
10212
10496
|
owner: false,
|
|
10213
|
-
|
|
10214
|
-
|
|
10497
|
+
preexisting: false,
|
|
10498
|
+
foreign: void 0,
|
|
10499
|
+
displaced: void 0
|
|
10215
10500
|
};
|
|
10216
10501
|
}
|
|
10502
|
+
/** Read one member of a plain object or array without trusting its shape. */
|
|
10503
|
+
function member(value, key) {
|
|
10504
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
10505
|
+
return value[key];
|
|
10506
|
+
}
|
|
10507
|
+
/** A string member, or `undefined` for anything else (including `null`). */
|
|
10508
|
+
function text(value) {
|
|
10509
|
+
return typeof value === "string" ? value : void 0;
|
|
10510
|
+
}
|
|
10511
|
+
/** The settings row describing the `web` entry, when the host serves one. */
|
|
10512
|
+
function selectionRow(settings) {
|
|
10513
|
+
let rows;
|
|
10514
|
+
try {
|
|
10515
|
+
rows = settings.describe();
|
|
10516
|
+
} catch {
|
|
10517
|
+
return;
|
|
10518
|
+
}
|
|
10519
|
+
if (!Array.isArray(rows)) return void 0;
|
|
10520
|
+
return rows.find((row) => text(member(row, "ns")) === "web");
|
|
10521
|
+
}
|
|
10217
10522
|
/**
|
|
10218
|
-
*
|
|
10219
|
-
* providers (issue #26).
|
|
10220
|
-
*
|
|
10221
|
-
* - Enabling writes `commandcode` and remembers whatever it displaced. When
|
|
10222
|
-
* the plugin already owns the selection, the original `displaced` is kept —
|
|
10223
|
-
* the field currently holds our own id, which must never be mistaken for the
|
|
10224
|
-
* user's backend.
|
|
10225
|
-
* - Disabling hands the selection back to the remembered backend only when
|
|
10226
|
-
* this plugin actually took it over. A fresh boot straight into
|
|
10227
|
-
* `webSearch: false`, or a `preexisting` field, leaves it ALONE: writing the
|
|
10228
|
-
* empty memory back would clear the user's own `searchProvider: commandcode`
|
|
10229
|
-
* pin and hand the selection to dsh-web's auto-select, where a second usable
|
|
10230
|
-
* provider makes every later search throw `WEB_PROVIDER_AMBIGUOUS`.
|
|
10523
|
+
* The volatile reference backing `web.searchProvider`.
|
|
10231
10524
|
*
|
|
10232
|
-
*
|
|
10233
|
-
* to
|
|
10525
|
+
* `config` is a TypeScript-private parameter property on `WebRuntime`, so at
|
|
10526
|
+
* runtime it is an ordinary own field — the only handle to a reference the host
|
|
10527
|
+
* exposes no public accessor for. Reading it structurally means a hardened
|
|
10528
|
+
* runtime degrades to "not writable" instead of throwing.
|
|
10234
10529
|
*/
|
|
10235
|
-
function
|
|
10530
|
+
function searchProviderRef(web) {
|
|
10531
|
+
return member(member(web, "config"), "searchProvider");
|
|
10532
|
+
}
|
|
10533
|
+
/** Observe the live selection; `undefined` when nothing can be observed. */
|
|
10534
|
+
function readView(deps) {
|
|
10535
|
+
const settings = deps.settings();
|
|
10536
|
+
if (settings !== void 0) {
|
|
10537
|
+
const row = selectionRow(settings);
|
|
10538
|
+
if (row !== void 0) {
|
|
10539
|
+
const user = member(row, "user");
|
|
10540
|
+
return {
|
|
10541
|
+
configured: text(member(member(row, "value"), "searchProvider")),
|
|
10542
|
+
pinned: text(member(user, "searchProvider")),
|
|
10543
|
+
base: text(member(member(row, "base"), "searchProvider")),
|
|
10544
|
+
user: typeof user === "object" && user !== null && !Array.isArray(user) ? { ...user } : {},
|
|
10545
|
+
persistent: true
|
|
10546
|
+
};
|
|
10547
|
+
}
|
|
10548
|
+
}
|
|
10549
|
+
const ref = searchProviderRef(deps.web);
|
|
10550
|
+
if (!isVolatileRef(ref)) return void 0;
|
|
10551
|
+
let current;
|
|
10236
10552
|
try {
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10553
|
+
current = ref.get();
|
|
10554
|
+
} catch {
|
|
10555
|
+
return;
|
|
10556
|
+
}
|
|
10557
|
+
if (current !== void 0 && typeof current !== "string") return void 0;
|
|
10558
|
+
return {
|
|
10559
|
+
configured: current,
|
|
10560
|
+
pinned: void 0,
|
|
10561
|
+
base: void 0,
|
|
10562
|
+
user: {},
|
|
10563
|
+
persistent: false
|
|
10564
|
+
};
|
|
10565
|
+
}
|
|
10566
|
+
/**
|
|
10567
|
+
* The selection seam over `ctx.web`.
|
|
10568
|
+
*
|
|
10569
|
+
* ## Why settings rather than the runtime instance
|
|
10570
|
+
*
|
|
10571
|
+
* 0.2.1-rc.2's `WebRuntime` reads `this.config.searchProvider.get()` on EVERY
|
|
10572
|
+
* search and fetch, and the loader commits a settings write into that same
|
|
10573
|
+
* reference in place (`Entry._commitVolatile`) without remounting the entry.
|
|
10574
|
+
* Writing the reference directly therefore works, but leaves the value out of
|
|
10575
|
+
* the profile: the next commit for that entry — including one caused by an
|
|
10576
|
+
* unrelated field such as `fetchProvider` — re-resolves the raw config and
|
|
10577
|
+
* overwrites it. `ctx.settings.update()` / `replace()` is the documented path
|
|
10578
|
+
* and the one the shipped web-search settings page uses, so this plugin takes
|
|
10579
|
+
* it first and falls back to the reference only when settings is unavailable or
|
|
10580
|
+
* refuses (see `volatileWrite`).
|
|
10581
|
+
*
|
|
10582
|
+
* ## Two writers, one field
|
|
10583
|
+
*
|
|
10584
|
+
* The shipped web-search settings page writes `web.searchProvider` too
|
|
10585
|
+
* (`deepseek-search-api` / `deepseek-official`). This seam never displaces a
|
|
10586
|
+
* pin it did not write: a non-`commandcode` value in the USER LAYER stops the
|
|
10587
|
+
* takeover, the value is recorded on {@link CommandCodeSearchSelection.foreign}
|
|
10588
|
+
* and named in one `info` line, and the provider stays registered but
|
|
10589
|
+
* unselected. A value the user layer does not carry is the deployment's own
|
|
10590
|
+
* default, and taking it over is the toggle's whole purpose. A user-layer
|
|
10591
|
+
* `commandcode` is treated as ours: nothing else writes that id, so it can only
|
|
10592
|
+
* be a previous run of this plugin or a deliberate manual pin, and turning the
|
|
10593
|
+
* toggle off restores the composed default in both cases.
|
|
10594
|
+
*
|
|
10595
|
+
* @param deps - the running `web` service, a settings resolver, and a log sink.
|
|
10596
|
+
* @returns a synchronous handle; failures are logged, never thrown.
|
|
10597
|
+
*/
|
|
10598
|
+
function createCommandCodeSearchSelection(deps) {
|
|
10599
|
+
const state = commandCodeSearchSelection();
|
|
10600
|
+
let pending;
|
|
10601
|
+
let activeKey;
|
|
10602
|
+
let running = false;
|
|
10603
|
+
let closed = false;
|
|
10604
|
+
let waiters = [];
|
|
10605
|
+
const warn = (message) => {
|
|
10606
|
+
try {
|
|
10607
|
+
deps.logger.warn(message);
|
|
10608
|
+
} catch {}
|
|
10609
|
+
};
|
|
10610
|
+
const info = (message) => {
|
|
10611
|
+
try {
|
|
10612
|
+
deps.logger.info(message);
|
|
10613
|
+
} catch {}
|
|
10614
|
+
};
|
|
10615
|
+
/** Commit `commandcode` in memory; the last resort before giving up. */
|
|
10616
|
+
const memoryWrite = (value) => {
|
|
10617
|
+
const written = volatileWrite(searchProviderRef(deps.web), value);
|
|
10618
|
+
if (!written) warn("llm-commandcode: web search selection could not be applied (settings refused the write and the running web config is not writable); the provider stays registered but unselected");
|
|
10619
|
+
return written;
|
|
10620
|
+
};
|
|
10621
|
+
/** Run one intent against a FRESH observation; never throws. */
|
|
10622
|
+
const perform = async (intent) => {
|
|
10623
|
+
let view;
|
|
10624
|
+
try {
|
|
10625
|
+
view = readView(deps);
|
|
10626
|
+
} catch {
|
|
10627
|
+
view = void 0;
|
|
10628
|
+
}
|
|
10629
|
+
if (view === void 0) return;
|
|
10630
|
+
const settings = view.persistent ? deps.settings() : void 0;
|
|
10631
|
+
if (intent.kind === "take") {
|
|
10632
|
+
if (view.pinned !== void 0 && view.pinned !== "commandcode") return;
|
|
10633
|
+
if (settings !== void 0) try {
|
|
10634
|
+
await settings.update("web", { searchProvider: COMMANDCODE_SEARCH_PROVIDER_ID });
|
|
10241
10635
|
return;
|
|
10636
|
+
} catch (error) {
|
|
10637
|
+
warn(`llm-commandcode: writing web.searchProvider through settings failed (${error instanceof Error ? error.message : String(error)}); falling back to the running config`);
|
|
10242
10638
|
}
|
|
10243
|
-
|
|
10244
|
-
state.preexisting = prior === COMMANDCODE_SEARCH_PROVIDER_ID;
|
|
10245
|
-
state.displaced = state.preexisting ? void 0 : prior;
|
|
10246
|
-
field.searchProviderId = COMMANDCODE_SEARCH_PROVIDER_ID;
|
|
10247
|
-
state.owner = true;
|
|
10639
|
+
memoryWrite(COMMANDCODE_SEARCH_PROVIDER_ID);
|
|
10248
10640
|
return;
|
|
10249
10641
|
}
|
|
10250
|
-
if (
|
|
10642
|
+
if (view.pinned !== void 0 && view.pinned !== "commandcode") return;
|
|
10643
|
+
if (settings !== void 0) {
|
|
10644
|
+
const section = { ...view.user };
|
|
10645
|
+
delete section.searchProvider;
|
|
10646
|
+
if (view.base !== void 0) section.searchProvider = view.base;
|
|
10647
|
+
try {
|
|
10648
|
+
await settings.replace("web", section);
|
|
10649
|
+
return;
|
|
10650
|
+
} catch (error) {
|
|
10651
|
+
warn(`llm-commandcode: restoring web.searchProvider through settings failed (${error instanceof Error ? error.message : String(error)}); falling back to the running config`);
|
|
10652
|
+
}
|
|
10653
|
+
}
|
|
10654
|
+
memoryWrite(intent.displaced);
|
|
10655
|
+
};
|
|
10656
|
+
const flush = () => {
|
|
10657
|
+
const done = waiters;
|
|
10658
|
+
waiters = [];
|
|
10659
|
+
for (const resolve of done) resolve();
|
|
10660
|
+
};
|
|
10661
|
+
const drain = async () => {
|
|
10662
|
+
running = true;
|
|
10663
|
+
try {
|
|
10664
|
+
while (pending !== void 0 && !closed) {
|
|
10665
|
+
const item = pending;
|
|
10666
|
+
pending = void 0;
|
|
10667
|
+
activeKey = item.key;
|
|
10668
|
+
await perform(item.intent);
|
|
10669
|
+
}
|
|
10670
|
+
} finally {
|
|
10671
|
+
running = false;
|
|
10672
|
+
activeKey = void 0;
|
|
10673
|
+
flush();
|
|
10674
|
+
}
|
|
10675
|
+
};
|
|
10676
|
+
/**
|
|
10677
|
+
* Queue one intent, skipping an identical one already queued or in flight. A
|
|
10678
|
+
* later DIFFERENT intent replaces the queued one: the toggle is a state, not a
|
|
10679
|
+
* log, so only the newest position has to land.
|
|
10680
|
+
*/
|
|
10681
|
+
const submit = (key, intent) => {
|
|
10682
|
+
if (closed) return;
|
|
10683
|
+
if (key === activeKey && pending === void 0) return;
|
|
10684
|
+
if (pending?.key === key) return;
|
|
10685
|
+
pending = {
|
|
10686
|
+
key,
|
|
10687
|
+
intent
|
|
10688
|
+
};
|
|
10689
|
+
if (!running) drain();
|
|
10690
|
+
};
|
|
10691
|
+
const apply = (enable) => {
|
|
10692
|
+
try {
|
|
10693
|
+
const view = readView(deps);
|
|
10694
|
+
if (view === void 0) return;
|
|
10695
|
+
if (!enable) {
|
|
10696
|
+
if (!state.owner) return;
|
|
10697
|
+
state.owner = false;
|
|
10698
|
+
if (state.preexisting) {
|
|
10699
|
+
state.preexisting = false;
|
|
10700
|
+
return;
|
|
10701
|
+
}
|
|
10702
|
+
submit("restore", {
|
|
10703
|
+
kind: "restore",
|
|
10704
|
+
displaced: state.displaced
|
|
10705
|
+
});
|
|
10706
|
+
return;
|
|
10707
|
+
}
|
|
10708
|
+
if (view.pinned !== void 0 && view.pinned !== "commandcode") {
|
|
10709
|
+
if (state.foreign !== view.pinned) {
|
|
10710
|
+
state.foreign = view.pinned;
|
|
10711
|
+
info(`llm-commandcode: web search backend "${view.pinned}" was chosen in the web-search settings page; leaving that explicit choice in place, so the commandcode provider stays registered but unselected`);
|
|
10712
|
+
}
|
|
10713
|
+
state.owner = false;
|
|
10714
|
+
state.preexisting = false;
|
|
10715
|
+
return;
|
|
10716
|
+
}
|
|
10717
|
+
state.foreign = void 0;
|
|
10718
|
+
if (view.configured === "commandcode") {
|
|
10719
|
+
state.owner = view.pinned === COMMANDCODE_SEARCH_PROVIDER_ID;
|
|
10720
|
+
state.preexisting = !state.owner;
|
|
10721
|
+
if (state.owner && state.displaced === void 0) state.displaced = view.base;
|
|
10722
|
+
return;
|
|
10723
|
+
}
|
|
10724
|
+
state.displaced = view.configured;
|
|
10725
|
+
state.preexisting = false;
|
|
10726
|
+
state.owner = true;
|
|
10727
|
+
submit("take", {
|
|
10728
|
+
kind: "take",
|
|
10729
|
+
displaced: view.configured
|
|
10730
|
+
});
|
|
10731
|
+
} catch (error) {
|
|
10732
|
+
warn(`llm-commandcode: aligning the web search selection failed (${error instanceof Error ? error.message : String(error)})`);
|
|
10733
|
+
}
|
|
10734
|
+
};
|
|
10735
|
+
return {
|
|
10736
|
+
state,
|
|
10737
|
+
apply,
|
|
10738
|
+
settled: () => running || pending !== void 0 ? new Promise((resolve) => {
|
|
10739
|
+
waiters.push(resolve);
|
|
10740
|
+
}) : Promise.resolve(),
|
|
10741
|
+
release: () => {
|
|
10742
|
+
closed = true;
|
|
10743
|
+
pending = void 0;
|
|
10744
|
+
if (state.owner && !state.preexisting) memoryWrite(state.displaced);
|
|
10251
10745
|
state.owner = false;
|
|
10252
|
-
if (!state.preexisting) field.searchProviderId = state.displaced;
|
|
10253
10746
|
state.preexisting = false;
|
|
10254
|
-
|
|
10747
|
+
if (!running) flush();
|
|
10255
10748
|
}
|
|
10256
|
-
}
|
|
10749
|
+
};
|
|
10257
10750
|
}
|
|
10258
10751
|
/** Command Code's lower/upper bound on `numResults` (from the CLI's `web_search` schema). */
|
|
10259
10752
|
const MIN_NUM_RESULTS = 1;
|
|
@@ -10337,7 +10830,7 @@ var CommandCodeSearchProvider = class {
|
|
|
10337
10830
|
});
|
|
10338
10831
|
} catch (error) {
|
|
10339
10832
|
if (signal?.aborted === true || isAbortError(error)) throw searchAborted(signal, error);
|
|
10340
|
-
throw new WebError(`Command Code web search request failed: ${
|
|
10833
|
+
throw new WebError(`Command Code web search request failed: ${readableErrorText(error)}`, "WEB_PROVIDER_ERROR", { cause: error });
|
|
10341
10834
|
}
|
|
10342
10835
|
if (!response.ok) {
|
|
10343
10836
|
let message = `Command Code web search failed (HTTP ${response.status})`;
|
|
@@ -10391,7 +10884,7 @@ var CommandCodeSearchProvider = class {
|
|
|
10391
10884
|
if (code === "MISSING_CREDENTIAL") throw new WebError(error.message, "WEB_PROVIDER_CREDENTIAL_MISSING", { cause: error });
|
|
10392
10885
|
if (code === "INVALID_CREDENTIAL" || code === "RATE_LIMIT") throw new WebError(error.message, "WEB_PROVIDER_ERROR", { cause: error });
|
|
10393
10886
|
}
|
|
10394
|
-
throw new WebError(`Command Code web search credential resolution failed: ${
|
|
10887
|
+
throw new WebError(`Command Code web search credential resolution failed: ${readableErrorText(error)}`, "WEB_PROVIDER_ERROR", { cause: error });
|
|
10395
10888
|
}
|
|
10396
10889
|
if (key === void 0 || key.length === 0) throw new WebError("Command Code web search has no API key; store COMMANDCODE_API_KEY through the credentials service (the web Models page writes it), export it in the launching environment, set config.apiKey, or run `command-code login` to write ~/.commandcode/auth.json", "WEB_PROVIDER_CREDENTIAL_MISSING");
|
|
10397
10890
|
return key;
|
|
@@ -10722,7 +11215,7 @@ function applyCommandCodeTuiSettings(ctx, deps) {
|
|
|
10722
11215
|
dispose: service.register(section)
|
|
10723
11216
|
};
|
|
10724
11217
|
} catch (error) {
|
|
10725
|
-
ctx.logger?.warn(`llm-commandcode: could not register the dsh-TUI settings section: ${
|
|
11218
|
+
ctx.logger?.warn(`llm-commandcode: could not register the dsh-TUI settings section: ${readableErrorText(error)}`);
|
|
10726
11219
|
}
|
|
10727
11220
|
};
|
|
10728
11221
|
refresh();
|
|
@@ -10805,78 +11298,6 @@ function transportBudgetMessage(failureMessage, maxRetries) {
|
|
|
10805
11298
|
return `${failureMessage};Command Code API 连接在连续重试后仍失败——已停止重试以免整轮卡死。插件已自动重试 ${maxRetries} 次。通常说明当前网络到 api.commandcode.ai 的连接不通,常见原因是代理未生效:DSH 只读取环境变量 HTTPS_PROXY/HTTP_PROXY(系统代理/PAC 不会被读取),请确认启动 DSH 的终端里已导出代理,或先关闭代理直连后重试。网络恢复后直接重发即可 — Command Code API transport still failed after ${maxRetries} automatic retries, which were stopped so the turn could not stall for minutes. This is a connectivity problem between this machine and api.commandcode.ai, not a context-window or request-size rejection. Common cause: the proxy is not reaching the harness — DSH reads HTTPS_PROXY/HTTP_PROXY from the environment only (a Windows system proxy/PAC setting is not consulted), so export it in the launching shell, or turn the proxy off and retry. Resend when the network recovers; the budget is per request and has been reset`;
|
|
10806
11299
|
}
|
|
10807
11300
|
//#endregion
|
|
10808
|
-
//#region src/config-volatile.ts
|
|
10809
|
-
/**
|
|
10810
|
-
* Volatile-config helpers for dsh 0.1.7's schema-derived profile Config.
|
|
10811
|
-
*
|
|
10812
|
-
* 0.1.7's `settings.describe()` projects each entry's schema through
|
|
10813
|
-
* `volatileForm()`, so a form contains ONLY nodes carrying `meta.volatile`
|
|
10814
|
-
* and a form edit is refused unless its path lies beneath a marked node. The
|
|
10815
|
-
* loader then resolves each marked field into a frozen `{ get() }` reference
|
|
10816
|
-
* that is written IN PLACE (no remount) and announced with
|
|
10817
|
-
* `loader/volatile-update`. Two helpers keep the rest of the plugin reading a
|
|
10818
|
-
* PLAIN `Config`: `markVolatile()` applies the mark, and
|
|
10819
|
-
* `unwrapVolatileConfig()` reads every top-level field through its reference
|
|
10820
|
-
* when one is present — FRESH per call, because a reference's identity is
|
|
10821
|
-
* stable while its value changes.
|
|
10822
|
-
*
|
|
10823
|
-
* Only TOP-LEVEL fields are marked (a form write names a top-level path such
|
|
10824
|
-
* as `accounts` or `modelVisibility.<id>`), so one unwrap level is complete.
|
|
10825
|
-
* The literal `apiKey` secret stays unmarked on purpose: no settings surface
|
|
10826
|
-
* writes it — page and TUI both write keys through the credentials seam — so a
|
|
10827
|
-
* config-file edit to it reloading the fiber is correct for a secret literal.
|
|
10828
|
-
*
|
|
10829
|
-
* @module dsh-commandcode-provider/config-volatile
|
|
10830
|
-
*/
|
|
10831
|
-
/**
|
|
10832
|
-
* Mark one schema field volatile.
|
|
10833
|
-
*
|
|
10834
|
-
* `.volatile()` is unconditional: it is a schemastery 3.18.3 feature and every
|
|
10835
|
-
* supported engine ships `~3.18.4` or `~3.18.5-alpha.1`, so the
|
|
10836
|
-
* method is always present. It is reached through a structural member so the caller's
|
|
10837
|
-
* declared field type survives — the mark changes what the schema PARSES to (a
|
|
10838
|
-
* live reference), not the plugin-facing `Config` shape.
|
|
10839
|
-
*/
|
|
10840
|
-
function markVolatile(schema) {
|
|
10841
|
-
return schema.volatile();
|
|
10842
|
-
}
|
|
10843
|
-
/**
|
|
10844
|
-
* Mark every field of a Config field dict volatile, except the named
|
|
10845
|
-
* composition-only secrets (`apiKey` here, which no settings surface writes).
|
|
10846
|
-
*/
|
|
10847
|
-
function markVolatileFields(fields, skip = []) {
|
|
10848
|
-
return Object.fromEntries(Object.entries(fields).map(([name, schema]) => [name, skip.includes(name) ? schema : markVolatile(schema)]));
|
|
10849
|
-
}
|
|
10850
|
-
/**
|
|
10851
|
-
* Whether a resolved config value is a volatile reference.
|
|
10852
|
-
*
|
|
10853
|
-
* Duck-typed on the two properties `createVolatile` produces (frozen object
|
|
10854
|
-
* with a `get` method) rather than cosmokit's private `write` symbol, because
|
|
10855
|
-
* cosmokit is not a peer of this bundle. Config values are plain parsed JSON
|
|
10856
|
-
* (or `!!jsExpr` wrappers), which are never frozen, so the test cannot
|
|
10857
|
-
* misfire on an ordinary field.
|
|
10858
|
-
*/
|
|
10859
|
-
function isVolatileRef(value) {
|
|
10860
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.isFrozen(value) && typeof value.get === "function";
|
|
10861
|
-
}
|
|
10862
|
-
/**
|
|
10863
|
-
* Read one config object as PLAIN values.
|
|
10864
|
-
*
|
|
10865
|
-
* Every top-level field found to be a volatile reference is read through
|
|
10866
|
-
* `.get()` — freshly on every call, so a live update lands on the very next
|
|
10867
|
-
* read (the loader resolves EVERY marked node into a reference, unset fields
|
|
10868
|
-
* included, so a loader-parsed config always yields a fresh object here). The
|
|
10869
|
-
* pass-through for a field that is not a reference is what keeps the unmarked
|
|
10870
|
-
* `apiKey` literal readable.
|
|
10871
|
-
*/
|
|
10872
|
-
function unwrapVolatileConfig(config) {
|
|
10873
|
-
const source = config;
|
|
10874
|
-
if (typeof source !== "object" || source === null) return config;
|
|
10875
|
-
const plain = {};
|
|
10876
|
-
for (const [field, value] of Object.entries(source)) plain[field] = isVolatileRef(value) ? value.get() : value;
|
|
10877
|
-
return plain;
|
|
10878
|
-
}
|
|
10879
|
-
//#endregion
|
|
10880
11301
|
//#region src/index.ts
|
|
10881
11302
|
/**
|
|
10882
11303
|
* dsh-commandcode-provider — DeepSeek Harness LLM provider plugin for Command
|
|
@@ -10936,6 +11357,7 @@ const Config = z.object(markVolatileFields({
|
|
|
10936
11357
|
modelVisibility: z.dict(z.boolean()),
|
|
10937
11358
|
contextWindow: z.number().min(1).max(CONTEXT_WINDOW_MAX).step(1),
|
|
10938
11359
|
contextWindowOverrides: z.dict(z.number().min(1).max(CONTEXT_WINDOW_MAX).step(1)),
|
|
11360
|
+
maxOutputTokens: z.number().min(0).max(MAX_OUTPUT_TOKEN_LIMIT).step(1),
|
|
10939
11361
|
webSearch: z.boolean().default(true),
|
|
10940
11362
|
showSidebarQuota: z.boolean().default(false),
|
|
10941
11363
|
accounts: z.array(z.object({
|
|
@@ -10983,6 +11405,7 @@ function resolveAdapterOptions(config) {
|
|
|
10983
11405
|
modelVisibility: readModelVisibility(config.modelVisibility),
|
|
10984
11406
|
contextWindow: isValidContextWindow(config.contextWindow) ? config.contextWindow : void 0,
|
|
10985
11407
|
contextWindowOverrides: readContextWindowOverrides(config.contextWindowOverrides),
|
|
11408
|
+
maxOutputTokens: isValidMaxOutputTokens(config.maxOutputTokens) ? config.maxOutputTokens : void 0,
|
|
10986
11409
|
zdr: config.zdr === true
|
|
10987
11410
|
};
|
|
10988
11411
|
}
|
|
@@ -11143,7 +11566,7 @@ function apply(ctx, config) {
|
|
|
11143
11566
|
else try {
|
|
11144
11567
|
report = await adapter.getUsage(account.key, connection);
|
|
11145
11568
|
} catch (error) {
|
|
11146
|
-
report = { failures: [
|
|
11569
|
+
report = { failures: [readableErrorText(error)] };
|
|
11147
11570
|
}
|
|
11148
11571
|
const state = account?.state;
|
|
11149
11572
|
const usable = accountUsable(state);
|
|
@@ -11225,13 +11648,24 @@ function apply(ctx, config) {
|
|
|
11225
11648
|
listModels: catalogForEditors,
|
|
11226
11649
|
analytics: createAnalyticsReader(ctx, adapter)
|
|
11227
11650
|
});
|
|
11228
|
-
const
|
|
11651
|
+
const selectionLogger = {
|
|
11652
|
+
warn: (message) => {
|
|
11653
|
+
ctx.logger.warn(message);
|
|
11654
|
+
},
|
|
11655
|
+
info: (message) => {
|
|
11656
|
+
ctx.logger.info(message);
|
|
11657
|
+
}
|
|
11658
|
+
};
|
|
11659
|
+
let searchSelection;
|
|
11229
11660
|
const applySearchSelection = (enabled) => {
|
|
11230
|
-
|
|
11661
|
+
searchSelection?.apply(enabled);
|
|
11231
11662
|
};
|
|
11232
|
-
let webRuntime;
|
|
11233
11663
|
ctx.inject(["web"], (webCtx) => {
|
|
11234
|
-
|
|
11664
|
+
searchSelection = createCommandCodeSearchSelection({
|
|
11665
|
+
web: webCtx.web,
|
|
11666
|
+
settings: () => ctx.get("settings"),
|
|
11667
|
+
logger: selectionLogger
|
|
11668
|
+
});
|
|
11235
11669
|
webCtx.web.registerSearchProvider(new CommandCodeSearchProvider({
|
|
11236
11670
|
resolveKey: async (apiBase) => {
|
|
11237
11671
|
const connection = options();
|
|
@@ -11243,8 +11677,8 @@ function apply(ctx, config) {
|
|
|
11243
11677
|
}));
|
|
11244
11678
|
applySearchSelection(current().webSearch ?? true);
|
|
11245
11679
|
webCtx.effect(() => () => {
|
|
11246
|
-
|
|
11247
|
-
|
|
11680
|
+
searchSelection?.release();
|
|
11681
|
+
searchSelection = void 0;
|
|
11248
11682
|
}, "dsh-commandcode-provider: web search selection");
|
|
11249
11683
|
});
|
|
11250
11684
|
let refreshTuiSettings;
|
|
@@ -11289,6 +11723,7 @@ function apply(ctx, config) {
|
|
|
11289
11723
|
dispose();
|
|
11290
11724
|
};
|
|
11291
11725
|
}, "dsh-commandcode-provider: settings auto-form policy");
|
|
11726
|
+
applySearchSelection(current().webSearch ?? true);
|
|
11292
11727
|
});
|
|
11293
11728
|
const loaderEvents = ctx;
|
|
11294
11729
|
ctx.effect(() => {
|
|
@@ -11302,6 +11737,6 @@ function apply(ctx, config) {
|
|
|
11302
11737
|
}, "dsh-commandcode-provider: volatile config updates");
|
|
11303
11738
|
}
|
|
11304
11739
|
//#endregion
|
|
11305
|
-
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,
|
|
11740
|
+
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, WEB_SELECTION_NAMESPACE, accountUsable, apply, applyCommandCodeTuiSettings, applyCommands, applyUsageRemote, buildCommandAuthUrl, buildCommandCodeTuiSection, capabilityDescription, commandCodeSearchSelection, commandDefinition, compareByPlan, createCommandCodeSearchSelection, dealLabel, formatContext, inject, loginStatusSchema, matchModelRule, modelVisibleInPlan, name, parseLoginStatus, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, selectAccountForModel, selectActiveAccount, studioBaseForApiBase, subscriptionPlanInfo, supportsZeroDataRetention, usageReportSchema, validateCommandApiKey };
|
|
11306
11741
|
|
|
11307
11742
|
//# sourceMappingURL=index.js.map
|