@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/README.i18n.yaml +2 -2
- package/README.md +27 -63
- package/README.zh.md +27 -63
- package/lib/client.js +7 -500
- package/lib/index.d.ts +3 -7
- package/lib/index.js +78 -82
- package/lib/protocol.d.ts +2 -3
- package/lib/protocol.js +0 -27
- package/lib/types/client/index.d.ts +2 -2
- package/lib/types/client/locales.d.ts +0 -34
- package/lib/types/client/store.d.ts +0 -17
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/protocol.d.ts +2 -3
- package/package.json +35 -62
- package/lib/model-auth.d.ts +0 -8
- package/lib/model-auth.js +0 -48
- package/lib/model-catalog.d.ts +0 -27
- package/lib/model-catalog.js +0 -145
- package/lib/model-discovery.d.ts +0 -22
- package/lib/model-discovery.js +0 -147
- package/lib/model-service.d.ts +0 -64
- package/lib/model-service.js +0 -371
- package/lib/model-types.d.ts +0 -92
- package/lib/model-types.js +0 -25
- package/lib/types/client/OAuthModelsSection.d.ts +0 -11
- package/lib/types/model-types.d.ts +0 -92
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'
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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,
|
|
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,
|
|
294
|
-
|
|
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.
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
},
|
|
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.
|
|
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
|
|
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
|
|
481
|
+
const connection = new OAuthConnection(ctx, socket, debug, refresh);
|
|
485
482
|
connections.add(connection);
|
|
486
|
-
socket.once('close', () => {
|
|
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
|
|
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 =
|
|
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 =
|
|
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 {
|
|
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:
|
|
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
|
|
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
|
package/lib/types/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 =
|
|
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 =
|
|
90
|
+
export type ServerMessage = {
|
|
92
91
|
type: 'snapshot';
|
|
93
92
|
requestId?: string;
|
|
94
93
|
accounts: readonly AccountView[];
|