dsh-opendesign-corouter 0.1.4 → 0.1.6
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/README.md +5 -5
- package/SECURITY.md +1 -1
- package/THIRD_PARTY_NOTICES.md +22 -1
- package/lib/browser-open.d.ts +104 -0
- package/lib/catalog.d.ts +9 -8
- package/lib/client.d.ts +8 -2
- package/lib/client.js +87 -12
- package/lib/device-auth.d.ts +5 -3
- package/lib/generated/typert.host.js +18 -4
- package/lib/generated/typert.remote-client.d.ts +2 -1
- package/lib/generated/typert.remote-client.js +18 -4
- package/lib/index.d.ts +5 -1
- package/lib/index.js +324 -16
- package/lib/index.js.map +3 -3
- package/lib/login.d.ts +10 -0
- package/lib/remote.d.ts +7 -0
- package/package.json +2 -2
|
@@ -8,22 +8,29 @@ function record(value) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
// src/device-auth.ts
|
|
11
|
-
function validateActivationUrl(value, userCode) {
|
|
11
|
+
function validateActivationUrl(value, userCode, deviceId) {
|
|
12
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
13
|
try {
|
|
14
14
|
const url = new URL(value);
|
|
15
15
|
if (url.origin !== "https://open-design.ai" || url.pathname !== "/cloud/cli/activate" || url.username || url.password || url.hash) return null;
|
|
16
16
|
const entries = [...url.searchParams];
|
|
17
|
-
if (entries.length
|
|
18
|
-
|
|
17
|
+
if (entries.length === 2) {
|
|
18
|
+
if (entries.some(([key]) => key !== "deviceId" && key !== "userCode")) return null;
|
|
19
|
+
const id = url.searchParams.get("deviceId");
|
|
20
|
+
const code = url.searchParams.get("userCode");
|
|
21
|
+
if (!validDeviceId(id) || !validUserCode(code) || deviceId !== void 0 && id !== deviceId || userCode !== void 0 && code !== userCode) return null;
|
|
22
|
+
} else if (entries.length === 1) {
|
|
19
23
|
const entry = entries[0];
|
|
20
24
|
if (!["code", "user_code"].includes(entry[0]) || !validUserCode(entry[1]) || userCode !== void 0 && entry[1] !== userCode) return null;
|
|
21
|
-
}
|
|
25
|
+
} else if (entries.length > 2) return null;
|
|
22
26
|
return url.href;
|
|
23
27
|
} catch {
|
|
24
28
|
return null;
|
|
25
29
|
}
|
|
26
30
|
}
|
|
31
|
+
function validDeviceId(value) {
|
|
32
|
+
return typeof value === "string" && /^[a-z0-9_-]{1,200}$/iu.test(value);
|
|
33
|
+
}
|
|
27
34
|
function validUserCode(value) {
|
|
28
35
|
return typeof value === "string" && /^[a-z0-9][a-z0-9-]{3,31}$/iu.test(value);
|
|
29
36
|
}
|
|
@@ -89,6 +96,7 @@ var MAX_API_KEY = 16384;
|
|
|
89
96
|
var HTTP_ASCII = /^[\x21-\x7e]+$/u;
|
|
90
97
|
var ENV_REF = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
91
98
|
var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
|
|
99
|
+
var ACTIVATION_OPEN_CODES = ["opened", "no-link", "invalid-url", "unavailable", "failed"];
|
|
92
100
|
var LOGIN_PHASES = ["idle", "launching", "waiting", "verifying", "success", "cancelled", "error"];
|
|
93
101
|
var QUOTA_STATES = ["ok", "partial", "need-login", "error"];
|
|
94
102
|
var SESSION_SOURCES = ["desktop", "login", "environment"];
|
|
@@ -264,6 +272,10 @@ function parseSaveApiKeyRequest(value) {
|
|
|
264
272
|
if (key.length > MAX_API_KEY) throw new Error(`API Key \u4E0D\u80FD\u8D85\u8FC7 ${MAX_API_KEY} \u5B57\u7B26`);
|
|
265
273
|
return { value: key, expectedRevision: text(value.expectedRevision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION) };
|
|
266
274
|
}
|
|
275
|
+
function parseActivationOpenState(value) {
|
|
276
|
+
if (!record(value)) throw new Error("\u6388\u6743\u9875\u6253\u5F00\u7ED3\u679C\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
277
|
+
return { code: member(value.code, ACTIVATION_OPEN_CODES, "\u6388\u6743\u9875\u6253\u5F00\u7ED3\u679C"), opened: flag(value.opened, "\u6388\u6743\u9875\u662F\u5426\u5DF2\u6253\u5F00") };
|
|
278
|
+
}
|
|
267
279
|
|
|
268
280
|
// src/generated/typert.remote-client.ts
|
|
269
281
|
var StatusCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#Status", create: () => z.unknown().transform((value) => parseStatus(value)) };
|
|
@@ -271,6 +283,7 @@ var QuotaSnapshotCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#
|
|
|
271
283
|
var LoginStateCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#LoginState", create: () => z.unknown().transform((value) => parseLoginState(value)) };
|
|
272
284
|
var StartLoginRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#StartLoginRequest", create: () => z.unknown().transform((value) => parseStartLoginRequest(value)) };
|
|
273
285
|
var CancelLoginRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#CancelLoginRequest", create: () => z.unknown().transform((value) => parseCancelLoginRequest(value)) };
|
|
286
|
+
var ActivationOpenStateCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#ActivationOpenState", create: () => z.unknown().transform((value) => parseActivationOpenState(value)) };
|
|
274
287
|
var SaveApiKeyRequestCodec = { mode: "strict", typeSymbol: "dsh-opendesign-corouter#SaveApiKeyRequest", create: () => z.unknown().transform((value) => parseSaveApiKeyRequest(value)) };
|
|
275
288
|
var TYPERT_REMOTE = createTypertRemoteInfo({ package: "dsh-opendesign-corouter", methods: {
|
|
276
289
|
"openDesignCoRouter/status": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: StatusCodec },
|
|
@@ -280,6 +293,7 @@ var TYPERT_REMOTE = createTypertRemoteInfo({ package: "dsh-opendesign-corouter",
|
|
|
280
293
|
"openDesignCoRouter/startLogin": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: StartLoginRequestCodec, strictCodec: StartLoginRequestCodec }], result: LoginStateCodec },
|
|
281
294
|
"openDesignCoRouter/loginStatus": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: LoginStateCodec },
|
|
282
295
|
"openDesignCoRouter/cancelLogin": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: CancelLoginRequestCodec, strictCodec: CancelLoginRequestCodec }], result: LoginStateCodec },
|
|
296
|
+
"openDesignCoRouter/openActivationPage": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [], result: ActivationOpenStateCodec },
|
|
283
297
|
"openDesignCoRouter/saveApiKey": { service: "openDesignCoRouter", invocation: { kind: "direct" }, parameters: [{ name: "input", source: "json", codec: SaveApiKeyRequestCodec, strictCodec: SaveApiKeyRequestCodec }], result: StatusCodec }
|
|
284
298
|
} }, z);
|
|
285
299
|
var typert_remote_client_default = TYPERT_REMOTE;
|
package/lib/index.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ import type { CatalogModel } from './catalog.js';
|
|
|
5
5
|
import { readConfig, type LiveConfig } from './config.js';
|
|
6
6
|
import { resolveSession } from './auth.js';
|
|
7
7
|
import { type LoginOptions } from './login.js';
|
|
8
|
+
import { type ActivationLauncherOptions } from './browser-open.js';
|
|
8
9
|
import { readQuota, type QuotaSnapshot } from './quota.js';
|
|
9
10
|
import { readDesktopQuota } from './desktop-quota.js';
|
|
10
|
-
import { type Status, type LoginState, type StartLoginRequest, type CancelLoginRequest, type SaveApiKeyRequest } from './remote.js';
|
|
11
|
+
import { type Status, type LoginState, type StartLoginRequest, type CancelLoginRequest, type SaveApiKeyRequest, type ActivationOpenState } from './remote.js';
|
|
11
12
|
export { Config } from './config.js';
|
|
12
13
|
export { CoRouterAdapter } from './adapter.js';
|
|
13
14
|
export { MODELS } from './catalog.js';
|
|
@@ -20,11 +21,13 @@ export interface HostDependencies {
|
|
|
20
21
|
fetchModels?: typeof fetchModelCatalog;
|
|
21
22
|
modelsChanged?: () => void;
|
|
22
23
|
loginOptions?: LoginOptions;
|
|
24
|
+
activationOptions?: ActivationLauncherOptions;
|
|
23
25
|
}
|
|
24
26
|
export declare class OpenDesignCoRouterService extends TypertRemoteService {
|
|
25
27
|
readonly dependencies: HostDependencies;
|
|
26
28
|
private readonly _config;
|
|
27
29
|
private readonly _login;
|
|
30
|
+
private readonly _activation;
|
|
28
31
|
private readonly _lifetime;
|
|
29
32
|
private _revision;
|
|
30
33
|
private _fingerprint;
|
|
@@ -62,6 +65,7 @@ export declare class OpenDesignCoRouterService extends TypertRemoteService {
|
|
|
62
65
|
startLogin(input: StartLoginRequest): Promise<LoginState>;
|
|
63
66
|
loginStatus(): LoginState;
|
|
64
67
|
cancelLogin(input: CancelLoginRequest): LoginState;
|
|
68
|
+
openActivationPage(): Promise<ActivationOpenState>;
|
|
65
69
|
saveApiKey(input: SaveApiKeyRequest): Promise<Status>;
|
|
66
70
|
}
|
|
67
71
|
declare module '@deepseek-ai/cordis' {
|
package/lib/index.js
CHANGED
|
@@ -159,7 +159,7 @@ var MODELS = [
|
|
|
159
159
|
reasoningEfforts: EFFORTS_LOW_HIGH
|
|
160
160
|
},
|
|
161
161
|
{ id: "glm-5.2", name: "GLM-5.2", group: "paid" },
|
|
162
|
-
// ── Moonshot(
|
|
162
|
+
// ── Moonshot(3) ───────────────────────────────────────────
|
|
163
163
|
{
|
|
164
164
|
id: "kimi-k2.7-code",
|
|
165
165
|
name: "Kimi K2.7 Code",
|
|
@@ -170,7 +170,9 @@ var MODELS = [
|
|
|
170
170
|
reasoningEfforts: EFFORTS_STD
|
|
171
171
|
},
|
|
172
172
|
{ id: "kimi-k3", name: "Kimi K3", group: "paid" },
|
|
173
|
-
|
|
173
|
+
{ id: "kimi-k3.1-pro", name: "Kimi K3.1 Pro", group: "paid" },
|
|
174
|
+
// ── Google(3) ─────────────────────────────────────────────
|
|
175
|
+
{ id: "gemini-4-argon", name: "Gemini 4 Argon", group: "paid" },
|
|
174
176
|
{ id: "gemini-3.8-flash", name: "Gemini 3.8 Flash", group: "paid" },
|
|
175
177
|
{ id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", group: "paid" },
|
|
176
178
|
// ── xAI(3) ────────────────────────────────────────────────
|
|
@@ -207,7 +209,11 @@ var MODELS = [
|
|
|
207
209
|
reasoningEfforts: EFFORTS_STD
|
|
208
210
|
},
|
|
209
211
|
// ── Alibaba(1) ────────────────────────────────────────────
|
|
210
|
-
{ id: "qwen3.7-max", name: "Qwen3.7 Max", group: "paid" }
|
|
212
|
+
{ id: "qwen3.7-max", name: "Qwen3.7 Max", group: "paid" },
|
|
213
|
+
// Official API additions; display labels for undocumented entries are local formatting.
|
|
214
|
+
{ id: "minimax-m3.1-flash", name: "MiniMax M3.1 Flash", group: "plan" },
|
|
215
|
+
{ id: "muse-spark-1.3", name: "Muse Spark 1.3", group: "paid" },
|
|
216
|
+
{ id: "muse-wonder", name: "Muse Wonder", group: "paid" }
|
|
211
217
|
];
|
|
212
218
|
var BY_ID = new Map(MODELS.map((model) => [model.id, model]));
|
|
213
219
|
function findModel(id) {
|
|
@@ -2194,7 +2200,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2194
2200
|
|
|
2195
2201
|
// src/device-auth.ts
|
|
2196
2202
|
var DEVICE_AUTH_ENDPOINT = "https://amr-api.open-design.ai/api/v1/cli/device-authorizations";
|
|
2197
|
-
var DEVICE_AUTH_PLUGIN_VERSION = "0.1.
|
|
2203
|
+
var DEVICE_AUTH_PLUGIN_VERSION = "0.1.6";
|
|
2198
2204
|
var DEVICE_REQUEST_TIMEOUT_MS = 2e4;
|
|
2199
2205
|
var DEVICE_RESPONSE_MAX_BYTES = 65536;
|
|
2200
2206
|
var DEVICE_POLL_MIN_MS = 1e3;
|
|
@@ -2206,17 +2212,21 @@ var DeviceAuthError = class extends Error {
|
|
|
2206
2212
|
}
|
|
2207
2213
|
code;
|
|
2208
2214
|
};
|
|
2209
|
-
function validateActivationUrl(value, userCode) {
|
|
2215
|
+
function validateActivationUrl(value, userCode, deviceId) {
|
|
2210
2216
|
if (typeof value !== "string" || value.length > 512 || !/^https:\/\/open-design\.ai\/cloud\/cli\/activate(?:\?[^#]*)?$/u.test(value) || /[\s\\]/u.test(value)) return null;
|
|
2211
2217
|
try {
|
|
2212
2218
|
const url = new URL(value);
|
|
2213
2219
|
if (url.origin !== "https://open-design.ai" || url.pathname !== "/cloud/cli/activate" || url.username || url.password || url.hash) return null;
|
|
2214
2220
|
const entries = [...url.searchParams];
|
|
2215
|
-
if (entries.length
|
|
2216
|
-
|
|
2221
|
+
if (entries.length === 2) {
|
|
2222
|
+
if (entries.some(([key]) => key !== "deviceId" && key !== "userCode")) return null;
|
|
2223
|
+
const id = url.searchParams.get("deviceId");
|
|
2224
|
+
const code = url.searchParams.get("userCode");
|
|
2225
|
+
if (!validDeviceId(id) || !validUserCode(code) || deviceId !== void 0 && id !== deviceId || userCode !== void 0 && code !== userCode) return null;
|
|
2226
|
+
} else if (entries.length === 1) {
|
|
2217
2227
|
const entry = entries[0];
|
|
2218
2228
|
if (!["code", "user_code"].includes(entry[0]) || !validUserCode(entry[1]) || userCode !== void 0 && entry[1] !== userCode) return null;
|
|
2219
|
-
}
|
|
2229
|
+
} else if (entries.length > 2) return null;
|
|
2220
2230
|
return url.href;
|
|
2221
2231
|
} catch {
|
|
2222
2232
|
return null;
|
|
@@ -2225,6 +2235,9 @@ function validateActivationUrl(value, userCode) {
|
|
|
2225
2235
|
function object(value) {
|
|
2226
2236
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2227
2237
|
}
|
|
2238
|
+
function validDeviceId(value) {
|
|
2239
|
+
return typeof value === "string" && /^[a-z0-9_-]{1,200}$/iu.test(value);
|
|
2240
|
+
}
|
|
2228
2241
|
function validUserCode(value) {
|
|
2229
2242
|
return typeof value === "string" && /^[a-z0-9][a-z0-9-]{3,31}$/iu.test(value);
|
|
2230
2243
|
}
|
|
@@ -2232,8 +2245,8 @@ function secret(value) {
|
|
|
2232
2245
|
return typeof value === "string" && value.length > 0 && value.length <= 8192 && /^[\x21-\x7e]+$/u.test(value);
|
|
2233
2246
|
}
|
|
2234
2247
|
function parseDeviceAuthorization(value) {
|
|
2235
|
-
if (!object(value) ||
|
|
2236
|
-
const activationUrl = validateActivationUrl(value.activationUrl, value.userCode);
|
|
2248
|
+
if (!object(value) || !validDeviceId(value.deviceId) || !secret(value.deviceSecret) || !validUserCode(value.userCode)) throw new DeviceAuthError("opendesign/login-response");
|
|
2249
|
+
const activationUrl = validateActivationUrl(value.activationUrl, value.userCode, value.deviceId);
|
|
2237
2250
|
if (!activationUrl || value.userCode.toLowerCase().includes(value.deviceSecret.toLowerCase()) || decodeURIComponent(activationUrl).toLowerCase().includes(value.deviceSecret.toLowerCase())) throw new DeviceAuthError("opendesign/login-response");
|
|
2238
2251
|
const expiresAt = typeof value.expiresAt === "string" && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,3})?(?:Z|[+-]\d\d:\d\d)$/u.test(value.expiresAt) ? Date.parse(value.expiresAt) : NaN;
|
|
2239
2252
|
if (!Number.isFinite(expiresAt) || typeof value.pollIntervalSeconds !== "number" || !Number.isFinite(value.pollIntervalSeconds) || value.pollIntervalSeconds <= 0) throw new DeviceAuthError("opendesign/login-response");
|
|
@@ -2252,6 +2265,7 @@ function parseDevicePoll(value) {
|
|
|
2252
2265
|
if (["expired", "cancelled", "canceled", "denied", "rejected", "failed", "error"].includes(state)) throw new DeviceAuthError(state === "expired" ? "opendesign/login-expired" : "opendesign/login-denied");
|
|
2253
2266
|
if (value.error !== void 0 && value.error !== null && value.error !== "") throw new DeviceAuthError("opendesign/login-response");
|
|
2254
2267
|
const user = object(value.user) ? value.user : {};
|
|
2268
|
+
if ((state === "pending" || state === "authorization_pending") && [value.controlKey, value.runtimeKey, user.controlKey, user.runtimeKey].every((key) => key === void 0 || key === null)) return { state: "pending" };
|
|
2255
2269
|
const topPresent = value.controlKey !== void 0 || value.runtimeKey !== void 0;
|
|
2256
2270
|
const source = topPresent ? value : user;
|
|
2257
2271
|
if (source.controlKey !== void 0 || source.runtimeKey !== void 0) {
|
|
@@ -2406,6 +2420,11 @@ var ConsoleLogin = class {
|
|
|
2406
2420
|
} : {}
|
|
2407
2421
|
};
|
|
2408
2422
|
}
|
|
2423
|
+
/** Host-only: the live approval link, so the Host can open it in a bounded window. */
|
|
2424
|
+
activationLink() {
|
|
2425
|
+
const status = this.status();
|
|
2426
|
+
return status.state === "waiting" ? status.activationUrl : void 0;
|
|
2427
|
+
}
|
|
2409
2428
|
/** Synchronous/non-blocking and idempotent while capture/network/commit is pending. */
|
|
2410
2429
|
start(request) {
|
|
2411
2430
|
if (this.disposed) throw new LoginError("opendesign/login-disposed");
|
|
@@ -2421,7 +2440,8 @@ var ConsoleLogin = class {
|
|
|
2421
2440
|
status: { jobId: null, state: "launching", startedAt: new Date(started).toISOString(), message: "\u6B63\u5728\u7533\u8BF7\u5B98\u65B9\u8BBE\u5907\u6388\u6743\u7801" },
|
|
2422
2441
|
captured: void 0,
|
|
2423
2442
|
committing: false,
|
|
2424
|
-
cancelRequested: false
|
|
2443
|
+
cancelRequested: false,
|
|
2444
|
+
settled: false
|
|
2425
2445
|
};
|
|
2426
2446
|
job.status.jobId = job.id;
|
|
2427
2447
|
this.job = job;
|
|
@@ -2446,7 +2466,11 @@ var ConsoleLogin = class {
|
|
|
2446
2466
|
}
|
|
2447
2467
|
dispose() {
|
|
2448
2468
|
this.disposed = true;
|
|
2449
|
-
|
|
2469
|
+
const job = this.job;
|
|
2470
|
+
if (job) {
|
|
2471
|
+
this.requestCancellation(job, "\u670D\u52A1\u5DF2\u5378\u8F7D\uFF0C\u767B\u5F55\u5DF2\u53D6\u6D88");
|
|
2472
|
+
this.settle(job);
|
|
2473
|
+
}
|
|
2450
2474
|
}
|
|
2451
2475
|
alive(job) {
|
|
2452
2476
|
return this.job === job && !this.disposed && !job.controller.signal.aborted && ACTIVE.has(job.status.state);
|
|
@@ -2495,6 +2519,16 @@ var ConsoleLogin = class {
|
|
|
2495
2519
|
this.clearCode(job);
|
|
2496
2520
|
this.stopTimer(job);
|
|
2497
2521
|
job.controller.abort();
|
|
2522
|
+
this.settle(job);
|
|
2523
|
+
}
|
|
2524
|
+
/** Report one terminal transition per job, exactly once. */
|
|
2525
|
+
settle(job) {
|
|
2526
|
+
if (job.settled) return;
|
|
2527
|
+
job.settled = true;
|
|
2528
|
+
try {
|
|
2529
|
+
this.options.onSettled?.();
|
|
2530
|
+
} catch {
|
|
2531
|
+
}
|
|
2498
2532
|
}
|
|
2499
2533
|
async run(job) {
|
|
2500
2534
|
const signal = job.controller.signal;
|
|
@@ -2514,7 +2548,7 @@ var ConsoleLogin = class {
|
|
|
2514
2548
|
}
|
|
2515
2549
|
this.armExpiry(job);
|
|
2516
2550
|
job.status.state = "waiting";
|
|
2517
|
-
job.status.message = "\u8BF7\u5728\u5E38\u7528\u6D4F\u89C8\u5668\u6253\u5F00\u5B98\u65B9\u6388\u6743\u9875\uFF0C\
|
|
2551
|
+
job.status.message = "\u8BF7\u5728\u6388\u6743\u7A97\u53E3\u6216\u5E38\u7528\u6D4F\u89C8\u5668\u6253\u5F00\u5B98\u65B9\u6388\u6743\u9875\uFF0C\u767B\u5F55\u5E76\u786E\u8BA4\u8BBE\u5907\u7801";
|
|
2518
2552
|
job.status.activationUrl = device.activationUrl;
|
|
2519
2553
|
job.status.userCode = device.userCode;
|
|
2520
2554
|
job.status.expiresAt = new Date(job.deadline).toISOString();
|
|
@@ -2572,6 +2606,258 @@ function failureMessage(error) {
|
|
|
2572
2606
|
return "\u8BBE\u5907\u6388\u6743\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB";
|
|
2573
2607
|
}
|
|
2574
2608
|
|
|
2609
|
+
// src/browser-open.ts
|
|
2610
|
+
var ACTIVATION_NAVIGATE_TIMEOUT_MS = 15e3;
|
|
2611
|
+
var ACTIVATION_OPEN_BUDGET_MS = 2e4;
|
|
2612
|
+
var ACTIVATION_CLOSE_TIMEOUT_MS = 5e3;
|
|
2613
|
+
function chromeLaunchOptions() {
|
|
2614
|
+
return {
|
|
2615
|
+
channel: "chrome",
|
|
2616
|
+
headless: false,
|
|
2617
|
+
chromiumSandbox: true,
|
|
2618
|
+
ignoreDefaultArgs: ["--enable-automation"],
|
|
2619
|
+
args: ["--disable-blink-features=AutomationControlled"]
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
var ActivationLauncher = class {
|
|
2623
|
+
browser;
|
|
2624
|
+
page;
|
|
2625
|
+
pending;
|
|
2626
|
+
disposal;
|
|
2627
|
+
/** In-flight window closure, shared so disposal can join it. */
|
|
2628
|
+
closing;
|
|
2629
|
+
/** Closures of handles that were never adopted; disposal joins them too. */
|
|
2630
|
+
tracked = [];
|
|
2631
|
+
/** Chrome starts in flight, so disposal waits for the handle they will produce. */
|
|
2632
|
+
starting = [];
|
|
2633
|
+
disposed = false;
|
|
2634
|
+
/** Bumped by `close()`: an attempt from an older session must not resurrect a window. */
|
|
2635
|
+
session = 0;
|
|
2636
|
+
options;
|
|
2637
|
+
constructor(options = {}) {
|
|
2638
|
+
this.options = options;
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Show `url` in the temporary window, reusing one already open. Concurrent calls share
|
|
2642
|
+
* a single attempt: the caller's button stays honest about what actually happened.
|
|
2643
|
+
*/
|
|
2644
|
+
async open(url) {
|
|
2645
|
+
if (validateActivationUrl(url) === null) return { code: "invalid-url", opened: false };
|
|
2646
|
+
if (this.disposed) return { code: "failed", opened: false };
|
|
2647
|
+
if (this.pending) return this.pending;
|
|
2648
|
+
const attempt = this.attempt(url);
|
|
2649
|
+
this.pending = attempt;
|
|
2650
|
+
try {
|
|
2651
|
+
return await attempt;
|
|
2652
|
+
} finally {
|
|
2653
|
+
if (this.pending === attempt) this.pending = void 0;
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Close the temporary window and stay usable: a cancelled or expired job takes its
|
|
2658
|
+
* window away, while the next login may open a new one.
|
|
2659
|
+
*/
|
|
2660
|
+
async close() {
|
|
2661
|
+
this.session++;
|
|
2662
|
+
await this.closeCurrent();
|
|
2663
|
+
}
|
|
2664
|
+
/**
|
|
2665
|
+
* Retire the launcher for good. Cleanup is best-effort and bounded: it joins in-flight
|
|
2666
|
+
* starts and closures, but a browser that ignores `close()` may outlive the 5 s wait.
|
|
2667
|
+
*/
|
|
2668
|
+
async dispose() {
|
|
2669
|
+
this.disposed = true;
|
|
2670
|
+
this.session++;
|
|
2671
|
+
if (this.disposal) return this.disposal;
|
|
2672
|
+
this.disposal = (async () => {
|
|
2673
|
+
await this.bounded(Date.now() + ACTIVATION_CLOSE_TIMEOUT_MS, async () => {
|
|
2674
|
+
await Promise.allSettled(this.starting);
|
|
2675
|
+
});
|
|
2676
|
+
await this.closing;
|
|
2677
|
+
await this.closeCurrent();
|
|
2678
|
+
await this.bounded(Date.now() + ACTIVATION_CLOSE_TIMEOUT_MS, async () => {
|
|
2679
|
+
await Promise.all(this.tracked);
|
|
2680
|
+
});
|
|
2681
|
+
})();
|
|
2682
|
+
return this.disposal;
|
|
2683
|
+
}
|
|
2684
|
+
/**
|
|
2685
|
+
* Close the window this launcher owns right now. Ownership is taken before the async
|
|
2686
|
+
* close, so a job that adopts a new window afterwards is not affected by this closure.
|
|
2687
|
+
* The closure promise is shared so a later `dispose()` joins it.
|
|
2688
|
+
*/
|
|
2689
|
+
async closeCurrent() {
|
|
2690
|
+
if (this.closing === void 0) {
|
|
2691
|
+
const browser = this.browser;
|
|
2692
|
+
this.browser = void 0;
|
|
2693
|
+
this.page = void 0;
|
|
2694
|
+
if (browser === void 0) return;
|
|
2695
|
+
this.closing = closeBounded(browser);
|
|
2696
|
+
}
|
|
2697
|
+
const closing = this.closing;
|
|
2698
|
+
try {
|
|
2699
|
+
await closing;
|
|
2700
|
+
} finally {
|
|
2701
|
+
if (this.closing === closing) this.closing = void 0;
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
async attempt(url) {
|
|
2705
|
+
const session = this.session;
|
|
2706
|
+
const budget = this.options.timeoutMs ?? ACTIVATION_OPEN_BUDGET_MS;
|
|
2707
|
+
const deadline = Date.now() + budget;
|
|
2708
|
+
try {
|
|
2709
|
+
if (this.options.available?.() === false) return { code: "unavailable", opened: false };
|
|
2710
|
+
const browser = await this.ensureBrowser(session, deadline);
|
|
2711
|
+
if (browser === void 0) return { code: this.stale(session) ? "failed" : "unavailable", opened: false };
|
|
2712
|
+
if (this.stale(session)) {
|
|
2713
|
+
await this.bounded(deadline, () => this.closeCurrent());
|
|
2714
|
+
return { code: "failed", opened: false };
|
|
2715
|
+
}
|
|
2716
|
+
const page = await this.ensurePage(browser, session, deadline);
|
|
2717
|
+
if (page === void 0) {
|
|
2718
|
+
await this.bounded(deadline, () => this.closeCurrent());
|
|
2719
|
+
return { code: "failed", opened: false };
|
|
2720
|
+
}
|
|
2721
|
+
const remaining = Math.max(1, Math.min(ACTIVATION_NAVIGATE_TIMEOUT_MS, deadline - Date.now()));
|
|
2722
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout: remaining });
|
|
2723
|
+
if (this.stale(session)) {
|
|
2724
|
+
await this.bounded(deadline, () => this.closeCurrent());
|
|
2725
|
+
return { code: "failed", opened: false };
|
|
2726
|
+
}
|
|
2727
|
+
return { code: "opened", opened: true };
|
|
2728
|
+
} catch {
|
|
2729
|
+
await this.bounded(deadline, () => this.closeCurrent());
|
|
2730
|
+
return { code: "failed", opened: false };
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
/** The job that started this attempt ended, or the launcher retired. */
|
|
2734
|
+
stale(session) {
|
|
2735
|
+
return this.disposed || session !== this.session;
|
|
2736
|
+
}
|
|
2737
|
+
async ensureBrowser(session, deadline) {
|
|
2738
|
+
const open = this.browser;
|
|
2739
|
+
if (open !== void 0) {
|
|
2740
|
+
try {
|
|
2741
|
+
if (open.isConnected()) return open;
|
|
2742
|
+
} catch {
|
|
2743
|
+
}
|
|
2744
|
+
await this.bounded(deadline, () => this.closeCurrent());
|
|
2745
|
+
}
|
|
2746
|
+
for (let round = 0; round < 2; round++) {
|
|
2747
|
+
await this.bounded(deadline, async () => {
|
|
2748
|
+
await Promise.allSettled([this.closing, ...this.tracked, ...this.starting]);
|
|
2749
|
+
});
|
|
2750
|
+
if (this.stale(session) || Date.now() >= deadline) return void 0;
|
|
2751
|
+
if (this.closing === void 0 && this.tracked.length === 0 && this.starting.length === 0) break;
|
|
2752
|
+
}
|
|
2753
|
+
if (this.stale(session) || Date.now() >= deadline) return void 0;
|
|
2754
|
+
const launch = this.options.launch ?? defaultLaunch;
|
|
2755
|
+
const starting = launch();
|
|
2756
|
+
this.starting.push(starting);
|
|
2757
|
+
const browser = await this.bounded(deadline, () => starting);
|
|
2758
|
+
void starting.then((handle) => {
|
|
2759
|
+
if (handle !== browser) void this.trackClose(handle);
|
|
2760
|
+
}).catch(() => void 0).finally(() => {
|
|
2761
|
+
this.starting = this.starting.filter((item) => item !== starting);
|
|
2762
|
+
});
|
|
2763
|
+
if (browser === void 0) return void 0;
|
|
2764
|
+
if (this.stale(session)) {
|
|
2765
|
+
void this.trackClose(browser);
|
|
2766
|
+
return void 0;
|
|
2767
|
+
}
|
|
2768
|
+
this.adopt(browser);
|
|
2769
|
+
return browser;
|
|
2770
|
+
}
|
|
2771
|
+
async ensurePage(browser, session, deadline) {
|
|
2772
|
+
if (this.page !== void 0) return this.page;
|
|
2773
|
+
const page = await this.bounded(deadline, () => browser.newPage(), (late) => {
|
|
2774
|
+
void this.trackPageClose(late);
|
|
2775
|
+
});
|
|
2776
|
+
if (page === void 0) return void 0;
|
|
2777
|
+
if (this.stale(session) || this.browser !== browser) {
|
|
2778
|
+
await this.bounded(deadline, () => this.closeCurrent());
|
|
2779
|
+
return void 0;
|
|
2780
|
+
}
|
|
2781
|
+
this.page = page;
|
|
2782
|
+
return page;
|
|
2783
|
+
}
|
|
2784
|
+
/** A handle that arrived too late is closed, and still counts as work disposal joins. */
|
|
2785
|
+
trackClose(browser) {
|
|
2786
|
+
const closing = closeBounded(browser);
|
|
2787
|
+
this.tracked.push(closing);
|
|
2788
|
+
void closing.finally(() => {
|
|
2789
|
+
this.tracked = this.tracked.filter((item) => item !== closing);
|
|
2790
|
+
});
|
|
2791
|
+
return closing;
|
|
2792
|
+
}
|
|
2793
|
+
trackPageClose(page) {
|
|
2794
|
+
try {
|
|
2795
|
+
return Promise.resolve(page.close?.()).catch(() => void 0);
|
|
2796
|
+
} catch {
|
|
2797
|
+
return Promise.resolve();
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
adopt(browser) {
|
|
2801
|
+
this.browser = browser;
|
|
2802
|
+
browser.on?.("disconnected", () => {
|
|
2803
|
+
if (this.browser === browser) {
|
|
2804
|
+
this.browser = void 0;
|
|
2805
|
+
this.page = void 0;
|
|
2806
|
+
}
|
|
2807
|
+
});
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* Bound one launch step by the attempt's shared deadline. A step that settles only
|
|
2811
|
+
* after that deadline still hands its handle back: dropping it would leak a Chrome
|
|
2812
|
+
* process nobody can close.
|
|
2813
|
+
*/
|
|
2814
|
+
async bounded(deadline, start, discardOnLate) {
|
|
2815
|
+
let timer;
|
|
2816
|
+
let expired = false;
|
|
2817
|
+
const work = (async () => {
|
|
2818
|
+
try {
|
|
2819
|
+
return await start();
|
|
2820
|
+
} catch {
|
|
2821
|
+
return void 0;
|
|
2822
|
+
}
|
|
2823
|
+
})().then((value) => {
|
|
2824
|
+
if (expired && value !== void 0) discardOnLate?.(value);
|
|
2825
|
+
return value;
|
|
2826
|
+
});
|
|
2827
|
+
const timeout = new Promise((resolve) => {
|
|
2828
|
+
timer = setTimeout(() => {
|
|
2829
|
+
expired = true;
|
|
2830
|
+
resolve(void 0);
|
|
2831
|
+
}, Math.max(1, deadline - Date.now()));
|
|
2832
|
+
});
|
|
2833
|
+
try {
|
|
2834
|
+
return await Promise.race([work, timeout]);
|
|
2835
|
+
} finally {
|
|
2836
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
};
|
|
2840
|
+
function closeBounded(browser) {
|
|
2841
|
+
let timer;
|
|
2842
|
+
return Promise.race([
|
|
2843
|
+
(async () => {
|
|
2844
|
+
try {
|
|
2845
|
+
await browser.close();
|
|
2846
|
+
} catch {
|
|
2847
|
+
}
|
|
2848
|
+
})(),
|
|
2849
|
+
new Promise((resolve) => {
|
|
2850
|
+
timer = setTimeout(resolve, ACTIVATION_CLOSE_TIMEOUT_MS);
|
|
2851
|
+
})
|
|
2852
|
+
]).finally(() => {
|
|
2853
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2854
|
+
});
|
|
2855
|
+
}
|
|
2856
|
+
async function defaultLaunch() {
|
|
2857
|
+
const playwright = await import("playwright-core");
|
|
2858
|
+
return playwright.chromium.launch(chromeLaunchOptions());
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2575
2861
|
// src/quota.ts
|
|
2576
2862
|
import { attributionHeaders as attributionHeaders3 } from "@deepseek-ai/dsh-llm";
|
|
2577
2863
|
var API_BASE = "https://amr-api.open-design.ai/api/v1";
|
|
@@ -2859,6 +3145,7 @@ var MAX_API_KEY = 16384;
|
|
|
2859
3145
|
var HTTP_ASCII = /^[\x21-\x7e]+$/u;
|
|
2860
3146
|
var ENV_REF = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
2861
3147
|
var MODEL_ID2 = /^[A-Za-z0-9._:/-]{1,128}$/u;
|
|
3148
|
+
var ACTIVATION_OPEN_CODES = ["opened", "no-link", "invalid-url", "unavailable", "failed"];
|
|
2862
3149
|
var LOGIN_PHASES = ["idle", "launching", "waiting", "verifying", "success", "cancelled", "error"];
|
|
2863
3150
|
var QUOTA_STATES = ["ok", "partial", "need-login", "error"];
|
|
2864
3151
|
var SESSION_SOURCES = ["desktop", "login", "environment"];
|
|
@@ -3034,6 +3321,10 @@ function parseSaveApiKeyRequest(value) {
|
|
|
3034
3321
|
if (key.length > MAX_API_KEY) throw new Error(`API Key \u4E0D\u80FD\u8D85\u8FC7 ${MAX_API_KEY} \u5B57\u7B26`);
|
|
3035
3322
|
return { value: key, expectedRevision: text(value.expectedRevision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION) };
|
|
3036
3323
|
}
|
|
3324
|
+
function parseActivationOpenState(value) {
|
|
3325
|
+
if (!record(value)) throw new Error("\u6388\u6743\u9875\u6253\u5F00\u7ED3\u679C\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
3326
|
+
return { code: member(value.code, ACTIVATION_OPEN_CODES, "\u6388\u6743\u9875\u6253\u5F00\u7ED3\u679C"), opened: flag(value.opened, "\u6388\u6743\u9875\u662F\u5426\u5DF2\u6253\u5F00") };
|
|
3327
|
+
}
|
|
3037
3328
|
|
|
3038
3329
|
// src/index.ts
|
|
3039
3330
|
var name = "llm-opendesign-corouter";
|
|
@@ -3048,14 +3339,15 @@ function grantOf(value) {
|
|
|
3048
3339
|
if (typeof value.payload.cookie !== "string") return;
|
|
3049
3340
|
return { cookie: value.payload.cookie, ...typeof value.payload.expiresAt === "number" ? { expiresAt: value.payload.expiresAt } : {} };
|
|
3050
3341
|
}
|
|
3051
|
-
var _saveApiKey_dec, _cancelLogin_dec, _loginStatus_dec, _startLogin_dec, _refreshQuotaNow_dec, _refreshQuota_dec, _refreshModels_dec, _status_dec, _a, _init;
|
|
3052
|
-
var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status_dec = [Remote("status")], _refreshModels_dec = [Remote("refreshModels")], _refreshQuota_dec = [Remote("refreshQuota")], _refreshQuotaNow_dec = [Remote("refreshQuotaNow")], _startLogin_dec = [Remote("startLogin")], _loginStatus_dec = [Remote("loginStatus")], _cancelLogin_dec = [Remote("cancelLogin")], _saveApiKey_dec = [Remote("saveApiKey")], _a) {
|
|
3342
|
+
var _saveApiKey_dec, _openActivationPage_dec, _cancelLogin_dec, _loginStatus_dec, _startLogin_dec, _refreshQuotaNow_dec, _refreshQuota_dec, _refreshModels_dec, _status_dec, _a, _init;
|
|
3343
|
+
var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status_dec = [Remote("status")], _refreshModels_dec = [Remote("refreshModels")], _refreshQuota_dec = [Remote("refreshQuota")], _refreshQuotaNow_dec = [Remote("refreshQuotaNow")], _startLogin_dec = [Remote("startLogin")], _loginStatus_dec = [Remote("loginStatus")], _cancelLogin_dec = [Remote("cancelLogin")], _openActivationPage_dec = [Remote("openActivationPage")], _saveApiKey_dec = [Remote("saveApiKey")], _a) {
|
|
3053
3344
|
constructor(ctx, config, dependencies = { resolveSession, readQuota, readDesktopQuota }) {
|
|
3054
3345
|
super(ctx, "openDesignCoRouter");
|
|
3055
3346
|
this.dependencies = dependencies;
|
|
3056
3347
|
__runInitializers(_init, 5, this);
|
|
3057
3348
|
__publicField(this, "_config");
|
|
3058
3349
|
__publicField(this, "_login");
|
|
3350
|
+
__publicField(this, "_activation");
|
|
3059
3351
|
__publicField(this, "_lifetime", new AbortController());
|
|
3060
3352
|
__publicField(this, "_revision", randomUUID2());
|
|
3061
3353
|
__publicField(this, "_fingerprint", "");
|
|
@@ -3098,10 +3390,19 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
3098
3390
|
this._modelsFingerprint = "";
|
|
3099
3391
|
this._invalidateModels();
|
|
3100
3392
|
}
|
|
3101
|
-
},
|
|
3393
|
+
}, {
|
|
3394
|
+
...dependencies.loginOptions,
|
|
3395
|
+
// The Host owns the temporary window, so it closes it on every terminal transition —
|
|
3396
|
+
// including expiry, which happens on a timer even when no UI is polling.
|
|
3397
|
+
onSettled: () => {
|
|
3398
|
+
void this._activation.close();
|
|
3399
|
+
}
|
|
3400
|
+
});
|
|
3401
|
+
this._activation = new ActivationLauncher(dependencies.activationOptions);
|
|
3102
3402
|
ctx.effect(() => () => {
|
|
3103
3403
|
this._lifetime.abort();
|
|
3104
3404
|
this._login.dispose();
|
|
3405
|
+
return this._activation.dispose();
|
|
3105
3406
|
});
|
|
3106
3407
|
}
|
|
3107
3408
|
dependencies;
|
|
@@ -3353,6 +3654,12 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
3353
3654
|
cancelLogin(input) {
|
|
3354
3655
|
return parseLoginState(this._login.cancel(parseCancelLoginRequest(input)));
|
|
3355
3656
|
}
|
|
3657
|
+
async openActivationPage() {
|
|
3658
|
+
const url = this._login.activationLink();
|
|
3659
|
+
if (url === void 0) return { code: "no-link", opened: false };
|
|
3660
|
+
const result = await this._activation.open(url);
|
|
3661
|
+
return parseActivationOpenState({ code: result.code, opened: result.opened });
|
|
3662
|
+
}
|
|
3356
3663
|
async saveApiKey(input) {
|
|
3357
3664
|
const request = parseSaveApiKeyRequest(input), state = await this._state();
|
|
3358
3665
|
this._expect(request.expectedRevision);
|
|
@@ -3372,6 +3679,7 @@ __decorateElement(_init, 1, "refreshQuotaNow", _refreshQuotaNow_dec, OpenDesignC
|
|
|
3372
3679
|
__decorateElement(_init, 1, "startLogin", _startLogin_dec, OpenDesignCoRouterService);
|
|
3373
3680
|
__decorateElement(_init, 1, "loginStatus", _loginStatus_dec, OpenDesignCoRouterService);
|
|
3374
3681
|
__decorateElement(_init, 1, "cancelLogin", _cancelLogin_dec, OpenDesignCoRouterService);
|
|
3682
|
+
__decorateElement(_init, 1, "openActivationPage", _openActivationPage_dec, OpenDesignCoRouterService);
|
|
3375
3683
|
__decorateElement(_init, 1, "saveApiKey", _saveApiKey_dec, OpenDesignCoRouterService);
|
|
3376
3684
|
__decoratorMetadata(_init, OpenDesignCoRouterService);
|
|
3377
3685
|
function apply(ctx, config) {
|