dsh-opendesign-corouter 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/login.d.ts CHANGED
@@ -1,74 +1,12 @@
1
- /** Console origin and dashboard entry the official desktop uses. */
2
- export declare const CONSOLE_ORIGIN = "https://open-design.ai";
3
- export declare const CONSOLE_DASHBOARD_URL = "https://open-design.ai/cloud/dashboard";
4
- /** The API host whose cookies matter; `context.cookies()` uses it to pick the right ones. */
5
- export declare const API_COOKIE_URL = "https://amr-api.open-design.ai/api/v1/workspaces/current";
6
- export interface RawCookie {
7
- name?: string;
8
- value?: string;
9
- expires?: number;
10
- }
11
- /** One usable console session: the exact `Cookie` header a browser would send. */
12
- export interface ExtractedCookie {
13
- cookie: string;
14
- expiresAt: number;
15
- }
16
- /**
17
- * Pick the session cookies by **URL scope**, never by name. The real cookie is
18
- * better-auth's `__Secure-better-auth.session_token` today, but it can be renamed at any
19
- * time and no third party can enumerate the alternatives; joining every unexpired cookie
20
- * that applies to the API URL reproduces exactly what the browser itself sends. Before
21
- * login completes this may also collect non-auth cookies (`od_attr`, `__cf_bm`), which is
22
- * harmless: such a bundle fails `verify` and is therefore never saved.
23
- */
24
- export declare function extractConsoleSession(cookies: readonly RawCookie[], now?: number): ExtractedCookie | null;
25
- export interface LoginSite {
26
- origin: string;
27
- loginUrl: string;
28
- cookieUrls: readonly string[];
29
- extract(cookies: readonly RawCookie[], now: number): ExtractedCookie | null;
30
- }
31
- /** Site parameters, overridable so a rebrand (for example a new console origin) needs no code fork. */
32
- export declare function consoleSite(overrides?: {
33
- origin?: string;
34
- loginUrl?: string;
35
- cookieUrls?: readonly string[];
36
- }): LoginSite;
37
- export interface VerifyResult {
38
- /** Whether the account was read at all; a read-but-quota-unknown result still verifies. */
39
- balanceOk: boolean;
40
- /** Set when the quota endpoint failed; the cookie itself is still valid, so the host may still save it. */
41
- usageError?: {
42
- code: string;
43
- } | undefined;
44
- /**
45
- * Caller-supplied context, echoed nowhere and used only for the success message. The Host
46
- * re-reads the real quota after commit, so these are never trusted as state.
47
- */
48
- coverage?: string | undefined;
49
- windows?: readonly unknown[] | undefined;
50
- }
51
- /** Host-supplied operations. No Host type is imported: the caller wires these. */
1
+ import { type DeviceTokens } from './device-auth.js';
2
+ export type { DeviceTokens } from './device-auth.js';
3
+ /** Host operations. No quota verification: the authorization server issues the identity. */
52
4
  export interface LoginCallbacks {
53
- /** Snapshot the state a commit must not overwrite, keyed by the revision the caller saw. */
54
5
  capture(expectedRevision: string): unknown | Promise<unknown>;
55
- /** Prove the cookie works. Any throw means "not usable yet"; abort is a cancel. */
56
- verify(cookie: string, signal: AbortSignal): Promise<VerifyResult>;
57
- /**
58
- * Persist the verified cookie. The Host's store must run its write inside a mutex and call
59
- * `signal.throwIfAborted()` there, so a cancel that landed before the mutex was acquired
60
- * wins, and one that lands afterwards does not.
61
- *
62
- * `expiresAt` is the cookie bundle's earliest declared expiry in epoch ms, or `0` when the
63
- * console declared none (a session cookie). It is passed through so the Host can store it and
64
- * let `resolveSession` expire a stale grant proactively instead of waiting for a 401.
65
- */
66
- commit(cookie: string, captured: unknown, signal: AbortSignal, expiresAt: number): Promise<unknown>;
6
+ /** Re-check signal under the Host's atomic store mutex before accepting the write.
7
+ * Once accepted, resolve: success is the linearization point, even after cancellation. */
8
+ commit(tokens: DeviceTokens, captured: unknown, signal: AbortSignal): Promise<unknown>;
67
9
  }
68
- /**
69
- * Attempt phases, matching the shared wire contract (`LoginPhase` in `remote.ts`) exactly;
70
- * `success`/`cancelled`/`error` are terminal. The Host maps `status()` onto the wire type.
71
- */
72
10
  export type LoginPhase = 'idle' | 'launching' | 'waiting' | 'verifying' | 'success' | 'cancelled' | 'error';
73
11
  export interface LoginStatus {
74
12
  jobId: string | null;
@@ -77,8 +15,9 @@ export interface LoginStatus {
77
15
  finishedAt?: string;
78
16
  message?: string;
79
17
  errorCode?: string;
80
- /** The page could not be opened; the UI may offer the URL instead. */
81
- browserOpenFailed?: boolean;
18
+ activationUrl?: string;
19
+ userCode?: string;
20
+ expiresAt?: string;
82
21
  }
83
22
  export interface LoginStartRequest {
84
23
  expectedRevision: string;
@@ -86,88 +25,39 @@ export interface LoginStartRequest {
86
25
  export interface LoginJobRequest {
87
26
  jobId: string;
88
27
  }
89
- /** Minimal structural browser surface, so tests need no Playwright and Host wiring stays loose. */
90
- export interface BrowserLike {
91
- on?(event: 'disconnected', listener: () => void): void;
92
- newContext(): Promise<ContextLike>;
93
- close(): Promise<void>;
94
- }
95
- export interface ContextLike {
96
- newPage(): Promise<PageLike>;
97
- cookies(urls: readonly string[]): Promise<readonly RawCookie[]>;
98
- }
99
- export interface PageLike {
100
- on?(event: 'close', listener: () => void): void;
101
- goto(url: string, options?: {
102
- waitUntil?: string;
103
- timeout?: number;
104
- }): Promise<unknown>;
105
- }
106
- export type Launcher = (signal: AbortSignal) => Promise<BrowserLike | null>;
107
28
  export interface LoginOptions {
108
- site?: LoginSite;
109
- /** Total life of one login attempt. Default five minutes. */
110
- timeoutMs?: number;
111
- pollMs?: number;
29
+ fetcher?: typeof fetch;
112
30
  now?: () => number;
113
- /** Injected browser launcher; the default uses `playwright-core` and a real Chrome window. */
114
- launch?: Launcher;
115
- /** Injected sleep, for tests. */
116
31
  delay?: (ms: number, signal: AbortSignal) => Promise<void>;
32
+ /** May shorten, but cannot extend, the local five-minute maximum. */
33
+ timeoutMs?: number;
117
34
  }
118
35
  export declare class LoginError extends Error {
119
36
  readonly code: 'opendesign/login-state' | 'opendesign/login-disposed' | 'opendesign/login-capture';
120
37
  constructor(code: 'opendesign/login-state' | 'opendesign/login-disposed' | 'opendesign/login-capture');
121
38
  }
122
- /**
123
- * Chrome flags that let a *human* pass the console's bot check. The console looks at
124
- * `navigator.webdriver` when its Cloudflare challenge is submitted; `headless: false` alone
125
- * leaves it `true`, and this blink switch is what the official Playwright browser tool
126
- * appends for chromium. This is not a challenge bypass — the user still solves it — it only
127
- * avoids announcing automation before they can act. `ignoreDefaultArgs` is a no-op on
128
- * 1.63.0 and stays for forward compatibility.
129
- */
130
- export declare const CHROME_LAUNCH_OPTIONS: {
131
- readonly channel: "chrome";
132
- readonly headless: false;
133
- readonly ignoreDefaultArgs: readonly ["--enable-automation"];
134
- readonly args: readonly ["--disable-blink-features=AutomationControlled"];
135
- };
136
39
  export declare class ConsoleLogin {
137
- private job;
138
- private disposed;
139
40
  private readonly callbacks;
140
41
  private readonly options;
42
+ private job;
43
+ private disposed;
141
44
  private readonly now;
142
45
  private readonly delay;
46
+ private readonly fetcher;
143
47
  constructor(callbacks: LoginCallbacks, options?: LoginOptions);
144
- /** Idempotent, credential-free view of the current attempt. */
48
+ /** Explicit public allowlist. Codes are never available after waiting or expiry. */
145
49
  status(): LoginStatus;
146
- /**
147
- * Queue one attempt and return immediately: the RPC that starts a login must not wait on a
148
- * browser window or on a human. Re-entrant calls while an attempt is live return that
149
- * attempt's status instead of opening a second browser.
150
- */
50
+ /** Synchronous/non-blocking and idempotent while capture/network/commit is pending. */
151
51
  start(request: LoginStartRequest): LoginStatus;
152
- /**
153
- * Abandon the attempt. While `commit` is in flight this only aborts the signal and records
154
- * the request: the Host's atomic store decides, because the commit is the linearization
155
- * point. A cancel that wins that race reports `cancelled`; one that loses reports `success`
156
- * and the cookie really is stored.
157
- */
158
52
  cancel(request: LoginJobRequest): LoginStatus;
159
- /** Plugin teardown: abort the attempt and close the browser. */
160
53
  dispose(): void;
161
54
  private alive;
162
55
  private stopTimer;
163
- private close;
56
+ private armExpiry;
57
+ private clearCode;
58
+ private expire;
59
+ private requestCancellation;
164
60
  private finish;
165
- private resolveBrowser;
166
61
  private run;
167
- /**
168
- * Persist the verified session; `commit` resolving is the linearization point. Returns
169
- * `retry` when the store rejected the write transiently, so the caller decides when to try
170
- * again instead of claiming a success that did not happen.
171
- */
172
62
  private commit;
173
63
  }
@@ -30,9 +30,9 @@ export interface ModelSettings {
30
30
  /** Model id → settings. Keys are model ids, never provider-scoped. */
31
31
  export type ModelSettingsMap = Record<string, ModelSettings>;
32
32
  /**
33
- * Validate an untrusted settings payload (settings store, credential-free Host state)
34
- * into a fresh map. Rejects non-objects, oversized maps, reserved/unsafe ids, unknown
35
- * fields and out-of-range values; never returns the input's objects or arrays.
33
+ * Validate an untrusted settings payload into a fresh, allowlisted map.
34
+ * This is also a Schemastery transform: all runtime dependencies must live inside
35
+ * the function so JSON serialization and fresh-realm rehydration preserve validation.
36
36
  */
37
37
  export declare function parseModelSettingsMap(value: unknown): ModelSettingsMap;
38
38
  /**
package/lib/quota.d.ts CHANGED
@@ -2,10 +2,12 @@ export declare const API_BASE = "https://amr-api.open-design.ai/api/v1";
2
2
  export declare const CURRENT_URL = "https://amr-api.open-design.ai/api/v1/workspaces/current";
3
3
  export declare const SUMMARY_URL = "https://amr-api.open-design.ai/api/v1/billing/summary";
4
4
  export declare const usageUrl: (workspace: string) => string;
5
+ export declare const preflightUrl: (workspace: string) => string;
5
6
  export type SessionSource = 'desktop' | 'login' | 'environment';
6
7
  export interface Session {
7
8
  headers: Record<string, string>;
8
9
  source: SessionSource;
10
+ credentialType?: 'device';
9
11
  }
10
12
  export interface QuotaWindow {
11
13
  durationSeconds: number;
@@ -57,4 +59,8 @@ export declare function normalizeQuota(body: unknown, summary?: unknown): Pick<Q
57
59
  /** Billing period of the subscription pool; absent fields stay unknown, never derived. */
58
60
  export declare function normalizeSubscription(summary: unknown): Pick<QuotaSnapshot, 'subscriptionPeriodStart' | 'subscriptionPeriodEnd'>;
59
61
  export declare function normalizeBalances(summary: unknown): QuotaSnapshot['balances'];
62
+ /** Device contract: opencode-opendesign-auth 0.1.4, workspace preflight + personal summary.
63
+ * Unlike desktop reads, this path never invokes vela or borrows desktop account data.
64
+ */
65
+ export declare function readDeviceQuota(session: Session, signal?: AbortSignal, fetcher?: typeof fetch): Promise<QuotaSnapshot>;
60
66
  export declare function readQuota(session: Session, signal?: AbortSignal, fetcher?: typeof fetch): Promise<QuotaSnapshot>;
package/lib/remote.d.ts CHANGED
@@ -39,6 +39,10 @@ export interface LoginState {
39
39
  message?: string;
40
40
  startedAt?: string;
41
41
  finishedAt?: string;
42
+ /** Public device approval information; available only while waiting. */
43
+ activationUrl?: string;
44
+ userCode?: string;
45
+ expiresAt?: string;
42
46
  }
43
47
  /** Everything the settings/usage surface may read; no field is derived from a secret. */
44
48
  export interface Status {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-opendesign-corouter",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "OpenDesign CoRouter:套餐赠送与付费模型双 Provider、桌面登录态额度及独立登录",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -94,7 +94,6 @@
94
94
  },
95
95
  "dependencies": {
96
96
  "@deepseek-ai/schemastery": "~3.18.5",
97
- "playwright-core": "1.63.0",
98
97
  "zod": "4.6.6"
99
98
  },
100
99
  "overrides": {
@@ -116,6 +115,7 @@
116
115
  "@types/react": "^18.3.1",
117
116
  "@types/react-dom": "^18.3.0",
118
117
  "esbuild": "^0.28.2",
118
+ "playwright-core": "1.63.0",
119
119
  "react": "18.3.1",
120
120
  "react-dom": "18.3.1",
121
121
  "tsx": "^4.23.13",