@edge-sky/dsh-oauth-adapter 0.1.2-dev.2 → 0.1.3-beta.3

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.
@@ -21,10 +21,10 @@ export const providerFactories = {
21
21
  /** Validate data loaded from the settings file, including the opaque legacy profile. */
22
22
  export function validateModelStore(value) {
23
23
  if (!value || typeof value !== 'object')
24
- throw new Error('Invalid oauth-models settings');
24
+ throw new Error('Invalid OAuth modelStore settings');
25
25
  const store = value;
26
26
  if (store.version !== 1 || !store.providers || typeof store.providers !== 'object' || Array.isArray(store.providers))
27
- throw new Error('Unsupported oauth-models format');
27
+ throw new Error('Unsupported OAuth modelStore format');
28
28
  for (const [id, entry] of Object.entries(store.providers)) {
29
29
  if (!PROVIDERS.some(p => p.id === id) || !entry || !['pending', 'managed'].includes(entry.phase))
30
30
  throw new Error('Invalid OAuth model provider');
@@ -47,7 +47,7 @@ export function validateModelStore(value) {
47
47
  /** Exact IDs only: unrelated providers and display-name similarities confer no compatibility. */
48
48
  export function materialize(provider, state, catalog = providerFactories[provider]()) {
49
49
  const defaults = new Map(catalog.getModels().map(m => [m.id, m]));
50
- const parsed = PiConfig({ providers: { [provider]: state.legacy } }).providers?.[provider];
50
+ const parsed = structuredClone(PiConfig({ providers: { [provider]: state.legacy } }).providers.get()[provider]);
51
51
  if (!parsed)
52
52
  throw new Error('Missing provider profile');
53
53
  const manual = new Map(state.manual.map(m => [m.id, m]));
@@ -127,8 +127,9 @@ export function materialize(provider, state, catalog = providerFactories[provide
127
127
  maxRequestImageBytes: parsed.maxRequestImageBytes ?? 20971520,
128
128
  requestImagePixelBudget: parsed.requestImagePixelBudget ?? 4194304,
129
129
  requestImageMaxBytes: parsed.requestImageMaxBytes ?? 1048576,
130
- retryPolicy: resolveRetryPolicy(parsed.retryPolicy, `oauth-models/${provider}`),
130
+ retryPolicy: resolveRetryPolicy(parsed.retryPolicy, `dsh-oauth-adapter/${provider}`),
131
131
  configuredMaxTokens: caps,
132
+ modelErrors: new Map(),
132
133
  piProvider,
133
134
  },
134
135
  };
@@ -1,10 +1,11 @@
1
1
  /** Persistent OAuth model management and reversible ownership of existing provider routes. */
2
- import type { Context } from '@deepseek-ai/cordis';
2
+ import type { Context, Volatile } from '@deepseek-ai/cordis';
3
3
  import type { CredentialProvider } from '@deepseek-ai/dsh-credentials';
4
- import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
4
+ import type SettingsForms from '@deepseek-ai/dsh-settings';
5
5
  import type LlmRuntime from '@deepseek-ai/dsh-llm';
6
6
  import { type ProviderId } from './protocol.js';
7
7
  import { type ModelPage, type ManualModel } from './model-types.js';
8
+ import { type ModelStore } from './model-catalog.js';
8
9
  export interface ModelServiceOptions {
9
10
  timeoutMs: number;
10
11
  maxResponseBytes: number;
@@ -12,7 +13,7 @@ export interface ModelServiceOptions {
12
13
  codexClientVersion: string;
13
14
  }
14
15
  export type ModelContext = Context & {
15
- settings: SettingsProvider;
16
+ settings: SettingsForms;
16
17
  credentials: CredentialProvider;
17
18
  llm: LlmRuntime;
18
19
  };
@@ -20,6 +21,7 @@ export declare class OAuthModelService {
20
21
  private readonly ctx;
21
22
  private readonly options;
22
23
  private readonly changed;
24
+ private readonly modelStore;
23
25
  private profiles;
24
26
  private handle;
25
27
  private readonly queues;
@@ -34,7 +36,7 @@ export declare class OAuthModelService {
34
36
  private readonly signedOut;
35
37
  private readonly adapter;
36
38
  readonly ready: Promise<void>;
37
- constructor(ctx: ModelContext, options: ModelServiceOptions, changed: (provider: ProviderId) => void);
39
+ constructor(ctx: ModelContext, options: ModelServiceOptions, changed: (provider: ProviderId) => void, modelStore: Volatile<ModelStore>);
38
40
  private state;
39
41
  revision(): number;
40
42
  isManaged(provider: ProviderId): boolean;
@@ -51,7 +53,7 @@ export declare class OAuthModelService {
51
53
  /** Wait on the actual route registry; settings persistence alone does not imply adapter release. */
52
54
  private releaseLegacy;
53
55
  private ensureManaged;
54
- page(provider: ProviderId, offset: number): Promise<ModelPage>;
56
+ page(provider: ProviderId, offset: number, manualOffset?: number): Promise<ModelPage>;
55
57
  migrate(provider: ProviderId, revision: number): Promise<void>;
56
58
  saveManual(provider: ProviderId, model: ManualModel, revision: number): Promise<void>;
57
59
  deleteManual(provider: ProviderId, id: string, revision: number): Promise<void>;
@@ -1,4 +1,3 @@
1
- import z from '@deepseek-ai/schemastery';
2
1
  import { createModels } from '@earendil-works/pi-ai';
3
2
  import { SettingsConflictError } from '@deepseek-ai/dsh-settings';
4
3
  import { resolveImageAttachmentAccess } from '@deepseek-ai/dsh-llm';
@@ -8,15 +7,15 @@ import { MODEL_PAGE_SIZE, MODEL_PROTOCOLS, validManual } from './model-types.js'
8
7
  import { accountIdentity, credentialBridge, fromRecord } from './model-auth.js';
9
8
  import { materialize, providerFactories, validateModelStore } from './model-catalog.js';
10
9
  import { discoverAccountModels, ModelOperationError } from './model-discovery.js';
11
- const NS = 'oauth-models';
10
+ const NS = 'dsh-oauth-adapter';
12
11
  const LEGACY_NS = 'llm-pi-ai';
13
- const Schema = z.object({ version: z.const(1).default(1), providers: z.dict(z.any()).default({}) });
14
12
  function object(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined; }
15
13
  function failure(code, message) { throw new ModelOperationError(code, message); }
16
14
  export class OAuthModelService {
17
15
  ctx;
18
16
  options;
19
17
  changed;
18
+ modelStore;
20
19
  profiles = new Map();
21
20
  handle;
22
21
  queues = new Map();
@@ -31,11 +30,11 @@ export class OAuthModelService {
31
30
  signedOut = new Set();
32
31
  adapter;
33
32
  ready;
34
- constructor(ctx, options, changed) {
33
+ constructor(ctx, options, changed, modelStore) {
35
34
  this.ctx = ctx;
36
35
  this.options = options;
37
36
  this.changed = changed;
38
- const scope = ctx.settings.register(NS, Schema, { base: { version: 1, providers: {} }, validate: value => { validateModelStore(value); } });
37
+ this.modelStore = modelStore;
39
38
  this.adapter = new PiAiAdapter({
40
39
  profiles: () => this.profiles,
41
40
  resolveApiKey: async () => undefined,
@@ -47,8 +46,8 @@ export class OAuthModelService {
47
46
  resolveImageAccess: (attachments, ref) => resolveImageAttachmentAccess(attachments, hostPath => ctx.get('fs')?.processPathFromHostPath(hostPath), ref),
48
47
  });
49
48
  this.ready = this.initialize();
50
- this.disposers.push(scope.watch(() => {
51
- if (this.closed)
49
+ this.disposers.push(ctx.on('settings/document-updated', ns => {
50
+ if (this.closed || ns !== NS)
52
51
  return;
53
52
  for (const p of PROVIDERS)
54
53
  void this.enqueue(p.id, async () => { await this.reconcile(p.id); }).catch(error => this.report(p.id, error));
@@ -60,7 +59,7 @@ export class OAuthModelService {
60
59
  void this.enqueue(provider, async () => { await this.reconcile(provider); }).catch(error => this.report(provider, error));
61
60
  }));
62
61
  }
63
- state() { return validateModelStore(this.ctx.settings.get(NS)); }
62
+ state() { return validateModelStore(this.modelStore.get()); }
64
63
  revision() { return this.ctx.settings.describe().find(s => s.ns === NS)?.revision ?? 0; }
65
64
  isManaged(provider) { return this.profiles.has(provider); }
66
65
  checkRevision(revision) { if (revision !== this.revision())
@@ -71,7 +70,7 @@ export class OAuthModelService {
71
70
  this.checkOpen();
72
71
  materialize(provider, state);
73
72
  try {
74
- await this.ctx.settings.mutate(NS, [{ op: 'set', path: ['providers', provider], value: state }], revision);
73
+ await this.ctx.settings.mutate(NS, [{ op: 'set', path: ['modelStore', 'providers', provider], value: state }], revision);
75
74
  }
76
75
  catch (error) {
77
76
  if (error instanceof SettingsConflictError)
@@ -214,20 +213,25 @@ export class OAuthModelService {
214
213
  throw error;
215
214
  }
216
215
  }
217
- async page(provider, offset) {
216
+ async page(provider, offset, manualOffset = 0) {
218
217
  await this.ready;
219
218
  const state = this.state().providers[provider];
220
219
  const fallback = { phase: 'pending', legacy: this.legacy(provider).profile ?? {}, manual: [] };
221
- const rows = materialize(provider, state ?? fallback).rows;
220
+ const connected = await this.connected(provider);
221
+ const rows = connected ? materialize(provider, state ?? fallback).rows : [];
222
+ const automatic = rows.filter(row => row.source === 'catalog' || row.source === 'matched');
223
+ const manual = rows.filter(row => row.source === 'manual');
222
224
  const legacy = this.legacy(provider);
223
- offset = Math.min(offset, Math.max(0, Math.floor((rows.length - 1) / MODEL_PAGE_SIZE) * MODEL_PAGE_SIZE));
225
+ offset = Math.min(offset, Math.max(0, Math.floor((automatic.length - 1) / MODEL_PAGE_SIZE) * MODEL_PAGE_SIZE));
226
+ manualOffset = Math.min(manualOffset, Math.max(0, Math.floor((manual.length - 1) / MODEL_PAGE_SIZE) * MODEL_PAGE_SIZE));
224
227
  return {
225
- provider, revision: this.revision(), offset, total: rows.length, rows: rows.slice(offset, offset + MODEL_PAGE_SIZE),
228
+ provider, revision: this.revision(), offset, manualOffset, total: automatic.length + manual.length,
229
+ rows: [...automatic.slice(offset, offset + MODEL_PAGE_SIZE), ...manual.slice(manualOffset, manualOffset + MODEL_PAGE_SIZE)],
226
230
  source: state?.discovered ? 'account' : 'catalog',
227
231
  counts: { matched: rows.filter(r => r.source === 'matched' || r.source === 'catalog').length, pending: rows.filter(r => r.source === 'pending').length, manual: rows.filter(r => r.source === 'manual').length },
228
- connected: await this.connected(provider), writable: this.ctx.settings.writable,
232
+ connected, writable: this.ctx.settings.writable,
229
233
  managed: this.isManaged(provider), busy: this.syncs.has(provider),
230
- ...(state?.syncedAt === undefined ? {} : { syncedAt: state.syncedAt }),
234
+ ...(!connected || state?.syncedAt === undefined ? {} : { syncedAt: state.syncedAt }),
231
235
  ...(this.errors.has(provider) ? { error: this.errors.get(provider).message, errorCode: this.errors.get(provider).code } : {}),
232
236
  ...((state?.phase === 'pending' || !state && legacy.custom) && !legacy.blocked ? { migration: { fields: Object.keys(state?.legacy ?? legacy.profile ?? {}), models: (state?.legacy ?? legacy.profile)?.models?.length ?? 0 } } : {}),
233
237
  };
@@ -29,6 +29,7 @@ export interface ModelPage {
29
29
  provider: ProviderId;
30
30
  revision: number;
31
31
  offset: number;
32
+ manualOffset: number;
32
33
  total: number;
33
34
  rows: ModelRow[];
34
35
  source: 'catalog' | 'account';
@@ -54,6 +55,7 @@ export type ModelCommand = {
54
55
  requestId: string;
55
56
  provider: ProviderId;
56
57
  offset: number;
58
+ manualOffset: number;
57
59
  } | {
58
60
  type: 'models-sync' | 'models-migrate';
59
61
  requestId: string;
package/lib/protocol.js CHANGED
@@ -66,8 +66,8 @@ export function parseClientCommand(raw) {
66
66
  const provider = candidate.provider;
67
67
  const fail = { ok: false, requestId, message: 'model command fields are invalid' };
68
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;
69
+ return exactKeys(candidate, ['type', 'requestId', 'provider', 'offset', 'manualOffset']) && Number.isSafeInteger(candidate.offset) && Number(candidate.offset) >= 0 && Number.isSafeInteger(candidate.manualOffset) && Number(candidate.manualOffset) >= 0
70
+ ? { ok: true, value: { type: 'models-list', requestId, provider, offset: Number(candidate.offset), manualOffset: Number(candidate.manualOffset) } } : fail;
71
71
  }
72
72
  if (!Number.isSafeInteger(candidate.revision) || Number(candidate.revision) < 0)
73
73
  return fail;
@@ -6,7 +6,7 @@ export declare const en: {
6
6
  readonly modelRouteConflict: "This provider is owned by an existing configuration. Review the details before migration.";
7
7
  readonly loginSyncFailed: "Sign-in succeeded, but model synchronization failed. Open Models to retry.";
8
8
  readonly modelsTitle: "OAuth models";
9
- readonly modelsIntro: "Synchronize account-visible models or add a model manually. Manual models survive synchronization.";
9
+ readonly modelsIntro: "Models are automatically matched against the pi-ai catalog built into DSH. If a model you need is not matched, try adding it manually.";
10
10
  readonly modelId: "Model ID";
11
11
  readonly modelName: "Display name";
12
12
  readonly modelProtocol: "Request protocol";
@@ -16,7 +16,7 @@ export declare const en: {
16
16
  readonly accountCatalog: "Account-synchronized list";
17
17
  readonly builtinCatalog: "Built-in list · account access unverified";
18
18
  readonly lastModelSync: "Last successful synchronization";
19
- readonly matchedModels: "Matched";
19
+ readonly matchedModels: "Automatic matches";
20
20
  readonly pendingModels: "Needs configuration";
21
21
  readonly manualModels: "Manual";
22
22
  readonly modelConflict: "Models changed elsewhere. The list was refreshed; review it and retry.";
@@ -29,8 +29,9 @@ export declare const en: {
29
29
  readonly addModel: "Add model";
30
30
  readonly configureModel: "Configure";
31
31
  readonly editModel: "Edit";
32
+ readonly collapseModels: "Collapse";
32
33
  readonly deleteModel: "Delete manual entry";
33
- readonly noModels: "No selectable models. Configure an unmatched model or add one manually.";
34
+ readonly noModels: "No models in this group.";
34
35
  readonly modelsPagination: "OAuth model pages";
35
36
  readonly previousModels: "Previous";
36
37
  readonly nextModels: "Next";
@@ -58,7 +58,7 @@ export declare class OAuthAccountsController {
58
58
  cancel(attemptId: string): void;
59
59
  forget(provider: ProviderId): void;
60
60
  loadModels(): void;
61
- readModels(provider: ProviderId, offset?: number): void;
61
+ readModels(provider: ProviderId, offset?: number, manualOffset?: number): void;
62
62
  syncModels(provider: ProviderId): void;
63
63
  migrateModels(provider: ProviderId): void;
64
64
  saveModel(provider: ProviderId, model: ManualModel): void;
@@ -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\n.dsh-oauth-model-provider{display:flex;flex-direction:column;gap:10px;min-width:0;padding:14px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px}\n.dsh-oauth-model-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.dsh-oauth-model-header h3{min-width:0;overflow-wrap:anywhere}.dsh-oauth-model-header button{flex-shrink:0}\n.dsh-oauth-model-details{display:flex;flex-direction:column;gap:10px}.dsh-oauth-model-details[hidden]{display:none}\n.dsh-oauth-model-provider h3,.dsh-oauth-model-provider h4{margin:0;line-height:22px;font-weight:500}\n.dsh-oauth-model-provider h4{font-size:13px;color:var(--dsw-alias-label-secondary)}\n.dsh-oauth-model-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}\n.dsh-oauth-model-group{display:flex;flex-direction:column;gap:8px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2)}\n.dsh-oauth-model-list{display:flex;flex-direction:column;gap:0;margin:0;padding:0;list-style:none}\n.dsh-oauth-model-list li{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:10px 0;min-width:0}\n.dsh-oauth-model-list li>.dsh-oauth-model-form{box-sizing:border-box;width:100%;min-width:0}\n.dsh-oauth-model-list li+li{border-top:1px solid var(--dsw-alias-border-l2)}\n.dsh-oauth-model-identity{display:flex;flex-direction:column;gap:3px;min-width:0;overflow-wrap:anywhere}\n.dsh-oauth-model-identity strong{font-size:13px;line-height:20px;font-weight:500}\n.dsh-oauth-model-identity code{font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}\n.dsh-oauth-model-form{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px}\n.dsh-oauth-model-form label{display:flex;flex-direction:column;gap:5px;font-size:13px;line-height:20px}\n.dsh-oauth-pagination{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n";
3
3
  //# sourceMappingURL=styles.d.ts.map
@@ -29,6 +29,7 @@ export interface ModelPage {
29
29
  provider: ProviderId;
30
30
  revision: number;
31
31
  offset: number;
32
+ manualOffset: number;
32
33
  total: number;
33
34
  rows: ModelRow[];
34
35
  source: 'catalog' | 'account';
@@ -54,6 +55,7 @@ export type ModelCommand = {
54
55
  requestId: string;
55
56
  provider: ProviderId;
56
57
  offset: number;
58
+ manualOffset: number;
57
59
  } | {
58
60
  type: 'models-sync' | 'models-migrate';
59
61
  requestId: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@edge-sky/dsh-oauth-adapter",
3
3
  "description": "Web OAuth account surface for official DSH authorization flows",
4
- "version": "0.1.2-dev.2",
4
+ "version": "0.1.3-beta.3",
5
5
  "author": {
6
6
  "name": "edge-sky",
7
7
  "url": "https://github.com/edge-sky"
@@ -13,7 +13,7 @@
13
13
  "type": "git",
14
14
  "url": "git+https://github.com/edge-sky/dsh-oauth-adapter.git"
15
15
  },
16
- "packageManager": "pnpm@11.7.0",
16
+ "packageManager": "pnpm@11.23.0",
17
17
  "engines": {
18
18
  "node": "^22.19.0 || >=24.0.0"
19
19
  },
@@ -68,22 +68,22 @@
68
68
  }
69
69
  },
70
70
  "peerDependencies": {
71
- "@deepseek-ai/cordis": ">=4.0.2",
72
- "@deepseek-ai/dsh-authorization": ">=0.1.2-rc.1 <0.2.0-0",
73
- "@deepseek-ai/dsh-client-locale": ">=0.1.2-rc.1 <0.2.0-0",
74
- "@deepseek-ai/dsh-client-ui-settings": ">=0.1.2-rc.1 <0.2.0-0",
75
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.2-rc.1 <0.2.0-0",
76
- "@deepseek-ai/dsh-credentials": ">=0.1.2-rc.1 <0.2.0-0",
77
- "@deepseek-ai/dsh-host-webserver": ">=0.1.2-rc.1 <0.2.0-0",
78
- "@deepseek-ai/dsh-invariants": ">=0.1.2-rc.1 <0.2.0-0",
79
- "@deepseek-ai/dsh-llm": ">=0.1.2-rc.1 <0.2.0-0",
80
- "@deepseek-ai/dsh-llm-pi-ai": ">=0.1.2-rc.1 <0.2.0-0",
81
- "@deepseek-ai/dsh-settings": ">=0.1.2-rc.1 <0.2.0-0",
71
+ "@deepseek-ai/cordis": ">=4.0.4 <5",
72
+ "@deepseek-ai/dsh-authorization": ">=0.2.0-rc.2 <0.3.0-0",
73
+ "@deepseek-ai/dsh-client-locale": ">=0.2.0-rc.2 <0.3.0-0",
74
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.2.0-rc.2 <0.3.0-0",
75
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-rc.2 <0.3.0-0",
76
+ "@deepseek-ai/dsh-credentials": ">=0.2.0-rc.2 <0.3.0-0",
77
+ "@deepseek-ai/dsh-host-webserver": ">=0.2.0-rc.2 <0.3.0-0",
78
+ "@deepseek-ai/dsh-invariants": ">=0.2.0-rc.2 <0.3.0-0",
79
+ "@deepseek-ai/dsh-llm": ">=0.2.0-rc.2 <0.3.0-0",
80
+ "@deepseek-ai/dsh-llm-pi-ai": ">=0.2.0-rc.2 <0.3.0-0",
81
+ "@deepseek-ai/dsh-settings": ">=0.2.0-rc.2 <0.3.0-0",
82
82
  "react": ">=18.2.0",
83
- "@deepseek-ai/dsh-client-ui-settings-models": ">=0.1.2-rc.1 <0.2.0-0",
84
- "@deepseek-ai/dsh-attachment": ">=0.1.2-rc.1 <0.2.0-0",
85
- "@deepseek-ai/dsh-fs": ">=0.1.2-rc.1 <0.2.0-0",
86
- "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.2-rc.1 <0.2.0-0"
83
+ "@deepseek-ai/dsh-client-ui-settings-models": ">=0.2.0-rc.2 <0.3.0-0",
84
+ "@deepseek-ai/dsh-attachment": ">=0.2.0-rc.2 <0.3.0-0",
85
+ "@deepseek-ai/dsh-fs": ">=0.2.0-rc.2 <0.3.0-0",
86
+ "@deepseek-ai/dsh-client-ui-renderer": ">=0.2.0-rc.2 <0.3.0-0"
87
87
  },
88
88
  "peerDependenciesMeta": {
89
89
  "@deepseek-ai/cordis": {
@@ -133,22 +133,22 @@
133
133
  }
134
134
  },
135
135
  "dependencies": {
136
- "@deepseek-ai/schemastery": "^3.18.2",
136
+ "@deepseek-ai/schemastery": "~3.18.4",
137
137
  "ws": "^8.18.3",
138
- "@earendil-works/pi-ai": "0.84.4"
138
+ "@earendil-works/pi-ai": "^0.87.1"
139
139
  },
140
140
  "devDependencies": {
141
- "@deepseek-ai/cordis": "^4.0.2",
142
- "@deepseek-ai/dsh-authorization": "0.1.2-rc.1",
143
- "@deepseek-ai/dsh-client-locale": "0.1.2-rc.1",
144
- "@deepseek-ai/dsh-client-ui-settings": "0.1.2-rc.1",
145
- "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
146
- "@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
147
- "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
148
- "@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
149
- "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
150
- "@deepseek-ai/dsh-llm-pi-ai": "0.1.2-rc.1",
151
- "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
141
+ "@deepseek-ai/cordis": "~4.0.4",
142
+ "@deepseek-ai/dsh-authorization": "0.2.0-rc.2",
143
+ "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
144
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
145
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
146
+ "@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
147
+ "@deepseek-ai/dsh-host-webserver": "0.2.0-rc.2",
148
+ "@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
149
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
150
+ "@deepseek-ai/dsh-llm-pi-ai": "0.2.0-rc.2",
151
+ "@deepseek-ai/dsh-settings": "0.2.0-rc.2",
152
152
  "@types/node": "^22.20.0",
153
153
  "@types/react": "~18.3.1",
154
154
  "@types/react-dom": "~18.3.1",
@@ -158,19 +158,40 @@
158
158
  "tsdown": "^0.22.2",
159
159
  "typescript": "^6.0.3",
160
160
  "vitest": "^4.1.8",
161
- "@deepseek-ai/dsh-client-ui-settings-models": "0.1.2-rc.1",
162
- "@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
163
- "@deepseek-ai/dsh-fs": "0.1.2-rc.1",
164
- "@deepseek-ai/dsh-settings-file": "0.1.2-rc.1",
165
- "@deepseek-ai/dsh-credentials-local": "0.1.2-rc.1",
166
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
161
+ "@deepseek-ai/dsh-client-ui-settings-models": "0.2.0-rc.2",
162
+ "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
163
+ "@deepseek-ai/dsh-fs": "0.2.0-rc.2",
164
+ "@deepseek-ai/dsh-credentials-local": "0.2.0-rc.2",
165
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
167
166
  "jsdom": "^26.1.0",
168
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
169
- "@deepseek-ai/dsh-client-store": "0.1.2-rc.1",
170
- "@deepseek-ai/dsh-atomic-write": "0.1.2-rc.1",
171
- "@deepseek-ai/dsh-home-paths": "0.1.2-rc.1",
172
- "@deepseek-ai/dsh-launch-environment": "0.1.2-rc.1",
173
- "@deepseek-ai/dsh-session": "0.1.2-rc.1",
174
- "@deepseek-ai/dsh-scope": "0.1.2-rc.1"
167
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
168
+ "@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
169
+ "@deepseek-ai/dsh-atomic-write": "0.2.0-rc.2",
170
+ "@deepseek-ai/dsh-home-paths": "0.2.0-rc.2",
171
+ "@deepseek-ai/dsh-launch-environment": "0.2.0-rc.2",
172
+ "@deepseek-ai/dsh-session": "0.2.0-rc.2",
173
+ "@deepseek-ai/dsh-scope": "0.2.0-rc.2",
174
+ "clsx": "^2.0.0",
175
+ "anser": "^2.3.5",
176
+ "shiki": "^4.3.1",
177
+ "@shikijs/langs": "^4.3.1",
178
+ "mdast-util-from-markdown": "^2.0.3",
179
+ "mdast-util-gfm": "^3.1.0",
180
+ "mdast-util-math": "^3.0.0",
181
+ "micromark-extension-gfm": "^3.0.0",
182
+ "micromark-extension-math": "^3.1.0",
183
+ "micromark-core-commonmark": "^2.0.3",
184
+ "micromark-util-character": "^2.1.1",
185
+ "micromark-util-classify-character": "^2.0.1",
186
+ "micromark-util-symbol": "^2.0.1",
187
+ "micromark-factory-space": "^2.0.1",
188
+ "micromark-util-sanitize-uri": "^2.0.1",
189
+ "katex": "^0.16.47",
190
+ "zustand": "~4.4.7",
191
+ "immer": "^10.1.1",
192
+ "@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.2",
193
+ "@deepseek-ai/dsh-util-code-language": "0.2.0-rc.2",
194
+ "diff": "^9.0.0",
195
+ "simple-icons": "16.31.0"
175
196
  }
176
197
  }