@cortexkit/claustrum-client 0.2.0 → 0.4.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/dist/index.d.ts +1 -1
- package/dist/wire.d.ts +44 -7
- package/dist/wire.js +37 -5
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export { storeIdentity, storageFingerprint } from './identity.js';
|
|
|
3
3
|
export { writeEnrollmentTokenFile, type EnrollmentTokenFile, } from './enrollment.js';
|
|
4
4
|
export { MANIFEST_LOCK, manifestLockQuarantinePrefix, withManifestLock, writeHandleFileLocked, type ManifestHandleAccount, type ManifestHandleFile, type ManifestHandleProvider, type ManifestLockError, type ManifestLockErrorCode, } from './manifest-lock.js';
|
|
5
5
|
export { ClaustrumCredentialError, credentialErrorAction, ERROR_CLASS_WIRE_SET, type ClaustrumCredentialErrorAction, type ClaustrumCredentialErrorClass, } from './errors.js';
|
|
6
|
-
export { ClaustrumClient, type ClaustrumClientOptions, type ClaustrumConnector, type ClaustrumReporterSource, type CredentialStatus, type EnrollmentPollOutcome, type ScopedInventoryRow, type ServedCredential, } from './wire.js';
|
|
6
|
+
export { ClaustrumClient, type ClaustrumClientOptions, type ClaustrumConnector, type ClaustrumReporterSource, type CredentialStatus, type EnrollmentPollOutcome, type ScopedInventory, type ScopedInventoryRow, type ServedCredential, } from './wire.js';
|
|
7
7
|
export { HANDLE_FILE_CONTRACT, HandleFileValidationError, credentialIdMatchesProvider, defaultHandleFilePath, handleFileRevision, identifierIsValid, parseHandleFile, readHandleFile, type HandleAccount, type HandleFileIo, type HandleProvider, type OpenCodeHandleFileV1, } from './handles.js';
|
package/dist/wire.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export declare class ClaustrumClient {
|
|
|
63
63
|
* `params` is `{}` and must be PRESENT: the vault rejects an absent or null params
|
|
64
64
|
* object, so a caller cannot ask a different question by omitting it.
|
|
65
65
|
*/
|
|
66
|
-
listScoped(enrollmentToken?: string): Promise<
|
|
66
|
+
listScoped(enrollmentToken?: string): Promise<ScopedInventory>;
|
|
67
67
|
/**
|
|
68
68
|
* Fetch by credential id, authorized by a grant rather than a bearer handle.
|
|
69
69
|
*
|
|
@@ -121,9 +121,19 @@ export declare class ClaustrumClient {
|
|
|
121
121
|
requestId: string;
|
|
122
122
|
requestSecret: string;
|
|
123
123
|
}): Promise<EnrollmentPollOutcome>;
|
|
124
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Exchange a live token for its successor. The old token dies when the new one is issued.
|
|
126
|
+
*
|
|
127
|
+
* `expectedTokenGeneration` IS REQUIRED AND IS A FENCE, not bookkeeping: it must be the
|
|
128
|
+
* generation this caller currently holds. Two rotations racing — a retry after an
|
|
129
|
+
* uncertain reply, or two instances of the same consumer — would otherwise each mint a
|
|
130
|
+
* successor and each invalidate the other's, leaving a consumer holding a token the
|
|
131
|
+
* vault has already replaced. The vault refuses a rotation whose expectation does not
|
|
132
|
+
* match, so the loser learns it lost instead of discovering it on a later spend.
|
|
133
|
+
*/
|
|
125
134
|
enrollRotate(input: {
|
|
126
135
|
token: string;
|
|
136
|
+
expectedTokenGeneration: number;
|
|
127
137
|
}): Promise<{
|
|
128
138
|
token: string;
|
|
129
139
|
tokenGeneration: number;
|
|
@@ -133,11 +143,20 @@ export declare class ClaustrumClient {
|
|
|
133
143
|
/**
|
|
134
144
|
* A row from the caller's own grant-covered inventory.
|
|
135
145
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
146
|
+
* TWO SELECTION AXES, AND THEY ANSWER DIFFERENT QUESTIONS. Pick by what you are about
|
|
147
|
+
* to do with the token:
|
|
148
|
+
*
|
|
149
|
+
* - `refreshAdapter` — whose PROTOCOL the credential speaks. Use it when the token
|
|
150
|
+
* goes to a provider's own endpoints. Anything else is invalid there.
|
|
151
|
+
* - `serves` — which model VENDORS are reachable through it. Use it when choosing
|
|
152
|
+
* where a model can be served from, and the protocol is not yours to care about.
|
|
153
|
+
*
|
|
154
|
+
* Never the id spelling, in either case: on a live vault 8 rows serve Anthropic models
|
|
155
|
+
* and 3 of them contain no "anthropic" anywhere in their id.
|
|
156
|
+
*
|
|
157
|
+
* An earlier version of this comment called `serves` "the routing axis" full stop. That
|
|
158
|
+
* was written before `refreshAdapter` existed and is wrong for the protocol-native case:
|
|
159
|
+
* it would send a Claude OAuth request to `apikey:openrouter`.
|
|
141
160
|
*/
|
|
142
161
|
export interface ScopedInventoryRow {
|
|
143
162
|
readonly id: string;
|
|
@@ -167,6 +186,24 @@ export interface ScopedInventoryRow {
|
|
|
167
186
|
readonly email?: string;
|
|
168
187
|
readonly orgName?: string;
|
|
169
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* An inventory reply: the covered rows, plus the cursor that says whether they moved.
|
|
191
|
+
*
|
|
192
|
+
* `view` is a DIGEST OVER WHAT THIS CALLER CAN SEE — ids, categories, state, operations —
|
|
193
|
+
* and deliberately NOT over `record_version`, so a routine token refresh does not move it.
|
|
194
|
+
* Compare it against the previous call's to decide whether to reconcile at all; it is an
|
|
195
|
+
* opaque string and nothing but equality is defined on it.
|
|
196
|
+
*/
|
|
197
|
+
export interface ScopedInventory {
|
|
198
|
+
readonly rows: readonly ScopedInventoryRow[];
|
|
199
|
+
readonly view: string;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Exported for the golden-reply test only; `index.ts` re-exports by name, so this does not
|
|
203
|
+
* widen the published surface. The test drives THIS function rather than asserting keys,
|
|
204
|
+
* because a key check passes on a decoder that reads the wrong key.
|
|
205
|
+
*/
|
|
206
|
+
export declare function decodeScopedInventory(response: unknown, logUnknownClass: (errorClass: string) => void): ScopedInventory;
|
|
170
207
|
/** What a poll found. `pending` is not an error: the operator has not decided yet. */
|
|
171
208
|
export type EnrollmentPollOutcome = {
|
|
172
209
|
readonly status: 'pending';
|
package/dist/wire.js
CHANGED
|
@@ -220,9 +220,21 @@ export class ClaustrumClient {
|
|
|
220
220
|
});
|
|
221
221
|
return decodeEnrollmentPoll(response, this.#logger);
|
|
222
222
|
}
|
|
223
|
-
/**
|
|
223
|
+
/**
|
|
224
|
+
* Exchange a live token for its successor. The old token dies when the new one is issued.
|
|
225
|
+
*
|
|
226
|
+
* `expectedTokenGeneration` IS REQUIRED AND IS A FENCE, not bookkeeping: it must be the
|
|
227
|
+
* generation this caller currently holds. Two rotations racing — a retry after an
|
|
228
|
+
* uncertain reply, or two instances of the same consumer — would otherwise each mint a
|
|
229
|
+
* successor and each invalidate the other's, leaving a consumer holding a token the
|
|
230
|
+
* vault has already replaced. The vault refuses a rotation whose expectation does not
|
|
231
|
+
* match, so the loser learns it lost instead of discovering it on a later spend.
|
|
232
|
+
*/
|
|
224
233
|
async enrollRotate(input) {
|
|
225
|
-
const response = await this.#call('auth.enroll_rotate', {
|
|
234
|
+
const response = await this.#call('auth.enroll_rotate', {
|
|
235
|
+
token: input.token,
|
|
236
|
+
expected_token_generation: input.expectedTokenGeneration,
|
|
237
|
+
});
|
|
226
238
|
const result = decodeEnrollmentResult(response, this.#logger);
|
|
227
239
|
const token = result.token;
|
|
228
240
|
const generation = result.token_generation;
|
|
@@ -274,7 +286,14 @@ export class ClaustrumClient {
|
|
|
274
286
|
error instanceof SubcCallError &&
|
|
275
287
|
error.kind === 'terminal' &&
|
|
276
288
|
error.code !== 'missing_identity' &&
|
|
277
|
-
error.code !== 'invalid_control_body'
|
|
289
|
+
error.code !== 'invalid_control_body' &&
|
|
290
|
+
// The daemon answers unknown_module only when no module with this id is
|
|
291
|
+
// registered or supervised at all; a vault that is restarting or warming up
|
|
292
|
+
// is reported as module_warming or target_unavailable, which the SDK already
|
|
293
|
+
// retries in place. A fresh connection to the same daemon gets the same
|
|
294
|
+
// answer, and the wasted reconnect would also start the reconnect backoff,
|
|
295
|
+
// leaving a genuinely wedged connection unable to recover for a minute.
|
|
296
|
+
error.code !== 'unknown_module');
|
|
278
297
|
}
|
|
279
298
|
async #reconnect() {
|
|
280
299
|
if (this.#closed)
|
|
@@ -306,14 +325,26 @@ export class ClaustrumClient {
|
|
|
306
325
|
await this.#reconnecting;
|
|
307
326
|
}
|
|
308
327
|
}
|
|
309
|
-
|
|
328
|
+
/**
|
|
329
|
+
* Exported for the golden-reply test only; `index.ts` re-exports by name, so this does not
|
|
330
|
+
* widen the published surface. The test drives THIS function rather than asserting keys,
|
|
331
|
+
* because a key check passes on a decoder that reads the wrong key.
|
|
332
|
+
*/
|
|
333
|
+
export function decodeScopedInventory(response, logUnknownClass) {
|
|
310
334
|
if (hasCredentialError(response))
|
|
311
335
|
throw asCredentialError(response, 'invalid_response', logUnknownClass);
|
|
312
336
|
const result = isRecord(response) && isRecord(response.result) ? response.result : undefined;
|
|
313
337
|
const rows = result?.credentials;
|
|
314
338
|
if (!Array.isArray(rows))
|
|
315
339
|
throw asCredentialError(response, 'invalid_response', logUnknownClass);
|
|
316
|
-
|
|
340
|
+
// THE CURSOR IS NOT OPTIONAL. Dropping it silently, as this decoder did in 0.2.0, leaves
|
|
341
|
+
// a consumer unable to tell "nothing changed" from "I did not ask", so it reconciles
|
|
342
|
+
// every poll or not at all.
|
|
343
|
+
const view = result?.view;
|
|
344
|
+
if (typeof view !== 'string' || view.length === 0) {
|
|
345
|
+
throw asCredentialError(response, 'invalid_response', logUnknownClass);
|
|
346
|
+
}
|
|
347
|
+
const decoded = rows.map((row) => {
|
|
317
348
|
if (!isRecord(row))
|
|
318
349
|
throw asCredentialError(response, 'invalid_response', logUnknownClass);
|
|
319
350
|
const recordVersion = asRecordVersion(row.record_version);
|
|
@@ -370,6 +401,7 @@ function decodeScopedInventory(response, logUnknownClass) {
|
|
|
370
401
|
orgName: row.org_name,
|
|
371
402
|
};
|
|
372
403
|
});
|
|
404
|
+
return { rows: decoded, view };
|
|
373
405
|
}
|
|
374
406
|
function decodeEnrollmentResult(response, logUnknownClass) {
|
|
375
407
|
if (hasCredentialError(response))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/claustrum-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,6 +27,6 @@
|
|
|
27
27
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cortexkit/subc-client": "^0.
|
|
30
|
+
"@cortexkit/subc-client": "^0.16.0"
|
|
31
31
|
}
|
|
32
32
|
}
|