dsh-opendesign-corouter 0.1.2 → 0.1.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.
@@ -0,0 +1,34 @@
1
+ export interface DeviceTokens {
2
+ controlKey: string;
3
+ runtimeKey: string;
4
+ }
5
+ export declare const DEVICE_AUTH_ENDPOINT = "https://amr-api.open-design.ai/api/v1/cli/device-authorizations";
6
+ export declare const DEVICE_AUTH_PLUGIN_VERSION = "0.1.4";
7
+ export declare const DEVICE_REQUEST_TIMEOUT_MS = 20000;
8
+ export declare const DEVICE_RESPONSE_MAX_BYTES = 65536;
9
+ export declare const DEVICE_POLL_MIN_MS = 1000;
10
+ export declare const DEVICE_POLL_MAX_MS = 300000;
11
+ export declare class DeviceAuthError extends Error {
12
+ readonly code: string;
13
+ constructor(code: string);
14
+ }
15
+ /** Strict public-link allowlist: only an optional, single, non-secret code is allowed. */
16
+ export declare function validateActivationUrl(value: unknown, userCode?: string): string | null;
17
+ export interface DeviceAuthorization {
18
+ deviceId: string;
19
+ deviceSecret: string;
20
+ userCode: string;
21
+ activationUrl: string;
22
+ pollMs: number;
23
+ expiresAt: number;
24
+ }
25
+ export declare function parseDeviceAuthorization(value: unknown): DeviceAuthorization;
26
+ export type DevicePollResult = {
27
+ state: 'pending';
28
+ } | {
29
+ state: 'success';
30
+ tokens: DeviceTokens;
31
+ };
32
+ export declare function parseDevicePoll(value: unknown): DevicePollResult;
33
+ /** Bound fetch *and body consumption*, even if an injected transport ignores abort. */
34
+ export declare function postDeviceJson(fetcher: typeof fetch, url: string, body: unknown, signal: AbortSignal, timeoutMs?: number): Promise<unknown>;
@@ -7,60 +7,70 @@ function record(value) {
7
7
  return value !== null && typeof value === "object" && !Array.isArray(value);
8
8
  }
9
9
 
10
+ // src/device-auth.ts
11
+ function validateActivationUrl(value, userCode) {
12
+ if (typeof value !== "string" || value.length > 512 || !/^https:\/\/open-design\.ai\/cloud\/cli\/activate(?:\?[^#]*)?$/u.test(value) || /[\s\\]/u.test(value)) return null;
13
+ try {
14
+ const url = new URL(value);
15
+ if (url.origin !== "https://open-design.ai" || url.pathname !== "/cloud/cli/activate" || url.username || url.password || url.hash) return null;
16
+ const entries = [...url.searchParams];
17
+ if (entries.length > 1) return null;
18
+ if (entries.length) {
19
+ const entry = entries[0];
20
+ if (!["code", "user_code"].includes(entry[0]) || !validUserCode(entry[1]) || userCode !== void 0 && entry[1] !== userCode) return null;
21
+ }
22
+ return url.href;
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ function validUserCode(value) {
28
+ return typeof value === "string" && /^[a-z0-9][a-z0-9-]{3,31}$/iu.test(value);
29
+ }
30
+
10
31
  // src/protocol-options.ts
11
32
  var API_PROTOCOL_IDS = ["chat-completions", "responses", "messages"];
12
33
 
13
34
  // src/model-settings.ts
14
- var MAX_ENTRIES = 512;
15
- var MAX_MODEL_ID_LENGTH = 128;
16
- var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
17
- var MAX_TEMPERATURE = 2;
18
- var MAX_TOP_P = 1;
19
- var MAX_TOKENS = 1e7;
20
- var MAX_CONTEXT_WINDOW = 1e8;
21
- var REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"];
22
- var RESERVED_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
23
- var SETTINGS_FIELDS = /* @__PURE__ */ new Set(["enabled", "temperature", "topP", "maxTokens", "contextWindow", "reasoningEffort"]);
24
- function flag(value) {
25
- if (typeof value !== "boolean") throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
26
- return value;
27
- }
28
- function bounded(value, max, label) {
29
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${label} \u5FC5\u987B\u662F 0 \u5230 ${max} \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
30
- return value;
31
- }
32
- function capacity(value, max, label) {
33
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${label} \u5FC5\u987B\u662F 1 \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
34
- return value;
35
- }
36
- function effort(value) {
37
- const hit = REASONING_EFFORTS.find((candidate) => candidate === value);
38
- if (hit === void 0) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 reasoningEffort \u4E0D\u5728\u5141\u8BB8\u7684\u53D6\u503C\u5185");
39
- return hit;
40
- }
41
- function settingsOf(value) {
42
- if (!record(value)) throw new Error("\u6BCF\u4E2A\u6A21\u578B\u7684\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
43
- for (const field of Object.keys(value)) {
44
- if (!SETTINGS_FIELDS.has(field)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u542B\u6709\u4E0D\u652F\u6301\u7684\u5B57\u6BB5");
45
- }
46
- const out = {};
47
- if (value.enabled !== void 0) out.enabled = flag(value.enabled);
48
- if (value.temperature !== void 0) out.temperature = bounded(value.temperature, MAX_TEMPERATURE, "temperature");
49
- if (value.topP !== void 0) out.topP = bounded(value.topP, MAX_TOP_P, "topP");
50
- if (value.maxTokens !== void 0) out.maxTokens = capacity(value.maxTokens, MAX_TOKENS, "maxTokens");
51
- if (value.contextWindow !== void 0) out.contextWindow = capacity(value.contextWindow, MAX_CONTEXT_WINDOW, "contextWindow");
52
- if (value.reasoningEffort !== void 0) out.reasoningEffort = effort(value.reasoningEffort);
53
- return out;
54
- }
55
35
  function parseModelSettingsMap(value) {
56
- if (!record(value)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
36
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
57
37
  const keys = Object.keys(value);
58
- if (keys.length > MAX_ENTRIES) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u6700\u591A\u5141\u8BB8 ${MAX_ENTRIES} \u4E2A\u6A21\u578B`);
38
+ if (keys.length > 512) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u6700\u591A\u5141\u8BB8 512 \u4E2A\u6A21\u578B");
39
+ const fields = /* @__PURE__ */ new Set(["enabled", "temperature", "topP", "maxTokens", "contextWindow", "reasoningEffort"]);
40
+ const efforts = ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"];
59
41
  const out = {};
60
42
  for (const id of keys) {
61
- if (RESERVED_KEYS.has(id)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u4E0D\u5141\u8BB8\u4FDD\u7559\u5B57\u6BB5\u540D");
62
- if (!MODEL_ID.test(id)) throw new Error(`\u6A21\u578B ID \u5FC5\u987B\u662F 1\u2013${MAX_MODEL_ID_LENGTH} \u4F4D\u5B57\u6BCD\u3001\u6570\u5B57\u6216 . _ : / -`);
63
- out[id] = settingsOf(value[id]);
43
+ if (["__proto__", "constructor", "prototype"].includes(id)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u4E0D\u5141\u8BB8\u4FDD\u7559\u5B57\u6BB5\u540D");
44
+ if (!/^[A-Za-z0-9._:/-]{1,128}$/u.test(id)) throw new Error("\u6A21\u578B ID \u5FC5\u987B\u662F 1\u2013128 \u4F4D\u5B57\u6BCD\u3001\u6570\u5B57\u6216 . _ : / -");
45
+ const entry = value[id];
46
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) throw new Error("\u6BCF\u4E2A\u6A21\u578B\u7684\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
47
+ const settings = entry;
48
+ for (const field of Object.keys(settings)) {
49
+ if (!fields.has(field)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u542B\u6709\u4E0D\u652F\u6301\u7684\u5B57\u6BB5");
50
+ }
51
+ const parsed = {};
52
+ if (settings.enabled !== void 0) {
53
+ if (typeof settings.enabled !== "boolean") throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
54
+ parsed.enabled = settings.enabled;
55
+ }
56
+ for (const [field, max] of [["temperature", 2], ["topP", 1]]) {
57
+ const item = settings[field];
58
+ if (item === void 0) continue;
59
+ if (typeof item !== "number" || !Number.isFinite(item) || item < 0 || item > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${field} \u5FC5\u987B\u662F 0 \u5230 ${max} \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
60
+ parsed[field] = item;
61
+ }
62
+ for (const [field, max] of [["maxTokens", 1e7], ["contextWindow", 1e8]]) {
63
+ const item = settings[field];
64
+ if (item === void 0) continue;
65
+ if (typeof item !== "number" || !Number.isSafeInteger(item) || item < 1 || item > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${field} \u5FC5\u987B\u662F 1 \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
66
+ parsed[field] = item;
67
+ }
68
+ if (settings.reasoningEffort !== void 0) {
69
+ if (typeof settings.reasoningEffort !== "string" || !efforts.includes(settings.reasoningEffort)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 reasoningEffort \u4E0D\u5728\u5141\u8BB8\u7684\u53D6\u503C\u5185");
70
+ parsed.reasoningEffort = settings.reasoningEffort;
71
+ }
72
+ if (parsed.maxTokens !== void 0 && parsed.contextWindow !== void 0 && parsed.maxTokens >= parsed.contextWindow) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 maxTokens \u5FC5\u987B\u5C0F\u4E8E contextWindow");
73
+ out[id] = parsed;
64
74
  }
65
75
  return out;
66
76
  }
@@ -78,7 +88,7 @@ var MAX_WINDOWS = 64;
78
88
  var MAX_API_KEY = 16384;
79
89
  var HTTP_ASCII = /^[\x21-\x7e]+$/u;
80
90
  var ENV_REF = /^[A-Za-z_][A-Za-z0-9_]*$/u;
81
- var MODEL_ID2 = /^[A-Za-z0-9._:/-]{1,128}$/u;
91
+ var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
82
92
  var LOGIN_PHASES = ["idle", "launching", "waiting", "verifying", "success", "cancelled", "error"];
83
93
  var QUOTA_STATES = ["ok", "partial", "need-login", "error"];
84
94
  var SESSION_SOURCES = ["desktop", "login", "environment"];
@@ -92,7 +102,7 @@ function isoTime(value, label) {
92
102
  if (!Number.isFinite(Date.parse(time))) throw new Error(`${label} \u5FC5\u987B\u662F ISO \u65F6\u95F4\u5B57\u7B26\u4E32`);
93
103
  return time;
94
104
  }
95
- function flag2(value, label) {
105
+ function flag(value, label) {
96
106
  if (typeof value !== "boolean") throw new Error(`${label} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
97
107
  return value;
98
108
  }
@@ -148,11 +158,11 @@ function parseQuotaSnapshot(value) {
148
158
  ...value.subscriptionPeriodStart === void 0 ? {} : { subscriptionPeriodStart: isoTime(value.subscriptionPeriodStart, "\u8BA2\u9605\u5468\u671F\u5F00\u59CB\u65F6\u95F4") },
149
159
  ...value.subscriptionPeriodEnd === void 0 ? {} : { subscriptionPeriodEnd: isoTime(value.subscriptionPeriodEnd, "\u8BA2\u9605\u5468\u671F\u7ED3\u675F\u65F6\u95F4") },
150
160
  ...value.nextRefreshAt === void 0 ? {} : { nextRefreshAt: isoTime(value.nextRefreshAt, "\u4E0B\u6B21\u81EA\u52A8\u5237\u65B0\u65F6\u95F4") },
151
- ...value.stale === void 0 ? {} : { stale: flag2(value.stale, "\u5386\u53F2\u989D\u5EA6\u8BFB\u6570") },
161
+ ...value.stale === void 0 ? {} : { stale: flag(value.stale, "\u5386\u53F2\u989D\u5EA6\u8BFB\u6570") },
152
162
  ...value.refreshFailures === void 0 ? {} : { refreshFailures: count(value.refreshFailures, "\u8FDE\u7EED\u5237\u65B0\u5931\u8D25\u6B21\u6570") },
153
- ...value.autoRefreshPaused === void 0 ? {} : { autoRefreshPaused: flag2(value.autoRefreshPaused, "\u81EA\u52A8\u5237\u65B0\u6682\u505C\u72B6\u6001") },
163
+ ...value.autoRefreshPaused === void 0 ? {} : { autoRefreshPaused: flag(value.autoRefreshPaused, "\u81EA\u52A8\u5237\u65B0\u6682\u505C\u72B6\u6001") },
154
164
  ...value.tier === void 0 ? {} : { tier: text(value.tier, "\u5957\u9910\u540D", MAX_TIER) },
155
- ...value.eligible === void 0 ? {} : { eligible: flag2(value.eligible, "\u5957\u9910\u8D44\u683C") },
165
+ ...value.eligible === void 0 ? {} : { eligible: flag(value.eligible, "\u5957\u9910\u8D44\u683C") },
156
166
  windows: windows.map(parseQuotaWindow),
157
167
  balances: {
158
168
  ...balances.subscriptionUsd === void 0 ? {} : { subscriptionUsd: amount(balances.subscriptionUsd, "\u8BA2\u9605\u4F59\u989D") },
@@ -166,9 +176,19 @@ function parseLoginState(value) {
166
176
  if (!record(value)) throw new Error("\u767B\u5F55\u72B6\u6001\u5FC5\u987B\u662F\u5BF9\u8C61");
167
177
  const jobId = value.jobId;
168
178
  if (jobId !== null && jobId !== void 0 && typeof jobId !== "string") throw new Error("\u767B\u5F55\u4EFB\u52A1 id \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216 null");
179
+ const state = member(value.state, LOGIN_PHASES, "\u767B\u5F55\u9636\u6BB5");
180
+ let approval = {};
181
+ if (state === "waiting" && (value.activationUrl !== void 0 || value.userCode !== void 0 || value.expiresAt !== void 0)) {
182
+ const userCode = text(value.userCode, "\u8BBE\u5907\u6388\u6743\u7801", 64);
183
+ if (!/^[A-Za-z0-9-]+$/u.test(userCode)) throw new Error("\u8BBE\u5907\u6388\u6743\u7801\u683C\u5F0F\u65E0\u6548");
184
+ const activationUrl = validateActivationUrl(value.activationUrl, userCode);
185
+ if (activationUrl === null) throw new Error("\u8BBE\u5907\u6388\u6743\u94FE\u63A5\u65E0\u6548");
186
+ approval = { activationUrl, userCode, expiresAt: isoTime(value.expiresAt, "\u8BBE\u5907\u6388\u6743\u5230\u671F\u65F6\u95F4") };
187
+ }
169
188
  return {
170
189
  jobId: typeof jobId === "string" && jobId.length > 0 ? text(jobId, "\u767B\u5F55\u4EFB\u52A1 id", MAX_JOB_ID) : null,
171
- state: member(value.state, LOGIN_PHASES, "\u767B\u5F55\u9636\u6BB5"),
190
+ state,
191
+ ...approval,
172
192
  ...value.message === void 0 ? {} : { message: text(value.message, "\u767B\u5F55\u8BF4\u660E", MAX_MESSAGE) },
173
193
  ...value.startedAt === void 0 ? {} : { startedAt: isoTime(value.startedAt, "\u767B\u5F55\u5F00\u59CB\u65F6\u95F4") },
174
194
  ...value.finishedAt === void 0 ? {} : { finishedAt: isoTime(value.finishedAt, "\u767B\u5F55\u7ED3\u675F\u65F6\u95F4") }
@@ -177,12 +197,12 @@ function parseLoginState(value) {
177
197
  function parseStatusModel(value) {
178
198
  if (!record(value)) throw new Error("\u6A21\u578B\u6761\u76EE\u5FC5\u987B\u662F\u5BF9\u8C61");
179
199
  const id = text(value.id, "\u6A21\u578B id", 128);
180
- if (!MODEL_ID2.test(id)) throw new Error("\u6A21\u578B id \u542B\u4E0D\u5141\u8BB8\u7684\u5B57\u7B26");
200
+ if (!MODEL_ID.test(id)) throw new Error("\u6A21\u578B id \u542B\u4E0D\u5141\u8BB8\u7684\u5B57\u7B26");
181
201
  return {
182
202
  id,
183
203
  name: text(value.name, "\u6A21\u578B\u540D", 128),
184
204
  group: member(value.group, MODEL_GROUPS, "\u6A21\u578B\u5206\u7EC4"),
185
- ...value.enabled === void 0 ? {} : { enabled: flag2(value.enabled, "\u6A21\u578B\u542F\u7528\u72B6\u6001") },
205
+ ...value.enabled === void 0 ? {} : { enabled: flag(value.enabled, "\u6A21\u578B\u542F\u7528\u72B6\u6001") },
186
206
  ...value.contextWindow === void 0 ? {} : { contextWindow: positiveInteger(value.contextWindow, "\u4E0A\u4E0B\u6587\u9884\u7B97", 1e8) },
187
207
  ...value.maxOutputTokens === void 0 ? {} : { maxOutputTokens: positiveInteger(value.maxOutputTokens, "\u6700\u5927\u8F93\u51FA", 1e7) },
188
208
  ...value.reasoningEfforts === void 0 ? {} : { reasoningEfforts: parseEfforts(value.reasoningEfforts) }
@@ -193,8 +213,8 @@ function parseStatusConfig(value) {
193
213
  const apiKeyEnv = text(value.apiKeyEnv, "\u51ED\u636E\u5F15\u7528\u540D", MAX_ENV_REF);
194
214
  if (!ENV_REF.test(apiKeyEnv)) throw new Error("\u51ED\u636E\u5F15\u7528\u540D\u5FC5\u987B\u662F\u5408\u6CD5\u6807\u8BC6\u7B26");
195
215
  return {
196
- emptyTurnGuard: flag2(value.emptyTurnGuard, "\u7A7A\u8F6E\u4FDD\u62A4\u5F00\u5173"),
197
- useDesktopCredential: flag2(value.useDesktopCredential, "\u684C\u9762\u767B\u5F55\u6001\u5F00\u5173"),
216
+ emptyTurnGuard: flag(value.emptyTurnGuard, "\u7A7A\u8F6E\u4FDD\u62A4\u5F00\u5173"),
217
+ useDesktopCredential: flag(value.useDesktopCredential, "\u684C\u9762\u767B\u5F55\u6001\u5F00\u5173"),
198
218
  baseURL: text(value.baseURL, "\u63A8\u7406\u5730\u5740", MAX_BASE_URL),
199
219
  apiKeyEnv,
200
220
  ...value.apiProtocol === void 0 ? {} : { apiProtocol: member(value.apiProtocol, API_PROTOCOL_IDS, "\u63A8\u7406\u534F\u8BAE") },
@@ -216,7 +236,7 @@ function parseStatus(value) {
216
236
  }
217
237
  return {
218
238
  revision: text(value.revision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION),
219
- apiKeyConfigured: flag2(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
239
+ apiKeyConfigured: flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
220
240
  ...value.sessionSource === void 0 ? {} : { sessionSource: member(value.sessionSource, SESSION_SOURCES, "\u767B\u5F55\u6001\u6765\u6E90") },
221
241
  quota: parseQuotaSnapshot(value.quota),
222
242
  login: parseLoginState(value.login),
@@ -7,60 +7,70 @@ function record(value) {
7
7
  return value !== null && typeof value === "object" && !Array.isArray(value);
8
8
  }
9
9
 
10
+ // src/device-auth.ts
11
+ function validateActivationUrl(value, userCode) {
12
+ if (typeof value !== "string" || value.length > 512 || !/^https:\/\/open-design\.ai\/cloud\/cli\/activate(?:\?[^#]*)?$/u.test(value) || /[\s\\]/u.test(value)) return null;
13
+ try {
14
+ const url = new URL(value);
15
+ if (url.origin !== "https://open-design.ai" || url.pathname !== "/cloud/cli/activate" || url.username || url.password || url.hash) return null;
16
+ const entries = [...url.searchParams];
17
+ if (entries.length > 1) return null;
18
+ if (entries.length) {
19
+ const entry = entries[0];
20
+ if (!["code", "user_code"].includes(entry[0]) || !validUserCode(entry[1]) || userCode !== void 0 && entry[1] !== userCode) return null;
21
+ }
22
+ return url.href;
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ function validUserCode(value) {
28
+ return typeof value === "string" && /^[a-z0-9][a-z0-9-]{3,31}$/iu.test(value);
29
+ }
30
+
10
31
  // src/protocol-options.ts
11
32
  var API_PROTOCOL_IDS = ["chat-completions", "responses", "messages"];
12
33
 
13
34
  // src/model-settings.ts
14
- var MAX_ENTRIES = 512;
15
- var MAX_MODEL_ID_LENGTH = 128;
16
- var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
17
- var MAX_TEMPERATURE = 2;
18
- var MAX_TOP_P = 1;
19
- var MAX_TOKENS = 1e7;
20
- var MAX_CONTEXT_WINDOW = 1e8;
21
- var REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"];
22
- var RESERVED_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
23
- var SETTINGS_FIELDS = /* @__PURE__ */ new Set(["enabled", "temperature", "topP", "maxTokens", "contextWindow", "reasoningEffort"]);
24
- function flag(value) {
25
- if (typeof value !== "boolean") throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
26
- return value;
27
- }
28
- function bounded(value, max, label) {
29
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${label} \u5FC5\u987B\u662F 0 \u5230 ${max} \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
30
- return value;
31
- }
32
- function capacity(value, max, label) {
33
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${label} \u5FC5\u987B\u662F 1 \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
34
- return value;
35
- }
36
- function effort(value) {
37
- const hit = REASONING_EFFORTS.find((candidate) => candidate === value);
38
- if (hit === void 0) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 reasoningEffort \u4E0D\u5728\u5141\u8BB8\u7684\u53D6\u503C\u5185");
39
- return hit;
40
- }
41
- function settingsOf(value) {
42
- if (!record(value)) throw new Error("\u6BCF\u4E2A\u6A21\u578B\u7684\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
43
- for (const field of Object.keys(value)) {
44
- if (!SETTINGS_FIELDS.has(field)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u542B\u6709\u4E0D\u652F\u6301\u7684\u5B57\u6BB5");
45
- }
46
- const out = {};
47
- if (value.enabled !== void 0) out.enabled = flag(value.enabled);
48
- if (value.temperature !== void 0) out.temperature = bounded(value.temperature, MAX_TEMPERATURE, "temperature");
49
- if (value.topP !== void 0) out.topP = bounded(value.topP, MAX_TOP_P, "topP");
50
- if (value.maxTokens !== void 0) out.maxTokens = capacity(value.maxTokens, MAX_TOKENS, "maxTokens");
51
- if (value.contextWindow !== void 0) out.contextWindow = capacity(value.contextWindow, MAX_CONTEXT_WINDOW, "contextWindow");
52
- if (value.reasoningEffort !== void 0) out.reasoningEffort = effort(value.reasoningEffort);
53
- return out;
54
- }
55
35
  function parseModelSettingsMap(value) {
56
- if (!record(value)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
36
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
57
37
  const keys = Object.keys(value);
58
- if (keys.length > MAX_ENTRIES) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u6700\u591A\u5141\u8BB8 ${MAX_ENTRIES} \u4E2A\u6A21\u578B`);
38
+ if (keys.length > 512) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u6700\u591A\u5141\u8BB8 512 \u4E2A\u6A21\u578B");
39
+ const fields = /* @__PURE__ */ new Set(["enabled", "temperature", "topP", "maxTokens", "contextWindow", "reasoningEffort"]);
40
+ const efforts = ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"];
59
41
  const out = {};
60
42
  for (const id of keys) {
61
- if (RESERVED_KEYS.has(id)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u4E0D\u5141\u8BB8\u4FDD\u7559\u5B57\u6BB5\u540D");
62
- if (!MODEL_ID.test(id)) throw new Error(`\u6A21\u578B ID \u5FC5\u987B\u662F 1\u2013${MAX_MODEL_ID_LENGTH} \u4F4D\u5B57\u6BCD\u3001\u6570\u5B57\u6216 . _ : / -`);
63
- out[id] = settingsOf(value[id]);
43
+ if (["__proto__", "constructor", "prototype"].includes(id)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u4E0D\u5141\u8BB8\u4FDD\u7559\u5B57\u6BB5\u540D");
44
+ if (!/^[A-Za-z0-9._:/-]{1,128}$/u.test(id)) throw new Error("\u6A21\u578B ID \u5FC5\u987B\u662F 1\u2013128 \u4F4D\u5B57\u6BCD\u3001\u6570\u5B57\u6216 . _ : / -");
45
+ const entry = value[id];
46
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) throw new Error("\u6BCF\u4E2A\u6A21\u578B\u7684\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
47
+ const settings = entry;
48
+ for (const field of Object.keys(settings)) {
49
+ if (!fields.has(field)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u542B\u6709\u4E0D\u652F\u6301\u7684\u5B57\u6BB5");
50
+ }
51
+ const parsed = {};
52
+ if (settings.enabled !== void 0) {
53
+ if (typeof settings.enabled !== "boolean") throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
54
+ parsed.enabled = settings.enabled;
55
+ }
56
+ for (const [field, max] of [["temperature", 2], ["topP", 1]]) {
57
+ const item = settings[field];
58
+ if (item === void 0) continue;
59
+ if (typeof item !== "number" || !Number.isFinite(item) || item < 0 || item > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${field} \u5FC5\u987B\u662F 0 \u5230 ${max} \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
60
+ parsed[field] = item;
61
+ }
62
+ for (const [field, max] of [["maxTokens", 1e7], ["contextWindow", 1e8]]) {
63
+ const item = settings[field];
64
+ if (item === void 0) continue;
65
+ if (typeof item !== "number" || !Number.isSafeInteger(item) || item < 1 || item > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${field} \u5FC5\u987B\u662F 1 \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
66
+ parsed[field] = item;
67
+ }
68
+ if (settings.reasoningEffort !== void 0) {
69
+ if (typeof settings.reasoningEffort !== "string" || !efforts.includes(settings.reasoningEffort)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 reasoningEffort \u4E0D\u5728\u5141\u8BB8\u7684\u53D6\u503C\u5185");
70
+ parsed.reasoningEffort = settings.reasoningEffort;
71
+ }
72
+ if (parsed.maxTokens !== void 0 && parsed.contextWindow !== void 0 && parsed.maxTokens >= parsed.contextWindow) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 maxTokens \u5FC5\u987B\u5C0F\u4E8E contextWindow");
73
+ out[id] = parsed;
64
74
  }
65
75
  return out;
66
76
  }
@@ -78,7 +88,7 @@ var MAX_WINDOWS = 64;
78
88
  var MAX_API_KEY = 16384;
79
89
  var HTTP_ASCII = /^[\x21-\x7e]+$/u;
80
90
  var ENV_REF = /^[A-Za-z_][A-Za-z0-9_]*$/u;
81
- var MODEL_ID2 = /^[A-Za-z0-9._:/-]{1,128}$/u;
91
+ var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
82
92
  var LOGIN_PHASES = ["idle", "launching", "waiting", "verifying", "success", "cancelled", "error"];
83
93
  var QUOTA_STATES = ["ok", "partial", "need-login", "error"];
84
94
  var SESSION_SOURCES = ["desktop", "login", "environment"];
@@ -92,7 +102,7 @@ function isoTime(value, label) {
92
102
  if (!Number.isFinite(Date.parse(time))) throw new Error(`${label} \u5FC5\u987B\u662F ISO \u65F6\u95F4\u5B57\u7B26\u4E32`);
93
103
  return time;
94
104
  }
95
- function flag2(value, label) {
105
+ function flag(value, label) {
96
106
  if (typeof value !== "boolean") throw new Error(`${label} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
97
107
  return value;
98
108
  }
@@ -148,11 +158,11 @@ function parseQuotaSnapshot(value) {
148
158
  ...value.subscriptionPeriodStart === void 0 ? {} : { subscriptionPeriodStart: isoTime(value.subscriptionPeriodStart, "\u8BA2\u9605\u5468\u671F\u5F00\u59CB\u65F6\u95F4") },
149
159
  ...value.subscriptionPeriodEnd === void 0 ? {} : { subscriptionPeriodEnd: isoTime(value.subscriptionPeriodEnd, "\u8BA2\u9605\u5468\u671F\u7ED3\u675F\u65F6\u95F4") },
150
160
  ...value.nextRefreshAt === void 0 ? {} : { nextRefreshAt: isoTime(value.nextRefreshAt, "\u4E0B\u6B21\u81EA\u52A8\u5237\u65B0\u65F6\u95F4") },
151
- ...value.stale === void 0 ? {} : { stale: flag2(value.stale, "\u5386\u53F2\u989D\u5EA6\u8BFB\u6570") },
161
+ ...value.stale === void 0 ? {} : { stale: flag(value.stale, "\u5386\u53F2\u989D\u5EA6\u8BFB\u6570") },
152
162
  ...value.refreshFailures === void 0 ? {} : { refreshFailures: count(value.refreshFailures, "\u8FDE\u7EED\u5237\u65B0\u5931\u8D25\u6B21\u6570") },
153
- ...value.autoRefreshPaused === void 0 ? {} : { autoRefreshPaused: flag2(value.autoRefreshPaused, "\u81EA\u52A8\u5237\u65B0\u6682\u505C\u72B6\u6001") },
163
+ ...value.autoRefreshPaused === void 0 ? {} : { autoRefreshPaused: flag(value.autoRefreshPaused, "\u81EA\u52A8\u5237\u65B0\u6682\u505C\u72B6\u6001") },
154
164
  ...value.tier === void 0 ? {} : { tier: text(value.tier, "\u5957\u9910\u540D", MAX_TIER) },
155
- ...value.eligible === void 0 ? {} : { eligible: flag2(value.eligible, "\u5957\u9910\u8D44\u683C") },
165
+ ...value.eligible === void 0 ? {} : { eligible: flag(value.eligible, "\u5957\u9910\u8D44\u683C") },
156
166
  windows: windows.map(parseQuotaWindow),
157
167
  balances: {
158
168
  ...balances.subscriptionUsd === void 0 ? {} : { subscriptionUsd: amount(balances.subscriptionUsd, "\u8BA2\u9605\u4F59\u989D") },
@@ -166,9 +176,19 @@ function parseLoginState(value) {
166
176
  if (!record(value)) throw new Error("\u767B\u5F55\u72B6\u6001\u5FC5\u987B\u662F\u5BF9\u8C61");
167
177
  const jobId = value.jobId;
168
178
  if (jobId !== null && jobId !== void 0 && typeof jobId !== "string") throw new Error("\u767B\u5F55\u4EFB\u52A1 id \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216 null");
179
+ const state = member(value.state, LOGIN_PHASES, "\u767B\u5F55\u9636\u6BB5");
180
+ let approval = {};
181
+ if (state === "waiting" && (value.activationUrl !== void 0 || value.userCode !== void 0 || value.expiresAt !== void 0)) {
182
+ const userCode = text(value.userCode, "\u8BBE\u5907\u6388\u6743\u7801", 64);
183
+ if (!/^[A-Za-z0-9-]+$/u.test(userCode)) throw new Error("\u8BBE\u5907\u6388\u6743\u7801\u683C\u5F0F\u65E0\u6548");
184
+ const activationUrl = validateActivationUrl(value.activationUrl, userCode);
185
+ if (activationUrl === null) throw new Error("\u8BBE\u5907\u6388\u6743\u94FE\u63A5\u65E0\u6548");
186
+ approval = { activationUrl, userCode, expiresAt: isoTime(value.expiresAt, "\u8BBE\u5907\u6388\u6743\u5230\u671F\u65F6\u95F4") };
187
+ }
169
188
  return {
170
189
  jobId: typeof jobId === "string" && jobId.length > 0 ? text(jobId, "\u767B\u5F55\u4EFB\u52A1 id", MAX_JOB_ID) : null,
171
- state: member(value.state, LOGIN_PHASES, "\u767B\u5F55\u9636\u6BB5"),
190
+ state,
191
+ ...approval,
172
192
  ...value.message === void 0 ? {} : { message: text(value.message, "\u767B\u5F55\u8BF4\u660E", MAX_MESSAGE) },
173
193
  ...value.startedAt === void 0 ? {} : { startedAt: isoTime(value.startedAt, "\u767B\u5F55\u5F00\u59CB\u65F6\u95F4") },
174
194
  ...value.finishedAt === void 0 ? {} : { finishedAt: isoTime(value.finishedAt, "\u767B\u5F55\u7ED3\u675F\u65F6\u95F4") }
@@ -177,12 +197,12 @@ function parseLoginState(value) {
177
197
  function parseStatusModel(value) {
178
198
  if (!record(value)) throw new Error("\u6A21\u578B\u6761\u76EE\u5FC5\u987B\u662F\u5BF9\u8C61");
179
199
  const id = text(value.id, "\u6A21\u578B id", 128);
180
- if (!MODEL_ID2.test(id)) throw new Error("\u6A21\u578B id \u542B\u4E0D\u5141\u8BB8\u7684\u5B57\u7B26");
200
+ if (!MODEL_ID.test(id)) throw new Error("\u6A21\u578B id \u542B\u4E0D\u5141\u8BB8\u7684\u5B57\u7B26");
181
201
  return {
182
202
  id,
183
203
  name: text(value.name, "\u6A21\u578B\u540D", 128),
184
204
  group: member(value.group, MODEL_GROUPS, "\u6A21\u578B\u5206\u7EC4"),
185
- ...value.enabled === void 0 ? {} : { enabled: flag2(value.enabled, "\u6A21\u578B\u542F\u7528\u72B6\u6001") },
205
+ ...value.enabled === void 0 ? {} : { enabled: flag(value.enabled, "\u6A21\u578B\u542F\u7528\u72B6\u6001") },
186
206
  ...value.contextWindow === void 0 ? {} : { contextWindow: positiveInteger(value.contextWindow, "\u4E0A\u4E0B\u6587\u9884\u7B97", 1e8) },
187
207
  ...value.maxOutputTokens === void 0 ? {} : { maxOutputTokens: positiveInteger(value.maxOutputTokens, "\u6700\u5927\u8F93\u51FA", 1e7) },
188
208
  ...value.reasoningEfforts === void 0 ? {} : { reasoningEfforts: parseEfforts(value.reasoningEfforts) }
@@ -193,8 +213,8 @@ function parseStatusConfig(value) {
193
213
  const apiKeyEnv = text(value.apiKeyEnv, "\u51ED\u636E\u5F15\u7528\u540D", MAX_ENV_REF);
194
214
  if (!ENV_REF.test(apiKeyEnv)) throw new Error("\u51ED\u636E\u5F15\u7528\u540D\u5FC5\u987B\u662F\u5408\u6CD5\u6807\u8BC6\u7B26");
195
215
  return {
196
- emptyTurnGuard: flag2(value.emptyTurnGuard, "\u7A7A\u8F6E\u4FDD\u62A4\u5F00\u5173"),
197
- useDesktopCredential: flag2(value.useDesktopCredential, "\u684C\u9762\u767B\u5F55\u6001\u5F00\u5173"),
216
+ emptyTurnGuard: flag(value.emptyTurnGuard, "\u7A7A\u8F6E\u4FDD\u62A4\u5F00\u5173"),
217
+ useDesktopCredential: flag(value.useDesktopCredential, "\u684C\u9762\u767B\u5F55\u6001\u5F00\u5173"),
198
218
  baseURL: text(value.baseURL, "\u63A8\u7406\u5730\u5740", MAX_BASE_URL),
199
219
  apiKeyEnv,
200
220
  ...value.apiProtocol === void 0 ? {} : { apiProtocol: member(value.apiProtocol, API_PROTOCOL_IDS, "\u63A8\u7406\u534F\u8BAE") },
@@ -216,7 +236,7 @@ function parseStatus(value) {
216
236
  }
217
237
  return {
218
238
  revision: text(value.revision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION),
219
- apiKeyConfigured: flag2(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
239
+ apiKeyConfigured: flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
220
240
  ...value.sessionSource === void 0 ? {} : { sessionSource: member(value.sessionSource, SESSION_SOURCES, "\u767B\u5F55\u6001\u6765\u6E90") },
221
241
  quota: parseQuotaSnapshot(value.quota),
222
242
  login: parseLoginState(value.login),
package/lib/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { fetchModelCatalog } from './model-discovery.js';
4
4
  import type { CatalogModel } from './catalog.js';
5
5
  import { readConfig, type LiveConfig } from './config.js';
6
6
  import { resolveSession } from './auth.js';
7
+ import { type LoginOptions } from './login.js';
7
8
  import { readQuota, type QuotaSnapshot } from './quota.js';
8
9
  import { readDesktopQuota } from './desktop-quota.js';
9
10
  import { type Status, type LoginState, type StartLoginRequest, type CancelLoginRequest, type SaveApiKeyRequest } from './remote.js';
@@ -18,6 +19,7 @@ export interface HostDependencies {
18
19
  readDesktopQuota: typeof readDesktopQuota;
19
20
  fetchModels?: typeof fetchModelCatalog;
20
21
  modelsChanged?: () => void;
22
+ loginOptions?: LoginOptions;
21
23
  }
22
24
  export declare class OpenDesignCoRouterService extends TypertRemoteService {
23
25
  readonly dependencies: HostDependencies;
@@ -44,7 +46,9 @@ export declare class OpenDesignCoRouterService extends TypertRemoteService {
44
46
  modelCatalog(): readonly CatalogModel[];
45
47
  constructor(ctx: Context, config: () => ReturnType<typeof readConfig>, dependencies?: HostDependencies);
46
48
  private _credentials;
47
- resolveApiKey(ref: string): Promise<string | undefined>;
49
+ private _key;
50
+ resolveApiKey(ref: string, baseURL?: string): Promise<string | undefined>;
51
+ private _invalidateModels;
48
52
  private _state;
49
53
  private _conflict;
50
54
  private _expect;