@edge-sky/dsh-oauth-adapter 0.1.2-dev.2 → 0.1.2-rc.0

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/index.js CHANGED
@@ -3,31 +3,45 @@
3
3
  * `dsh-llm-pi-ai` plugin.
4
4
  * @module @edge-sky/dsh-oauth-adapter
5
5
  */
6
- import { OAuthModelService } from './model-service.js';
7
- import { ModelOperationError } from './model-discovery.js';
8
6
  import { randomUUID } from 'node:crypto';
9
7
  import { inspect } from 'node:util';
8
+ import { settingsNamespace } from '@deepseek-ai/dsh-settings';
10
9
  import z from '@deepseek-ai/schemastery';
11
10
  import WebSocket, { WebSocketServer } from 'ws';
12
11
  import { MAX_FRAME_BYTES, OAUTH_SOCKET_PATH, OAUTH_SOCKET_PROTOCOL, PROVIDERS, parseClientCommand, } from './protocol.js';
13
12
  /** Runtime-validated plugin configuration. */
14
13
  export const Config = z.object({
15
14
  debug: z.boolean().default(false),
16
- modelSyncTimeoutMs: z.number().min(1).max(2147483647).step(1).default(30000),
17
- modelSyncMaxResponseBytes: z.number().min(1024).step(1).default(4194304),
18
- modelSyncMaxPages: z.number().min(1).max(1000).step(1).default(100),
19
- codexClientVersion: z.string().default('0.149.0'),
20
15
  });
21
16
  /** Cordis plugin name. */
22
17
  export const name = 'dsh-oauth-adapter';
23
18
  /** Official services required by the OAuth account surface. */
24
- export const inject = ['authorization', 'credentials', 'settings', 'webServer', 'llm'];
19
+ export const inject = ['authorization', 'credentials', 'settings', 'webServer'];
20
+ const MODEL_SETTINGS_NS = settingsNamespace('llm-pi-ai');
25
21
  function providerMeta(providerId) {
26
22
  const provider = PROVIDERS.find(candidate => candidate.id === providerId);
27
23
  if (provider === undefined)
28
24
  throw new Error(`unsupported provider: ${providerId}`);
29
25
  return provider;
30
26
  }
27
+ function isRecord(value) {
28
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
29
+ }
30
+ function modelRouteEnabled(settings, provider) {
31
+ const section = settings.get(MODEL_SETTINGS_NS);
32
+ if (!isRecord(section) || !isRecord(section.providers))
33
+ return false;
34
+ return Object.hasOwn(section.providers, provider);
35
+ }
36
+ function pluginOwnsModelRoute(settings, provider) {
37
+ const descriptor = settings.describe().find(candidate => candidate.ns === MODEL_SETTINGS_NS);
38
+ if (!isRecord(descriptor?.user) || !isRecord(descriptor.user.providers))
39
+ return false;
40
+ const profile = descriptor.user.providers[provider.id];
41
+ return isRecord(profile)
42
+ && Object.keys(profile).length === 1
43
+ && profile.displayName === provider.modelGroupLabel;
44
+ }
31
45
  const GITHUB_DOMAIN_PROMPT = 'GitHub Enterprise URL/domain (blank for github.com)';
32
46
  function promptView(prompt, provider) {
33
47
  if (prompt.kind === 'select') {
@@ -162,17 +176,14 @@ class OAuthConnection {
162
176
  socket;
163
177
  debug;
164
178
  changed;
165
- models;
166
179
  active;
167
180
  queue = Promise.resolve();
168
181
  closed = false;
169
- tasks = new Set();
170
- constructor(ctx, socket, debug, changed, models) {
182
+ constructor(ctx, socket, debug, changed) {
171
183
  this.ctx = ctx;
172
184
  this.socket = socket;
173
185
  this.debug = debug;
174
186
  this.changed = changed;
175
- this.models = models;
176
187
  socket.on('message', (raw, isBinary) => {
177
188
  if (isBinary || rawLength(raw) > MAX_FRAME_BYTES) {
178
189
  this.sendError('invalid-message', 'message must be bounded UTF-8 JSON');
@@ -196,23 +207,16 @@ class OAuthConnection {
196
207
  this.debug('connection=opened');
197
208
  void this.sendSnapshot();
198
209
  }
199
- modelChanged(provider) { this.send({ type: 'models-changed', provider }); }
200
210
  refresh() {
201
211
  void this.sendSnapshot();
202
212
  }
203
- track(task) {
204
- this.tasks.add(task);
205
- void task.finally(() => this.tasks.delete(task)).catch(() => { });
206
- }
207
- async dispose() {
213
+ dispose() {
208
214
  this.closed = true;
209
215
  this.withdrawActive('OAuth surface stopped');
210
216
  this.socket.terminate();
211
- await this.queue;
212
- await Promise.allSettled([...this.tasks]);
213
217
  }
214
218
  send(message) {
215
- if (!this.closed && this.socket.readyState === WebSocket.OPEN)
219
+ if (this.socket.readyState === WebSocket.OPEN)
216
220
  this.socket.send(JSON.stringify(message));
217
221
  }
218
222
  sendError(code, message, requestId, attemptId, detail) {
@@ -224,8 +228,6 @@ class OAuthConnection {
224
228
  });
225
229
  }
226
230
  async receive(raw) {
227
- if (this.closed)
228
- return;
229
231
  const parsed = parseClientCommand(raw);
230
232
  if (!parsed.ok) {
231
233
  this.sendError(parsed.message === 'provider is unsupported' ? 'unsupported-provider' : 'invalid-message', parsed.message, parsed.requestId);
@@ -234,10 +236,6 @@ class OAuthConnection {
234
236
  await this.handle(parsed.value);
235
237
  }
236
238
  async handle(command) {
237
- if (command.type.startsWith('models-')) {
238
- this.track(this.handleModels(command));
239
- return;
240
- }
241
239
  switch (command.type) {
242
240
  case 'refresh':
243
241
  await this.sendSnapshot(command.requestId);
@@ -255,45 +253,41 @@ class OAuthConnection {
255
253
  await this.forget(command);
256
254
  }
257
255
  }
258
- async handleModels(command) {
259
- try {
260
- switch (command.type) {
261
- case 'models-list': break;
262
- case 'models-sync':
263
- await this.models.sync(command.provider, command.revision);
264
- break;
265
- case 'models-migrate':
266
- await this.models.migrate(command.provider, command.revision);
267
- break;
268
- case 'models-save':
269
- await this.models.saveManual(command.provider, command.model, command.revision);
270
- break;
271
- case 'models-delete':
272
- await this.models.deleteManual(command.provider, command.id, command.revision);
273
- break;
274
- }
275
- if (!this.closed)
276
- this.send({ type: 'models-page', requestId: command.requestId,
277
- page: await this.models.page(command.provider, command.type === 'models-list' ? command.offset : 0) });
278
- }
279
- catch (error) {
280
- if (!this.closed)
281
- this.send({ type: 'models-error', requestId: command.requestId, provider: command.provider,
282
- code: error instanceof ModelOperationError ? error.code : 'operation-failed',
283
- message: error instanceof ModelOperationError ? error.message : 'The model operation failed. Refresh and retry.' });
284
- }
285
- }
286
256
  async account(provider) {
287
- await this.models.ready;
288
257
  const entry = this.ctx.authorization.describe(provider.key);
289
258
  const credential = await this.ctx.credentials.describeRecord(provider.key);
259
+ let modelsEnabled = modelRouteEnabled(this.ctx.settings, provider.id);
260
+ if (credential.configured && !modelsEnabled && this.ctx.settings.writable) {
261
+ try {
262
+ modelsEnabled = await this.enableModelRoute(provider);
263
+ }
264
+ catch (error) {
265
+ this.ctx.logger.warn('dsh-oauth-adapter: model route activation failed for %s', provider.id);
266
+ this.ctx.logger.warn(error);
267
+ }
268
+ }
290
269
  return {
291
- provider: provider.id, label: entry?.label ?? provider.fallbackLabel,
270
+ provider: provider.id,
271
+ label: entry?.label ?? provider.fallbackLabel,
292
272
  available: entry?.methods.some(method => method.id === 'oauth') === true,
293
- configured: credential.configured, modelsEnabled: this.models.isManaged(provider.id),
294
- writable: credential.writable, inFlight: entry?.inFlight ?? false,
273
+ configured: credential.configured,
274
+ modelsEnabled,
275
+ writable: credential.writable,
276
+ inFlight: entry?.inFlight ?? false,
295
277
  };
296
278
  }
279
+ async enableModelRoute(provider) {
280
+ if (modelRouteEnabled(this.ctx.settings, provider.id))
281
+ return true;
282
+ if (!this.ctx.settings.writable)
283
+ return false;
284
+ await this.ctx.settings.mutate(MODEL_SETTINGS_NS, [{
285
+ op: 'set',
286
+ path: ['providers', provider.id],
287
+ value: { displayName: provider.modelGroupLabel },
288
+ }]);
289
+ return modelRouteEnabled(this.ctx.settings, provider.id);
290
+ }
297
291
  async sendSnapshot(requestId) {
298
292
  try {
299
293
  const accounts = await Promise.all(PROVIDERS.map(provider => this.account(provider)));
@@ -324,12 +318,11 @@ class OAuthConnection {
324
318
  id: randomUUID(), provider: command.provider, key: provider.key,
325
319
  controller: new AbortController(), prompts: new Map(),
326
320
  };
327
- this.models.loginStarted(provider.id);
328
321
  this.active = attempt;
329
322
  this.changed();
330
323
  this.send({ type: 'started', requestId: command.requestId, attemptId: attempt.id, provider: attempt.provider });
331
324
  this.debug(`provider=${attempt.provider} stage=started`);
332
- this.track(this.ctx.authorization.begin({
325
+ void this.ctx.authorization.begin({
333
326
  key: attempt.key,
334
327
  method: 'oauth',
335
328
  signal: attempt.controller.signal,
@@ -345,20 +338,26 @@ class OAuthConnection {
345
338
  prompt: prompt => this.openPrompt(attempt, prompt),
346
339
  },
347
340
  }).then(async (outcome) => {
348
- try {
349
- await this.models.loginFinished(provider.id, outcome.status === 'authorized');
350
- }
351
- catch (error) {
352
- this.sendError('model-route-unavailable', 'Sign-in completed, but model synchronization did not complete. Open Models to retry.', undefined, attempt.id, formatErrorDetail(error));
341
+ if (outcome.status === 'authorized') {
342
+ try {
343
+ const enabled = await this.enableModelRoute(provider);
344
+ if (!enabled) {
345
+ this.sendError('model-route-unavailable', 'sign-in completed, but the OAuth model route could not be enabled', undefined, attempt.id);
346
+ }
347
+ }
348
+ catch (error) {
349
+ this.ctx.logger.warn('dsh-oauth-adapter: model route activation failed for %s', provider.id);
350
+ this.ctx.logger.warn(error);
351
+ this.sendError('model-route-unavailable', 'sign-in completed, but the OAuth model route could not be enabled', undefined, attempt.id, formatErrorDetail(error));
352
+ }
353
353
  }
354
354
  this.finish(attempt, outcome.status);
355
- }, async (error) => {
356
- await this.models.loginFinished(provider.id, false).catch(() => { });
355
+ }, (error) => {
357
356
  this.ctx.logger.warn('dsh-oauth-adapter: provider sign-in failed for %s', attempt.provider);
358
357
  this.ctx.logger.warn(error);
359
358
  this.sendError('sign-in-failed', 'sign-in failed', undefined, attempt.id, formatErrorDetail(error));
360
359
  this.finish(attempt, 'failed');
361
- }));
360
+ });
362
361
  }
363
362
  openPrompt(attempt, prompt) {
364
363
  if (this.active !== attempt || this.closed)
@@ -429,7 +428,12 @@ class OAuthConnection {
429
428
  return;
430
429
  }
431
430
  try {
432
- await this.models.forget(provider.id);
431
+ await this.ctx.credentials.deleteRecord(provider.key);
432
+ if (pluginOwnsModelRoute(this.ctx.settings, provider)) {
433
+ await this.ctx.settings.mutate(MODEL_SETTINGS_NS, [{
434
+ op: 'unset', path: ['providers', provider.id],
435
+ }]);
436
+ }
433
437
  this.send({ type: 'ack', requestId: command.requestId });
434
438
  await this.sendSnapshot();
435
439
  }
@@ -462,28 +466,21 @@ class OAuthConnection {
462
466
  attempt.prompts.clear();
463
467
  }
464
468
  }
465
- /** Mount the loopback-only OAuth WebSocket over official rc services. */
469
+ /** Mount the loopback-only OAuth WebSocket over official rc2 services. */
466
470
  export function apply(ctx, config) {
467
471
  const debug = config.debug === true
468
472
  ? (message) => { ctx.logger.debug('dsh-oauth-adapter: %s', message); }
469
473
  : () => { };
470
474
  const connections = new Set();
471
- const models = new OAuthModelService(ctx, {
472
- timeoutMs: config.modelSyncTimeoutMs ?? 30000,
473
- maxResponseBytes: config.modelSyncMaxResponseBytes ?? 4194304,
474
- maxPages: config.modelSyncMaxPages ?? 100,
475
- codexClientVersion: config.codexClientVersion ?? '0.149.0',
476
- }, provider => { for (const connection of connections)
477
- connection.modelChanged(provider); });
478
475
  const server = new WebSocketServer({
479
476
  noServer: true,
480
477
  maxPayload: MAX_FRAME_BYTES,
481
478
  handleProtocols: protocols => protocols.has(OAUTH_SOCKET_PROTOCOL) ? OAUTH_SOCKET_PROTOCOL : false,
482
479
  });
483
480
  server.on('connection', (socket) => {
484
- const connection = new OAuthConnection(ctx, socket, debug, refresh, models);
481
+ const connection = new OAuthConnection(ctx, socket, debug, refresh);
485
482
  connections.add(connection);
486
- socket.once('close', () => { void connection.dispose().then(() => { connections.delete(connection); }); });
483
+ socket.once('close', () => { connections.delete(connection); });
487
484
  });
488
485
  server.on('error', (error) => {
489
486
  ctx.logger.warn('dsh-oauth-adapter: WebSocket server failed');
@@ -510,10 +507,9 @@ export function apply(ctx, config) {
510
507
  return async () => {
511
508
  credentialDisposer();
512
509
  unregister();
513
- const closing = [...connections].map(connection => connection.dispose());
510
+ for (const connection of connections)
511
+ connection.dispose();
514
512
  connections.clear();
515
- await models.dispose();
516
- await Promise.allSettled(closing);
517
513
  await new Promise((resolve) => { server.close(() => { resolve(); }); });
518
514
  };
519
515
  }
package/lib/protocol.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  /** Shared browser/Host wire vocabulary for the OAuth account surface. */
2
- import type { ModelCommand, ModelMessage } from './model-types.js';
3
2
  export declare const OAUTH_SOCKET_PATH = "/_edge-sky/dsh-oauth";
4
3
  export declare const OAUTH_SOCKET_PROTOCOL = "dsh-oauth-v1";
5
4
  export declare const MAX_FRAME_BYTES: number;
@@ -65,7 +64,7 @@ export type PromptView = {
65
64
  description?: string;
66
65
  }[];
67
66
  };
68
- export type ClientCommand = ModelCommand | {
67
+ export type ClientCommand = {
69
68
  type: 'refresh';
70
69
  requestId: string;
71
70
  } | {
@@ -88,7 +87,7 @@ export type ClientCommand = ModelCommand | {
88
87
  provider: ProviderId;
89
88
  };
90
89
  export type OAuthErrorCode = 'invalid-message' | 'unsupported-provider' | 'authorization-unavailable' | 'busy' | 'invalid-attempt' | 'invalid-prompt' | 'credential-read-only' | 'model-route-unavailable' | 'sign-in-failed' | 'operation-failed';
91
- export type ServerMessage = ModelMessage | {
90
+ export type ServerMessage = {
92
91
  type: 'snapshot';
93
92
  requestId?: string;
94
93
  accounts: readonly AccountView[];
package/lib/protocol.js CHANGED
@@ -1,5 +1,4 @@
1
1
  /** Shared browser/Host wire vocabulary for the OAuth account surface. */
2
- import { validManual, modelText } from './model-types.js';
3
2
  export const OAUTH_SOCKET_PATH = '/_edge-sky/dsh-oauth';
4
3
  export const OAUTH_SOCKET_PROTOCOL = 'dsh-oauth-v1';
5
4
  export const MAX_FRAME_BYTES = 16 * 1024;
@@ -60,32 +59,6 @@ export function parseClientCommand(raw) {
60
59
  const requestId = isId(candidate.requestId) ? candidate.requestId : undefined;
61
60
  if (requestId === undefined)
62
61
  return { ok: false, message: 'requestId is invalid' };
63
- if (candidate.type.startsWith('models-')) {
64
- if (!isProviderId(candidate.provider))
65
- return { ok: false, requestId, message: 'provider is unsupported' };
66
- const provider = candidate.provider;
67
- const fail = { ok: false, requestId, message: 'model command fields are invalid' };
68
- if (candidate.type === 'models-list') {
69
- return exactKeys(candidate, ['type', 'requestId', 'provider', 'offset']) && Number.isSafeInteger(candidate.offset) && Number(candidate.offset) >= 0
70
- ? { ok: true, value: { type: 'models-list', requestId, provider, offset: Number(candidate.offset) } } : fail;
71
- }
72
- if (!Number.isSafeInteger(candidate.revision) || Number(candidate.revision) < 0)
73
- return fail;
74
- const revision = Number(candidate.revision);
75
- if (candidate.type === 'models-sync' || candidate.type === 'models-migrate') {
76
- return exactKeys(candidate, ['type', 'requestId', 'provider', 'revision'])
77
- ? { ok: true, value: { type: candidate.type, requestId, provider, revision } } : fail;
78
- }
79
- if (candidate.type === 'models-save') {
80
- return exactKeys(candidate, ['type', 'requestId', 'provider', 'revision', 'model']) && validManual(provider, candidate.model)
81
- ? { ok: true, value: { type: 'models-save', requestId, provider, revision, model: candidate.model } } : fail;
82
- }
83
- if (candidate.type === 'models-delete') {
84
- return exactKeys(candidate, ['type', 'requestId', 'provider', 'revision', 'id']) && modelText(candidate.id)
85
- ? { ok: true, value: { type: 'models-delete', requestId, provider, revision, id: candidate.id } } : fail;
86
- }
87
- return fail;
88
- }
89
62
  switch (candidate.type) {
90
63
  case 'refresh':
91
64
  return exactKeys(candidate, ['requestId', 'type'])
@@ -1,5 +1,5 @@
1
1
  /** Browser half of the standalone DSH OAuth Accounts page. */
2
- import type { Context } from '@deepseek-ai/cordis';
2
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
3
3
  import { type OAuthLocaleKey } from './locales.js';
4
4
  export { OAuthAccountsSection } from './OAuthAccountsSection.js';
5
5
  export { OAuthAccountsController } from './store.js';
@@ -12,5 +12,5 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
12
12
  /** Required browser services for settings placement and localized copy. */
13
13
  export declare const inject: string[];
14
14
  /** Register the OAuth account page and its app-lifetime connection controller. */
15
- export declare function apply(ctx: Context): void;
15
+ export declare function apply(ctx: ClientContext): void;
16
16
  //# sourceMappingURL=index.d.ts.map
@@ -1,39 +1,5 @@
1
1
  /** English copy for the OAuth accounts page. */
2
2
  export declare const en: {
3
- readonly modelUnsupported: "This provider does not support account discovery with the current credential. You can still add models manually.";
4
- readonly modelAuthFailed: "Model discovery was denied. Reconnect or check this account’s permissions. The previous list was retained.";
5
- readonly modelTimedOut: "Model synchronization timed out. The previous list was retained.";
6
- readonly modelRouteConflict: "This provider is owned by an existing configuration. Review the details before migration.";
7
- readonly loginSyncFailed: "Sign-in succeeded, but model synchronization failed. Open Models to retry.";
8
- readonly modelsTitle: "OAuth models";
9
- readonly modelsIntro: "Synchronize account-visible models or add a model manually. Manual models survive synchronization.";
10
- readonly modelId: "Model ID";
11
- readonly modelName: "Display name";
12
- readonly modelProtocol: "Request protocol";
13
- readonly manualModelHint: "Saving configures a request; it does not verify account access or model capabilities.";
14
- readonly saveModel: "Save model";
15
- readonly modelConnected: "Connected";
16
- readonly accountCatalog: "Account-synchronized list";
17
- readonly builtinCatalog: "Built-in list · account access unverified";
18
- readonly lastModelSync: "Last successful synchronization";
19
- readonly matchedModels: "Matched";
20
- readonly pendingModels: "Needs configuration";
21
- readonly manualModels: "Manual";
22
- readonly modelConflict: "Models changed elsewhere. The list was refreshed; review it and retry.";
23
- readonly modelOperationFailed: "The model operation failed. Review the details before retrying.";
24
- readonly reviewMigration: "Review migration";
25
- readonly migrationDescription: "Move this OAuth provider configuration to plugin management while preserving its ID and credentials. The following configuration fields will be retained:";
26
- readonly confirmMigration: "Migrate this provider";
27
- readonly modelWorking: "Working…";
28
- readonly syncModels: "Synchronize models";
29
- readonly addModel: "Add model";
30
- readonly configureModel: "Configure";
31
- readonly editModel: "Edit";
32
- readonly deleteModel: "Delete manual entry";
33
- readonly noModels: "No selectable models. Configure an unmatched model or add one manually.";
34
- readonly modelsPagination: "OAuth model pages";
35
- readonly previousModels: "Previous";
36
- readonly nextModels: "Next";
37
3
  readonly nav: "OAuth Accounts";
38
4
  readonly title: "OAuth Accounts";
39
5
  readonly intro: "Connect subscription-backed providers. Their models appear in separate OAuth groups, apart from API-key providers.";
@@ -1,6 +1,5 @@
1
1
  /** Browser state machine for the plugin-owned OAuth WebSocket. */
2
2
  import type { AccountView, PromptView, ProviderId, ServerMessage } from '../protocol.js';
3
- import type { ModelPage, ManualModel } from '../model-types.js';
4
3
  export interface NoticeView {
5
4
  message: string;
6
5
  url?: string;
@@ -22,12 +21,6 @@ export interface AttemptView {
22
21
  export interface OAuthAccountsSnapshot {
23
22
  connection: 'connecting' | 'open' | 'reconnecting' | 'closed';
24
23
  accounts: readonly AccountView[];
25
- modelPages?: Partial<Record<ProviderId, ModelPage>>;
26
- modelBusy?: Partial<Record<ProviderId, boolean>>;
27
- modelErrors?: Partial<Record<ProviderId, {
28
- code: string;
29
- message: string;
30
- }>>;
31
24
  attempt?: AttemptView;
32
25
  error?: string;
33
26
  errorDetail?: string;
@@ -47,9 +40,6 @@ export declare class OAuthAccountsController {
47
40
  private reconnectTimer;
48
41
  private reconnectDelay;
49
42
  private disposed;
50
- private modelsVisible;
51
- private readonly modelRequests;
52
- private readonly latestModelReads;
53
43
  constructor();
54
44
  readonly subscribe: (listener: Listener) => (() => void);
55
45
  readonly getSnapshot: () => OAuthAccountsSnapshot;
@@ -57,13 +47,6 @@ export declare class OAuthAccountsController {
57
47
  respond(attemptId: string, promptId: string, value: string): void;
58
48
  cancel(attemptId: string): void;
59
49
  forget(provider: ProviderId): void;
60
- loadModels(): void;
61
- readModels(provider: ProviderId, offset?: number): void;
62
- syncModels(provider: ProviderId): void;
63
- migrateModels(provider: ProviderId): void;
64
- saveModel(provider: ProviderId, model: ManualModel): void;
65
- deleteModel(provider: ProviderId, id: string): void;
66
- private writeModels;
67
50
  retry(): void;
68
51
  dispose(): void;
69
52
  private connect;
@@ -1,3 +1,3 @@
1
1
  /** Plugin-owned styles, injected and removed with the browser Cordis fiber. */
2
- export declare const styles = "\n.dsh-oauth-section{display:flex;flex-direction:column;gap:12px;max-width:720px;color:var(--dsw-alias-label-primary)}\n.dsh-oauth-title{margin:0;font-size:16px;line-height:24px;font-weight:500}\n.dsh-oauth-intro,.dsh-oauth-muted{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}\n.dsh-oauth-error{display:flex;flex-direction:column;gap:6px;margin:0;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}.dsh-oauth-error p{margin:0}.dsh-oauth-error pre{box-sizing:border-box;max-width:100%;max-height:320px;margin:0;padding:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;overflow:auto;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:11px/17px ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.dsh-oauth-error-details summary{cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none}.dsh-oauth-error-details[open] summary{margin-bottom:6px}\n.dsh-oauth-list{display:flex;flex-direction:column;gap:8px;margin:4px 0 0;padding:0;list-style:none}\n.dsh-oauth-card{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px}\n.dsh-oauth-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.dsh-oauth-name{font-size:14px;font-weight:500}.dsh-oauth-state{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);font-size:12px;color:var(--dsw-alias-label-secondary)}\n.dsh-oauth-dot{width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-label-tertiary)}.dsh-oauth-state[data-state=ready] .dsh-oauth-dot{background:var(--dsw-alias-state-success-primary)}.dsh-oauth-state[data-state=warning] .dsh-oauth-dot{background:var(--dsw-alias-state-error-primary)}.dsh-oauth-state[data-state=authorizing] .dsh-oauth-dot{background:var(--dsw-alias-button-info-fill)}\n.dsh-oauth-actions{display:flex;gap:6px;margin-left:auto}.dsh-oauth-button{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;min-height:30px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:15px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px;cursor:pointer}.dsh-oauth-button[data-variant=primary]{border-color:transparent;background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.dsh-oauth-button[data-variant=danger]{border-color:transparent;color:var(--dsw-alias-state-error-primary)}.dsh-oauth-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-button[data-variant=primary]:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover);color:var(--dsw-alias-label-primary-foreground)}.dsh-oauth-button[data-variant=danger]:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.dsh-oauth-button:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.dsh-oauth-button:disabled{cursor:not-allowed;opacity:.4}\n.dsh-oauth-flow{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l3);font-size:13px;line-height:20px}.dsh-oauth-link{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:8px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);text-decoration:none}.dsh-oauth-link:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-link-label{font-weight:500}.dsh-oauth-link-host{max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsh-oauth-link-icon{color:var(--dsw-alias-brand-text)}.dsh-oauth-code{display:flex;align-items:center;gap:8px}.dsh-oauth-code code{padding:5px 8px;border-radius:6px;background:var(--dsw-alias-bg-module-platform);font-size:14px;letter-spacing:.08em}\n.dsh-oauth-prompt{display:flex;flex-direction:column;gap:7px}.dsh-oauth-input,.dsh-oauth-select{box-sizing:border-box;width:100%;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}\n\n.dsh-oauth-model-provider { border: 1px solid var(--border-color, currentColor); border-radius: 8px; padding: 16px; margin-block: 16px; }\n.dsh-oauth-model-provider button { margin: 4px; }\n.dsh-oauth-model-list { padding: 0; list-style: none; }\n.dsh-oauth-model-list li { padding-block: 8px; border-bottom: 1px solid var(--border-color, currentColor); overflow-wrap: anywhere; }\n.dsh-oauth-model-list code { margin-inline: 8px; }\n.dsh-oauth-model-form { display: grid; gap: 12px; margin-block: 12px; }\n.dsh-oauth-model-form label { display: grid; gap: 4px; }\n.dsh-oauth-model-provider nav { display: flex; align-items: center; gap: 8px; }\n";
2
+ export declare const styles = "\n.dsh-oauth-section{display:flex;flex-direction:column;gap:12px;max-width:720px;color:var(--dsw-alias-label-primary)}\n.dsh-oauth-title{margin:0;font-size:16px;line-height:24px;font-weight:500}\n.dsh-oauth-intro,.dsh-oauth-muted{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}\n.dsh-oauth-error{display:flex;flex-direction:column;gap:6px;margin:0;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}.dsh-oauth-error p{margin:0}.dsh-oauth-error pre{box-sizing:border-box;max-width:100%;max-height:320px;margin:0;padding:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;overflow:auto;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:11px/17px ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.dsh-oauth-error-details summary{cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none}.dsh-oauth-error-details[open] summary{margin-bottom:6px}\n.dsh-oauth-list{display:flex;flex-direction:column;gap:8px;margin:4px 0 0;padding:0;list-style:none}\n.dsh-oauth-card{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px}\n.dsh-oauth-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.dsh-oauth-name{font-size:14px;font-weight:500}.dsh-oauth-state{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);font-size:12px;color:var(--dsw-alias-label-secondary)}\n.dsh-oauth-dot{width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-label-tertiary)}.dsh-oauth-state[data-state=ready] .dsh-oauth-dot{background:var(--dsw-alias-state-success-primary)}.dsh-oauth-state[data-state=warning] .dsh-oauth-dot{background:var(--dsw-alias-state-error-primary)}.dsh-oauth-state[data-state=authorizing] .dsh-oauth-dot{background:var(--dsw-alias-button-info-fill)}\n.dsh-oauth-actions{display:flex;gap:6px;margin-left:auto}.dsh-oauth-button{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;min-height:30px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:15px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px;cursor:pointer}.dsh-oauth-button[data-variant=primary]{border-color:transparent;background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.dsh-oauth-button[data-variant=danger]{border-color:transparent;color:var(--dsw-alias-state-error-primary)}.dsh-oauth-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-button[data-variant=primary]:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover);color:var(--dsw-alias-label-primary-foreground)}.dsh-oauth-button[data-variant=danger]:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.dsh-oauth-button:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.dsh-oauth-button:disabled{cursor:not-allowed;opacity:.4}\n.dsh-oauth-flow{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l3);font-size:13px;line-height:20px}.dsh-oauth-link{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:8px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);text-decoration:none}.dsh-oauth-link:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.dsh-oauth-link-label{font-weight:500}.dsh-oauth-link-host{max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsh-oauth-link-icon{color:var(--dsw-alias-brand-text)}.dsh-oauth-code{display:flex;align-items:center;gap:8px}.dsh-oauth-code code{padding:5px 8px;border-radius:6px;background:var(--dsw-alias-bg-module-platform);font-size:14px;letter-spacing:.08em}\n.dsh-oauth-prompt{display:flex;flex-direction:column;gap:7px}.dsh-oauth-input,.dsh-oauth-select{box-sizing:border-box;width:100%;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}\n";
3
3
  //# sourceMappingURL=styles.d.ts.map
@@ -1,5 +1,4 @@
1
1
  /** Shared browser/Host wire vocabulary for the OAuth account surface. */
2
- import type { ModelCommand, ModelMessage } from './model-types.js';
3
2
  export declare const OAUTH_SOCKET_PATH = "/_edge-sky/dsh-oauth";
4
3
  export declare const OAUTH_SOCKET_PROTOCOL = "dsh-oauth-v1";
5
4
  export declare const MAX_FRAME_BYTES: number;
@@ -65,7 +64,7 @@ export type PromptView = {
65
64
  description?: string;
66
65
  }[];
67
66
  };
68
- export type ClientCommand = ModelCommand | {
67
+ export type ClientCommand = {
69
68
  type: 'refresh';
70
69
  requestId: string;
71
70
  } | {
@@ -88,7 +87,7 @@ export type ClientCommand = ModelCommand | {
88
87
  provider: ProviderId;
89
88
  };
90
89
  export type OAuthErrorCode = 'invalid-message' | 'unsupported-provider' | 'authorization-unavailable' | 'busy' | 'invalid-attempt' | 'invalid-prompt' | 'credential-read-only' | 'model-route-unavailable' | 'sign-in-failed' | 'operation-failed';
91
- export type ServerMessage = ModelMessage | {
90
+ export type ServerMessage = {
92
91
  type: 'snapshot';
93
92
  requestId?: string;
94
93
  accounts: readonly AccountView[];