dsh-qoder-connect 0.2.6 → 0.2.8

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.d.ts CHANGED
@@ -87,26 +87,56 @@ declare const QODER_GLOBAL_AUTH_PATH = "/plugins/dsh-qoder-connect/global/auth";
87
87
  * One action the PAT route accepts.
88
88
  *
89
89
  * `save-pat` validates the pasted token against the variant's region and, only
90
- * on success, persists it; `clear` removes the stored credential. There is no
91
- * device flow and no credential import: the token is the whole auth surface.
90
+ * on success, appends it to the saved list and makes it active; `activate`
91
+ * switches to another saved entry; `remove` deletes one saved entry; `clear`
92
+ * removes every saved credential. There is no device flow and no credential
93
+ * import: the token is the whole auth surface.
94
+ *
95
+ * All of these are writes — `activate` changes which account signs later
96
+ * requests — so the route requires the in-process key even for the two that do
97
+ * not touch a token's bytes.
92
98
  */
93
- type QoderAuthAction = 'save-pat' | 'clear';
99
+ type QoderAuthAction = 'save-pat' | 'activate' | 'remove' | 'clear';
94
100
  /** Request body accepted by the PAT route. */
95
101
  interface QoderAuthRequest {
96
102
  action: QoderAuthAction;
97
103
  /** The token to validate and store; required for `save-pat`. */
98
104
  pat?: string;
105
+ /** Optional display label for a saved entry; `save-pat` only. */
106
+ label?: string;
107
+ /** Target saved entry; required for `activate` and `remove`. */
108
+ id?: string;
99
109
  }
100
110
  /** Where the credential in effect came from. */
101
111
  type QoderCredentialSource = 'card' | 'env' | 'cli';
102
112
  /**
103
- * Redacted summary of one variant's PAT.
113
+ * One saved PAT, redacted for the switch select.
114
+ *
115
+ * Never the token itself: the id is a one-way hash prefix, the label is
116
+ * display copy, and the tail is the last four characters — enough to tell two
117
+ * saved tokens apart, not enough to misuse one.
118
+ */
119
+ interface QoderPatProfile {
120
+ id: string;
121
+ /** Optional display label supplied when the token was saved. */
122
+ label?: string;
123
+ /** Last four characters of the token. */
124
+ patTail?: string;
125
+ /** When this token was saved, epoch milliseconds. */
126
+ savedAtMs?: number;
127
+ }
128
+ /**
129
+ * Redacted summary of one variant's active PAT.
104
130
  *
105
131
  * Never the token itself: the browser half displays where the credential came
106
132
  * from, when it was saved, and the last four characters — enough to tell two
107
133
  * tokens apart, not enough to misuse one.
108
134
  */
109
135
  interface QoderPatSummary {
136
+ /** Local id of the saved entry in effect; absent for the environment token. */
137
+ id?: string;
138
+ /** Display label of the saved entry in effect, when it carries one. */
139
+ label?: string;
110
140
  source: QoderCredentialSource;
111
141
  /** When the stored token was saved, epoch milliseconds; absent for env fallback. */
112
142
  savedAtMs?: number;
@@ -255,6 +285,16 @@ type QoderWebStatus = {
255
285
  region?: QoderRegion;
256
286
  /** Redacted summary of the PAT in effect. */
257
287
  pat?: QoderPatSummary;
288
+ /**
289
+ * Every saved token for this variant, newest first, redacted.
290
+ *
291
+ * One entry means the card has nothing to switch; two or more render the
292
+ * account select. The list is variant-scoped, like the credential file
293
+ * itself: a token minted for one region is never offered to the other.
294
+ */
295
+ patProfiles?: readonly QoderPatProfile[];
296
+ /** The saved entry in effect; absent when the token comes from the environment. */
297
+ activePatId?: string;
258
298
  credits?: QoderWebCredits;
259
299
  creditsError?: string;
260
300
  /** The models the plugin serves, as catalog snapshots. */
@@ -388,12 +428,27 @@ declare const QODER_AUTH_FILENAME = ".qoder-auth.json";
388
428
  interface AuthLogger {
389
429
  warn?(...args: unknown[]): void;
390
430
  }
431
+ /** One saved credential, as the file stores it. */
432
+ interface QoderCredentialEntry {
433
+ /** Stable local id (a one-way hash prefix of the token). */
434
+ id: string;
435
+ pat: string;
436
+ region: QoderRegion;
437
+ /** When the stored token was saved, epoch milliseconds. */
438
+ savedAtMs?: number;
439
+ /** Optional display label; never sent upstream. */
440
+ label?: string;
441
+ }
391
442
  /** Runtime credential, timestamps in epoch milliseconds. */
392
443
  interface QoderCredential {
444
+ /** Local id of the saved entry; absent for an environment token. */
445
+ id?: string;
393
446
  pat: string;
394
447
  region: QoderRegion;
395
448
  /** When the stored token was saved; absent for environment-sourced tokens. */
396
449
  savedAtMs?: number;
450
+ /** Display label, when the saved entry carries one. */
451
+ label?: string;
397
452
  source: QoderCredentialSource;
398
453
  }
399
454
  /** Read-only sign-in summary for status, the PAT route, and doctor output. */
@@ -407,6 +462,10 @@ interface QoderAuthStatus {
407
462
  reason?: string;
408
463
  /** Absolute path of the credential file this store owns. */
409
464
  filePath: string;
465
+ /** Every saved token for this variant, newest first, redacted. */
466
+ profiles?: readonly QoderPatProfile[];
467
+ /** The entry id in effect, when a saved entry is active. */
468
+ activeId?: string;
410
469
  }
411
470
  /** Constructor options. */
412
471
  interface QoderStoreOptions {
@@ -418,13 +477,21 @@ interface QoderStoreOptions {
418
477
  }
419
478
  /** Last four characters of a token, for display. */
420
479
  declare function patTail(pat: string): string | undefined;
480
+ /**
481
+ * Stable local id for one token.
482
+ *
483
+ * A PAT is a bearer secret, so the id is a one-way hash prefix rather than
484
+ * any suffix of it: surfaces that show the id (the select, status documents,
485
+ * logs) leak nothing usable, while two saves of the same token still resolve
486
+ * to the same entry.
487
+ */
488
+ declare function qoderCredentialId(pat: string): string;
421
489
  /**
422
490
  * Stable identity key for a credential, used by probe records and saved
423
491
  * catalogs.
424
492
  *
425
- * A PAT is a bearer secret, so the key is its one-way hash rather than any
426
- * suffix of it: surfaces that show the key (cards, JSON diagnostics) leak
427
- * nothing usable, while two reads of the same token still correlate locally.
493
+ * Deliberately wider than the local id: these keys address stored data, not
494
+ * UI, and 16 hex characters keep two tokens from ever sharing a bucket.
428
495
  */
429
496
  declare function qoderCredentialIdentity(credential: Pick<QoderCredential, 'pat'>): string;
430
497
  declare class QoderCredentialStore {
@@ -439,10 +506,11 @@ declare class QoderCredentialStore {
439
506
  /**
440
507
  * The credential currently in effect, or `undefined` when there is none.
441
508
  *
442
- * Resolution order: the plugin-owned file, then the arm's environment
443
- * variable (only when no file exists — a saved token always wins over a
444
- * stray env), then nothing. There is no refresh and no expiry check: a PAT
445
- * lives until revoked.
509
+ * Resolution order: the active saved entry (or the first one when the
510
+ * pointer is stale), then the arm's environment variable (only when no
511
+ * saved entry exists — a saved token always wins over a stray env), then
512
+ * nothing. There is no refresh and no expiry check: a PAT lives until
513
+ * revoked.
446
514
  */
447
515
  current(): Promise<QoderCredential | undefined>;
448
516
  /**
@@ -455,12 +523,48 @@ declare class QoderCredentialStore {
455
523
  resolve(): Promise<QoderCredential>;
456
524
  /** The token in effect as a bare string, rejecting when there is none. */
457
525
  patPromise(): Promise<string>;
526
+ /** Every saved entry (tokens included; host-side callers only). */
527
+ entries(): Promise<readonly QoderCredentialEntry[]>;
528
+ /**
529
+ * Add a token and make it active.
530
+ *
531
+ * Re-saving an existing token updates that entry (its saved time, and its
532
+ * label when one is supplied) instead of appending a duplicate — the id is
533
+ * derived from the token, so "same PAT" is the same entry by construction.
534
+ * The caller is responsible for having validated the token against the
535
+ * region first (`validateApiKey` upstream).
536
+ *
537
+ * @param pat - the token to store.
538
+ * @param label - optional display label for the select row.
539
+ * @returns the stored entry.
540
+ */
541
+ add(pat: string, label?: string): Promise<QoderCredentialEntry>;
458
542
  /**
459
- * Persist a token for this variant. The caller is responsible for having
460
- * validated it against the region first (`validateApiKey` upstream).
543
+ * Make one saved entry active.
544
+ *
545
+ * @param id - the entry id.
546
+ * @returns the activated entry, or `undefined` when no entry has that id.
547
+ */
548
+ activate(id: string): Promise<QoderCredentialEntry | undefined>;
549
+ /**
550
+ * Remove one saved entry.
551
+ *
552
+ * The active pointer moves to the first remaining entry; removing the last
553
+ * entry deletes the file, exactly like {@link clear} — a saved token is the
554
+ * only thing that shadows the environment fallback.
555
+ *
556
+ * @param id - the entry id.
557
+ * @returns the removed entry, or `undefined` when no entry has that id.
558
+ */
559
+ remove(id: string): Promise<QoderCredentialEntry | undefined>;
560
+ /**
561
+ * Persist a token for this variant and make it active.
562
+ *
563
+ * Kept for the CLI and older call sites: it is {@link add} behind the
564
+ * runtime-credential shape they expect.
461
565
  */
462
566
  save(pat: string, source?: QoderCredentialSource): Promise<QoderCredential>;
463
- /** Remove the stored credential. A missing file is not an error. */
567
+ /** Remove every stored credential. A missing file is not an error. */
464
568
  clear(): Promise<void>;
465
569
  /** Alias kept for call-site readability (`logout` is just clearing). */
466
570
  logout(): Promise<void>;
@@ -470,7 +574,7 @@ declare class QoderCredentialStore {
470
574
  private pendingLegacyReason;
471
575
  private fromEnvironment;
472
576
  private readOwn;
473
- private writeOwn;
577
+ private writeDocument;
474
578
  }
475
579
  /**
476
580
  * The plugin-owned credential path for one variant's default filename.
@@ -850,6 +954,18 @@ type QoderChatResult = {
850
954
  status: number;
851
955
  kind: UpstreamErrorKind;
852
956
  message: string;
957
+ /**
958
+ * The upstream's own "try again in N ms" answer (Qoder's queue body carries
959
+ * `retryAfterSeconds`, and a plain 429 may carry `Retry-After`).
960
+ *
961
+ * It has to survive this layer to be usable: the shim turns it into the
962
+ * `Retry-After` header and the `[retry-after=<n>s]` message tag, and the
963
+ * adapter reads that tag back into `failure.providerRetryAfterMs` — the
964
+ * only field the harness retry policy honors. Dropped here, the host can
965
+ * only fall back to its own backoff (observed: 19134ms against an upstream
966
+ * that asked for 30s, so every retry hit the still-closed window).
967
+ */
968
+ retryAfterMs?: number;
853
969
  };
854
970
  /** Provenance of the last successful catalog fetch. */
855
971
  interface QoderCatalogFetch {
@@ -1146,6 +1262,15 @@ declare class QoderProbeStore {
1146
1262
  set(modelId: string, record: QoderProbeRecord): void;
1147
1263
  /** Drop every record; used by the card's explicit "clear" action. */
1148
1264
  clear(): void;
1265
+ /**
1266
+ * Drop every record that belongs to one account, leaving the other saved
1267
+ * accounts' observations intact.
1268
+ *
1269
+ * Used when a saved token is removed: its detected effort levels are that
1270
+ * account's data, and no other account can use them. A plain {@link clear}
1271
+ * here would erase the observations of every account the user still has.
1272
+ */
1273
+ clearFor(account: string): void;
1149
1274
  /** Every record currently held, for status display. */
1150
1275
  all(): Readonly<Record<string, QoderProbeRecord>>;
1151
1276
  /** Build a record stamped with this store's clock, version, and account. */
@@ -1346,22 +1471,27 @@ declare class QoderProbeService {
1346
1471
  }
1347
1472
  //#endregion
1348
1473
  //#region src/auth-route.d.ts
1349
- /** Outcome of a save attempt: stored, or refused with a stable reason. */
1474
+ /** Outcome of a save/activate/remove attempt: stored, or refused with a stable reason. */
1350
1475
  type QoderAuthSaveResult = {
1351
1476
  ok: true;
1352
1477
  status: QoderAuthStatus;
1353
1478
  } | {
1354
1479
  ok: false;
1355
- error: 'qoder_invalid_pat' | 'qoder_missing_pat';
1480
+ error: 'qoder_invalid_pat' | 'qoder_missing_pat' | 'qoder_unknown_pat';
1356
1481
  };
1357
1482
  /** Constructor dependencies. */
1358
1483
  interface QoderAuthRouteOptions {
1359
1484
  /**
1360
1485
  * Validate one pasted token against this variant's region and, only on
1361
- * success, persist it. The route never decides validity itself.
1486
+ * success, persist it and make it active. The route never decides validity
1487
+ * itself.
1362
1488
  */
1363
- save: (pat: string) => Promise<QoderAuthSaveResult>;
1364
- /** Remove the stored credential. */
1489
+ save: (pat: string, label?: string) => Promise<QoderAuthSaveResult>;
1490
+ /** Switch to one already-saved token. */
1491
+ activate: (id: string) => Promise<QoderAuthSaveResult>;
1492
+ /** Remove one saved token; the host moves the active pointer. */
1493
+ remove: (id: string) => Promise<QoderAuthSaveResult>;
1494
+ /** Remove every saved token (`logout`). */
1365
1495
  clear: () => Promise<void>;
1366
1496
  /**
1367
1497
  * Route path to mount. Defaults to the China variant's path so callers that
@@ -1375,7 +1505,7 @@ declare function createAuthKey(): string;
1375
1505
  * The PAT route's handler, extracted so tests can mount it on a bare server
1376
1506
  * with a known key.
1377
1507
  *
1378
- * @param deps - the save/clear operations for one variant.
1508
+ * @param deps - the save/activate/remove/clear operations for one variant.
1379
1509
  * @param key - the in-process control key this route requires.
1380
1510
  * @returns the Node request handler.
1381
1511
  */
@@ -1568,4 +1698,4 @@ declare const QUOTA_SECTION_KEYS: readonly ["sidebarQuotaCN", "sidebarQuotaGloba
1568
1698
  */
1569
1699
  declare function apply(ctx: Context, config: Config): void;
1570
1700
  //#endregion
1571
- export { CHINA_VARIANT, CN_SECTION_KEYS, Config, FALLBACK_QODER_MODELS, GLOBAL_SECTION_KEYS, GLOBAL_VARIANT, KIND_STATUS, PROBE_EFFORT_CANDIDATES, type ProbeAttempt, type ProbeOutcome, type ProbeSender, QODER_AUTH_FILENAME, QODER_AUTH_PATH, QODER_DATA_DIR_ENV, QODER_DATA_DIR_NAME, QODER_GLOBAL_AUTH_PATH, QODER_GLOBAL_PROBE_PATH, QODER_GLOBAL_SETTINGS_NS, QODER_GLOBAL_STATUS_PATH, QODER_HOST_HEARTBEAT_FILENAME, QODER_PAT_ENV_CN, QODER_PAT_ENV_GLOBAL, QODER_PROBE_FILENAME, QODER_PROBE_PATH, QODER_PROVIDER, QODER_QUOTA_SETTINGS_NS, QODER_SETTINGS_NS, QODER_STATUS_PATH, QODER_STREAM_IDLE_TIMEOUT_MS, QODER_VARIANTS, QUOTA_POLL_DEFAULT_MS, QUOTA_POLL_MIN_MS, QUOTA_SECTION_KEYS, type QoderAdapter, type QoderAuthRequest, type QoderAuthRouteOptions, type QoderAuthSaveResult, type QoderAuthStatus, QoderCatalog, type QoderCatalogFetch, type QoderCatalogModelSnapshot, QoderCatalogStore, type QoderCatalogStoreOptions, type QoderChatResult, type QoderCredential, QoderCredentialStore, type QoderCreditAccount, type QoderCredits, type QoderEffort, type QoderHostHeartbeat, type QoderModelBilling, type QoderModelInfo, type QoderModelReasoning, type QoderPatSummary, type QoderProbeAction, type QoderProbeRecord, QoderProbeService, type QoderProbeStatus, QoderProbeStore, type QoderProbeValidation, type QoderShim, QoderUpstreamClient, type QoderVariant, type QoderVariantId, type QoderWebCatalog, type QoderWebCreditAccount, type QoderWebCredits, type QoderWebProbeModel, type QoderWebProbeSection, type QoderWebStatus, type ShimLogger, type UpstreamErrorKind, apply, classifyUpstreamError, clearHostHeartbeat, createAuthKey, createQoderAdapter, createQoderShim, fingerprintModel, inject, isHeartbeatProcessAlive, kindFromQoderFailure, modelInfoOf, name, normalizeCredits, patTail, probeModel, processStartTimeMs, qoderAuthHandler, qoderCatalogPath, qoderCredentialIdentity, qoderHostHeartbeatPath, qoderOwnAuthPath, qoderPluginDataDir, qoderProbePath, randomSentinel, readHostHeartbeat, registerQoderAuthRoute, variantFor };
1701
+ export { CHINA_VARIANT, CN_SECTION_KEYS, Config, FALLBACK_QODER_MODELS, GLOBAL_SECTION_KEYS, GLOBAL_VARIANT, KIND_STATUS, PROBE_EFFORT_CANDIDATES, type ProbeAttempt, type ProbeOutcome, type ProbeSender, QODER_AUTH_FILENAME, QODER_AUTH_PATH, QODER_DATA_DIR_ENV, QODER_DATA_DIR_NAME, QODER_GLOBAL_AUTH_PATH, QODER_GLOBAL_PROBE_PATH, QODER_GLOBAL_SETTINGS_NS, QODER_GLOBAL_STATUS_PATH, QODER_HOST_HEARTBEAT_FILENAME, QODER_PAT_ENV_CN, QODER_PAT_ENV_GLOBAL, QODER_PROBE_FILENAME, QODER_PROBE_PATH, QODER_PROVIDER, QODER_QUOTA_SETTINGS_NS, QODER_SETTINGS_NS, QODER_STATUS_PATH, QODER_STREAM_IDLE_TIMEOUT_MS, QODER_VARIANTS, QUOTA_POLL_DEFAULT_MS, QUOTA_POLL_MIN_MS, QUOTA_SECTION_KEYS, type QoderAdapter, type QoderAuthRequest, type QoderAuthRouteOptions, type QoderAuthSaveResult, type QoderAuthStatus, QoderCatalog, type QoderCatalogFetch, type QoderCatalogModelSnapshot, QoderCatalogStore, type QoderCatalogStoreOptions, type QoderChatResult, type QoderCredential, type QoderCredentialEntry, QoderCredentialStore, type QoderCreditAccount, type QoderCredits, type QoderEffort, type QoderHostHeartbeat, type QoderModelBilling, type QoderModelInfo, type QoderModelReasoning, type QoderPatProfile, type QoderPatSummary, type QoderProbeAction, type QoderProbeRecord, QoderProbeService, type QoderProbeStatus, QoderProbeStore, type QoderProbeValidation, type QoderShim, QoderUpstreamClient, type QoderVariant, type QoderVariantId, type QoderWebCatalog, type QoderWebCreditAccount, type QoderWebCredits, type QoderWebProbeModel, type QoderWebProbeSection, type QoderWebStatus, type ShimLogger, type UpstreamErrorKind, apply, classifyUpstreamError, clearHostHeartbeat, createAuthKey, createQoderAdapter, createQoderShim, fingerprintModel, inject, isHeartbeatProcessAlive, kindFromQoderFailure, modelInfoOf, name, normalizeCredits, patTail, probeModel, processStartTimeMs, qoderAuthHandler, qoderCatalogPath, qoderCredentialId, qoderCredentialIdentity, qoderHostHeartbeatPath, qoderOwnAuthPath, qoderPluginDataDir, qoderProbePath, randomSentinel, readHostHeartbeat, registerQoderAuthRoute, variantFor };