dsh-opendesign-corouter 0.1.3 → 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.
- package/README.md +6 -6
- package/SECURITY.md +3 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/lib/adapter.d.ts +1 -1
- package/lib/auth.d.ts +18 -3
- package/lib/client.d.ts +8 -1
- package/lib/client.js +65 -14
- package/lib/device-auth.d.ts +34 -0
- package/lib/generated/typert.host.js +32 -1
- package/lib/generated/typert.remote-client.js +32 -1
- package/lib/index.d.ts +5 -1
- package/lib/index.js +506 -277
- package/lib/index.js.map +4 -4
- package/lib/login.d.ts +21 -133
- package/lib/quota.d.ts +6 -0
- package/lib/remote.d.ts +4 -0
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -1752,8 +1752,8 @@ var CoRouterAdapter = class extends LlmAdapter {
|
|
|
1752
1752
|
async *#stream(options, config, row) {
|
|
1753
1753
|
const signal = AbortSignal.any([combinedSignal(options.signal, config.requestTimeoutMs), this.#lifetime.signal]);
|
|
1754
1754
|
signal.throwIfAborted();
|
|
1755
|
-
const raw = await this.options.apiKey(config.apiKeyEnv);
|
|
1756
|
-
if (!raw) throw new LlmError10("\u672A\u914D\u7F6E
|
|
1755
|
+
const raw = await this.options.apiKey(config.apiKeyEnv, config.baseURL);
|
|
1756
|
+
if (!raw) throw new LlmError10("\u672A\u914D\u7F6E\u5F53\u524D\u7F51\u5173\u7684\u63A8\u7406\u51ED\u636E\uFF1B\u8BF7\u5B8C\u6210\u8BBE\u5907\u6388\u6743\u6216\u914D\u7F6E\u5BF9\u5E94 API Key", "MISSING_CREDENTIAL");
|
|
1757
1757
|
const key = assertUsableApiKey(raw, "dsh-opendesign-corouter", config.apiKeyEnv);
|
|
1758
1758
|
const setting = config.modelSettings?.[row.id] ?? {};
|
|
1759
1759
|
const protocolId = config.apiProtocol ?? "chat-completions";
|
|
@@ -2005,7 +2005,7 @@ function isOfficialApiUrl(raw) {
|
|
|
2005
2005
|
if (url.username || url.password) return false;
|
|
2006
2006
|
if (url.search || url.hash) return false;
|
|
2007
2007
|
if (url.pathname !== "/" && url.pathname !== "") return false;
|
|
2008
|
-
return url.hostname.toLowerCase() === API_HOST;
|
|
2008
|
+
return url.hostname.toLowerCase() === API_HOST && !url.port;
|
|
2009
2009
|
}
|
|
2010
2010
|
function normalizeApiUrl(raw) {
|
|
2011
2011
|
const value = typeof raw === "string" ? raw.trim() : "";
|
|
@@ -2080,6 +2080,24 @@ async function readDesktopCredential(deps = {}) {
|
|
|
2080
2080
|
}
|
|
2081
2081
|
return parseDesktopConfigText(text2, profile);
|
|
2082
2082
|
}
|
|
2083
|
+
function deviceGrant(value) {
|
|
2084
|
+
if (!record(value) || value.kind !== "device" || typeof value.controlKey !== "string" || typeof value.runtimeKey !== "string") return;
|
|
2085
|
+
try {
|
|
2086
|
+
const controlKey = safeSecret(value.controlKey), runtimeKey = safeSecret(value.runtimeKey);
|
|
2087
|
+
if ([controlKey, runtimeKey].some((key) => key.length > 8192 || /\s/u.test(key))) return;
|
|
2088
|
+
return { kind: "device", controlKey, runtimeKey, ...typeof value.savedAt === "string" ? { savedAt: value.savedAt } : {} };
|
|
2089
|
+
} catch {
|
|
2090
|
+
return void 0;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
function isOfficialRuntimeUrl(raw) {
|
|
2094
|
+
try {
|
|
2095
|
+
const url = new URL(raw);
|
|
2096
|
+
return url.origin === "https://amr-link.open-design.ai" && !url.username && !url.password && !url.search && !url.hash;
|
|
2097
|
+
} catch {
|
|
2098
|
+
return false;
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2083
2101
|
function cookieHeader(raw) {
|
|
2084
2102
|
const value = raw.trim();
|
|
2085
2103
|
if (!value || !value.includes("=")) return void 0;
|
|
@@ -2100,6 +2118,10 @@ function desktopSession(credential, apiUrl = credential.apiUrl ?? DEFAULT_API_UR
|
|
|
2100
2118
|
...credential.plan ? { plan: credential.plan } : {}
|
|
2101
2119
|
};
|
|
2102
2120
|
}
|
|
2121
|
+
function deviceSession(tokens) {
|
|
2122
|
+
const controlKey = typeof tokens === "string" ? tokens : tokens.controlKey;
|
|
2123
|
+
return { headers: { authorization: `Bearer ${safeSecret(controlKey)}` }, source: "login", via: "login", apiUrl: DEFAULT_API_URL, credentialType: "device" };
|
|
2124
|
+
}
|
|
2103
2125
|
function cookieSession(cookie, source, apiUrl = DEFAULT_API_URL) {
|
|
2104
2126
|
const header = cookieHeader(cookie);
|
|
2105
2127
|
if (!header) return void 0;
|
|
@@ -2140,10 +2162,15 @@ async function resolveSession(deps = {}) {
|
|
|
2140
2162
|
} catch {
|
|
2141
2163
|
grant = void 0;
|
|
2142
2164
|
}
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2165
|
+
if (grant && "kind" in grant && grant.kind === "device") {
|
|
2166
|
+
const device = deviceGrant(grant);
|
|
2167
|
+
if (device) return { state: "ready", session: deviceSession(device), warnings };
|
|
2168
|
+
} else if (grant && "cookie" in grant) {
|
|
2169
|
+
const expiry = typeof grant.expiresAt === "number" && Number.isFinite(grant.expiresAt) ? grant.expiresAt : 0;
|
|
2170
|
+
if (expiry === 0 || expiry > now()) {
|
|
2171
|
+
const session = cookieSession(grant.cookie, "login");
|
|
2172
|
+
if (session) return { state: "ready", session, warnings };
|
|
2173
|
+
}
|
|
2147
2174
|
}
|
|
2148
2175
|
}
|
|
2149
2176
|
if (deps.resolveRef) {
|
|
@@ -2164,61 +2191,207 @@ async function resolveSession(deps = {}) {
|
|
|
2164
2191
|
|
|
2165
2192
|
// src/login.ts
|
|
2166
2193
|
import { randomUUID } from "node:crypto";
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
var
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2194
|
+
|
|
2195
|
+
// src/device-auth.ts
|
|
2196
|
+
var DEVICE_AUTH_ENDPOINT = "https://amr-api.open-design.ai/api/v1/cli/device-authorizations";
|
|
2197
|
+
var DEVICE_AUTH_PLUGIN_VERSION = "0.1.4";
|
|
2198
|
+
var DEVICE_REQUEST_TIMEOUT_MS = 2e4;
|
|
2199
|
+
var DEVICE_RESPONSE_MAX_BYTES = 65536;
|
|
2200
|
+
var DEVICE_POLL_MIN_MS = 1e3;
|
|
2201
|
+
var DEVICE_POLL_MAX_MS = 3e5;
|
|
2202
|
+
var DeviceAuthError = class extends Error {
|
|
2203
|
+
constructor(code) {
|
|
2204
|
+
super("\u8BBE\u5907\u6388\u6743\u672A\u5B8C\u6210\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB");
|
|
2205
|
+
this.code = code;
|
|
2206
|
+
}
|
|
2207
|
+
code;
|
|
2208
|
+
};
|
|
2209
|
+
function validateActivationUrl(value, userCode) {
|
|
2210
|
+
if (typeof value !== "string" || value.length > 512 || !/^https:\/\/open-design\.ai\/cloud\/cli\/activate(?:\?[^#]*)?$/u.test(value) || /[\s\\]/u.test(value)) return null;
|
|
2211
|
+
try {
|
|
2212
|
+
const url = new URL(value);
|
|
2213
|
+
if (url.origin !== "https://open-design.ai" || url.pathname !== "/cloud/cli/activate" || url.username || url.password || url.hash) return null;
|
|
2214
|
+
const entries = [...url.searchParams];
|
|
2215
|
+
if (entries.length > 1) return null;
|
|
2216
|
+
if (entries.length) {
|
|
2217
|
+
const entry = entries[0];
|
|
2218
|
+
if (!["code", "user_code"].includes(entry[0]) || !validUserCode(entry[1]) || userCode !== void 0 && entry[1] !== userCode) return null;
|
|
2219
|
+
}
|
|
2220
|
+
return url.href;
|
|
2221
|
+
} catch {
|
|
2222
|
+
return null;
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
function object(value) {
|
|
2226
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2227
|
+
}
|
|
2228
|
+
function validUserCode(value) {
|
|
2229
|
+
return typeof value === "string" && /^[a-z0-9][a-z0-9-]{3,31}$/iu.test(value);
|
|
2230
|
+
}
|
|
2231
|
+
function secret(value) {
|
|
2232
|
+
return typeof value === "string" && value.length > 0 && value.length <= 8192 && /^[\x21-\x7e]+$/u.test(value);
|
|
2233
|
+
}
|
|
2234
|
+
function parseDeviceAuthorization(value) {
|
|
2235
|
+
if (!object(value) || typeof value.deviceId !== "string" || !/^[a-z0-9_-]{1,200}$/iu.test(value.deviceId) || !secret(value.deviceSecret) || !validUserCode(value.userCode)) throw new DeviceAuthError("opendesign/login-response");
|
|
2236
|
+
const activationUrl = validateActivationUrl(value.activationUrl, value.userCode);
|
|
2237
|
+
if (!activationUrl || value.userCode.toLowerCase().includes(value.deviceSecret.toLowerCase()) || decodeURIComponent(activationUrl).toLowerCase().includes(value.deviceSecret.toLowerCase())) throw new DeviceAuthError("opendesign/login-response");
|
|
2238
|
+
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
|
+
if (!Number.isFinite(expiresAt) || typeof value.pollIntervalSeconds !== "number" || !Number.isFinite(value.pollIntervalSeconds) || value.pollIntervalSeconds <= 0) throw new DeviceAuthError("opendesign/login-response");
|
|
2174
2240
|
return {
|
|
2175
|
-
|
|
2176
|
-
|
|
2241
|
+
deviceId: value.deviceId,
|
|
2242
|
+
deviceSecret: value.deviceSecret,
|
|
2243
|
+
userCode: value.userCode,
|
|
2244
|
+
activationUrl,
|
|
2245
|
+
expiresAt,
|
|
2246
|
+
pollMs: Math.max(DEVICE_POLL_MIN_MS, Math.min(DEVICE_POLL_MAX_MS, value.pollIntervalSeconds * 1e3))
|
|
2177
2247
|
};
|
|
2178
2248
|
}
|
|
2179
|
-
function
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2249
|
+
function parseDevicePoll(value) {
|
|
2250
|
+
if (!object(value)) throw new DeviceAuthError("opendesign/login-response");
|
|
2251
|
+
const state = typeof value.status === "string" ? value.status.toLowerCase() : "";
|
|
2252
|
+
if (["expired", "cancelled", "canceled", "denied", "rejected", "failed", "error"].includes(state)) throw new DeviceAuthError(state === "expired" ? "opendesign/login-expired" : "opendesign/login-denied");
|
|
2253
|
+
if (value.error !== void 0 && value.error !== null && value.error !== "") throw new DeviceAuthError("opendesign/login-response");
|
|
2254
|
+
const user = object(value.user) ? value.user : {};
|
|
2255
|
+
const topPresent = value.controlKey !== void 0 || value.runtimeKey !== void 0;
|
|
2256
|
+
const source = topPresent ? value : user;
|
|
2257
|
+
if (source.controlKey !== void 0 || source.runtimeKey !== void 0) {
|
|
2258
|
+
if (!secret(source.controlKey) || !secret(source.runtimeKey)) throw new DeviceAuthError("opendesign/login-incomplete");
|
|
2259
|
+
if (topPresent && (user.controlKey !== void 0 && user.controlKey !== source.controlKey || user.runtimeKey !== void 0 && user.runtimeKey !== source.runtimeKey)) throw new DeviceAuthError("opendesign/login-response");
|
|
2260
|
+
return { state: "success", tokens: { controlKey: source.controlKey, runtimeKey: source.runtimeKey } };
|
|
2261
|
+
}
|
|
2262
|
+
if (state === "pending" || state === "authorization_pending") return { state: "pending" };
|
|
2263
|
+
throw new DeviceAuthError("opendesign/login-response");
|
|
2264
|
+
}
|
|
2265
|
+
function validRequestUrl(value) {
|
|
2266
|
+
return value === DEVICE_AUTH_ENDPOINT || /^https:\/\/amr-api\.open-design\.ai\/api\/v1\/cli\/device-authorizations\/[a-zA-Z0-9_-]{1,200}\/token$/u.test(value);
|
|
2267
|
+
}
|
|
2268
|
+
async function postDeviceJson(fetcher, url, body, signal, timeoutMs = DEVICE_REQUEST_TIMEOUT_MS) {
|
|
2269
|
+
if (!validRequestUrl(url)) throw new DeviceAuthError("opendesign/login-url");
|
|
2270
|
+
signal.throwIfAborted();
|
|
2271
|
+
const controller = new AbortController();
|
|
2272
|
+
let timedOut = false;
|
|
2273
|
+
let timer;
|
|
2274
|
+
let rejectAbort = () => void 0;
|
|
2275
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
2276
|
+
rejectAbort = reject;
|
|
2277
|
+
});
|
|
2278
|
+
const cancel = () => {
|
|
2279
|
+
controller.abort();
|
|
2280
|
+
rejectAbort(new DeviceAuthError(timedOut ? "opendesign/login-request-timeout" : "opendesign/login-cancelled"));
|
|
2186
2281
|
};
|
|
2282
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
2283
|
+
timer = setTimeout(() => {
|
|
2284
|
+
timedOut = true;
|
|
2285
|
+
cancel();
|
|
2286
|
+
}, Math.max(1, Math.min(DEVICE_REQUEST_TIMEOUT_MS, timeoutMs)));
|
|
2287
|
+
const read = async () => {
|
|
2288
|
+
let response;
|
|
2289
|
+
try {
|
|
2290
|
+
response = await fetcher(url, {
|
|
2291
|
+
method: "POST",
|
|
2292
|
+
redirect: "error",
|
|
2293
|
+
credentials: "omit",
|
|
2294
|
+
referrerPolicy: "no-referrer",
|
|
2295
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
2296
|
+
body: JSON.stringify(body),
|
|
2297
|
+
signal: controller.signal
|
|
2298
|
+
});
|
|
2299
|
+
} catch {
|
|
2300
|
+
throw new DeviceAuthError("opendesign/login-network");
|
|
2301
|
+
}
|
|
2302
|
+
if (controller.signal.aborted) {
|
|
2303
|
+
void response.body?.cancel().catch(() => void 0);
|
|
2304
|
+
throw new DeviceAuthError("opendesign/login-cancelled");
|
|
2305
|
+
}
|
|
2306
|
+
const rejectResponse = (code) => {
|
|
2307
|
+
void response.body?.cancel().catch(() => void 0);
|
|
2308
|
+
throw new DeviceAuthError(code);
|
|
2309
|
+
};
|
|
2310
|
+
if (response.redirected || response.url && response.url !== url || response.status >= 300 && response.status < 400) return rejectResponse("opendesign/login-redirect");
|
|
2311
|
+
if (response.status === 401 || response.status === 403) return rejectResponse("opendesign/login-denied");
|
|
2312
|
+
if (response.status === 404 || response.status === 410) return rejectResponse("opendesign/login-expired");
|
|
2313
|
+
if (!response.ok) return rejectResponse("opendesign/login-network");
|
|
2314
|
+
const declared = response.headers.get("content-length");
|
|
2315
|
+
if (declared !== null && (!/^\d+$/u.test(declared) || Number(declared) > DEVICE_RESPONSE_MAX_BYTES)) return rejectResponse("opendesign/login-response-size");
|
|
2316
|
+
if (!response.body) throw new DeviceAuthError("opendesign/login-response");
|
|
2317
|
+
const reader = response.body.getReader();
|
|
2318
|
+
const stopReading = () => {
|
|
2319
|
+
void reader.cancel().catch(() => void 0);
|
|
2320
|
+
};
|
|
2321
|
+
controller.signal.addEventListener("abort", stopReading, { once: true });
|
|
2322
|
+
const chunks = [];
|
|
2323
|
+
let length = 0;
|
|
2324
|
+
try {
|
|
2325
|
+
for (; ; ) {
|
|
2326
|
+
const part = await reader.read();
|
|
2327
|
+
if (controller.signal.aborted) throw new DeviceAuthError("opendesign/login-cancelled");
|
|
2328
|
+
if (part.done) break;
|
|
2329
|
+
length += part.value.byteLength;
|
|
2330
|
+
if (length > DEVICE_RESPONSE_MAX_BYTES) {
|
|
2331
|
+
stopReading();
|
|
2332
|
+
throw new DeviceAuthError("opendesign/login-response-size");
|
|
2333
|
+
}
|
|
2334
|
+
chunks.push(part.value);
|
|
2335
|
+
}
|
|
2336
|
+
const bytes = new Uint8Array(length);
|
|
2337
|
+
let offset = 0;
|
|
2338
|
+
for (const chunk of chunks) {
|
|
2339
|
+
bytes.set(chunk, offset);
|
|
2340
|
+
offset += chunk.length;
|
|
2341
|
+
}
|
|
2342
|
+
try {
|
|
2343
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
2344
|
+
} catch {
|
|
2345
|
+
throw new DeviceAuthError("opendesign/login-response");
|
|
2346
|
+
}
|
|
2347
|
+
} finally {
|
|
2348
|
+
controller.signal.removeEventListener("abort", stopReading);
|
|
2349
|
+
reader.releaseLock();
|
|
2350
|
+
}
|
|
2351
|
+
};
|
|
2352
|
+
try {
|
|
2353
|
+
return await Promise.race([read(), aborted]);
|
|
2354
|
+
} catch (error) {
|
|
2355
|
+
if (error instanceof DeviceAuthError) throw error;
|
|
2356
|
+
throw new DeviceAuthError("opendesign/login-network");
|
|
2357
|
+
} finally {
|
|
2358
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2359
|
+
signal.removeEventListener("abort", cancel);
|
|
2360
|
+
}
|
|
2187
2361
|
}
|
|
2362
|
+
|
|
2363
|
+
// src/login.ts
|
|
2188
2364
|
var LoginError = class extends Error {
|
|
2189
2365
|
constructor(code) {
|
|
2190
|
-
super(code === "opendesign/login-
|
|
2366
|
+
super(code === "opendesign/login-disposed" ? "\u767B\u5F55\u670D\u52A1\u5DF2\u5378\u8F7D" : "\u767B\u5F55\u4EFB\u52A1\u4E0D\u5B58\u5728\u6216\u72B6\u6001\u5DF2\u6539\u53D8");
|
|
2191
2367
|
this.code = code;
|
|
2192
2368
|
}
|
|
2193
2369
|
code;
|
|
2194
2370
|
};
|
|
2195
2371
|
var ACTIVE = /* @__PURE__ */ new Set(["launching", "waiting", "verifying"]);
|
|
2196
|
-
var
|
|
2197
|
-
var
|
|
2198
|
-
var FATAL_COMMIT_CODES = /* @__PURE__ */ new Set(["opendesign/revision-conflict", "opendesign/invalid-request", "opendesign/config", "opendesign/login-disposed"]);
|
|
2199
|
-
var CHROME_LAUNCH_OPTIONS = {
|
|
2200
|
-
channel: "chrome",
|
|
2201
|
-
headless: false,
|
|
2202
|
-
ignoreDefaultArgs: ["--enable-automation"],
|
|
2203
|
-
args: ["--disable-blink-features=AutomationControlled"]
|
|
2204
|
-
};
|
|
2372
|
+
var MAX_TIMEOUT_MS = 3e5;
|
|
2373
|
+
var SAFE_HOST_CODES = /* @__PURE__ */ new Set(["opendesign/revision-conflict", "opendesign/invalid-request", "opendesign/config", "opendesign/login-disposed"]);
|
|
2205
2374
|
var ConsoleLogin = class {
|
|
2206
|
-
job = null;
|
|
2207
|
-
disposed = false;
|
|
2208
|
-
callbacks;
|
|
2209
|
-
options;
|
|
2210
|
-
now;
|
|
2211
|
-
delay;
|
|
2212
2375
|
constructor(callbacks, options = {}) {
|
|
2213
2376
|
this.callbacks = callbacks;
|
|
2214
2377
|
this.options = options;
|
|
2215
2378
|
this.now = options.now ?? Date.now;
|
|
2216
2379
|
this.delay = options.delay ?? sleep;
|
|
2380
|
+
this.fetcher = options.fetcher ?? fetch;
|
|
2217
2381
|
}
|
|
2218
|
-
|
|
2382
|
+
callbacks;
|
|
2383
|
+
options;
|
|
2384
|
+
job = null;
|
|
2385
|
+
disposed = false;
|
|
2386
|
+
now;
|
|
2387
|
+
delay;
|
|
2388
|
+
fetcher;
|
|
2389
|
+
/** Explicit public allowlist. Codes are never available after waiting or expiry. */
|
|
2219
2390
|
status() {
|
|
2220
2391
|
if (!this.job) return { jobId: null, state: "idle" };
|
|
2221
|
-
const
|
|
2392
|
+
const job = this.job;
|
|
2393
|
+
if (job.status.state === "waiting" && this.now() >= job.deadline) this.expire(job);
|
|
2394
|
+
const s = job.status;
|
|
2222
2395
|
return {
|
|
2223
2396
|
jobId: s.jobId,
|
|
2224
2397
|
state: s.state,
|
|
@@ -2226,240 +2399,177 @@ var ConsoleLogin = class {
|
|
|
2226
2399
|
...s.finishedAt ? { finishedAt: s.finishedAt } : {},
|
|
2227
2400
|
...s.message ? { message: s.message } : {},
|
|
2228
2401
|
...s.errorCode ? { errorCode: s.errorCode } : {},
|
|
2229
|
-
...s.
|
|
2402
|
+
...s.state === "waiting" && this.now() < job.deadline ? {
|
|
2403
|
+
...s.activationUrl ? { activationUrl: s.activationUrl } : {},
|
|
2404
|
+
...s.userCode ? { userCode: s.userCode } : {},
|
|
2405
|
+
...s.expiresAt ? { expiresAt: s.expiresAt } : {}
|
|
2406
|
+
} : {}
|
|
2230
2407
|
};
|
|
2231
2408
|
}
|
|
2232
|
-
/**
|
|
2233
|
-
* Queue one attempt and return immediately: the RPC that starts a login must not wait on a
|
|
2234
|
-
* browser window or on a human. Re-entrant calls while an attempt is live return that
|
|
2235
|
-
* attempt's status instead of opening a second browser.
|
|
2236
|
-
*/
|
|
2409
|
+
/** Synchronous/non-blocking and idempotent while capture/network/commit is pending. */
|
|
2237
2410
|
start(request) {
|
|
2238
2411
|
if (this.disposed) throw new LoginError("opendesign/login-disposed");
|
|
2412
|
+
this.status();
|
|
2239
2413
|
if (this.job && ACTIVE.has(this.job.status.state)) return this.status();
|
|
2414
|
+
const started = this.now();
|
|
2415
|
+
const requested = this.options.timeoutMs;
|
|
2416
|
+
const timeoutMs = typeof requested === "number" && Number.isFinite(requested) && requested > 0 ? Math.min(MAX_TIMEOUT_MS, requested) : MAX_TIMEOUT_MS;
|
|
2240
2417
|
const job = {
|
|
2241
2418
|
id: randomUUID(),
|
|
2242
|
-
startedAt: new Date(this.now()).toISOString(),
|
|
2243
2419
|
controller: new AbortController(),
|
|
2244
|
-
|
|
2245
|
-
status: { jobId:
|
|
2420
|
+
deadline: started + timeoutMs,
|
|
2421
|
+
status: { jobId: null, state: "launching", startedAt: new Date(started).toISOString(), message: "\u6B63\u5728\u7533\u8BF7\u5B98\u65B9\u8BBE\u5907\u6388\u6743\u7801" },
|
|
2246
2422
|
captured: void 0,
|
|
2247
|
-
capturedAt: void 0,
|
|
2248
|
-
verifying: false,
|
|
2249
2423
|
committing: false,
|
|
2250
2424
|
cancelRequested: false
|
|
2251
2425
|
};
|
|
2252
2426
|
job.status.jobId = job.id;
|
|
2253
|
-
job.status.startedAt = job.startedAt;
|
|
2254
2427
|
this.job = job;
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2428
|
+
this.armExpiry(job);
|
|
2429
|
+
void Promise.resolve().then(async () => {
|
|
2430
|
+
if (!this.alive(job)) return;
|
|
2431
|
+
try {
|
|
2432
|
+
job.captured = await this.callbacks.capture(request.expectedRevision);
|
|
2433
|
+
} catch (error) {
|
|
2434
|
+
if (this.alive(job)) this.finish(job, "error", "\u65E0\u6CD5\u6355\u83B7\u5F53\u524D\u8BBE\u7F6E\u72B6\u6001\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u91CD\u8BD5", hostCode(error) ?? "opendesign/login-capture");
|
|
2435
|
+
return;
|
|
2436
|
+
}
|
|
2437
|
+
if (this.alive(job)) await this.run(job);
|
|
2438
|
+
}).catch((error) => {
|
|
2439
|
+
if (this.alive(job)) this.finish(job, "error", failureMessage(error), error instanceof DeviceAuthError ? error.code : "opendesign/login-network");
|
|
2263
2440
|
});
|
|
2264
2441
|
return this.status();
|
|
2265
2442
|
}
|
|
2266
|
-
/**
|
|
2267
|
-
* Abandon the attempt. While `commit` is in flight this only aborts the signal and records
|
|
2268
|
-
* the request: the Host's atomic store decides, because the commit is the linearization
|
|
2269
|
-
* point. A cancel that wins that race reports `cancelled`; one that loses reports `success`
|
|
2270
|
-
* and the cookie really is stored.
|
|
2271
|
-
*/
|
|
2272
2443
|
cancel(request) {
|
|
2273
|
-
|
|
2274
|
-
if (!job || job.status.jobId !== request.jobId) return this.status();
|
|
2275
|
-
this.requestCancellation(job, "\u767B\u5F55\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58 Cookie");
|
|
2444
|
+
if (this.job?.id === request.jobId) this.requestCancellation(this.job, "\u767B\u5F55\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58\u6388\u6743");
|
|
2276
2445
|
return this.status();
|
|
2277
2446
|
}
|
|
2278
|
-
/** Plugin teardown: abort the attempt and close the browser. */
|
|
2279
2447
|
dispose() {
|
|
2280
|
-
const job = this.job;
|
|
2281
|
-
if (job) this.requestCancellation(job, "\u670D\u52A1\u5DF2\u5378\u8F7D\uFF0C\u767B\u5F55\u5DF2\u53D6\u6D88");
|
|
2282
2448
|
this.disposed = true;
|
|
2449
|
+
if (this.job) this.requestCancellation(this.job, "\u670D\u52A1\u5DF2\u5378\u8F7D\uFF0C\u767B\u5F55\u5DF2\u53D6\u6D88");
|
|
2450
|
+
}
|
|
2451
|
+
alive(job) {
|
|
2452
|
+
return this.job === job && !this.disposed && !job.controller.signal.aborted && ACTIVE.has(job.status.state);
|
|
2453
|
+
}
|
|
2454
|
+
stopTimer(job) {
|
|
2455
|
+
if (job.timer !== void 0) {
|
|
2456
|
+
clearTimeout(job.timer);
|
|
2457
|
+
job.timer = void 0;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
armExpiry(job) {
|
|
2461
|
+
this.stopTimer(job);
|
|
2462
|
+
job.timer = setTimeout(() => this.expire(job), Math.max(1, job.deadline - this.now()));
|
|
2463
|
+
job.timer.unref?.();
|
|
2464
|
+
}
|
|
2465
|
+
clearCode(job) {
|
|
2466
|
+
delete job.status.activationUrl;
|
|
2467
|
+
delete job.status.userCode;
|
|
2468
|
+
delete job.status.expiresAt;
|
|
2469
|
+
}
|
|
2470
|
+
expire(job) {
|
|
2471
|
+
if (job.committing) this.requestCancellation(job, "\u6388\u6743\u5DF2\u8FC7\u671F\uFF0C\u672A\u4FDD\u5B58\u6388\u6743");
|
|
2472
|
+
else this.finish(job, "error", "\u8BBE\u5907\u6388\u6743\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB", "opendesign/login-expired");
|
|
2283
2473
|
}
|
|
2284
|
-
/** Every cancellation source must let an in-flight atomic store decide the final outcome. */
|
|
2285
2474
|
requestCancellation(job, message) {
|
|
2286
2475
|
if (this.job !== job || !ACTIVE.has(job.status.state)) return;
|
|
2476
|
+
this.clearCode(job);
|
|
2287
2477
|
if (!job.committing) {
|
|
2288
2478
|
this.finish(job, "cancelled", message);
|
|
2289
2479
|
return;
|
|
2290
2480
|
}
|
|
2291
2481
|
if (!job.cancelRequested) {
|
|
2292
2482
|
job.cancellationMessage = message;
|
|
2293
|
-
job.status.message = "\u6B63\u5728\u786E\u8BA4\
|
|
2483
|
+
job.status.message = "\u6B63\u5728\u786E\u8BA4\u6388\u6743\u4FDD\u5B58\u7ED3\u679C";
|
|
2294
2484
|
}
|
|
2295
2485
|
job.cancelRequested = true;
|
|
2296
2486
|
this.stopTimer(job);
|
|
2297
2487
|
job.controller.abort();
|
|
2298
|
-
void this.close(job);
|
|
2299
|
-
}
|
|
2300
|
-
alive(job) {
|
|
2301
|
-
if (this.disposed || this.job !== job) return false;
|
|
2302
|
-
if (job.controller.signal.aborted && !job.committing) return false;
|
|
2303
|
-
return job.status.state !== "success" && job.status.state !== "cancelled" && job.status.state !== "error";
|
|
2304
2488
|
}
|
|
2305
|
-
|
|
2306
|
-
if (job.
|
|
2307
|
-
clearTimeout(job.timer);
|
|
2308
|
-
job.timer = void 0;
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
async close(job) {
|
|
2312
|
-
const browser = job.browser;
|
|
2313
|
-
job.browser = void 0;
|
|
2314
|
-
try {
|
|
2315
|
-
await browser?.close();
|
|
2316
|
-
} catch {
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
finish(job, state, message, errorCode2) {
|
|
2320
|
-
if (this.job !== job) return;
|
|
2321
|
-
if (job.status.state === "success" || job.status.state === "cancelled" || job.status.state === "error") return;
|
|
2489
|
+
finish(job, state, message, errorCode) {
|
|
2490
|
+
if (this.job !== job || !ACTIVE.has(job.status.state)) return;
|
|
2322
2491
|
job.status.state = state;
|
|
2323
2492
|
job.status.finishedAt = new Date(this.now()).toISOString();
|
|
2324
2493
|
job.status.message = message;
|
|
2325
|
-
if (
|
|
2326
|
-
job
|
|
2494
|
+
if (errorCode) job.status.errorCode = errorCode;
|
|
2495
|
+
this.clearCode(job);
|
|
2327
2496
|
this.stopTimer(job);
|
|
2328
2497
|
job.controller.abort();
|
|
2329
|
-
void this.close(job);
|
|
2330
|
-
}
|
|
2331
|
-
async resolveBrowser(job) {
|
|
2332
|
-
if (!this.alive(job)) return null;
|
|
2333
|
-
const launch = this.options.launch ?? defaultLaunch;
|
|
2334
|
-
const browser = await launch(job.controller.signal);
|
|
2335
|
-
if (!browser) return null;
|
|
2336
|
-
job.browser = browser;
|
|
2337
|
-
browser.on?.("disconnected", () => this.requestCancellation(job, "\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u672A\u4FDD\u5B58 Cookie"));
|
|
2338
|
-
return browser;
|
|
2339
2498
|
}
|
|
2340
2499
|
async run(job) {
|
|
2341
|
-
const
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
if (!this.alive(job))
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
page.on?.("close", () => this.requestCancellation(job, "\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u672A\u4FDD\u5B58 Cookie"));
|
|
2355
|
-
if (!this.alive(job)) {
|
|
2356
|
-
await this.close(job);
|
|
2500
|
+
const signal = job.controller.signal;
|
|
2501
|
+
const raw = await postDeviceJson(this.fetcher, DEVICE_AUTH_ENDPOINT, {
|
|
2502
|
+
// The third-party protocol's official-CLI version is NOT our version. Compatibility
|
|
2503
|
+
// of this actual plugin version requires a real-account check outside offline tests.
|
|
2504
|
+
cliVersion: DEVICE_AUTH_PLUGIN_VERSION,
|
|
2505
|
+
host: "DSH CoRouter",
|
|
2506
|
+
profile: "prod"
|
|
2507
|
+
}, signal, job.deadline - this.now());
|
|
2508
|
+
if (!this.alive(job)) return;
|
|
2509
|
+
const device = parseDeviceAuthorization(raw);
|
|
2510
|
+
job.deadline = Math.min(job.deadline, device.expiresAt);
|
|
2511
|
+
if (this.now() >= job.deadline) {
|
|
2512
|
+
this.expire(job);
|
|
2357
2513
|
return;
|
|
2358
2514
|
}
|
|
2359
|
-
|
|
2360
|
-
await page.goto(site.loginUrl, { waitUntil: "domcontentloaded", timeout: 3e4 });
|
|
2361
|
-
} catch {
|
|
2362
|
-
if (!this.alive(job)) return;
|
|
2363
|
-
try {
|
|
2364
|
-
await page.goto(site.origin, { waitUntil: "domcontentloaded", timeout: 3e4 });
|
|
2365
|
-
} catch {
|
|
2366
|
-
job.status.browserOpenFailed = true;
|
|
2367
|
-
}
|
|
2368
|
-
}
|
|
2369
|
-
if (!this.alive(job)) return;
|
|
2515
|
+
this.armExpiry(job);
|
|
2370
2516
|
job.status.state = "waiting";
|
|
2371
|
-
job.status.message = "\u8BF7\u5728\
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2517
|
+
job.status.message = "\u8BF7\u5728\u5E38\u7528\u6D4F\u89C8\u5668\u6253\u5F00\u5B98\u65B9\u6388\u6743\u9875\uFF0C\u4F7F\u7528\u5DF2\u4FDD\u5B58\u7684\u5BC6\u7801\u767B\u5F55\u5E76\u786E\u8BA4\u8BBE\u5907\u7801";
|
|
2518
|
+
job.status.activationUrl = device.activationUrl;
|
|
2519
|
+
job.status.userCode = device.userCode;
|
|
2520
|
+
job.status.expiresAt = new Date(job.deadline).toISOString();
|
|
2375
2521
|
while (this.alive(job)) {
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
if (!this.alive(job)) return;
|
|
2398
|
-
failures += 1;
|
|
2399
|
-
nextAttempt = this.now() + Math.max(5e3, Math.min(6e4, 2e3 * 2 ** Math.min(failures, 5)), retryAfterMs(error));
|
|
2400
|
-
job.status.state = "waiting";
|
|
2401
|
-
job.status.message = errorCode(error) === "opendesign/auth" ? "\u767B\u5F55\u5C1A\u672A\u751F\u6548\uFF0C\u8BF7\u7EE7\u7EED\u5728\u5B98\u65B9\u7A97\u53E3\u5B8C\u6210\u767B\u5F55\uFF1B\u672A\u4FDD\u5B58" : "\u989D\u5EA6\u9A8C\u8BC1\u6682\u672A\u6210\u529F\uFF0C\u5C06\u9650\u9891\u91CD\u8BD5\uFF1B\u672A\u4FDD\u5B58";
|
|
2402
|
-
} finally {
|
|
2403
|
-
job.verifying = false;
|
|
2404
|
-
}
|
|
2405
|
-
}
|
|
2522
|
+
await this.delay(Math.min(device.pollMs, Math.max(1, job.deadline - this.now())), signal);
|
|
2523
|
+
if (!this.alive(job)) return;
|
|
2524
|
+
if (this.now() >= job.deadline) {
|
|
2525
|
+
this.expire(job);
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
const result = parseDevicePoll(await postDeviceJson(
|
|
2529
|
+
this.fetcher,
|
|
2530
|
+
`${DEVICE_AUTH_ENDPOINT}/${device.deviceId}/token`,
|
|
2531
|
+
{ deviceSecret: device.deviceSecret },
|
|
2532
|
+
signal,
|
|
2533
|
+
job.deadline - this.now()
|
|
2534
|
+
));
|
|
2535
|
+
if (!this.alive(job)) return;
|
|
2536
|
+
if (this.now() >= job.deadline) {
|
|
2537
|
+
this.expire(job);
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
if (result.state === "success") {
|
|
2541
|
+
await this.commit(job, result.tokens);
|
|
2542
|
+
return;
|
|
2406
2543
|
}
|
|
2407
|
-
await this.delay(pollMs, job.controller.signal);
|
|
2408
2544
|
}
|
|
2409
2545
|
}
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
* `retry` when the store rejected the write transiently, so the caller decides when to try
|
|
2413
|
-
* again instead of claiming a success that did not happen.
|
|
2414
|
-
*/
|
|
2415
|
-
async commit(job, session) {
|
|
2416
|
-
if (job.committing || !this.alive(job)) return "cancelled";
|
|
2546
|
+
async commit(job, tokens) {
|
|
2547
|
+
if (!this.alive(job) || job.committing) return;
|
|
2417
2548
|
job.committing = true;
|
|
2549
|
+
this.clearCode(job);
|
|
2418
2550
|
job.status.state = "verifying";
|
|
2419
|
-
job.status.message = "\
|
|
2551
|
+
job.status.message = "\u5B98\u65B9\u6388\u6743\u5DF2\u5B8C\u6210\uFF0C\u6B63\u5728\u4FDD\u5B58\u6388\u6743";
|
|
2420
2552
|
try {
|
|
2421
|
-
await this.callbacks.commit(
|
|
2553
|
+
await this.callbacks.commit(tokens, job.captured, job.controller.signal);
|
|
2422
2554
|
job.committing = false;
|
|
2423
|
-
this.finish(job, "success", "\
|
|
2424
|
-
return "success";
|
|
2555
|
+
this.finish(job, "success", "\u8BBE\u5907\u6388\u6743\u5DF2\u5B8C\u6210\u5E76\u4FDD\u5B58\uFF1B\u989D\u5EA6\u72B6\u6001\u53E6\u884C\u663E\u793A");
|
|
2425
2556
|
} catch (error) {
|
|
2426
2557
|
job.committing = false;
|
|
2427
|
-
if (job.cancelRequested || job.controller.signal.aborted)
|
|
2428
|
-
|
|
2429
|
-
return "cancelled";
|
|
2430
|
-
}
|
|
2431
|
-
if (FATAL_COMMIT_CODES.has(errorCode(error) ?? "")) {
|
|
2432
|
-
this.finish(job, "error", "\u8BBE\u7F6E\u5DF2\u6539\u53D8\u6216\u65E0\u6CD5\u4FDD\u5B58\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u91CD\u8BD5", errorCode(error));
|
|
2433
|
-
return "error";
|
|
2434
|
-
}
|
|
2435
|
-
job.status.state = "waiting";
|
|
2436
|
-
job.status.message = "\u767B\u5F55\u5DF2\u9A8C\u8BC1\uFF0C\u4F46\u4FDD\u5B58\u6682\u672A\u6210\u529F\uFF0C\u5C06\u9650\u9891\u91CD\u8BD5\uFF1B\u5C1A\u672A\u4FDD\u5B58";
|
|
2437
|
-
return "retry";
|
|
2558
|
+
if (job.cancelRequested || job.controller.signal.aborted) this.finish(job, "cancelled", job.cancellationMessage ?? "\u767B\u5F55\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58\u6388\u6743");
|
|
2559
|
+
else this.finish(job, "error", hostCode(error) ? "\u8BBE\u7F6E\u5DF2\u6539\u53D8\u6216\u65E0\u6CD5\u4FDD\u5B58\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u91CD\u8BD5" : "\u6388\u6743\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB", hostCode(error) ?? "opendesign/login-commit");
|
|
2438
2560
|
}
|
|
2439
2561
|
}
|
|
2440
2562
|
};
|
|
2441
|
-
function
|
|
2442
|
-
if (error && typeof error === "object" && "code" in error && typeof error.code === "string") return error.code;
|
|
2563
|
+
function hostCode(error) {
|
|
2564
|
+
if (error !== null && typeof error === "object" && "code" in error && typeof error.code === "string" && SAFE_HOST_CODES.has(error.code)) return error.code;
|
|
2443
2565
|
return void 0;
|
|
2444
2566
|
}
|
|
2445
|
-
function
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
if (
|
|
2449
|
-
|
|
2450
|
-
return
|
|
2451
|
-
}
|
|
2452
|
-
async function defaultLaunch(signal) {
|
|
2453
|
-
if (signal.aborted) return null;
|
|
2454
|
-
const { chromium } = await import("playwright-core");
|
|
2455
|
-
if (signal.aborted) return null;
|
|
2456
|
-
return await chromium.launch({
|
|
2457
|
-
channel: CHROME_LAUNCH_OPTIONS.channel,
|
|
2458
|
-
headless: CHROME_LAUNCH_OPTIONS.headless,
|
|
2459
|
-
// Copied so Playwright, which wants mutable arrays, cannot freeze on the readonly literals.
|
|
2460
|
-
ignoreDefaultArgs: [...CHROME_LAUNCH_OPTIONS.ignoreDefaultArgs],
|
|
2461
|
-
args: [...CHROME_LAUNCH_OPTIONS.args]
|
|
2462
|
-
});
|
|
2567
|
+
function failureMessage(error) {
|
|
2568
|
+
const code = error instanceof DeviceAuthError ? error.code : "";
|
|
2569
|
+
if (code === "opendesign/login-expired") return "\u8BBE\u5907\u6388\u6743\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB";
|
|
2570
|
+
if (code === "opendesign/login-denied") return "\u8BBE\u5907\u6388\u6743\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB";
|
|
2571
|
+
if (code === "opendesign/login-request-timeout") return "\u8BBE\u5907\u6388\u6743\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB";
|
|
2572
|
+
return "\u8BBE\u5907\u6388\u6743\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB";
|
|
2463
2573
|
}
|
|
2464
2574
|
|
|
2465
2575
|
// src/quota.ts
|
|
@@ -2468,6 +2578,7 @@ var API_BASE = "https://amr-api.open-design.ai/api/v1";
|
|
|
2468
2578
|
var CURRENT_URL = `${API_BASE}/workspaces/current`;
|
|
2469
2579
|
var SUMMARY_URL = `${API_BASE}/billing/summary`;
|
|
2470
2580
|
var usageUrl = (workspace) => `${API_BASE}/workspaces/${encodeURIComponent(workspace)}/billing/coding-plan-usage`;
|
|
2581
|
+
var preflightUrl = (workspace) => `${API_BASE}/workspaces/${encodeURIComponent(workspace)}/billing/preflight`;
|
|
2471
2582
|
var ConsoleError = class extends Error {
|
|
2472
2583
|
constructor(kind, status) {
|
|
2473
2584
|
super(kind === "auth" ? "OpenDesign \u767B\u5F55\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55" : kind === "permission" ? "OpenDesign \u5F53\u524D\u767B\u5F55\u6001\u65E0\u6743\u8BFB\u53D6\u8BE5\u989D\u5EA6\u5C42" : kind === "rate-limit" ? "OpenDesign \u989D\u5EA6\u63A5\u53E3\u9650\u6D41\uFF0C\u8BF7\u7A0D\u540E\u5237\u65B0" : kind === "response" ? "OpenDesign \u989D\u5EA6\u63A5\u53E3\u8FD4\u56DE\u683C\u5F0F\u53D1\u751F\u53D8\u5316" : "OpenDesign \u989D\u5EA6\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6");
|
|
@@ -2478,46 +2589,90 @@ var ConsoleError = class extends Error {
|
|
|
2478
2589
|
status;
|
|
2479
2590
|
};
|
|
2480
2591
|
async function consoleJson(url, session, signal, fetcher = fetch) {
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
await response.body?.cancel();
|
|
2489
|
-
throw new ConsoleError(response.status === 401 ? "auth" : "permission", response.status);
|
|
2490
|
-
}
|
|
2491
|
-
if (!response.ok) {
|
|
2492
|
-
await response.body?.cancel();
|
|
2493
|
-
throw new ConsoleError(response.status === 429 ? "rate-limit" : "network", response.status);
|
|
2494
|
-
}
|
|
2495
|
-
const reader = response.body?.getReader();
|
|
2496
|
-
if (!reader) throw new ConsoleError("response");
|
|
2497
|
-
let size = 0, raw = "";
|
|
2498
|
-
const decoder = new TextDecoder();
|
|
2499
|
-
try {
|
|
2500
|
-
while (true) {
|
|
2501
|
-
const chunk = await reader.read();
|
|
2502
|
-
if (chunk.done) break;
|
|
2503
|
-
size += chunk.value.byteLength;
|
|
2504
|
-
if (size > 2 * 1024 * 1024) throw new ConsoleError("response");
|
|
2505
|
-
raw += decoder.decode(chunk.value, { stream: true });
|
|
2592
|
+
signal?.throwIfAborted();
|
|
2593
|
+
if (session.credentialType === "device") {
|
|
2594
|
+
let target;
|
|
2595
|
+
try {
|
|
2596
|
+
target = new URL(url);
|
|
2597
|
+
} catch {
|
|
2598
|
+
throw new ConsoleError("response");
|
|
2506
2599
|
}
|
|
2507
|
-
|
|
2508
|
-
} finally {
|
|
2509
|
-
await reader.cancel().catch(() => void 0);
|
|
2510
|
-
reader.releaseLock();
|
|
2600
|
+
if (target.origin !== "https://amr-api.open-design.ai" || target.username || target.password) throw new ConsoleError("response");
|
|
2511
2601
|
}
|
|
2512
|
-
|
|
2602
|
+
const requestSignal = combinedSignal(signal, 2e4);
|
|
2603
|
+
let rejectAbort = () => void 0;
|
|
2604
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
2605
|
+
rejectAbort = reject;
|
|
2606
|
+
});
|
|
2607
|
+
const onAbort = () => rejectAbort(new ConsoleError("network"));
|
|
2608
|
+
requestSignal.addEventListener("abort", onAbort, { once: true });
|
|
2609
|
+
const read = async () => {
|
|
2610
|
+
const response = await fetcher(url, { headers: { ...attributionHeaders3(), accept: "application/json", ...session.headers }, signal: requestSignal, redirect: "error", credentials: "omit" });
|
|
2611
|
+
const cancelBody = () => {
|
|
2612
|
+
void response.body?.cancel().catch(() => void 0);
|
|
2613
|
+
};
|
|
2614
|
+
if (requestSignal.aborted) {
|
|
2615
|
+
cancelBody();
|
|
2616
|
+
throw new ConsoleError("network");
|
|
2617
|
+
}
|
|
2618
|
+
if (response.redirected || response.url && response.url !== url) {
|
|
2619
|
+
cancelBody();
|
|
2620
|
+
throw new ConsoleError("response");
|
|
2621
|
+
}
|
|
2622
|
+
if (response.status === 401 || response.status === 403) {
|
|
2623
|
+
cancelBody();
|
|
2624
|
+
throw new ConsoleError(response.status === 401 ? "auth" : "permission", response.status);
|
|
2625
|
+
}
|
|
2626
|
+
if (!response.ok) {
|
|
2627
|
+
cancelBody();
|
|
2628
|
+
throw new ConsoleError(response.status === 429 ? "rate-limit" : "network", response.status);
|
|
2629
|
+
}
|
|
2630
|
+
const declared = response.headers.get("content-length");
|
|
2631
|
+
if (declared !== null && (!/^\d+$/u.test(declared) || Number(declared) > 2 * 1024 * 1024)) {
|
|
2632
|
+
cancelBody();
|
|
2633
|
+
throw new ConsoleError("response");
|
|
2634
|
+
}
|
|
2635
|
+
const reader = response.body?.getReader();
|
|
2636
|
+
if (!reader) throw new ConsoleError("response");
|
|
2637
|
+
const cancelReader = () => {
|
|
2638
|
+
void reader.cancel().catch(() => void 0);
|
|
2639
|
+
};
|
|
2640
|
+
requestSignal.addEventListener("abort", cancelReader, { once: true });
|
|
2641
|
+
let size = 0, raw = "";
|
|
2642
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
2643
|
+
try {
|
|
2644
|
+
while (true) {
|
|
2645
|
+
const chunk = await reader.read();
|
|
2646
|
+
if (requestSignal.aborted) throw new ConsoleError("network");
|
|
2647
|
+
if (chunk.done) break;
|
|
2648
|
+
size += chunk.value.byteLength;
|
|
2649
|
+
if (size > 2 * 1024 * 1024) throw new ConsoleError("response");
|
|
2650
|
+
raw += decoder.decode(chunk.value, { stream: true });
|
|
2651
|
+
}
|
|
2652
|
+
raw += decoder.decode();
|
|
2653
|
+
} finally {
|
|
2654
|
+
requestSignal.removeEventListener("abort", cancelReader);
|
|
2655
|
+
cancelReader();
|
|
2656
|
+
reader.releaseLock();
|
|
2657
|
+
}
|
|
2658
|
+
let body;
|
|
2659
|
+
try {
|
|
2660
|
+
body = JSON.parse(raw);
|
|
2661
|
+
} catch {
|
|
2662
|
+
throw new ConsoleError("response");
|
|
2663
|
+
}
|
|
2664
|
+
if (record(body) && (body.code === 401 || body.code === "401")) throw new ConsoleError("auth", 401);
|
|
2665
|
+
if (record(body) && (body.code === 403 || body.code === "403")) throw new ConsoleError("permission", 403);
|
|
2666
|
+
return body;
|
|
2667
|
+
};
|
|
2513
2668
|
try {
|
|
2514
|
-
|
|
2515
|
-
} catch {
|
|
2516
|
-
|
|
2669
|
+
return await Promise.race([read(), aborted]);
|
|
2670
|
+
} catch (error) {
|
|
2671
|
+
signal?.throwIfAborted();
|
|
2672
|
+
throw error instanceof ConsoleError ? error : new ConsoleError("network");
|
|
2673
|
+
} finally {
|
|
2674
|
+
requestSignal.removeEventListener("abort", onAbort);
|
|
2517
2675
|
}
|
|
2518
|
-
if (record(body) && (body.code === 401 || body.code === "401")) throw new ConsoleError("auth", 401);
|
|
2519
|
-
if (record(body) && (body.code === 403 || body.code === "403")) throw new ConsoleError("permission", 403);
|
|
2520
|
-
return body;
|
|
2521
2676
|
}
|
|
2522
2677
|
function number(value, negative = false) {
|
|
2523
2678
|
const n = typeof value === "number" ? value : typeof value === "string" && /^-?\d+(\.\d+)?$/u.test(value.trim()) ? Number(value) : NaN;
|
|
@@ -2555,7 +2710,51 @@ function normalizeBalances(summary) {
|
|
|
2555
2710
|
const totalUsd = number(summary.balanceUsd, true) ?? number(summary.totalAvailableCreditsUsd, true) ?? (divisor !== void 0 && total !== void 0 ? total / divisor : void 0);
|
|
2556
2711
|
return { ...divisor !== void 0 && subscription !== void 0 ? { subscriptionUsd: subscription / divisor } : {}, ...divisor !== void 0 && recharge !== void 0 ? { rechargeUsd: recharge / divisor } : {}, ...totalUsd !== void 0 ? { totalUsd } : {} };
|
|
2557
2712
|
}
|
|
2713
|
+
async function readDeviceQuota(session, signal, fetcher = fetch) {
|
|
2714
|
+
const common = { source: session.source, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2715
|
+
let current;
|
|
2716
|
+
try {
|
|
2717
|
+
current = await consoleJson(CURRENT_URL, session, signal, fetcher);
|
|
2718
|
+
} catch (error) {
|
|
2719
|
+
signal?.throwIfAborted();
|
|
2720
|
+
if (error instanceof ConsoleError && error.kind === "permission") return { ...common, state: "partial", windows: [], balances: {}, note: "\u5F53\u524D\u8BBE\u5907\u6388\u6743\u65E0\u6743\u8BFB\u53D6\u5DE5\u4F5C\u533A\uFF1B\u989D\u5EA6\u6682\u4E0D\u53EF\u786E\u8BA4" };
|
|
2721
|
+
throw error;
|
|
2722
|
+
}
|
|
2723
|
+
if (!record(current)) throw new ConsoleError("response");
|
|
2724
|
+
const workspaceId = typeof current.activeWorkspaceId === "string" && current.activeWorkspaceId.trim() ? current.activeWorkspaceId : current.workspaceId;
|
|
2725
|
+
if (typeof workspaceId !== "string" || !/^[A-Za-z0-9_-]{1,64}$/u.test(workspaceId)) throw new ConsoleError("response");
|
|
2726
|
+
const personal = current.workspaceType === "personal";
|
|
2727
|
+
const [preflight, billing] = await Promise.allSettled([
|
|
2728
|
+
consoleJson(preflightUrl(workspaceId), session, signal, fetcher),
|
|
2729
|
+
personal ? consoleJson(SUMMARY_URL, session, signal, fetcher) : Promise.resolve(void 0)
|
|
2730
|
+
]);
|
|
2731
|
+
signal?.throwIfAborted();
|
|
2732
|
+
for (const item of [preflight, billing]) if (item.status === "rejected" && item.reason instanceof ConsoleError && item.reason.kind === "auth") throw item.reason;
|
|
2733
|
+
const summary = billing.status === "fulfilled" ? billing.value : void 0;
|
|
2734
|
+
const flight = preflight.status === "fulfilled" && record(preflight.value) ? preflight.value : void 0;
|
|
2735
|
+
let normalized;
|
|
2736
|
+
try {
|
|
2737
|
+
normalized = normalizeQuota(flight?.codingPlan, personal ? summary : void 0);
|
|
2738
|
+
} catch (error) {
|
|
2739
|
+
if (!(error instanceof ConsoleError)) throw error;
|
|
2740
|
+
}
|
|
2741
|
+
const balances = personal ? normalizeBalances(summary) : {};
|
|
2742
|
+
const total = personal ? number(flight?.balanceUsd, true) : void 0;
|
|
2743
|
+
if (total !== void 0) balances.totalUsd = total;
|
|
2744
|
+
const permission = [preflight, billing].some((item) => item.status === "rejected" && item.reason instanceof ConsoleError && item.reason.kind === "permission");
|
|
2745
|
+
if (!normalized && !Object.keys(balances).length && !permission) {
|
|
2746
|
+
if (preflight.status === "rejected") throw preflight.reason;
|
|
2747
|
+
throw new ConsoleError("response");
|
|
2748
|
+
}
|
|
2749
|
+
const windows = normalized?.windows ?? [];
|
|
2750
|
+
const missingWindows = normalized?.eligible !== false && !windows.length;
|
|
2751
|
+
const missingBalances = personal && Object.keys(balances).length < 3;
|
|
2752
|
+
const partial = missingWindows || missingBalances || !personal || permission;
|
|
2753
|
+
const note = !personal ? "\u56E2\u961F\u6216\u672A\u77E5\u5DE5\u4F5C\u533A\uFF1A\u8D26\u53F7\u94B1\u5305\u53E3\u5F84\u672A\u6838\u5B9E\uFF0C\u6682\u4E0D\u663E\u793A" : missingWindows ? "\u5957\u9910\u989D\u5EA6\u7A97\u53E3\u6682\u4E0D\u53EF\u786E\u8BA4" : missingBalances ? "\u90E8\u5206\u94B1\u5305\u4F59\u989D\u6682\u4E0D\u53EF\u786E\u8BA4" : permission ? "\u90E8\u5206\u989D\u5EA6\u5C42\u65E0\u6743\u8BFB\u53D6" : normalized?.eligible === false ? "\u8BE5\u8D26\u53F7\u6CA1\u6709 Design Plan \u8BA2\u9605\uFF1B\u94B1\u5305\u4F59\u989D\u7167\u5E38\u663E\u793A" : void 0;
|
|
2754
|
+
return { ...common, ...normalized, ...normalizeSubscription(personal ? summary : void 0), windows, balances, state: partial ? "partial" : "ok", ...note ? { note } : {} };
|
|
2755
|
+
}
|
|
2558
2756
|
async function readQuota(session, signal, fetcher = fetch) {
|
|
2757
|
+
if (session.credentialType === "device") return readDeviceQuota(session, signal, fetcher);
|
|
2559
2758
|
const current = await consoleJson(CURRENT_URL, session, signal, fetcher);
|
|
2560
2759
|
if (!record(current) || typeof current.workspaceId !== "string" || !/^[A-Za-z0-9_-]{1,64}$/u.test(current.workspaceId)) throw new ConsoleError("response");
|
|
2561
2760
|
const personal = current.workspaceType === "personal";
|
|
@@ -2747,9 +2946,19 @@ function parseLoginState(value) {
|
|
|
2747
2946
|
if (!record(value)) throw new Error("\u767B\u5F55\u72B6\u6001\u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
2748
2947
|
const jobId = value.jobId;
|
|
2749
2948
|
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");
|
|
2949
|
+
const state = member(value.state, LOGIN_PHASES, "\u767B\u5F55\u9636\u6BB5");
|
|
2950
|
+
let approval = {};
|
|
2951
|
+
if (state === "waiting" && (value.activationUrl !== void 0 || value.userCode !== void 0 || value.expiresAt !== void 0)) {
|
|
2952
|
+
const userCode = text(value.userCode, "\u8BBE\u5907\u6388\u6743\u7801", 64);
|
|
2953
|
+
if (!/^[A-Za-z0-9-]+$/u.test(userCode)) throw new Error("\u8BBE\u5907\u6388\u6743\u7801\u683C\u5F0F\u65E0\u6548");
|
|
2954
|
+
const activationUrl = validateActivationUrl(value.activationUrl, userCode);
|
|
2955
|
+
if (activationUrl === null) throw new Error("\u8BBE\u5907\u6388\u6743\u94FE\u63A5\u65E0\u6548");
|
|
2956
|
+
approval = { activationUrl, userCode, expiresAt: isoTime(value.expiresAt, "\u8BBE\u5907\u6388\u6743\u5230\u671F\u65F6\u95F4") };
|
|
2957
|
+
}
|
|
2750
2958
|
return {
|
|
2751
2959
|
jobId: typeof jobId === "string" && jobId.length > 0 ? text(jobId, "\u767B\u5F55\u4EFB\u52A1 id", MAX_JOB_ID) : null,
|
|
2752
|
-
state
|
|
2960
|
+
state,
|
|
2961
|
+
...approval,
|
|
2753
2962
|
...value.message === void 0 ? {} : { message: text(value.message, "\u767B\u5F55\u8BF4\u660E", MAX_MESSAGE) },
|
|
2754
2963
|
...value.startedAt === void 0 ? {} : { startedAt: isoTime(value.startedAt, "\u767B\u5F55\u5F00\u59CB\u65F6\u95F4") },
|
|
2755
2964
|
...value.finishedAt === void 0 ? {} : { finishedAt: isoTime(value.finishedAt, "\u767B\u5F55\u7ED3\u675F\u65F6\u95F4") }
|
|
@@ -2834,7 +3043,9 @@ var SESSION = credentialKey(name, "console");
|
|
|
2834
3043
|
var hash = (value) => createHash("sha256").update(JSON.stringify(value) ?? "").digest("hex");
|
|
2835
3044
|
var needLogin = () => ({ state: "need-login", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), windows: [], balances: {}, note: "\u5C1A\u672A\u8BFB\u53D6\u989D\u5EA6\uFF1B\u53EF\u4F7F\u7528\u684C\u9762\u767B\u5F55\u6001\u6216\u5728\u8FDE\u63A5\u9875\u767B\u5F55" });
|
|
2836
3045
|
function grantOf(value) {
|
|
2837
|
-
if (value?.kind !== "grant" || !record(value.payload)
|
|
3046
|
+
if (value?.kind !== "grant" || !record(value.payload)) return;
|
|
3047
|
+
if (value.payload.kind === "device") return deviceGrant(value.payload);
|
|
3048
|
+
if (typeof value.payload.cookie !== "string") return;
|
|
2838
3049
|
return { cookie: value.payload.cookie, ...typeof value.payload.expiresAt === "number" ? { expiresAt: value.payload.expiresAt } : {} };
|
|
2839
3050
|
}
|
|
2840
3051
|
var _saveApiKey_dec, _cancelLogin_dec, _loginStatus_dec, _startLogin_dec, _refreshQuotaNow_dec, _refreshQuota_dec, _refreshModels_dec, _status_dec, _a, _init;
|
|
@@ -2871,13 +3082,7 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
2871
3082
|
this._credentials();
|
|
2872
3083
|
return { revision: expectedRevision, sessionHash: hash(state.sessionRecord), configHash: hash(state.config) };
|
|
2873
3084
|
},
|
|
2874
|
-
|
|
2875
|
-
const session = cookieSession(cookie, "login");
|
|
2876
|
-
if (!session) throw new ConsoleError("auth");
|
|
2877
|
-
const quota = await this.dependencies.readQuota(session, signal);
|
|
2878
|
-
return { balanceOk: true, coverage: quota.state === "ok" ? "complete" : "partial", windows: quota.windows };
|
|
2879
|
-
},
|
|
2880
|
-
commit: async (cookie, captured, signal, expiresAt) => {
|
|
3085
|
+
commit: async (tokens, captured, signal) => {
|
|
2881
3086
|
if (!record(captured) || typeof captured.revision !== "string" || typeof captured.sessionHash !== "string" || typeof captured.configHash !== "string") throw this._conflict();
|
|
2882
3087
|
await this._state();
|
|
2883
3088
|
this._expect(captured.revision);
|
|
@@ -2885,11 +3090,15 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
2885
3090
|
await this._credentials().modifyRecord(SESSION, async (current) => {
|
|
2886
3091
|
signal.throwIfAborted();
|
|
2887
3092
|
if (hash(current) !== captured.sessionHash || hash(this._config()) !== captured.configHash) throw this._conflict();
|
|
2888
|
-
|
|
3093
|
+
const grant = deviceGrant({ kind: "device", ...tokens });
|
|
3094
|
+
if (!grant) throw new ConsoleError("auth");
|
|
3095
|
+
return { kind: "grant", payload: { ...grant, savedAt: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
2889
3096
|
});
|
|
2890
3097
|
this._invalidateQuota();
|
|
3098
|
+
this._modelsFingerprint = "";
|
|
3099
|
+
this._invalidateModels();
|
|
2891
3100
|
}
|
|
2892
|
-
});
|
|
3101
|
+
}, dependencies.loginOptions);
|
|
2893
3102
|
ctx.effect(() => () => {
|
|
2894
3103
|
this._lifetime.abort();
|
|
2895
3104
|
this._login.dispose();
|
|
@@ -2904,28 +3113,43 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
2904
3113
|
if (!service) throw new RemoteError("opendesign/credentials-unavailable", "DSH \u51ED\u636E\u670D\u52A1\u4E0D\u53EF\u7528\uFF1B\u53EF\u5148\u4F7F\u7528\u542F\u52A8\u73AF\u5883\u53D8\u91CF", void 0);
|
|
2905
3114
|
return service;
|
|
2906
3115
|
}
|
|
2907
|
-
async
|
|
3116
|
+
async _key(ref, baseURL, apiRecord, sessionRecord) {
|
|
2908
3117
|
const credentials = this.ctx.get("credentials");
|
|
2909
3118
|
const ambient = credentials ? (await credentials.resolve(credentialRef(ref)))?.value : launchEnvironmentOf(this.ctx).get(ref)?.value;
|
|
2910
3119
|
if (ambient?.trim()) return ambient;
|
|
2911
|
-
|
|
2912
|
-
|
|
3120
|
+
if (apiRecord?.kind === "api-key" && apiRecord.key?.trim()) return apiRecord.key;
|
|
3121
|
+
const grant = grantOf(sessionRecord);
|
|
3122
|
+
return isOfficialRuntimeUrl(baseURL) && grant && "kind" in grant && grant.kind === "device" ? grant.runtimeKey : void 0;
|
|
3123
|
+
}
|
|
3124
|
+
async resolveApiKey(ref, baseURL = this._config().baseURL) {
|
|
3125
|
+
const credentials = this.ctx.get("credentials");
|
|
3126
|
+
const [apiRecord, sessionRecord] = await Promise.all([credentials?.readRecord(API_KEY), credentials?.readRecord(SESSION)]);
|
|
3127
|
+
return this._key(ref, baseURL, apiRecord, sessionRecord);
|
|
3128
|
+
}
|
|
3129
|
+
_invalidateModels() {
|
|
3130
|
+
const changed = this._modelsUpdatedAt !== void 0;
|
|
3131
|
+
this._modelsEpoch++;
|
|
3132
|
+
this._modelsController?.abort();
|
|
3133
|
+
this._modelsPending = void 0;
|
|
3134
|
+
this._models = MODELS;
|
|
3135
|
+
this._modelsUpdatedAt = void 0;
|
|
3136
|
+
this._modelsError = void 0;
|
|
3137
|
+
if (changed) {
|
|
3138
|
+
try {
|
|
3139
|
+
this.dependencies.modelsChanged?.();
|
|
3140
|
+
} catch {
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
2913
3143
|
}
|
|
2914
3144
|
async _state() {
|
|
2915
3145
|
const credentials = this.ctx.get("credentials"), config = this._config();
|
|
2916
|
-
const [apiRecord, sessionRecord
|
|
3146
|
+
const [apiRecord, sessionRecord] = await Promise.all([credentials?.readRecord(API_KEY), credentials?.readRecord(SESSION)]);
|
|
3147
|
+
const key = await this._key(config.apiKeyEnv, config.baseURL, apiRecord, sessionRecord);
|
|
2917
3148
|
const state = { apiRecord, sessionRecord, key, config };
|
|
2918
3149
|
const modelsFingerprint = hash({ key, baseURL: config.baseURL });
|
|
2919
3150
|
if (modelsFingerprint !== this._modelsFingerprint) {
|
|
2920
|
-
const changed = this._modelsUpdatedAt !== void 0;
|
|
2921
3151
|
this._modelsFingerprint = modelsFingerprint;
|
|
2922
|
-
this.
|
|
2923
|
-
this._modelsController?.abort();
|
|
2924
|
-
this._modelsPending = void 0;
|
|
2925
|
-
this._models = MODELS;
|
|
2926
|
-
this._modelsUpdatedAt = void 0;
|
|
2927
|
-
this._modelsError = void 0;
|
|
2928
|
-
if (changed) this.dependencies.modelsChanged?.();
|
|
3152
|
+
this._invalidateModels();
|
|
2929
3153
|
}
|
|
2930
3154
|
const fingerprint = hash(state);
|
|
2931
3155
|
if (fingerprint !== this._fingerprint) {
|
|
@@ -3048,9 +3272,10 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
3048
3272
|
let identity;
|
|
3049
3273
|
let refreshFailed = false;
|
|
3050
3274
|
try {
|
|
3275
|
+
const grant = grantOf(await this.ctx.get("credentials")?.readRecord(SESSION));
|
|
3051
3276
|
const authDeps = {
|
|
3052
3277
|
useDesktop: config.useDesktopCredential,
|
|
3053
|
-
readGrant: async () =>
|
|
3278
|
+
readGrant: async () => grant,
|
|
3054
3279
|
resolveRef: async (ref) => {
|
|
3055
3280
|
const credentials = this.ctx.get("credentials");
|
|
3056
3281
|
return credentials ? (await credentials.resolve(credentialRef(ref)))?.value : launchEnvironmentOf(this.ctx).get(ref)?.value;
|
|
@@ -3068,7 +3293,10 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
3068
3293
|
if (fallback.state === "ready") {
|
|
3069
3294
|
try {
|
|
3070
3295
|
const complete = await this.dependencies.readQuota(fallback.session, signal);
|
|
3071
|
-
if (complete.state === "ok" && (complete.windows.length > 0 || complete.eligible === false))
|
|
3296
|
+
if (complete.state === "ok" && (complete.windows.length > 0 || complete.eligible === false)) {
|
|
3297
|
+
result = { ...complete, note: `\u684C\u9762\u767B\u5F55\u6001\u4EC5\u63D0\u4F9B\u90E8\u5206\u989D\u5EA6\uFF1B\u5F53\u524D\u663E\u793A\u72EC\u7ACB\u767B\u5F55\u8D26\u53F7\u7684\u5B8C\u6574\u989D\u5EA6\u3002${complete.note ? ` ${complete.note}` : ""}` };
|
|
3298
|
+
identity = hash(fallback.session);
|
|
3299
|
+
}
|
|
3072
3300
|
} catch (error) {
|
|
3073
3301
|
if (signal.aborted) throw error;
|
|
3074
3302
|
}
|
|
@@ -3078,6 +3306,7 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
|
|
|
3078
3306
|
if (!(error instanceof ConsoleError) || !["auth", "permission"].includes(error.kind)) throw error;
|
|
3079
3307
|
const fallback = await this.dependencies.resolveSession({ ...authDeps, useDesktop: false });
|
|
3080
3308
|
if (fallback.state !== "ready") throw new ConsoleError("auth");
|
|
3309
|
+
identity = hash(fallback.session);
|
|
3081
3310
|
result = await this.dependencies.readQuota(fallback.session, signal);
|
|
3082
3311
|
}
|
|
3083
3312
|
}
|
|
@@ -3155,7 +3384,7 @@ function apply(ctx, config) {
|
|
|
3155
3384
|
const adapter = new CoRouterAdapter({
|
|
3156
3385
|
config: current,
|
|
3157
3386
|
models: () => service.modelCatalog(),
|
|
3158
|
-
apiKey: (ref) => service.resolveApiKey(ref),
|
|
3387
|
+
apiKey: (ref, baseURL) => service.resolveApiKey(ref, baseURL),
|
|
3159
3388
|
onEmptyTurn: () => {
|
|
3160
3389
|
service.guardedTurns++;
|
|
3161
3390
|
},
|
|
@@ -3178,7 +3407,7 @@ function apply(ctx, config) {
|
|
|
3178
3407
|
ctx.llm.registerModelDiscovery(SETTINGS_NS, async (request, signal) => {
|
|
3179
3408
|
if (request.provider !== void 0 && ![PLAN_PROVIDER, PAID_PROVIDER].includes(request.provider)) throw new LlmError11("\u4E0D\u662F\u672C\u63D2\u4EF6\u7684\u6A21\u578B\u901A\u9053", "INVALID_REQUEST");
|
|
3180
3409
|
const config2 = current();
|
|
3181
|
-
const key = request.apiKey ?? await service.resolveApiKey(config2.apiKeyEnv);
|
|
3410
|
+
const key = request.apiKey ?? await service.resolveApiKey(config2.apiKeyEnv, config2.baseURL);
|
|
3182
3411
|
if (!key) throw new LlmError11("\u8BF7\u5148\u914D\u7F6E CoRouter API Key", "MISSING_CREDENTIAL");
|
|
3183
3412
|
const models = await fetchModelCatalog(config2.baseURL, key, signal);
|
|
3184
3413
|
return models.map((row) => ({ id: row.id, name: row.name, ...row.contextWindow === void 0 ? {} : { contextWindow: row.contextWindow }, ...row.maxOutputTokens === void 0 ? {} : { maxTokens: row.maxOutputTokens }, ...row.inputModalities ? { inputModalities: row.inputModalities } : {} }));
|