dsh-keys-manager 0.0.0 → 0.2.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/LICENSE +21 -0
- package/README.md +203 -16
- package/README.zh-CN.md +125 -0
- package/bundle/cordis.patch.yml +5 -0
- package/icon.svg +12 -0
- package/lib/client/format.d.ts +8 -0
- package/lib/client/index.d.ts +3 -0
- package/lib/client/locales.d.ts +4 -0
- package/lib/client/runtime.d.ts +21 -0
- package/lib/client/store.d.ts +4 -0
- package/lib/client/styles.d.ts +2 -0
- package/lib/client/types.d.ts +130 -0
- package/lib/client/ui.d.ts +18 -0
- package/lib/client.js +2106 -0
- package/lib/core.d.ts +115 -0
- package/lib/core.js +282 -0
- package/lib/core.js.map +1 -0
- package/lib/domain.d.ts +246 -0
- package/lib/domain.js +9 -0
- package/lib/domain.js.map +1 -0
- package/lib/host-types.d.ts +240 -0
- package/lib/host-types.js +2 -0
- package/lib/host-types.js.map +1 -0
- package/lib/index.d.ts +860 -0
- package/lib/index.js +4157 -0
- package/lib/index.js.map +1 -0
- package/lib/providers.d.ts +28 -0
- package/lib/providers.js +1308 -0
- package/lib/providers.js.map +1 -0
- package/lib/store.d.ts +62 -0
- package/lib/store.js +432 -0
- package/lib/store.js.map +1 -0
- package/locale/en.json +25 -0
- package/locale/zh.json +25 -0
- package/package.json +79 -14
- package/scripts/build-client.mjs +53 -0
- package/scripts/prepare-release.mjs +243 -0
- package/scripts/verify-dist.mjs +30 -0
- package/index.js +0 -30
package/lib/core.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure key-selection and cooldown policy for the Keys Manager.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here touches Cordis, the filesystem, or a secret. The Host passes
|
|
5
|
+
* plain provider records in and gets the same records back, which keeps the
|
|
6
|
+
* round-robin, sticky-session, cooldown-escalation, and retry decisions
|
|
7
|
+
* unit-testable without a running Harness.
|
|
8
|
+
*/
|
|
9
|
+
import type { KeyRecord, KeyStatus, ManagerSettings, ProviderRecord, ProviderStatus } from './domain.js';
|
|
10
|
+
import type { LlmFailureLike } from './host-types.js';
|
|
11
|
+
/** Defaults also exposed in the manager's policy card. */
|
|
12
|
+
export declare const DEFAULT_SETTINGS: ManagerSettings;
|
|
13
|
+
/** Normalize one persisted settings object. */
|
|
14
|
+
export declare function normalizeSettings(value: unknown): ManagerSettings;
|
|
15
|
+
/** Apply and validate a settings patch from the UI; throws on nonsense. */
|
|
16
|
+
export declare function patchSettings(current: ManagerSettings, patch: unknown): ManagerSettings;
|
|
17
|
+
/** Clamp an untyped numeric setting. */
|
|
18
|
+
export declare function clampInteger(value: unknown, min: number, max: number, fallback: number): number;
|
|
19
|
+
/** Whether a key may serve a request now. */
|
|
20
|
+
export declare function isSelectable(key: KeyRecord | undefined, now: number): key is KeyRecord;
|
|
21
|
+
/** Pick the next healthy key for a provider in creation order, advancing the cursor. */
|
|
22
|
+
export declare function pickRoundRobin(provider: ProviderRecord, now: number, exclude?: ReadonlySet<string>): KeyRecord | undefined;
|
|
23
|
+
/** Pick the next healthy key other than `excludeKeyId`, advancing the cursor. */
|
|
24
|
+
export declare function pickHealthyExcluding(provider: ProviderRecord, excludeKeyId: string, now: number): KeyRecord | undefined;
|
|
25
|
+
/** Coerce a persisted cursor into range without mutating it. */
|
|
26
|
+
export declare function normalizeCursor(cursor: number, length: number): number;
|
|
27
|
+
/** Result of marking one key failure. */
|
|
28
|
+
export interface CooldownResult {
|
|
29
|
+
cooldownMs: number;
|
|
30
|
+
level: number;
|
|
31
|
+
cooldownUntil: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Record one failed key use and start (or extend) its cooldown.
|
|
35
|
+
*
|
|
36
|
+
* First failure: base cooldown. A failure while cooling, or within the
|
|
37
|
+
* recovery grace window after the previous cooldown expired, doubles the
|
|
38
|
+
* level. A long healthy gap resets the level, so an occasional failure does
|
|
39
|
+
* not inherit ancient escalation. The result is always capped at seven days.
|
|
40
|
+
*/
|
|
41
|
+
export declare function applyFailureCooldown(key: KeyRecord, settings: ManagerSettings, now: number, failure: LlmFailureLike | undefined): CooldownResult;
|
|
42
|
+
/**
|
|
43
|
+
* Record one successful use: clear the failure streak, but keep the previous
|
|
44
|
+
* cooldown timestamp/level until the recovery grace window has elapsed. A
|
|
45
|
+
* failure shortly after recovery must still escalate.
|
|
46
|
+
*/
|
|
47
|
+
export declare function markKeySuccess(key: KeyRecord, now: number): void;
|
|
48
|
+
/** Record that a request used a key, without changing health. */
|
|
49
|
+
export declare function markKeyUsed(key: KeyRecord, now: number): void;
|
|
50
|
+
/** Status a key row renders. */
|
|
51
|
+
export declare function statusOfKey(key: KeyRecord | undefined, now: number): KeyStatus;
|
|
52
|
+
/** Status a provider row renders. */
|
|
53
|
+
export declare function statusOfProvider(keys: readonly KeyRecord[], now: number): ProviderStatus;
|
|
54
|
+
/** Whether a model-request failure should affect key health at all. */
|
|
55
|
+
export declare function shouldFailover(code: string | undefined): boolean;
|
|
56
|
+
/** Failure code pi-ai raises for OAuth-specific problems (grant expired, refresh rejected, ...). */
|
|
57
|
+
export declare const OAUTH_FAILURE_CODE = "oauth";
|
|
58
|
+
/** Credential-reference scope prefix under which OAuth grant payloads live. */
|
|
59
|
+
export declare const OAUTH_GRANT_SCOPE = "keys-manager";
|
|
60
|
+
/** How one model-request failure affects pool health. */
|
|
61
|
+
export type FailureDisposition =
|
|
62
|
+
/** OAuth-specific failure: failover-eligible and starts a cooldown. */
|
|
63
|
+
'oauth'
|
|
64
|
+
/** Ordinary key failure: failover-eligible and starts a cooldown (existing semantics). */
|
|
65
|
+
| 'failover'
|
|
66
|
+
/** Request/config-shaped failure: the candidate's health is untouched. */
|
|
67
|
+
| 'ignore';
|
|
68
|
+
/**
|
|
69
|
+
* Classify one model-request failure against the pool policy.
|
|
70
|
+
*
|
|
71
|
+
* pi-ai raises `ModelsError("oauth", ...)` when the OAuth grant itself is
|
|
72
|
+
* the problem; that must cool the account down and fail over, while the
|
|
73
|
+
* existing `NON_KEY_FAILURE_CODES` exclusions (e.g.
|
|
74
|
+
* `CONTEXT_WINDOW_EXCEEDED`) stay excluded unchanged.
|
|
75
|
+
*/
|
|
76
|
+
export declare function classifyOAuthFailure(failure: LlmFailureLike | undefined): FailureDisposition;
|
|
77
|
+
/** Whether a provider route supports OAuth account sign-in. */
|
|
78
|
+
export declare function isOAuthCapable(provider: {
|
|
79
|
+
nativeAuth?: {
|
|
80
|
+
env?: readonly string[];
|
|
81
|
+
oauth?: boolean;
|
|
82
|
+
} | null;
|
|
83
|
+
} | undefined): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Build the plugin-scope credential reference that holds one account's
|
|
86
|
+
* grant payload, e.g. `keys-manager/anthropic-3fa2c1`. The name part stays
|
|
87
|
+
* within the lowercase-hyphen credential key charset: a stem whose first
|
|
88
|
+
* character is a digit is prefixed with `p-` so the reference is always
|
|
89
|
+
* acceptable to the credential store's key grammar.
|
|
90
|
+
*/
|
|
91
|
+
export declare function accountRef(provider: string, hash: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* SF3 note on arbitration (former `cascadeOrder` removed): there is no
|
|
94
|
+
* kind-ranked cascade. The live selection model is projection presence
|
|
95
|
+
* (the projected grant serves via pi-ai's stored-credential preference)
|
|
96
|
+
* plus probe fail-skip over a kind-agnostic cursor-anchored ring — an
|
|
97
|
+
* OAuth row leaves that ring only when its account probe fails on its
|
|
98
|
+
* non-credential-grammar ref. Any per-account ranking is roadmap work.
|
|
99
|
+
*/
|
|
100
|
+
/** Session/provider binding key. */
|
|
101
|
+
export declare function bindingKey(sessionId: string, provider: string): string;
|
|
102
|
+
/** Validate a new key input before it reaches the credential store. */
|
|
103
|
+
export declare function validateKeyInput(alias: unknown, value: unknown): {
|
|
104
|
+
alias: string;
|
|
105
|
+
value: string;
|
|
106
|
+
};
|
|
107
|
+
/** Validate a rename/enable patch. */
|
|
108
|
+
export declare function validateKeyPatch(patch: unknown): {
|
|
109
|
+
alias?: string;
|
|
110
|
+
enabled?: boolean;
|
|
111
|
+
};
|
|
112
|
+
/** Maximum keys per provider (also used by the Host). */
|
|
113
|
+
export declare const KEY_LIMIT = 32;
|
|
114
|
+
/** Re-export the shared update type for callers that only import core. */
|
|
115
|
+
export type { ManagerSettings };
|
package/lib/core.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/** Defaults also exposed in the manager's policy card. */
|
|
2
|
+
export const DEFAULT_SETTINGS = {
|
|
3
|
+
retryCount: 2,
|
|
4
|
+
baseCooldownMs: 5 * 60 * 1000,
|
|
5
|
+
recoveryGraceMs: 10 * 60 * 1000,
|
|
6
|
+
maxCooldownMs: 7 * 24 * 60 * 60 * 1000,
|
|
7
|
+
stickySession: true,
|
|
8
|
+
autoSwitch: true,
|
|
9
|
+
};
|
|
10
|
+
const MIN_COOLDOWN_MS = 30 * 1000;
|
|
11
|
+
const MAX_KEYS_PER_PROVIDER = 32;
|
|
12
|
+
const MAX_ALIAS_LENGTH = 80;
|
|
13
|
+
const MAX_KEY_LENGTH = 16 * 1024;
|
|
14
|
+
/** Failure codes that describe the request or configuration, not the key. */
|
|
15
|
+
const NON_KEY_FAILURE_CODES = new Set([
|
|
16
|
+
'ABORTED',
|
|
17
|
+
'CANCELLED',
|
|
18
|
+
'CONTEXT_WINDOW_EXCEEDED',
|
|
19
|
+
'EMPTY_RESPONSE_REQUEST',
|
|
20
|
+
'IMAGE_OFFLOAD_REQUIRED',
|
|
21
|
+
'INVALID_PREPARED_CALL',
|
|
22
|
+
'INVALID_REQUEST',
|
|
23
|
+
'NO_ADAPTER',
|
|
24
|
+
'REGISTRATION_DISPOSED',
|
|
25
|
+
'UNKNOWN_MODEL',
|
|
26
|
+
'UNSUPPORTED_OPTION',
|
|
27
|
+
]);
|
|
28
|
+
/** Normalize one persisted settings object. */
|
|
29
|
+
export function normalizeSettings(value) {
|
|
30
|
+
const source = value !== null && typeof value === 'object' ? value : {};
|
|
31
|
+
return {
|
|
32
|
+
retryCount: clampInteger(source.retryCount, 1, 3, DEFAULT_SETTINGS.retryCount),
|
|
33
|
+
baseCooldownMs: clampInteger(source.baseCooldownMs, MIN_COOLDOWN_MS, DEFAULT_SETTINGS.maxCooldownMs, DEFAULT_SETTINGS.baseCooldownMs),
|
|
34
|
+
recoveryGraceMs: clampInteger(source.recoveryGraceMs, 0, DEFAULT_SETTINGS.maxCooldownMs, DEFAULT_SETTINGS.recoveryGraceMs),
|
|
35
|
+
maxCooldownMs: DEFAULT_SETTINGS.maxCooldownMs,
|
|
36
|
+
stickySession: source.stickySession !== false,
|
|
37
|
+
autoSwitch: source.autoSwitch !== false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Apply and validate a settings patch from the UI; throws on nonsense. */
|
|
41
|
+
export function patchSettings(current, patch) {
|
|
42
|
+
const source = patch !== null && typeof patch === 'object' ? patch : {};
|
|
43
|
+
if (source.retryCount !== undefined) {
|
|
44
|
+
if (!Number.isInteger(source.retryCount) || source.retryCount < 1 || source.retryCount > 3) {
|
|
45
|
+
throw new TypeError('retryCount must be an integer from 1 to 3');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (source.baseCooldownMs !== undefined) {
|
|
49
|
+
if (!Number.isFinite(source.baseCooldownMs) || source.baseCooldownMs < MIN_COOLDOWN_MS) {
|
|
50
|
+
throw new TypeError('baseCooldownMs must be at least 30 seconds');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (source.recoveryGraceMs !== undefined) {
|
|
54
|
+
if (!Number.isFinite(source.recoveryGraceMs) || source.recoveryGraceMs < 0) {
|
|
55
|
+
throw new TypeError('recoveryGraceMs must be zero or positive');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return normalizeSettings({ ...current, ...source });
|
|
59
|
+
}
|
|
60
|
+
/** Clamp an untyped numeric setting. */
|
|
61
|
+
export function clampInteger(value, min, max, fallback) {
|
|
62
|
+
const number = typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
63
|
+
return Math.min(max, Math.max(min, Math.round(number)));
|
|
64
|
+
}
|
|
65
|
+
/** Whether a key may serve a request now. */
|
|
66
|
+
export function isSelectable(key, now) {
|
|
67
|
+
if (key === undefined)
|
|
68
|
+
return false;
|
|
69
|
+
if (key.enabled === false || key.valueMissing === true)
|
|
70
|
+
return false;
|
|
71
|
+
return !(Number(key.cooldownUntil) > now);
|
|
72
|
+
}
|
|
73
|
+
/** Pick the next healthy key for a provider in creation order, advancing the cursor. */
|
|
74
|
+
export function pickRoundRobin(provider, now, exclude) {
|
|
75
|
+
const keys = Array.isArray(provider.keys) ? provider.keys : [];
|
|
76
|
+
if (keys.length === 0)
|
|
77
|
+
return undefined;
|
|
78
|
+
const start = normalizeCursor(provider.cursor, keys.length);
|
|
79
|
+
for (let offset = 0; offset < keys.length; offset += 1) {
|
|
80
|
+
const index = (start + offset) % keys.length;
|
|
81
|
+
const key = keys[index];
|
|
82
|
+
if (exclude?.has(key.id))
|
|
83
|
+
continue;
|
|
84
|
+
if (isSelectable(key, now)) {
|
|
85
|
+
provider.cursor = (index + 1) % keys.length;
|
|
86
|
+
return key;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
/** Pick the next healthy key other than `excludeKeyId`, advancing the cursor. */
|
|
92
|
+
export function pickHealthyExcluding(provider, excludeKeyId, now) {
|
|
93
|
+
const keys = Array.isArray(provider.keys) ? provider.keys : [];
|
|
94
|
+
if (keys.length === 0)
|
|
95
|
+
return undefined;
|
|
96
|
+
const start = normalizeCursor(provider.cursor, keys.length);
|
|
97
|
+
for (let offset = 0; offset < keys.length; offset += 1) {
|
|
98
|
+
const index = (start + offset) % keys.length;
|
|
99
|
+
const key = keys[index];
|
|
100
|
+
if (key.id === excludeKeyId)
|
|
101
|
+
continue;
|
|
102
|
+
if (isSelectable(key, now)) {
|
|
103
|
+
provider.cursor = (index + 1) % keys.length;
|
|
104
|
+
return key;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
/** Coerce a persisted cursor into range without mutating it. */
|
|
110
|
+
export function normalizeCursor(cursor, length) {
|
|
111
|
+
if (!Number.isInteger(cursor) || length <= 0)
|
|
112
|
+
return 0;
|
|
113
|
+
return ((cursor % length) + length) % length;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Record one failed key use and start (or extend) its cooldown.
|
|
117
|
+
*
|
|
118
|
+
* First failure: base cooldown. A failure while cooling, or within the
|
|
119
|
+
* recovery grace window after the previous cooldown expired, doubles the
|
|
120
|
+
* level. A long healthy gap resets the level, so an occasional failure does
|
|
121
|
+
* not inherit ancient escalation. The result is always capped at seven days.
|
|
122
|
+
*/
|
|
123
|
+
export function applyFailureCooldown(key, settings, now, failure) {
|
|
124
|
+
const normalized = normalizeSettings(settings);
|
|
125
|
+
const previousLevel = Number.isInteger(key.failureLevel) ? key.failureLevel : 0;
|
|
126
|
+
const previousUntil = Number(key.cooldownUntil) || 0;
|
|
127
|
+
let level = 0;
|
|
128
|
+
if (previousUntil > 0) {
|
|
129
|
+
if (now <= previousUntil)
|
|
130
|
+
level = previousLevel + 1;
|
|
131
|
+
else if (now - previousUntil <= normalized.recoveryGraceMs)
|
|
132
|
+
level = previousLevel + 1;
|
|
133
|
+
else
|
|
134
|
+
level = 0;
|
|
135
|
+
}
|
|
136
|
+
const providerDelay = Number(failure?.providerRetryAfterMs);
|
|
137
|
+
const base = Math.max(MIN_COOLDOWN_MS, normalized.baseCooldownMs);
|
|
138
|
+
const exponential = Math.min(normalized.maxCooldownMs, base * 2 ** Math.min(level, 30));
|
|
139
|
+
const requested = Number.isFinite(providerDelay) && providerDelay > 0 ? Math.max(exponential, providerDelay) : exponential;
|
|
140
|
+
const cooldownMs = Math.max(1, Math.min(normalized.maxCooldownMs, requested));
|
|
141
|
+
key.failureLevel = level;
|
|
142
|
+
key.cooldownUntil = now + cooldownMs;
|
|
143
|
+
key.lastFailureAt = now;
|
|
144
|
+
key.lastFailureCode = typeof failure?.code === 'string' ? failure.code : '';
|
|
145
|
+
key.consecutiveFailures = (Number(key.consecutiveFailures) || 0) + 1;
|
|
146
|
+
key.totalFailures = (Number(key.totalFailures) || 0) + 1;
|
|
147
|
+
return { cooldownMs, level, cooldownUntil: key.cooldownUntil };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Record one successful use: clear the failure streak, but keep the previous
|
|
151
|
+
* cooldown timestamp/level until the recovery grace window has elapsed. A
|
|
152
|
+
* failure shortly after recovery must still escalate.
|
|
153
|
+
*/
|
|
154
|
+
export function markKeySuccess(key, now) {
|
|
155
|
+
key.lastUsedAt = now;
|
|
156
|
+
key.consecutiveFailures = 0;
|
|
157
|
+
key.lastFailureCode = '';
|
|
158
|
+
key.totalUses = (Number(key.totalUses) || 0) + 1;
|
|
159
|
+
}
|
|
160
|
+
/** Record that a request used a key, without changing health. */
|
|
161
|
+
export function markKeyUsed(key, now) {
|
|
162
|
+
key.lastUsedAt = now;
|
|
163
|
+
}
|
|
164
|
+
/** Status a key row renders. */
|
|
165
|
+
export function statusOfKey(key, now) {
|
|
166
|
+
if (key === undefined)
|
|
167
|
+
return 'missing';
|
|
168
|
+
if (key.enabled === false)
|
|
169
|
+
return 'paused';
|
|
170
|
+
if (key.valueMissing === true)
|
|
171
|
+
return 'missing';
|
|
172
|
+
if (Number(key.cooldownUntil) > now)
|
|
173
|
+
return 'cooling';
|
|
174
|
+
return 'active';
|
|
175
|
+
}
|
|
176
|
+
/** Status a provider row renders. */
|
|
177
|
+
export function statusOfProvider(keys, now) {
|
|
178
|
+
if (keys.length === 0)
|
|
179
|
+
return 'empty';
|
|
180
|
+
const statuses = keys.map(key => statusOfKey(key, now));
|
|
181
|
+
if (statuses.includes('active'))
|
|
182
|
+
return 'active';
|
|
183
|
+
if (statuses.includes('cooling'))
|
|
184
|
+
return 'cooling';
|
|
185
|
+
if (statuses.includes('paused'))
|
|
186
|
+
return 'paused';
|
|
187
|
+
return 'missing';
|
|
188
|
+
}
|
|
189
|
+
/** Whether a model-request failure should affect key health at all. */
|
|
190
|
+
export function shouldFailover(code) {
|
|
191
|
+
return typeof code === 'string' && code.length > 0 && !NON_KEY_FAILURE_CODES.has(code);
|
|
192
|
+
}
|
|
193
|
+
/** Failure code pi-ai raises for OAuth-specific problems (grant expired, refresh rejected, ...). */
|
|
194
|
+
export const OAUTH_FAILURE_CODE = 'oauth';
|
|
195
|
+
/** Credential-reference scope prefix under which OAuth grant payloads live. */
|
|
196
|
+
export const OAUTH_GRANT_SCOPE = 'keys-manager';
|
|
197
|
+
/**
|
|
198
|
+
* Classify one model-request failure against the pool policy.
|
|
199
|
+
*
|
|
200
|
+
* pi-ai raises `ModelsError("oauth", ...)` when the OAuth grant itself is
|
|
201
|
+
* the problem; that must cool the account down and fail over, while the
|
|
202
|
+
* existing `NON_KEY_FAILURE_CODES` exclusions (e.g.
|
|
203
|
+
* `CONTEXT_WINDOW_EXCEEDED`) stay excluded unchanged.
|
|
204
|
+
*/
|
|
205
|
+
export function classifyOAuthFailure(failure) {
|
|
206
|
+
const code = typeof failure?.code === 'string' ? failure.code : '';
|
|
207
|
+
if (code === OAUTH_FAILURE_CODE)
|
|
208
|
+
return 'oauth';
|
|
209
|
+
return shouldFailover(code) ? 'failover' : 'ignore';
|
|
210
|
+
}
|
|
211
|
+
/** Whether a provider route supports OAuth account sign-in. */
|
|
212
|
+
export function isOAuthCapable(provider) {
|
|
213
|
+
return provider?.nativeAuth?.oauth === true;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Build the plugin-scope credential reference that holds one account's
|
|
217
|
+
* grant payload, e.g. `keys-manager/anthropic-3fa2c1`. The name part stays
|
|
218
|
+
* within the lowercase-hyphen credential key charset: a stem whose first
|
|
219
|
+
* character is a digit is prefixed with `p-` so the reference is always
|
|
220
|
+
* acceptable to the credential store's key grammar.
|
|
221
|
+
*/
|
|
222
|
+
export function accountRef(provider, hash) {
|
|
223
|
+
const folded = provider.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
224
|
+
const guarded = folded.length === 0 ? 'provider' : /^[0-9]/.test(folded) ? `p-${folded}` : folded;
|
|
225
|
+
const stem = guarded.slice(0, 40);
|
|
226
|
+
const suffix = hash.toLowerCase().replace(/[^a-z0-9]+/g, '').slice(0, 12) || '0';
|
|
227
|
+
return `${OAUTH_GRANT_SCOPE}/${stem}-${suffix}`;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* SF3 note on arbitration (former `cascadeOrder` removed): there is no
|
|
231
|
+
* kind-ranked cascade. The live selection model is projection presence
|
|
232
|
+
* (the projected grant serves via pi-ai's stored-credential preference)
|
|
233
|
+
* plus probe fail-skip over a kind-agnostic cursor-anchored ring — an
|
|
234
|
+
* OAuth row leaves that ring only when its account probe fails on its
|
|
235
|
+
* non-credential-grammar ref. Any per-account ranking is roadmap work.
|
|
236
|
+
*/
|
|
237
|
+
/** Session/provider binding key. */
|
|
238
|
+
export function bindingKey(sessionId, provider) {
|
|
239
|
+
return `${sessionId}\u0000${provider}`;
|
|
240
|
+
}
|
|
241
|
+
/** Validate a new key input before it reaches the credential store. */
|
|
242
|
+
export function validateKeyInput(alias, value) {
|
|
243
|
+
const cleanAlias = typeof alias === 'string' ? alias.trim() : '';
|
|
244
|
+
if (cleanAlias.length === 0)
|
|
245
|
+
throw new TypeError('alias is required');
|
|
246
|
+
if (cleanAlias.length > MAX_ALIAS_LENGTH)
|
|
247
|
+
throw new TypeError(`alias must be at most ${MAX_ALIAS_LENGTH} characters`);
|
|
248
|
+
if (typeof value !== 'string')
|
|
249
|
+
throw new TypeError('key value is required');
|
|
250
|
+
const cleanValue = value.trim();
|
|
251
|
+
if (cleanValue.length === 0)
|
|
252
|
+
throw new TypeError('key value is required');
|
|
253
|
+
if (cleanValue.length > MAX_KEY_LENGTH)
|
|
254
|
+
throw new TypeError('key value is too long');
|
|
255
|
+
return { alias: cleanAlias, value: cleanValue };
|
|
256
|
+
}
|
|
257
|
+
/** Validate a rename/enable patch. */
|
|
258
|
+
export function validateKeyPatch(patch) {
|
|
259
|
+
if (patch === null || typeof patch !== 'object' || Array.isArray(patch))
|
|
260
|
+
throw new TypeError('patch is required');
|
|
261
|
+
const source = patch;
|
|
262
|
+
const result = {};
|
|
263
|
+
if (source.alias !== undefined) {
|
|
264
|
+
const alias = typeof source.alias === 'string' ? source.alias.trim() : '';
|
|
265
|
+
if (alias.length === 0)
|
|
266
|
+
throw new TypeError('alias is required');
|
|
267
|
+
if (alias.length > MAX_ALIAS_LENGTH)
|
|
268
|
+
throw new TypeError(`alias must be at most ${MAX_ALIAS_LENGTH} characters`);
|
|
269
|
+
result.alias = alias;
|
|
270
|
+
}
|
|
271
|
+
if (source.enabled !== undefined) {
|
|
272
|
+
if (typeof source.enabled !== 'boolean')
|
|
273
|
+
throw new TypeError('enabled must be a boolean');
|
|
274
|
+
result.enabled = source.enabled;
|
|
275
|
+
}
|
|
276
|
+
if (Object.keys(result).length === 0)
|
|
277
|
+
throw new TypeError('patch has no supported fields');
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
/** Maximum keys per provider (also used by the Host). */
|
|
281
|
+
export const KEY_LIMIT = MAX_KEYS_PER_PROVIDER;
|
|
282
|
+
//# sourceMappingURL=core.js.map
|
package/lib/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/lib/domain.d.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, transport-safe domain shapes for the Keys Manager.
|
|
3
|
+
*
|
|
4
|
+
* The Host and the Web Client both compile against these declarations. They
|
|
5
|
+
* deliberately contain no Cordis, Node, or React types so the client bundle
|
|
6
|
+
* can import them type-only without pulling Host code into the browser.
|
|
7
|
+
*/
|
|
8
|
+
/** Health state a key row renders. */
|
|
9
|
+
export type KeyStatus = 'active' | 'cooling' | 'paused' | 'missing';
|
|
10
|
+
/** Health state a provider row renders. */
|
|
11
|
+
export type ProviderStatus = KeyStatus | 'empty' | 'unmanaged';
|
|
12
|
+
/** Manager policy shared by the Host selection engine and the UI form. */
|
|
13
|
+
export interface ManagerSettings {
|
|
14
|
+
/** Retries on the same key before switching to another key. */
|
|
15
|
+
retryCount: number;
|
|
16
|
+
/** First cooldown after a key starts failing. */
|
|
17
|
+
baseCooldownMs: number;
|
|
18
|
+
/** A failure within this window after recovery escalates the cooldown. */
|
|
19
|
+
recoveryGraceMs: number;
|
|
20
|
+
/** Hard ceiling for any cooldown. */
|
|
21
|
+
maxCooldownMs: number;
|
|
22
|
+
/** Keep one key for one session instead of rotating every request. */
|
|
23
|
+
stickySession: boolean;
|
|
24
|
+
/** Automatically mark and switch away from a failing key. */
|
|
25
|
+
autoSwitch: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* OAuth account metadata persisted for one `kind: 'oauth'` pool member.
|
|
29
|
+
*
|
|
30
|
+
* The manager state stays secret-free: the grant payload (refresh/access
|
|
31
|
+
* tokens) lives in the credential store under `grantRef`, and only this
|
|
32
|
+
* reference plus display metadata is stored here.
|
|
33
|
+
*/
|
|
34
|
+
export interface OAuthAccount {
|
|
35
|
+
/** OAuth2 provider id the account belongs to, e.g. 'anthropic'. */
|
|
36
|
+
provider: string;
|
|
37
|
+
/** Account email when the provider disclosed one. */
|
|
38
|
+
email?: string;
|
|
39
|
+
/** Provider-side account identifier when disclosed. */
|
|
40
|
+
accountId?: string;
|
|
41
|
+
/** Epoch ms when the current access token expires; informational only. */
|
|
42
|
+
expiresAt?: number;
|
|
43
|
+
/** Plugin-scope credential reference holding this account's grant payload. */
|
|
44
|
+
grantRef: string;
|
|
45
|
+
}
|
|
46
|
+
/** OAuth account facts surfaced to the UI; the grant reference stays host-side. */
|
|
47
|
+
export interface OAuthAccountView {
|
|
48
|
+
provider: string;
|
|
49
|
+
email?: string;
|
|
50
|
+
accountId?: string;
|
|
51
|
+
expiresAt?: number;
|
|
52
|
+
}
|
|
53
|
+
/** One persisted key. Key literals live only in the credential store. */
|
|
54
|
+
export interface KeyRecord {
|
|
55
|
+
id: string;
|
|
56
|
+
alias: string;
|
|
57
|
+
/** Credential reference holding this key's literal. */
|
|
58
|
+
ref: string;
|
|
59
|
+
kind: 'api-key' | 'oauth';
|
|
60
|
+
enabled: boolean;
|
|
61
|
+
createdAt: number;
|
|
62
|
+
lastUsedAt: number;
|
|
63
|
+
cooldownUntil: number;
|
|
64
|
+
lastFailureAt: number;
|
|
65
|
+
lastFailureCode: string;
|
|
66
|
+
failureLevel: number;
|
|
67
|
+
consecutiveFailures: number;
|
|
68
|
+
totalUses: number;
|
|
69
|
+
totalFailures: number;
|
|
70
|
+
valueMissing: boolean;
|
|
71
|
+
importedFrom: string | null;
|
|
72
|
+
/** OAuth account metadata; null for API-key rows. */
|
|
73
|
+
oauth: OAuthAccount | null;
|
|
74
|
+
}
|
|
75
|
+
/** One model entry installed into a pi-ai route profile. */
|
|
76
|
+
export interface ModelRecord {
|
|
77
|
+
id: string;
|
|
78
|
+
name?: string;
|
|
79
|
+
contextWindow?: number;
|
|
80
|
+
maxTokens?: number;
|
|
81
|
+
input?: Array<'text' | 'image'>;
|
|
82
|
+
reasoningEfforts?: Record<string, unknown>;
|
|
83
|
+
compat?: Record<string, unknown>;
|
|
84
|
+
}
|
|
85
|
+
/** One managed provider and its adoption facts. */
|
|
86
|
+
export interface ProviderRecord {
|
|
87
|
+
provider: string;
|
|
88
|
+
displayName: string;
|
|
89
|
+
source: 'dsh' | 'extra';
|
|
90
|
+
settingsNs: string;
|
|
91
|
+
settingsPath: string[];
|
|
92
|
+
adapter: 'pi-ai' | 'deepseek';
|
|
93
|
+
/** Broker reference written into the provider profile's `apiKeyEnv`. */
|
|
94
|
+
brokerRef: string;
|
|
95
|
+
/** The profile's reference before adoption, restored on release. */
|
|
96
|
+
originalApiKeyEnv: string | null;
|
|
97
|
+
/** True when the profile itself was created by adding an extra provider. */
|
|
98
|
+
createdProfile: boolean;
|
|
99
|
+
/** True when a profile existed before adoption. */
|
|
100
|
+
hadProfile: boolean;
|
|
101
|
+
extraId: string | null;
|
|
102
|
+
api: string | null;
|
|
103
|
+
baseURL: string | null;
|
|
104
|
+
models: ModelRecord[];
|
|
105
|
+
adoptedAt: number;
|
|
106
|
+
/** Next round-robin position in `keys`. */
|
|
107
|
+
cursor: number;
|
|
108
|
+
keys: KeyRecord[];
|
|
109
|
+
}
|
|
110
|
+
/** Durable, secret-free manager document. */
|
|
111
|
+
export interface StateDocument {
|
|
112
|
+
version: 1;
|
|
113
|
+
settings: ManagerSettings;
|
|
114
|
+
providers: Record<string, ProviderRecord>;
|
|
115
|
+
sessionBindings: Record<string, {
|
|
116
|
+
keyId: string;
|
|
117
|
+
boundAt: number;
|
|
118
|
+
}>;
|
|
119
|
+
}
|
|
120
|
+
/** Public key row returned to the UI (no credential reference or value). */
|
|
121
|
+
export interface KeyView {
|
|
122
|
+
id: string;
|
|
123
|
+
alias: string;
|
|
124
|
+
status: KeyStatus;
|
|
125
|
+
enabled: boolean;
|
|
126
|
+
createdAt: number;
|
|
127
|
+
lastUsedAt: number;
|
|
128
|
+
cooldownUntil: number;
|
|
129
|
+
failureLevel: number;
|
|
130
|
+
consecutiveFailures: number;
|
|
131
|
+
totalUses: number;
|
|
132
|
+
totalFailures: number;
|
|
133
|
+
importedFrom: string | null;
|
|
134
|
+
valueMissing: boolean;
|
|
135
|
+
kind: 'api-key' | 'oauth';
|
|
136
|
+
/** Identity line + expiry for `kind: 'oauth'` rows; absent for API keys. */
|
|
137
|
+
oauth?: OAuthAccountView | null;
|
|
138
|
+
/**
|
|
139
|
+
* True when this account's grant IS the provider's live projection
|
|
140
|
+
* (round-2 review, step 6); only ever set on `kind: 'oauth'` rows.
|
|
141
|
+
* Round-3 N3: this per-key flag is the single wire carrier of ownership —
|
|
142
|
+
* the provider-view `projectedAccountId` duplicate was removed.
|
|
143
|
+
*/
|
|
144
|
+
projected?: boolean;
|
|
145
|
+
}
|
|
146
|
+
/** Public provider row returned to the UI. */
|
|
147
|
+
export interface ProviderView {
|
|
148
|
+
provider: string;
|
|
149
|
+
displayName: string;
|
|
150
|
+
source: 'dsh' | 'extra';
|
|
151
|
+
/** Where the route came from: DSH catalog, this plugin's catalog, or a hand-declared profile. */
|
|
152
|
+
origin?: 'dsh' | 'plugin' | 'custom';
|
|
153
|
+
/** True when this plugin created the route profile and release removes it. */
|
|
154
|
+
createdProfile?: boolean;
|
|
155
|
+
managed: boolean;
|
|
156
|
+
configured: boolean;
|
|
157
|
+
active: boolean;
|
|
158
|
+
/** Key-pool verdict: can this route take API keys (Add key / addKey). */
|
|
159
|
+
supported: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Account-management verdict: can this route be adopted for OAuth
|
|
162
|
+
* account sign-in. Distinct from `supported` (MF1): an OAuth-only route
|
|
163
|
+
* is account-adoptable while carrying no key-pool credential.
|
|
164
|
+
*/
|
|
165
|
+
accountSupported?: boolean;
|
|
166
|
+
reason?: string;
|
|
167
|
+
settingsNs: string;
|
|
168
|
+
settingsPath: string[];
|
|
169
|
+
apiKeyEnv: string | null;
|
|
170
|
+
brokerRef: string | null;
|
|
171
|
+
bindingHealthy?: boolean;
|
|
172
|
+
extra: boolean;
|
|
173
|
+
extraId: string | null;
|
|
174
|
+
api: string | null;
|
|
175
|
+
baseURL: string | null;
|
|
176
|
+
env: string[];
|
|
177
|
+
defaultBaseURL: string | null;
|
|
178
|
+
nativeAuth: {
|
|
179
|
+
env: string[];
|
|
180
|
+
oauth: boolean;
|
|
181
|
+
} | null;
|
|
182
|
+
models: ModelRecord[];
|
|
183
|
+
keyCount: number;
|
|
184
|
+
healthyKeyCount: number;
|
|
185
|
+
status: ProviderStatus;
|
|
186
|
+
keys: KeyView[];
|
|
187
|
+
warning?: string;
|
|
188
|
+
}
|
|
189
|
+
/** Complete manager snapshot the UI renders. */
|
|
190
|
+
export interface StateView {
|
|
191
|
+
now: number;
|
|
192
|
+
writable: boolean;
|
|
193
|
+
settings: ManagerSettings;
|
|
194
|
+
providers: ProviderView[];
|
|
195
|
+
managedCount: number;
|
|
196
|
+
keyCount: number;
|
|
197
|
+
healthyKeyCount: number;
|
|
198
|
+
coolingKeyCount: number;
|
|
199
|
+
pausedKeyCount: number;
|
|
200
|
+
bindingsCount: number;
|
|
201
|
+
warnings: string[];
|
|
202
|
+
}
|
|
203
|
+
/** One curated extra built-in provider. */
|
|
204
|
+
export interface ExtraProvider {
|
|
205
|
+
id: string;
|
|
206
|
+
name: string;
|
|
207
|
+
api: string;
|
|
208
|
+
baseURL: string;
|
|
209
|
+
env: string[];
|
|
210
|
+
models: ModelRecord[];
|
|
211
|
+
}
|
|
212
|
+
/** Serializable notice captured on an interactive sign-in attempt. */
|
|
213
|
+
export interface AccountAttemptNotice {
|
|
214
|
+
message: string;
|
|
215
|
+
url?: string;
|
|
216
|
+
code?: string;
|
|
217
|
+
}
|
|
218
|
+
/** Serializable prompt parked on an interactive sign-in attempt. */
|
|
219
|
+
export interface AccountAttemptPrompt {
|
|
220
|
+
kind: 'text' | 'secret' | 'select';
|
|
221
|
+
message: string;
|
|
222
|
+
placeholder?: string;
|
|
223
|
+
options?: ReadonlyArray<{
|
|
224
|
+
id: string;
|
|
225
|
+
label: string;
|
|
226
|
+
description?: string;
|
|
227
|
+
}>;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Serializable state of one interactive OAuth sign-in attempt, shared by the
|
|
231
|
+
* Host service and the Web Client. Secrets never appear: prompts and notices
|
|
232
|
+
* carry messages, URLs, and codes only.
|
|
233
|
+
*/
|
|
234
|
+
export interface AccountAttemptView {
|
|
235
|
+
id: string;
|
|
236
|
+
provider: string;
|
|
237
|
+
alias?: string;
|
|
238
|
+
status: 'pending' | 'prompt' | 'ready' | 'failed' | 'cancelled';
|
|
239
|
+
notices: AccountAttemptNotice[];
|
|
240
|
+
prompt?: AccountAttemptPrompt;
|
|
241
|
+
error?: string;
|
|
242
|
+
/** Pool member id of the account row a successful login produced. */
|
|
243
|
+
accountId?: string;
|
|
244
|
+
createdAt: number;
|
|
245
|
+
updatedAt: number;
|
|
246
|
+
}
|
package/lib/domain.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, transport-safe domain shapes for the Keys Manager.
|
|
3
|
+
*
|
|
4
|
+
* The Host and the Web Client both compile against these declarations. They
|
|
5
|
+
* deliberately contain no Cordis, Node, or React types so the client bundle
|
|
6
|
+
* can import them type-only without pulling Host code into the browser.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=domain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain.js","sourceRoot":"","sources":["../src/domain.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|