@mars-sea/dsh-commandcode-provider 0.2.2 → 0.2.4
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/LICENSE +1 -7
- package/NOTICE +10 -0
- package/README.md +56 -97
- package/README.zh-CN.md +56 -96
- package/assets/screenshots/model-picker.png +0 -0
- package/assets/screenshots/usage-dashboard.png +0 -0
- package/lib/client.js +2 -2
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +8 -4
- package/lib/index.js +50 -6
- package/lib/index.js.map +1 -1
- package/package.json +4 -2
package/lib/index.d.ts
CHANGED
|
@@ -102,7 +102,7 @@ declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
|
102
102
|
/** Head-of-request timeout: how long to wait for the first response byte. */
|
|
103
103
|
declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
|
|
104
104
|
/** Stream idle timeout: a generation that stalls this long is a dead connection. */
|
|
105
|
-
declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS =
|
|
105
|
+
declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
|
|
106
106
|
/**
|
|
107
107
|
* Official display label for a model's minimum plan, or undefined for models
|
|
108
108
|
* outside the snapshot (e.g. future catalog additions).
|
|
@@ -140,9 +140,13 @@ interface CommandCodeConnectionOptions {
|
|
|
140
140
|
workingDir: string;
|
|
141
141
|
/** Model catalog cache path. */
|
|
142
142
|
modelsCachePath: string;
|
|
143
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* Milliseconds to wait for generate response headers / first byte (default 60s).
|
|
145
|
+
* Must not bound the subsequent body stream — long generations are gated by
|
|
146
|
+
* {@link streamIdleTimeoutMs} and the caller AbortSignal instead.
|
|
147
|
+
*/
|
|
144
148
|
requestTimeoutMs: number;
|
|
145
|
-
/** Milliseconds a stream may stall before it is treated as a dead connection (default
|
|
149
|
+
/** Milliseconds a stream may stall before it is treated as a dead connection (default 300s). */
|
|
146
150
|
streamIdleTimeoutMs: number;
|
|
147
151
|
}
|
|
148
152
|
/**
|
|
@@ -276,7 +280,7 @@ interface Config {
|
|
|
276
280
|
modelsCachePath?: string;
|
|
277
281
|
/** Milliseconds to wait for the generate response's first byte; defaults to 60s. */
|
|
278
282
|
requestTimeoutMs?: number;
|
|
279
|
-
/** Milliseconds a stream may stall before being treated as a dead connection; defaults to
|
|
283
|
+
/** Milliseconds a stream may stall before being treated as a dead connection; defaults to 300s. */
|
|
280
284
|
streamIdleTimeoutMs?: number;
|
|
281
285
|
}
|
|
282
286
|
declare const Config: z<Config>;
|
package/lib/index.js
CHANGED
|
@@ -383,7 +383,7 @@ const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
|
383
383
|
/** Head-of-request timeout: how long to wait for the first response byte. */
|
|
384
384
|
const DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
385
385
|
/** Stream idle timeout: a generation that stalls this long is a dead connection. */
|
|
386
|
-
const DEFAULT_STREAM_IDLE_TIMEOUT_MS =
|
|
386
|
+
const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
387
387
|
const MODEL_CACHE_VERSION = 1;
|
|
388
388
|
function isRecord(value) {
|
|
389
389
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -446,6 +446,23 @@ function stringValue(value) {
|
|
|
446
446
|
function numberValue(value) {
|
|
447
447
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
448
448
|
}
|
|
449
|
+
function booleanValue(value) {
|
|
450
|
+
return typeof value === "boolean" ? value : void 0;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Terminal stream-error markers from the official CLI (`Xw` in command-code's
|
|
454
|
+
* cli.mjs): these always mean "retrying cannot succeed", so the adapter must
|
|
455
|
+
* not classify them as transient server errors.
|
|
456
|
+
*/
|
|
457
|
+
const TERMINAL_STREAM_ERROR_MARKERS = [
|
|
458
|
+
"premium_credits_exhausted",
|
|
459
|
+
"model_not_in_plan",
|
|
460
|
+
"insufficient credits"
|
|
461
|
+
];
|
|
462
|
+
function hasTerminalStreamMarker(message) {
|
|
463
|
+
const lower = message.toLowerCase();
|
|
464
|
+
return TERMINAL_STREAM_ERROR_MARKERS.some((marker) => lower.includes(marker));
|
|
465
|
+
}
|
|
449
466
|
function recordOrEmpty(value) {
|
|
450
467
|
if (isRecord(value)) return value;
|
|
451
468
|
if (typeof value === "string") try {
|
|
@@ -827,6 +844,19 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
827
844
|
},
|
|
828
845
|
threadId: randomUUID()
|
|
829
846
|
};
|
|
847
|
+
const connectAbort = new AbortController();
|
|
848
|
+
let connectTimedOut = false;
|
|
849
|
+
const connectTimer = setTimeout(() => {
|
|
850
|
+
connectTimedOut = true;
|
|
851
|
+
connectAbort.abort(new DOMException(`Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms`, "TimeoutError"));
|
|
852
|
+
}, connection.requestTimeoutMs);
|
|
853
|
+
const onCallerAbort = () => {
|
|
854
|
+
connectAbort.abort(options.signal?.reason);
|
|
855
|
+
};
|
|
856
|
+
if (options.signal) {
|
|
857
|
+
if (options.signal.aborted) onCallerAbort();
|
|
858
|
+
else options.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
859
|
+
}
|
|
830
860
|
let response;
|
|
831
861
|
try {
|
|
832
862
|
response = await this.fetchImpl(`${connection.apiBase}/alpha/generate`, {
|
|
@@ -842,14 +872,18 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
842
872
|
...attributionHeaders()
|
|
843
873
|
},
|
|
844
874
|
body: JSON.stringify(body),
|
|
845
|
-
signal:
|
|
875
|
+
signal: connectAbort.signal
|
|
846
876
|
});
|
|
877
|
+
clearTimeout(connectTimer);
|
|
847
878
|
} catch (error) {
|
|
879
|
+
clearTimeout(connectTimer);
|
|
880
|
+
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
848
881
|
if (options.signal?.aborted) throw error;
|
|
849
|
-
if (error instanceof DOMException && error.name === "TimeoutError") throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms: ${errorChain(error)}`, "TIMEOUT", { cause: error });
|
|
882
|
+
if (connectTimedOut || error instanceof DOMException && error.name === "TimeoutError") throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate did not respond within ${connection.requestTimeoutMs}ms: ${errorChain(error)}`, "TIMEOUT", { cause: error });
|
|
850
883
|
throw new LlmError(`Command Code API request to ${connection.apiBase}/alpha/generate failed: ${errorChain(error)}`, "TRANSPORT", { cause: error });
|
|
851
884
|
}
|
|
852
885
|
if (!response.ok) {
|
|
886
|
+
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
853
887
|
const errText = await response.text().catch(() => "");
|
|
854
888
|
let providerCode;
|
|
855
889
|
try {
|
|
@@ -860,7 +894,10 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
860
894
|
if (response.status === 401) throw new LlmError(`Command Code API error 401 (${detail}): the API key is missing or invalid — check the key stored for COMMANDCODE_API_KEY (Models page) or the auth file`, "INVALID_CREDENTIAL", { status: 401 });
|
|
861
895
|
throw new LlmError(`Command Code API error ${response.status}${detail === `HTTP ${response.status}` ? "" : ` (${detail})`}: ${errText.slice(0, 500)}`, response.status === 429 ? "RATE_LIMIT" : "PROVIDER_HTTP_ERROR", { status: response.status });
|
|
862
896
|
}
|
|
863
|
-
if (!response.body)
|
|
897
|
+
if (!response.body) {
|
|
898
|
+
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
899
|
+
throw new LlmError("Command Code API returned no response body", "PROVIDER_PROTOCOL_ERROR");
|
|
900
|
+
}
|
|
864
901
|
const reader = response.body.getReader();
|
|
865
902
|
const decoder = new TextDecoder();
|
|
866
903
|
let buffer = "";
|
|
@@ -1015,8 +1052,14 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1015
1052
|
break;
|
|
1016
1053
|
}
|
|
1017
1054
|
case "error": {
|
|
1055
|
+
const err = isRecord(event.error) ? event.error : void 0;
|
|
1018
1056
|
const detail = isRecord(event.error) ? stringValue(event.error.message) ?? JSON.stringify(event.error) : stringValue(event.error) ?? stringValue(event.message) ?? "Stream error";
|
|
1019
|
-
|
|
1057
|
+
const statusCode = err ? numberValue(err.statusCode) : void 0;
|
|
1058
|
+
const isRetryable = err ? booleanValue(err.isRetryable) : void 0;
|
|
1059
|
+
const retryableStatus = statusCode !== void 0 && (statusCode === 429 || statusCode >= 500);
|
|
1060
|
+
const terminal = hasTerminalStreamMarker(detail);
|
|
1061
|
+
if (!(isRetryable === true || (statusCode !== void 0 ? retryableStatus : isRetryable !== false && !terminal))) throw new LlmError(`Command Code stream error: ${detail}`, "PROVIDER_STREAM_ERROR", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
1062
|
+
throw new LlmError(`Command Code stream error: ${detail}`, "SERVER", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
1020
1063
|
}
|
|
1021
1064
|
}
|
|
1022
1065
|
return chunks;
|
|
@@ -1063,6 +1106,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1063
1106
|
}
|
|
1064
1107
|
} finally {
|
|
1065
1108
|
clearIdle();
|
|
1109
|
+
if (options.signal) options.signal.removeEventListener("abort", onCallerAbort);
|
|
1066
1110
|
await reader.cancel().catch(() => void 0);
|
|
1067
1111
|
reader.releaseLock();
|
|
1068
1112
|
}
|
|
@@ -1209,7 +1253,7 @@ function resolveAdapterOptions(config) {
|
|
|
1209
1253
|
workingDir: config.workingDir ?? process.cwd(),
|
|
1210
1254
|
modelsCachePath: config.modelsCachePath ?? DEFAULT_MODELS_CACHE_PATH,
|
|
1211
1255
|
requestTimeoutMs: config.requestTimeoutMs ?? 6e4,
|
|
1212
|
-
streamIdleTimeoutMs: config.streamIdleTimeoutMs ??
|
|
1256
|
+
streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? 3e5
|
|
1213
1257
|
};
|
|
1214
1258
|
}
|
|
1215
1259
|
function apply(ctx, config) {
|