dsh-qoder-connect 0.2.6 → 0.2.7
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.en.md +21 -6
- package/README.md +21 -6
- package/assets/3.png +0 -0
- package/assets/5.png +0 -0
- package/assets/6.png +0 -0
- package/assets/7.png +0 -0
- package/assets/8.png +0 -0
- package/lib/bin.js +109 -13
- package/lib/client.js +301 -117
- package/lib/index.d.ts +140 -22
- package/lib/index.js +72 -212
- package/lib/{variants-COxX6r1j.js → variants-B_ZzENbC.js} +459 -49
- package/package.json +1 -1
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,
|
|
91
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
426
|
-
*
|
|
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
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
* lives until
|
|
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[]>;
|
|
458
528
|
/**
|
|
459
|
-
*
|
|
460
|
-
*
|
|
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>;
|
|
542
|
+
/**
|
|
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
|
|
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
|
|
577
|
+
private writeDocument;
|
|
474
578
|
}
|
|
475
579
|
/**
|
|
476
580
|
* The plugin-owned credential path for one variant's default filename.
|
|
@@ -1146,6 +1250,15 @@ declare class QoderProbeStore {
|
|
|
1146
1250
|
set(modelId: string, record: QoderProbeRecord): void;
|
|
1147
1251
|
/** Drop every record; used by the card's explicit "clear" action. */
|
|
1148
1252
|
clear(): void;
|
|
1253
|
+
/**
|
|
1254
|
+
* Drop every record that belongs to one account, leaving the other saved
|
|
1255
|
+
* accounts' observations intact.
|
|
1256
|
+
*
|
|
1257
|
+
* Used when a saved token is removed: its detected effort levels are that
|
|
1258
|
+
* account's data, and no other account can use them. A plain {@link clear}
|
|
1259
|
+
* here would erase the observations of every account the user still has.
|
|
1260
|
+
*/
|
|
1261
|
+
clearFor(account: string): void;
|
|
1149
1262
|
/** Every record currently held, for status display. */
|
|
1150
1263
|
all(): Readonly<Record<string, QoderProbeRecord>>;
|
|
1151
1264
|
/** Build a record stamped with this store's clock, version, and account. */
|
|
@@ -1346,22 +1459,27 @@ declare class QoderProbeService {
|
|
|
1346
1459
|
}
|
|
1347
1460
|
//#endregion
|
|
1348
1461
|
//#region src/auth-route.d.ts
|
|
1349
|
-
/** Outcome of a save attempt: stored, or refused with a stable reason. */
|
|
1462
|
+
/** Outcome of a save/activate/remove attempt: stored, or refused with a stable reason. */
|
|
1350
1463
|
type QoderAuthSaveResult = {
|
|
1351
1464
|
ok: true;
|
|
1352
1465
|
status: QoderAuthStatus;
|
|
1353
1466
|
} | {
|
|
1354
1467
|
ok: false;
|
|
1355
|
-
error: 'qoder_invalid_pat' | 'qoder_missing_pat';
|
|
1468
|
+
error: 'qoder_invalid_pat' | 'qoder_missing_pat' | 'qoder_unknown_pat';
|
|
1356
1469
|
};
|
|
1357
1470
|
/** Constructor dependencies. */
|
|
1358
1471
|
interface QoderAuthRouteOptions {
|
|
1359
1472
|
/**
|
|
1360
1473
|
* Validate one pasted token against this variant's region and, only on
|
|
1361
|
-
* success, persist it. The route never decides validity
|
|
1474
|
+
* success, persist it and make it active. The route never decides validity
|
|
1475
|
+
* itself.
|
|
1362
1476
|
*/
|
|
1363
|
-
save: (pat: string) => Promise<QoderAuthSaveResult>;
|
|
1364
|
-
/**
|
|
1477
|
+
save: (pat: string, label?: string) => Promise<QoderAuthSaveResult>;
|
|
1478
|
+
/** Switch to one already-saved token. */
|
|
1479
|
+
activate: (id: string) => Promise<QoderAuthSaveResult>;
|
|
1480
|
+
/** Remove one saved token; the host moves the active pointer. */
|
|
1481
|
+
remove: (id: string) => Promise<QoderAuthSaveResult>;
|
|
1482
|
+
/** Remove every saved token (`logout`). */
|
|
1365
1483
|
clear: () => Promise<void>;
|
|
1366
1484
|
/**
|
|
1367
1485
|
* Route path to mount. Defaults to the China variant's path so callers that
|
|
@@ -1375,7 +1493,7 @@ declare function createAuthKey(): string;
|
|
|
1375
1493
|
* The PAT route's handler, extracted so tests can mount it on a bare server
|
|
1376
1494
|
* with a known key.
|
|
1377
1495
|
*
|
|
1378
|
-
* @param deps - the save/clear operations for one variant.
|
|
1496
|
+
* @param deps - the save/activate/remove/clear operations for one variant.
|
|
1379
1497
|
* @param key - the in-process control key this route requires.
|
|
1380
1498
|
* @returns the Node request handler.
|
|
1381
1499
|
*/
|
|
@@ -1568,4 +1686,4 @@ declare const QUOTA_SECTION_KEYS: readonly ["sidebarQuotaCN", "sidebarQuotaGloba
|
|
|
1568
1686
|
*/
|
|
1569
1687
|
declare function apply(ctx: Context, config: Config): void;
|
|
1570
1688
|
//#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 };
|
|
1689
|
+
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 };
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { $ as configurePluginPaths, A as probeModel, B as QODER_PROBE_PATH, C as normalizeCredits, D as utc8WallClock, E as formatUtc8Date, F as QoderCatalog, G as QODER_PAT_ENV_GLOBAL, H as QODER_STATUS_PATH, I as QODER_AUTH_PATH, J as qoderCredentialId, K as QoderCredentialStore, L as QODER_GLOBAL_AUTH_PATH, M as QoderCatalogStore, N as qoderCatalogPath, O as getMachineId, P as FALLBACK_QODER_MODELS, Q as QODER_DATA_DIR_NAME, R as QODER_GLOBAL_PROBE_PATH, S as modelInfoOf, T as createQoderTransport, U as QODER_AUTH_FILENAME, V as QODER_SETTINGS_FACE_PATH, W as QODER_PAT_ENV_CN, X as qoderOwnAuthPath, Y as qoderCredentialIdentity, Z as QODER_DATA_DIR_ENV, _ as qoderProbePath, a as QODER_HOST_HEARTBEAT_FILENAME, b as classifyUpstreamError, c as processStartTimeMs, d as writeHostHeartbeat, et as qoderMachineIdPath, f as QODER_CONNECT_VERSION, g as newestFirst, h as fingerprintModel, i as variantFor, j as randomSentinel, k as PROBE_EFFORT_CANDIDATES, l as qoderHostHeartbeatPath, m as QoderProbeStore, n as GLOBAL_VARIANT, o as clearHostHeartbeat, p as QODER_PROBE_FILENAME, q as patTail, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, tt as qoderPluginDataDir, u as readHostHeartbeat, v as KIND_STATUS, w as validateApiKey, x as kindFromQoderFailure, y as QoderUpstreamClient, z as QODER_GLOBAL_STATUS_PATH } from "./variants-B_ZzENbC.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
|
-
import {
|
|
4
|
-
import { dirname, join
|
|
3
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { createProvider } from "@earendil-works/pi-ai";
|
|
7
7
|
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
@@ -60,7 +60,8 @@ function originIsLoopback(origin) {
|
|
|
60
60
|
//#endregion
|
|
61
61
|
//#region src/auth-route.ts
|
|
62
62
|
/**
|
|
63
|
-
* PAT route: saves (validate-then-persist) or clears
|
|
63
|
+
* PAT route: saves (validate-then-persist), switches, removes, or clears the
|
|
64
|
+
* tokens of one variant.
|
|
64
65
|
*
|
|
65
66
|
* The state-changing endpoints the plugin exposes share one guard shape — a
|
|
66
67
|
* loopback Host and Origin, plus the in-process key the card receives with its
|
|
@@ -111,9 +112,20 @@ function parseRequest(text) {
|
|
|
111
112
|
if (action === "save-pat") {
|
|
112
113
|
const pat = wrapped["pat"];
|
|
113
114
|
if (typeof pat !== "string" || pat.trim() === "") return void 0;
|
|
115
|
+
const label = wrapped["label"];
|
|
116
|
+
if (label !== void 0 && (typeof label !== "string" || label.length > 200)) return void 0;
|
|
114
117
|
return {
|
|
115
118
|
action,
|
|
116
|
-
pat
|
|
119
|
+
pat,
|
|
120
|
+
...label === void 0 ? {} : { label }
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (action === "activate" || action === "remove") {
|
|
124
|
+
const id = wrapped["id"];
|
|
125
|
+
if (typeof id !== "string" || id.trim() === "") return void 0;
|
|
126
|
+
return {
|
|
127
|
+
action,
|
|
128
|
+
id: id.trim()
|
|
117
129
|
};
|
|
118
130
|
}
|
|
119
131
|
}
|
|
@@ -143,7 +155,7 @@ function keyMatches$1(expected, presented) {
|
|
|
143
155
|
* The PAT route's handler, extracted so tests can mount it on a bare server
|
|
144
156
|
* with a known key.
|
|
145
157
|
*
|
|
146
|
-
* @param deps - the save/clear operations for one variant.
|
|
158
|
+
* @param deps - the save/activate/remove/clear operations for one variant.
|
|
147
159
|
* @param key - the in-process control key this route requires.
|
|
148
160
|
* @returns the Node request handler.
|
|
149
161
|
*/
|
|
@@ -177,7 +189,15 @@ function qoderAuthHandler(deps, key) {
|
|
|
177
189
|
json$2(res, 200, { ok: true });
|
|
178
190
|
return;
|
|
179
191
|
}
|
|
180
|
-
|
|
192
|
+
if (request.action === "activate") {
|
|
193
|
+
json$2(res, 200, await deps.activate(request.id));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (request.action === "remove") {
|
|
197
|
+
json$2(res, 200, await deps.remove(request.id));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
json$2(res, 200, await deps.save(request.pat, request.label));
|
|
181
201
|
} catch (error) {
|
|
182
202
|
json$2(res, 200, {
|
|
183
203
|
ok: false,
|
|
@@ -683,203 +703,6 @@ function createQoderShim(options) {
|
|
|
683
703
|
};
|
|
684
704
|
}
|
|
685
705
|
//#endregion
|
|
686
|
-
//#region src/probe-store.ts
|
|
687
|
-
/**
|
|
688
|
-
* Local record of reasoning-effort probes.
|
|
689
|
-
*
|
|
690
|
-
* What this stores is an *observation*, never a claim about the upstream: a
|
|
691
|
-
* model's row is only consulted when the catalog carries no explicit
|
|
692
|
-
* `supportedEfforts` set, and it always loses to a declared set. The plan this
|
|
693
|
-
* implements (`docs/reasoning-effort-probe-plan.md` §5) requires that a result
|
|
694
|
-
* is invalidated whenever the model's catalog row changes, so every record
|
|
695
|
-
* carries a fingerprint of the fields the probe depended on.
|
|
696
|
-
*
|
|
697
|
-
* The file lives in the plugin's own state directory, never beside the
|
|
698
|
-
* product's files, and carries no token, prompt, or response body — only
|
|
699
|
-
* model ids, effort spellings, and timestamps.
|
|
700
|
-
*
|
|
701
|
-
* @module dsh-qoder-connect/probe-store
|
|
702
|
-
*/
|
|
703
|
-
/** Basename of the China variant's probe record inside the plugin's state dir. */
|
|
704
|
-
const QODER_PROBE_FILENAME = ".qoder-probe.json";
|
|
705
|
-
/** On-disk format this reader accepts; other versions are discarded. */
|
|
706
|
-
const PROBE_FORMAT_VERSION = 1;
|
|
707
|
-
/**
|
|
708
|
-
* How long an observation stays usable. Conservative on purpose: the plan's
|
|
709
|
-
* whole argument is that upstream metadata moves fast, so a result that has
|
|
710
|
-
* outlived its fingerprint's usefulness should not quietly keep granting a
|
|
711
|
-
* picker entry.
|
|
712
|
-
*/
|
|
713
|
-
const DEFAULT_TTL_MS = 12096e5;
|
|
714
|
-
/**
|
|
715
|
-
* Plugin-owned probe record path inside the plugin's state directory.
|
|
716
|
-
*
|
|
717
|
-
* One file per variant. The two regions serve overlapping model ids with
|
|
718
|
-
* different entitlements, and {@link fingerprintModel} covers only
|
|
719
|
-
* `id`/`reasoning`/`supportsImages` — never the provider — so a single shared
|
|
720
|
-
* file would let one variant's observation answer for the other. The paths
|
|
721
|
-
* differ; the format does not.
|
|
722
|
-
*/
|
|
723
|
-
function qoderProbePath(filename = QODER_PROBE_FILENAME) {
|
|
724
|
-
return join(qoderStateDir(), filename);
|
|
725
|
-
}
|
|
726
|
-
/**
|
|
727
|
-
* Fingerprint the catalog fields a probe depends on.
|
|
728
|
-
*
|
|
729
|
-
* Deliberately excludes display-only fields (`name`, `billing`, `contextWindow`)
|
|
730
|
-
* so a rename or a rate relabel does not throw away a valid observation, and
|
|
731
|
-
* deliberately includes the whole reasoning object so any change to the
|
|
732
|
-
* declared shape re-probes.
|
|
733
|
-
*/
|
|
734
|
-
function fingerprintModel(info) {
|
|
735
|
-
const basis = JSON.stringify({
|
|
736
|
-
id: info.id,
|
|
737
|
-
reasoning: info.reasoning ?? null,
|
|
738
|
-
supportsImages: info.supportsImages ?? null
|
|
739
|
-
});
|
|
740
|
-
return createHash("sha256").update(basis).digest("hex").slice(0, 16);
|
|
741
|
-
}
|
|
742
|
-
/** Read-and-validate the documents on disk; anything malformed reads as empty. */
|
|
743
|
-
function readDocument(path) {
|
|
744
|
-
if (!existsSync(path)) return void 0;
|
|
745
|
-
let parsed;
|
|
746
|
-
try {
|
|
747
|
-
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
748
|
-
} catch {
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
752
|
-
const wrapped = parsed;
|
|
753
|
-
if (wrapped["version"] !== PROBE_FORMAT_VERSION) return void 0;
|
|
754
|
-
const records = wrapped["records"];
|
|
755
|
-
if (typeof records !== "object" || records === null || Array.isArray(records)) return void 0;
|
|
756
|
-
return parsed;
|
|
757
|
-
}
|
|
758
|
-
/** One record's shape check; a bad row is dropped rather than trusted. */
|
|
759
|
-
function isRecord(value) {
|
|
760
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
761
|
-
const wrapped = value;
|
|
762
|
-
const validation = wrapped["validation"];
|
|
763
|
-
if (validation !== "validating" && validation !== "non-validating" && validation !== "unknown") return false;
|
|
764
|
-
if (typeof wrapped["fingerprint"] !== "string") return false;
|
|
765
|
-
if (typeof wrapped["probedAtMs"] !== "number" || !Number.isFinite(wrapped["probedAtMs"])) return false;
|
|
766
|
-
if (typeof wrapped["pluginVersion"] !== "string") return false;
|
|
767
|
-
const efforts = wrapped["efforts"];
|
|
768
|
-
if (!Array.isArray(efforts) || efforts.some((effort) => typeof effort !== "string")) return false;
|
|
769
|
-
return true;
|
|
770
|
-
}
|
|
771
|
-
/**
|
|
772
|
-
* The plugin's probe records: read once, written atomically, never trusted
|
|
773
|
-
* across a fingerprint change or past the TTL.
|
|
774
|
-
*/
|
|
775
|
-
var QoderProbeStore = class {
|
|
776
|
-
path;
|
|
777
|
-
ttlMs;
|
|
778
|
-
pluginVersion;
|
|
779
|
-
now;
|
|
780
|
-
records;
|
|
781
|
-
constructor(options) {
|
|
782
|
-
const opts = typeof options === "string" ? {
|
|
783
|
-
path: options,
|
|
784
|
-
pluginVersion: "0.0.0"
|
|
785
|
-
} : options;
|
|
786
|
-
this.path = opts.path ?? qoderProbePath();
|
|
787
|
-
this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
788
|
-
this.pluginVersion = opts.pluginVersion;
|
|
789
|
-
this.now = opts.now ?? (() => Date.now());
|
|
790
|
-
}
|
|
791
|
-
/** Resolved state-file path, for the CLI and tests. */
|
|
792
|
-
filePath() {
|
|
793
|
-
return this.path;
|
|
794
|
-
}
|
|
795
|
-
load() {
|
|
796
|
-
if (this.records === void 0) {
|
|
797
|
-
const document = readDocument(this.path);
|
|
798
|
-
const records = {};
|
|
799
|
-
for (const [id, record] of Object.entries(document?.records ?? {})) if (isRecord(record)) records[id] = record;
|
|
800
|
-
this.records = records;
|
|
801
|
-
}
|
|
802
|
-
return this.records;
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* The usable record for a model, or `undefined` when there is none, it is
|
|
806
|
-
* expired, it was taken against a different catalog row, or it belongs to a
|
|
807
|
-
* different account.
|
|
808
|
-
*
|
|
809
|
-
* @param account - the account in effect, as `uid:enterpriseId`. Records are
|
|
810
|
-
* only returned for the account that produced them.
|
|
811
|
-
*/
|
|
812
|
-
get(modelId, fingerprint, account) {
|
|
813
|
-
const record = this.load()[modelId];
|
|
814
|
-
if (record === void 0) return void 0;
|
|
815
|
-
if (record.fingerprint !== fingerprint) return void 0;
|
|
816
|
-
if (record.account !== account) return void 0;
|
|
817
|
-
if (this.now() - record.probedAtMs > this.ttlMs) return void 0;
|
|
818
|
-
return record;
|
|
819
|
-
}
|
|
820
|
-
/**
|
|
821
|
-
* Store one observation. Only a decisive answer (`validating` /
|
|
822
|
-
* `non-validating`) replaces an existing decisive record: a transient
|
|
823
|
-
* `unknown` must not erase knowledge the user already paid for.
|
|
824
|
-
*/
|
|
825
|
-
set(modelId, record) {
|
|
826
|
-
const records = this.load();
|
|
827
|
-
const existing = records[modelId];
|
|
828
|
-
if (record.validation === "unknown" && existing !== void 0 && existing.fingerprint === record.fingerprint && existing.validation !== "unknown") return;
|
|
829
|
-
records[modelId] = record;
|
|
830
|
-
this.persist();
|
|
831
|
-
}
|
|
832
|
-
/** Drop every record; used by the card's explicit "clear" action. */
|
|
833
|
-
clear() {
|
|
834
|
-
this.records = {};
|
|
835
|
-
this.persist();
|
|
836
|
-
}
|
|
837
|
-
/** Every record currently held, for status display. */
|
|
838
|
-
all() {
|
|
839
|
-
return { ...this.load() };
|
|
840
|
-
}
|
|
841
|
-
/** Build a record stamped with this store's clock, version, and account. */
|
|
842
|
-
record(fingerprint, validation, efforts, account) {
|
|
843
|
-
return {
|
|
844
|
-
fingerprint,
|
|
845
|
-
validation,
|
|
846
|
-
efforts: validation === "validating" ? [...efforts] : [],
|
|
847
|
-
probedAtMs: this.now(),
|
|
848
|
-
pluginVersion: this.pluginVersion,
|
|
849
|
-
account
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
/**
|
|
853
|
-
* Write through a temporary file and rename, so a crash mid-write cannot
|
|
854
|
-
* leave a half-parsed document that reads as "no records" and silently drops
|
|
855
|
-
* every observation.
|
|
856
|
-
*/
|
|
857
|
-
persist() {
|
|
858
|
-
const directory = dirname(this.path);
|
|
859
|
-
try {
|
|
860
|
-
if (!existsSync(directory)) mkdirSync(directory, { recursive: true });
|
|
861
|
-
const document = {
|
|
862
|
-
version: PROBE_FORMAT_VERSION,
|
|
863
|
-
records: this.load()
|
|
864
|
-
};
|
|
865
|
-
const temporary = resolve(`${this.path}.tmp`);
|
|
866
|
-
writeFileSync(temporary, `${JSON.stringify(document, null, 2)}\n`, { mode: 384 });
|
|
867
|
-
renameSync(temporary, this.path);
|
|
868
|
-
} catch {}
|
|
869
|
-
}
|
|
870
|
-
};
|
|
871
|
-
/**
|
|
872
|
-
* Order observations newest-first for display.
|
|
873
|
-
*
|
|
874
|
-
* The store keeps insertion order so the file reads chronologically, but the
|
|
875
|
-
* card wants the most recent detection at the top: a sweep the user just ran
|
|
876
|
-
* should not appear below every earlier one, which is what appending to an
|
|
877
|
-
* insertion-ordered list does.
|
|
878
|
-
*/
|
|
879
|
-
function newestFirst(records) {
|
|
880
|
-
return [...records].sort((a, b) => b.probedAt - a.probedAt);
|
|
881
|
-
}
|
|
882
|
-
//#endregion
|
|
883
706
|
//#region src/probe-service.ts
|
|
884
707
|
/**
|
|
885
708
|
* Serial probe runner. One instance is shared by the manual API and any
|
|
@@ -1070,6 +893,8 @@ async function qoderWebStatus(deps) {
|
|
|
1070
893
|
status: "signed-in",
|
|
1071
894
|
...authStatus.region === void 0 ? {} : { region: authStatus.region },
|
|
1072
895
|
...authStatus.pat === void 0 ? {} : { pat: authStatus.pat },
|
|
896
|
+
...authStatus.profiles === void 0 ? {} : { patProfiles: authStatus.profiles },
|
|
897
|
+
...authStatus.activeId === void 0 ? {} : { activePatId: authStatus.activeId },
|
|
1073
898
|
...deps.authKey === void 0 ? {} : { authKey: deps.authKey }
|
|
1074
899
|
};
|
|
1075
900
|
const modelsField = deps.models().map(modelSnapshot);
|
|
@@ -2687,10 +2512,7 @@ function apply(ctx, config) {
|
|
|
2687
2512
|
runtime.catalogGeneration += 1;
|
|
2688
2513
|
runtime.inflightFetch?.controller.abort();
|
|
2689
2514
|
runtime.inflightFetch = void 0;
|
|
2690
|
-
if (hadCredential && known !== identity)
|
|
2691
|
-
runtime.probeStore.clear();
|
|
2692
|
-
runtime.invalidate();
|
|
2693
|
-
}
|
|
2515
|
+
if (hadCredential && known !== identity) runtime.invalidate();
|
|
2694
2516
|
if (identity === void 0) {
|
|
2695
2517
|
if (known !== void 0) runtime.savedCatalogs.delete(known);
|
|
2696
2518
|
runtime.catalog.set(runtime.fallback);
|
|
@@ -2765,13 +2587,13 @@ function apply(ctx, config) {
|
|
|
2765
2587
|
});
|
|
2766
2588
|
registerQoderAuthRoute(webCtx, {
|
|
2767
2589
|
path: runtime.variant.authPath,
|
|
2768
|
-
save: async (pat) => {
|
|
2590
|
+
save: async (pat, label) => {
|
|
2769
2591
|
if (!await validateApiKey(pat, runtime.variant.region)) return {
|
|
2770
2592
|
ok: false,
|
|
2771
2593
|
error: "qoder_invalid_pat"
|
|
2772
2594
|
};
|
|
2773
|
-
const
|
|
2774
|
-
const identity = credentialIdentity(
|
|
2595
|
+
const entry = await runtime.store.add(pat, label);
|
|
2596
|
+
const identity = credentialIdentity(entry);
|
|
2775
2597
|
adoptIdentity(runtime, identity);
|
|
2776
2598
|
await fetchCatalog(runtime, identity);
|
|
2777
2599
|
return {
|
|
@@ -2779,7 +2601,45 @@ function apply(ctx, config) {
|
|
|
2779
2601
|
status: await runtime.store.status()
|
|
2780
2602
|
};
|
|
2781
2603
|
},
|
|
2604
|
+
activate: async (id) => {
|
|
2605
|
+
const entry = await runtime.store.activate(id);
|
|
2606
|
+
if (entry === void 0) return {
|
|
2607
|
+
ok: false,
|
|
2608
|
+
error: "qoder_unknown_pat"
|
|
2609
|
+
};
|
|
2610
|
+
const identity = credentialIdentity(entry);
|
|
2611
|
+
adoptIdentity(runtime, identity);
|
|
2612
|
+
detach(ctx, fetchCatalog(runtime, identity), `${runtime.variant.id} catalog refresh after switch`);
|
|
2613
|
+
return {
|
|
2614
|
+
ok: true,
|
|
2615
|
+
status: await runtime.store.status()
|
|
2616
|
+
};
|
|
2617
|
+
},
|
|
2618
|
+
remove: async (id) => {
|
|
2619
|
+
const removed = await runtime.store.remove(id);
|
|
2620
|
+
if (removed === void 0) return {
|
|
2621
|
+
ok: false,
|
|
2622
|
+
error: "qoder_unknown_pat"
|
|
2623
|
+
};
|
|
2624
|
+
const finished = credentialIdentity(removed);
|
|
2625
|
+
runtime.probeStore.clearFor(finished);
|
|
2626
|
+
runtime.savedCatalogs.delete(finished);
|
|
2627
|
+
const active = await runtime.store.current().catch(() => void 0);
|
|
2628
|
+
const identity = active === void 0 ? void 0 : credentialIdentity(active);
|
|
2629
|
+
adoptIdentity(runtime, identity);
|
|
2630
|
+
if (identity !== void 0) detach(ctx, fetchCatalog(runtime, identity), `${runtime.variant.id} catalog refresh after removal`);
|
|
2631
|
+
return {
|
|
2632
|
+
ok: true,
|
|
2633
|
+
status: await runtime.store.status()
|
|
2634
|
+
};
|
|
2635
|
+
},
|
|
2782
2636
|
clear: async () => {
|
|
2637
|
+
const saved = await runtime.store.entries().catch(() => []);
|
|
2638
|
+
for (const entry of saved) {
|
|
2639
|
+
const identity = credentialIdentity(entry);
|
|
2640
|
+
runtime.probeStore.clearFor(identity);
|
|
2641
|
+
runtime.savedCatalogs.delete(identity);
|
|
2642
|
+
}
|
|
2783
2643
|
await runtime.store.clear();
|
|
2784
2644
|
adoptIdentity(runtime, void 0);
|
|
2785
2645
|
}
|
|
@@ -3097,4 +2957,4 @@ function apply(ctx, config) {
|
|
|
3097
2957
|
}), "variant startup");
|
|
3098
2958
|
}
|
|
3099
2959
|
//#endregion
|
|
3100
|
-
export { CHINA_VARIANT, CN_SECTION_KEYS, Config, FALLBACK_QODER_MODELS, GLOBAL_SECTION_KEYS, GLOBAL_VARIANT, KIND_STATUS, PROBE_EFFORT_CANDIDATES, 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, QoderCatalog, QoderCatalogStore, QoderCredentialStore, QoderProbeService, QoderProbeStore, QoderUpstreamClient, 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 };
|
|
2960
|
+
export { CHINA_VARIANT, CN_SECTION_KEYS, Config, FALLBACK_QODER_MODELS, GLOBAL_SECTION_KEYS, GLOBAL_VARIANT, KIND_STATUS, PROBE_EFFORT_CANDIDATES, 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, QoderCatalog, QoderCatalogStore, QoderCredentialStore, QoderProbeService, QoderProbeStore, QoderUpstreamClient, 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 };
|