@cortexkit/anthropic-auth-core 2.2.4 → 2.2.6
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.js +255 -37
- package/dist/internal-types/claustrum/consumer.d.ts +7 -1
- package/dist/internal-types/claustrum/errors.d.ts +1 -1
- package/dist/internal-types/claustrum/index.d.ts +1 -1
- package/dist/internal-types/claustrum/roster.d.ts +31 -0
- package/dist/internal-types/commands/builtins.d.ts +37 -0
- package/dist/internal-types/commands/index.d.ts +1 -1
- package/dist/internal-types/setup/enroll.d.ts +1 -1
- package/dist/native-custody.d.ts +1 -1
- package/dist/native-local-publication.d.ts +8 -0
- package/dist/native-quota-observations.d.ts +17 -0
- package/dist/native-runtime.d.ts +4 -4
- package/dist/pool-store.d.ts +3 -2
- package/dist/sticky-routing.d.ts +2 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2730,7 +2730,7 @@ async function assertHostsStopped(fence, env) {
|
|
|
2730
2730
|
|
|
2731
2731
|
// ../../node_modules/@cortexkit/common-auth/dist/setup/enroll.js
|
|
2732
2732
|
var ENROLLMENT_INCOMPLETE_MESSAGE = "Claustrum enrollment did not complete. Inspect and approve with ck, then rerun setup.";
|
|
2733
|
-
var UNSUPPORTED_VAULT_MESSAGE =
|
|
2733
|
+
var UNSUPPORTED_VAULT_MESSAGE = 'This Claustrum vault is too old to connect plugins. Run ck upgrade to move to Claustrum v0.2.0 or newer (read "Upgrading from v0.1.3" in its release notes first), or run setup without --claustrum to use local accounts.';
|
|
2734
2734
|
var ENROLLMENT_WAIT_MS = 300000;
|
|
2735
2735
|
var APPROVAL_POLL_MS = 1000;
|
|
2736
2736
|
var PROPOSE_BACKOFF_MS = [1000, 2000, 4000, 8000, 16000, 30000];
|
|
@@ -18408,6 +18408,52 @@ function acceptVaultRoute(path2, routeId) {
|
|
|
18408
18408
|
};
|
|
18409
18409
|
});
|
|
18410
18410
|
}
|
|
18411
|
+
function invalidOrder(message) {
|
|
18412
|
+
return new ClaustrumConsumerError("invalid-order", message);
|
|
18413
|
+
}
|
|
18414
|
+
function vaultOrderProblem(rows, routeIds) {
|
|
18415
|
+
if (!Array.isArray(routeIds))
|
|
18416
|
+
return "route ids must be an array of route ids";
|
|
18417
|
+
const rosterIds = new Set(rows.map((row) => row.routeId));
|
|
18418
|
+
const given = new Set;
|
|
18419
|
+
for (const id2 of routeIds) {
|
|
18420
|
+
if (typeof id2 !== "string")
|
|
18421
|
+
return "route ids must be an array of route ids";
|
|
18422
|
+
if (given.has(id2))
|
|
18423
|
+
return `route id ${id2} appears more than once`;
|
|
18424
|
+
if (!rosterIds.has(id2))
|
|
18425
|
+
return `route id ${id2} is not in the roster`;
|
|
18426
|
+
given.add(id2);
|
|
18427
|
+
}
|
|
18428
|
+
const missing2 = [...rosterIds].filter((id2) => !given.has(id2)).length;
|
|
18429
|
+
if (missing2 > 0)
|
|
18430
|
+
return `the order leaves out ${missing2} roster route id${missing2 === 1 ? "" : "s"}`;
|
|
18431
|
+
return;
|
|
18432
|
+
}
|
|
18433
|
+
function reorderVaultRoster(path2, routeIds, options = {}) {
|
|
18434
|
+
return mutateVaultRoster(path2, (current) => {
|
|
18435
|
+
const rows = current?.rows ?? [];
|
|
18436
|
+
const problem = vaultOrderProblem(rows, routeIds);
|
|
18437
|
+
if (problem)
|
|
18438
|
+
throw invalidOrder(problem);
|
|
18439
|
+
const byId = new Map;
|
|
18440
|
+
for (const row of rows) {
|
|
18441
|
+
const group = byId.get(row.routeId);
|
|
18442
|
+
if (group)
|
|
18443
|
+
group.push(row);
|
|
18444
|
+
else
|
|
18445
|
+
byId.set(row.routeId, [row]);
|
|
18446
|
+
}
|
|
18447
|
+
const ids = [...routeIds];
|
|
18448
|
+
const next = ids.flatMap((id2) => byId.get(id2) ?? []);
|
|
18449
|
+
if (!current || next.every((row, index) => row === rows[index]))
|
|
18450
|
+
return { result: { ids, outcome: "unchanged" } };
|
|
18451
|
+
return {
|
|
18452
|
+
next: { ...current, rows: next },
|
|
18453
|
+
result: { ids, outcome: "reordered" }
|
|
18454
|
+
};
|
|
18455
|
+
}, { ...options.signal && { signal: options.signal } });
|
|
18456
|
+
}
|
|
18411
18457
|
function receiptFence(row, receipt) {
|
|
18412
18458
|
if (!memberIds(row).includes(receipt.credentialId))
|
|
18413
18459
|
return "refuse";
|
|
@@ -19087,7 +19133,11 @@ class ClaustrumConsumer {
|
|
|
19087
19133
|
let current;
|
|
19088
19134
|
try {
|
|
19089
19135
|
current = await this.authorize(routeId, signal);
|
|
19090
|
-
|
|
19136
|
+
signal?.throwIfAborted();
|
|
19137
|
+
} catch (error) {
|
|
19138
|
+
if (signal?.aborted)
|
|
19139
|
+
throw error;
|
|
19140
|
+
}
|
|
19091
19141
|
if (decideScopedRetryAfter401(options.site, served, current, this.#logger)) {
|
|
19092
19142
|
await response.body?.cancel().catch(() => {});
|
|
19093
19143
|
served = current;
|
|
@@ -19114,6 +19164,12 @@ class ClaustrumConsumer {
|
|
|
19114
19164
|
await acceptVaultRoute(this.#options.rosterPath, routeId);
|
|
19115
19165
|
this.#roster = pendingVaultQuota(this.#options.rosterPath, await readVaultRoster(this.#options.rosterPath));
|
|
19116
19166
|
}
|
|
19167
|
+
async reorder(routeIds) {
|
|
19168
|
+
this.#assertOpen();
|
|
19169
|
+
const result = await reorderVaultRoster(this.#options.rosterPath, routeIds);
|
|
19170
|
+
this.#roster = pendingVaultQuota(this.#options.rosterPath, await readVaultRoster(this.#options.rosterPath));
|
|
19171
|
+
return result;
|
|
19172
|
+
}
|
|
19117
19173
|
async recordQuota(routeId, observation, attempt) {
|
|
19118
19174
|
this.#assertOpen();
|
|
19119
19175
|
return recordVaultQuota(this.#options.rosterPath, {
|
|
@@ -19187,6 +19243,7 @@ function nativeCode(value) {
|
|
|
19187
19243
|
case "host-slot-login":
|
|
19188
19244
|
case "host-slot-placeholder":
|
|
19189
19245
|
case "placeholder-refresh":
|
|
19246
|
+
case "invalid-order":
|
|
19190
19247
|
return value;
|
|
19191
19248
|
default:
|
|
19192
19249
|
return "unavailable";
|
|
@@ -21323,6 +21380,77 @@ function createNativeLocalCredentialService(options) {
|
|
|
21323
21380
|
return Object.freeze({ authorize: authorize2 });
|
|
21324
21381
|
}
|
|
21325
21382
|
|
|
21383
|
+
// src/native-local-publication.ts
|
|
21384
|
+
var publishers = new Map;
|
|
21385
|
+
function acquireNativeLocalPublisher(key) {
|
|
21386
|
+
let owned = publishers.get(key);
|
|
21387
|
+
if (!owned) {
|
|
21388
|
+
owned = { writer: createPublisher(), leases: 0 };
|
|
21389
|
+
publishers.set(key, owned);
|
|
21390
|
+
}
|
|
21391
|
+
owned.leases++;
|
|
21392
|
+
let released = false;
|
|
21393
|
+
return {
|
|
21394
|
+
...owned.writer,
|
|
21395
|
+
release() {
|
|
21396
|
+
if (released)
|
|
21397
|
+
return;
|
|
21398
|
+
released = true;
|
|
21399
|
+
if (--owned.leases === 0)
|
|
21400
|
+
publishers.delete(key);
|
|
21401
|
+
}
|
|
21402
|
+
};
|
|
21403
|
+
}
|
|
21404
|
+
function createPublisher() {
|
|
21405
|
+
let tail = Promise.resolve();
|
|
21406
|
+
const pending = new Map;
|
|
21407
|
+
const run = (write) => {
|
|
21408
|
+
const result = tail.then(write);
|
|
21409
|
+
tail = result.catch(() => {});
|
|
21410
|
+
return result;
|
|
21411
|
+
};
|
|
21412
|
+
return {
|
|
21413
|
+
run,
|
|
21414
|
+
enqueue(key, patch, write) {
|
|
21415
|
+
const queued = pending.get(key);
|
|
21416
|
+
if (queued) {
|
|
21417
|
+
const previous = queued.patch;
|
|
21418
|
+
if (previous.quota && patch.quota && patch.quota.source !== "headers" && quotaSnapshotCheckedAt(previous.quota) > quotaSnapshotCheckedAt(patch.quota))
|
|
21419
|
+
return Promise.resolve(false);
|
|
21420
|
+
queued.patch = { ...previous, ...patch };
|
|
21421
|
+
if (previous.quota && patch.quota)
|
|
21422
|
+
queued.patch.quota = fromNativeQuotaMap(nativeQuotaCodec.merge(toNativeQuotaMap(previous.quota), toNativeQuotaMap(patch.quota)));
|
|
21423
|
+
if (previous.lastUsed !== undefined || patch.lastUsed !== undefined)
|
|
21424
|
+
queued.patch.lastUsed = Math.max(previous.lastUsed ?? 0, patch.lastUsed ?? 0);
|
|
21425
|
+
for (const field of [
|
|
21426
|
+
"quotaErrorClearedAt",
|
|
21427
|
+
"quotaErrorGeneration"
|
|
21428
|
+
])
|
|
21429
|
+
if (previous[field] !== undefined || patch[field] !== undefined)
|
|
21430
|
+
queued.patch[field] = Math.max(previous[field] ?? 0, patch[field] ?? 0);
|
|
21431
|
+
if (previous.lastQuotaRefreshError && patch.lastQuotaRefreshError && previous.lastQuotaRefreshError.checkedAt > patch.lastQuotaRefreshError.checkedAt)
|
|
21432
|
+
queued.patch.lastQuotaRefreshError = previous.lastQuotaRefreshError;
|
|
21433
|
+
return queued.result;
|
|
21434
|
+
}
|
|
21435
|
+
const batch = { patch, result: Promise.resolve(false) };
|
|
21436
|
+
pending.set(key, batch);
|
|
21437
|
+
batch.result = run(async () => {
|
|
21438
|
+
await new Promise((resolve10) => setTimeout(resolve10, 0));
|
|
21439
|
+
pending.delete(key);
|
|
21440
|
+
return write(batch.patch);
|
|
21441
|
+
});
|
|
21442
|
+
return batch.result;
|
|
21443
|
+
},
|
|
21444
|
+
async drain() {
|
|
21445
|
+
let observed;
|
|
21446
|
+
do {
|
|
21447
|
+
observed = tail;
|
|
21448
|
+
await observed;
|
|
21449
|
+
} while (observed !== tail);
|
|
21450
|
+
}
|
|
21451
|
+
};
|
|
21452
|
+
}
|
|
21453
|
+
|
|
21326
21454
|
// src/native-usage-publication.ts
|
|
21327
21455
|
import { types as types3 } from "node:util";
|
|
21328
21456
|
function usagePublicationRefusalIsFatal(error) {
|
|
@@ -21364,6 +21492,7 @@ var codes = new Set([
|
|
|
21364
21492
|
"host-slot-login",
|
|
21365
21493
|
"host-slot-placeholder",
|
|
21366
21494
|
"placeholder-refresh",
|
|
21495
|
+
"invalid-order",
|
|
21367
21496
|
"publication-refused",
|
|
21368
21497
|
"unsafe-runtime",
|
|
21369
21498
|
"invalid-runtime",
|
|
@@ -21468,7 +21597,7 @@ class NativeQuotaObservations extends Map {
|
|
|
21468
21597
|
let clearAt;
|
|
21469
21598
|
let matched = false;
|
|
21470
21599
|
for (const observation of this.values()) {
|
|
21471
|
-
if (observation.entry.quota.source === "headers")
|
|
21600
|
+
if (observation.entry.quota.source === "headers" && observation.routeBeforeSave !== true)
|
|
21472
21601
|
continue;
|
|
21473
21602
|
if (observation.lineage !== lineage || observation.accountUuid !== accountUuid2 || getQuotaNextRefreshAt(observation.entry.quota, storage, observation.entry.checkedAt) <= now)
|
|
21474
21603
|
continue;
|
|
@@ -21498,6 +21627,17 @@ class NativeQuotaObservations extends Map {
|
|
|
21498
21627
|
}
|
|
21499
21628
|
}));
|
|
21500
21629
|
}
|
|
21630
|
+
hold(accountId, observation) {
|
|
21631
|
+
const current = this.get(accountId);
|
|
21632
|
+
if (current && current.lineage === observation.lineage && quotaSnapshotCheckedAt(current.entry.quota) > quotaSnapshotCheckedAt(observation.entry.quota))
|
|
21633
|
+
return false;
|
|
21634
|
+
this.set(accountId, observation);
|
|
21635
|
+
return true;
|
|
21636
|
+
}
|
|
21637
|
+
forget(accountId, observation) {
|
|
21638
|
+
if (this.get(accountId) === observation)
|
|
21639
|
+
this.delete(accountId);
|
|
21640
|
+
}
|
|
21501
21641
|
drain() {
|
|
21502
21642
|
return this.publications.drain();
|
|
21503
21643
|
}
|
|
@@ -24505,7 +24645,7 @@ class StickySessionRouter {
|
|
|
24505
24645
|
}) > 0) : undefined;
|
|
24506
24646
|
const selected = preferred ?? this.selectCandidate({ ...input, candidates, state });
|
|
24507
24647
|
if (!selected) {
|
|
24508
|
-
if (current) {
|
|
24648
|
+
if (current && !input.preserveOnNoCandidate) {
|
|
24509
24649
|
delete state.assignments[key];
|
|
24510
24650
|
save("delete");
|
|
24511
24651
|
}
|
|
@@ -26207,11 +26347,12 @@ function acquireNativeAccountRuntime(options) {
|
|
|
26207
26347
|
const quotaObservations = use().quotaObservations;
|
|
26208
26348
|
const onLockEvent = use().onLockEvent;
|
|
26209
26349
|
const drainRoutingPublications = use().drainRoutingPublications;
|
|
26350
|
+
const drainQuotaPublications = use().drainQuotaPublications;
|
|
26210
26351
|
return {
|
|
26211
26352
|
onLockEvent,
|
|
26212
26353
|
vault,
|
|
26213
26354
|
quotaObservations,
|
|
26214
|
-
drainQuotaPublications
|
|
26355
|
+
drainQuotaPublications,
|
|
26215
26356
|
drainRoutingPublications,
|
|
26216
26357
|
getRelayConfig: () => use().getRelayConfig(),
|
|
26217
26358
|
getSettingsPath: () => use().getSettingsPath(),
|
|
@@ -26239,7 +26380,10 @@ function acquireNativeAccountRuntime(options) {
|
|
|
26239
26380
|
fetchProfileForDisplay: (id2, transport, signal) => use().fetchProfileForDisplay(id2, transport, signal),
|
|
26240
26381
|
close: () => {
|
|
26241
26382
|
vault.close();
|
|
26242
|
-
return
|
|
26383
|
+
return Promise.all([
|
|
26384
|
+
drainQuotaPublications(),
|
|
26385
|
+
drainRoutingPublications()
|
|
26386
|
+
]).then(() => {});
|
|
26243
26387
|
}
|
|
26244
26388
|
};
|
|
26245
26389
|
}
|
|
@@ -26279,6 +26423,9 @@ function createNativeAccountRuntime(options) {
|
|
|
26279
26423
|
}
|
|
26280
26424
|
});
|
|
26281
26425
|
const shutdown = new AbortController;
|
|
26426
|
+
let closing;
|
|
26427
|
+
const quotaPublications = acquireNativeLocalPublisher(paths.config);
|
|
26428
|
+
const metadataPublications = acquireNativeLocalPublisher(paths.runtime);
|
|
26282
26429
|
const store = createNativePoolStore({
|
|
26283
26430
|
paths,
|
|
26284
26431
|
quota: nativeQuotaCodec,
|
|
@@ -26401,6 +26548,51 @@ function createNativeAccountRuntime(options) {
|
|
|
26401
26548
|
throw error;
|
|
26402
26549
|
}
|
|
26403
26550
|
}
|
|
26551
|
+
async function localMetadataMatches(subject) {
|
|
26552
|
+
const binding2 = subject.binding;
|
|
26553
|
+
const row = await poolRow(binding2.rowId);
|
|
26554
|
+
if (row?.stamp !== "bound" || !row.credential || !nativeLocalPoolBindingMatches(binding2, paths, row))
|
|
26555
|
+
return false;
|
|
26556
|
+
if ("kind" in subject)
|
|
26557
|
+
return row.credential.type === "api" && fingerprintOf(row.credential) === subject.credentialFingerprint && row.credential.baseURL === subject.baseURL && row.credential.authHeader === subject.authHeader;
|
|
26558
|
+
return row.credential.type === "oauth" && fingerprintOf(row.credential) === subject.credentialFingerprint && nativeLocalCredentialValidationMatches(subject, binding2, row.credential);
|
|
26559
|
+
}
|
|
26560
|
+
async function localMetadataWrite(subject, change) {
|
|
26561
|
+
const binding2 = subject.binding;
|
|
26562
|
+
if (await authority() !== "local")
|
|
26563
|
+
return false;
|
|
26564
|
+
const matches2 = await localMetadataMatches(subject);
|
|
26565
|
+
await localAuthority();
|
|
26566
|
+
if (!matches2)
|
|
26567
|
+
return false;
|
|
26568
|
+
try {
|
|
26569
|
+
await updateNativeRuntime(paths.runtime, paths.storageId, (current) => {
|
|
26570
|
+
const old = current.accounts[binding2.rowId];
|
|
26571
|
+
const previous = old?.binding;
|
|
26572
|
+
const same = previous?.kind === "local" && previous.rowId === binding2.rowId && previous.identity === binding2.identity && previous.credentialEpoch === binding2.credentialEpoch;
|
|
26573
|
+
current.accounts[binding2.rowId] = change(same && old ? old : { binding: binding2 });
|
|
26574
|
+
return current;
|
|
26575
|
+
}, {
|
|
26576
|
+
onLockEvent: options.local?.onLockEvent,
|
|
26577
|
+
beforeRename: async () => {
|
|
26578
|
+
if (!await localMetadataMatches(subject))
|
|
26579
|
+
throw new NativeRuntimeError("publication-refused");
|
|
26580
|
+
await localAuthority();
|
|
26581
|
+
},
|
|
26582
|
+
beforePublish: () => localAuthority()
|
|
26583
|
+
});
|
|
26584
|
+
if (await authority() !== "local")
|
|
26585
|
+
throw new NativeRuntimeError("publication-refused");
|
|
26586
|
+
return true;
|
|
26587
|
+
} catch (error) {
|
|
26588
|
+
if (error instanceof NativeRuntimeError && (error.code === "runtime-conflict" || error.code === "publication-refused")) {
|
|
26589
|
+
if (await authority() !== "local")
|
|
26590
|
+
throw error;
|
|
26591
|
+
return false;
|
|
26592
|
+
}
|
|
26593
|
+
throw error;
|
|
26594
|
+
}
|
|
26595
|
+
}
|
|
26404
26596
|
async function initializeCredentialEpoch(id2) {
|
|
26405
26597
|
const row = await poolRow(id2);
|
|
26406
26598
|
if (!row?.credential || row.credentialEpoch === undefined)
|
|
@@ -26536,7 +26728,13 @@ function createNativeAccountRuntime(options) {
|
|
|
26536
26728
|
onLockEvent,
|
|
26537
26729
|
vault,
|
|
26538
26730
|
quotaObservations: vault.quotaObservations,
|
|
26539
|
-
drainQuotaPublications: () =>
|
|
26731
|
+
drainQuotaPublications: async () => {
|
|
26732
|
+
await vault.quotaObservations.drain();
|
|
26733
|
+
await Promise.all([
|
|
26734
|
+
quotaPublications.drain(),
|
|
26735
|
+
metadataPublications.drain()
|
|
26736
|
+
]);
|
|
26737
|
+
},
|
|
26540
26738
|
drainRoutingPublications: () => vaultRouter.drain(),
|
|
26541
26739
|
async getRelayConfig() {
|
|
26542
26740
|
const { mode, paths: paths2, store: store2 } = await selectedStorage();
|
|
@@ -26994,30 +27192,45 @@ function createNativeAccountRuntime(options) {
|
|
|
26994
27192
|
};
|
|
26995
27193
|
},
|
|
26996
27194
|
async publishLocal(subject, rawPatch) {
|
|
26997
|
-
const
|
|
26998
|
-
|
|
26999
|
-
|
|
27000
|
-
|
|
27001
|
-
const
|
|
27002
|
-
|
|
27003
|
-
if (row?.stamp !== "bound" || row.credential?.type !== "oauth" || !nativeLocalPoolBindingMatches(subject.binding, paths, row) || !quotaOnly && !nativeLocalCredentialValidationMatches(subject, subject.binding, row.credential))
|
|
27004
|
-
return false;
|
|
27005
|
-
if (quota2) {
|
|
27006
|
-
if (quota2.source !== "headers" && row.quota && quotaSnapshotCheckedAt(fromNativeQuotaMap(row.quota)) > quotaSnapshotCheckedAt(quota2))
|
|
27195
|
+
const captured = captureNativeMetadataPatch(rawPatch);
|
|
27196
|
+
const { quota: _quota, ...metadata2 } = captured;
|
|
27197
|
+
const mixed = captured.quota !== undefined && Object.keys(metadata2).length > 0;
|
|
27198
|
+
let admission;
|
|
27199
|
+
const admitted = () => admission ??= (async () => {
|
|
27200
|
+
if (await authority() !== "local")
|
|
27007
27201
|
return false;
|
|
27008
|
-
|
|
27009
|
-
|
|
27010
|
-
|
|
27011
|
-
|
|
27012
|
-
|
|
27013
|
-
|
|
27014
|
-
|
|
27015
|
-
|
|
27202
|
+
const matches2 = await localMetadataMatches(subject);
|
|
27203
|
+
await localAuthority();
|
|
27204
|
+
return matches2;
|
|
27205
|
+
})();
|
|
27206
|
+
const key = JSON.stringify(subject);
|
|
27207
|
+
const writes = [];
|
|
27208
|
+
if (captured.quota)
|
|
27209
|
+
writes.push(quotaPublications.enqueue(mixed ? `${key}:mixed` : key, { quota: captured.quota }, async ({ quota: quota2 }) => {
|
|
27210
|
+
if (mixed && !await admitted())
|
|
27211
|
+
return false;
|
|
27212
|
+
if (!quota2 || await authority() !== "local")
|
|
27213
|
+
return false;
|
|
27214
|
+
const row = await poolRow(subject.binding.rowId);
|
|
27215
|
+
if (row?.stamp !== "bound" || row.credential?.type !== "oauth" || !nativeLocalPoolBindingMatches(subject.binding, paths, row) || quota2.accountIdentity !== subject.binding.identity)
|
|
27216
|
+
return false;
|
|
27217
|
+
if (quota2.source !== "headers" && row.quota && quotaSnapshotCheckedAt(fromNativeQuotaMap(row.quota)) > quotaSnapshotCheckedAt(quota2))
|
|
27218
|
+
return false;
|
|
27219
|
+
await store.recordQuota(subject.binding.rowId, {
|
|
27220
|
+
credentialEpoch: subject.binding.credentialEpoch,
|
|
27221
|
+
identity: subject.binding.identity
|
|
27222
|
+
}, toNativeQuotaMap(quota2));
|
|
27223
|
+
return true;
|
|
27224
|
+
}));
|
|
27225
|
+
if (Object.keys(metadata2).length)
|
|
27226
|
+
writes.push(metadataPublications.enqueue(mixed ? `${key}:mixed` : key, metadata2, async (patch) => {
|
|
27227
|
+
if (mixed && !await admitted())
|
|
27228
|
+
return false;
|
|
27229
|
+
return localMetadataWrite(subject, (old) => applyNativeMetadataPatch(old, patch));
|
|
27230
|
+
}));
|
|
27231
|
+
return (await Promise.all(writes)).every(Boolean);
|
|
27016
27232
|
},
|
|
27017
|
-
publishApi: (subject, patch) =>
|
|
27018
|
-
...old,
|
|
27019
|
-
lastUsed: Math.max(old.lastUsed ?? 0, patch.lastUsed)
|
|
27020
|
-
})),
|
|
27233
|
+
publishApi: (subject, patch) => metadataPublications.enqueue(JSON.stringify(subject), patch, (merged) => localMetadataWrite(subject, (old) => applyNativeMetadataPatch(old, merged))),
|
|
27021
27234
|
async resetBackoff(routeId, fence, kind = "all") {
|
|
27022
27235
|
if ("accessToken" in fence)
|
|
27023
27236
|
return vault.resetBackoff(fence, kind);
|
|
@@ -27049,13 +27262,13 @@ function createNativeAccountRuntime(options) {
|
|
|
27049
27262
|
if (await physicalId(routeId) !== fence.binding.rowId)
|
|
27050
27263
|
return;
|
|
27051
27264
|
let lineage;
|
|
27052
|
-
const kept = await
|
|
27265
|
+
const kept = await metadataPublications.run(() => localMetadataWrite(fence, (old) => {
|
|
27053
27266
|
lineage = old.authLineageId ?? randomUUID15();
|
|
27054
27267
|
return {
|
|
27055
27268
|
...old,
|
|
27056
27269
|
authLineageId: lineage
|
|
27057
27270
|
};
|
|
27058
|
-
});
|
|
27271
|
+
}));
|
|
27059
27272
|
return kept ? lineage : undefined;
|
|
27060
27273
|
},
|
|
27061
27274
|
async incrementPrimeUsage(routeId, fence, delta = {}) {
|
|
@@ -27066,7 +27279,7 @@ function createNativeAccountRuntime(options) {
|
|
|
27066
27279
|
if (await physicalId(routeId) !== fence.binding.rowId)
|
|
27067
27280
|
return false;
|
|
27068
27281
|
const captured = primeDelta(delta);
|
|
27069
|
-
return
|
|
27282
|
+
return metadataPublications.run(() => localMetadataWrite(fence, (old) => ({
|
|
27070
27283
|
...old,
|
|
27071
27284
|
prime: {
|
|
27072
27285
|
count: (old.prime?.count ?? 0) + 1,
|
|
@@ -27074,7 +27287,7 @@ function createNativeAccountRuntime(options) {
|
|
|
27074
27287
|
outputTokens: (old.prime?.outputTokens ?? 0) + captured.outputTokens,
|
|
27075
27288
|
since: old.prime?.since ?? now()
|
|
27076
27289
|
}
|
|
27077
|
-
}));
|
|
27290
|
+
})));
|
|
27078
27291
|
},
|
|
27079
27292
|
async fetchQuota(routeId, fetchImpl = fetch, signal, dispatch, purpose) {
|
|
27080
27293
|
const snapshot = await runtime2.read();
|
|
@@ -27138,11 +27351,16 @@ function createNativeAccountRuntime(options) {
|
|
|
27138
27351
|
});
|
|
27139
27352
|
},
|
|
27140
27353
|
close() {
|
|
27141
|
-
if (
|
|
27354
|
+
if (closing)
|
|
27355
|
+
return closing;
|
|
27356
|
+
const draining = runtime2.drainQuotaPublications();
|
|
27357
|
+
vault.close();
|
|
27358
|
+
closing = Promise.all([draining, vaultRouter.drain()]).then(() => {}).finally(() => {
|
|
27142
27359
|
shutdown.abort(new NativeCustodyError("closed"));
|
|
27143
|
-
|
|
27144
|
-
|
|
27145
|
-
|
|
27360
|
+
quotaPublications.release();
|
|
27361
|
+
metadataPublications.release();
|
|
27362
|
+
});
|
|
27363
|
+
return closing;
|
|
27146
27364
|
}
|
|
27147
27365
|
};
|
|
27148
27366
|
function readLocalProfile(id2, fetchImpl, signal) {
|
|
@@ -4,7 +4,7 @@ import type { RoutingRow } from '../routing/index.js';
|
|
|
4
4
|
import { type ClaustrumFamily, type ClaustrumScopedAttempt, type ClaustrumScopedClient, type IdentityParser, type KeptReceiptState, type VaultGrantReach } from './custody.js';
|
|
5
5
|
import { type ClaustrumLogger } from './errors.js';
|
|
6
6
|
import { type InsulaQuotaOptions, type InsulaQuotaResult } from './insula.js';
|
|
7
|
-
import { type AccountMapper, type QuotaReceipt, type VaultRosterFile } from './roster.js';
|
|
7
|
+
import { type AccountMapper, type QuotaReceipt, type ReorderVaultRosterResult, type VaultRosterFile } from './roster.js';
|
|
8
8
|
import { type WatchMode } from './watch.js';
|
|
9
9
|
export interface ClaustrumConsumerOptions<Q = QuotaObservation> {
|
|
10
10
|
/**
|
|
@@ -157,6 +157,12 @@ export declare class ClaustrumConsumer<Q = QuotaObservation> {
|
|
|
157
157
|
/** Decline a vault account: it stays listed and never routes until accepted. */
|
|
158
158
|
decline(routeId: string): Promise<void>;
|
|
159
159
|
accept(routeId: string): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Set the order vault accounts are tried in (`reorderVaultRoster`), then
|
|
162
|
+
* re-read the roster, so the next routing decision in this process uses
|
|
163
|
+
* the new order without waiting for a discovery. Never contacts the vault.
|
|
164
|
+
*/
|
|
165
|
+
reorder(routeIds: readonly string[]): Promise<ReorderVaultRosterResult>;
|
|
160
166
|
/**
|
|
161
167
|
* Store a quota or profile observation for a vault route. The receipt the
|
|
162
168
|
* reading was taken with is required: the observation lands only while the
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `code`, a `class` and an `action`), so callers can tell the vault's verdict
|
|
5
5
|
* apart from a check made on this side.
|
|
6
6
|
*/
|
|
7
|
-
export type ClaustrumConsumerFailureKind = 'closed' | 'not-enrolled' | 'invalid-token' | 'unavailable' | 'identity-changed' | 'identity-unasserted' | 'insufficient-validity' | 'invalid-material' | 'not-active' | 'route-unavailable' | 'route-declined' | 'no-receipt' | 'unsafe-file' | 'invalid-state' | 'wrong-consumer' | 'roster-busy' | 'host-slot-login' | 'host-slot-placeholder' | 'placeholder-refresh';
|
|
7
|
+
export type ClaustrumConsumerFailureKind = 'closed' | 'not-enrolled' | 'invalid-token' | 'unavailable' | 'identity-changed' | 'identity-unasserted' | 'insufficient-validity' | 'invalid-material' | 'not-active' | 'route-unavailable' | 'route-declined' | 'no-receipt' | 'unsafe-file' | 'invalid-state' | 'wrong-consumer' | 'roster-busy' | 'host-slot-login' | 'host-slot-placeholder' | 'placeholder-refresh' | 'invalid-order';
|
|
8
8
|
export declare class ClaustrumConsumerError extends Error {
|
|
9
9
|
readonly kind: ClaustrumConsumerFailureKind;
|
|
10
10
|
constructor(kind: ClaustrumConsumerFailureKind, message: string);
|
|
@@ -10,7 +10,7 @@ export { assertHostSlotMatchesMode, assertNotCustodyPlaceholder, CUSTODY_PLACEHO
|
|
|
10
10
|
export { acquireInsulaQuotaSource, type InsulaQuotaContext, type InsulaQuotaLookup, type InsulaQuotaMiss, type InsulaQuotaOptions, type InsulaQuotaReading, type InsulaQuotaResult, type InsulaQuotaSource, type InsulaUsageEntry, type InsulaUsageTransport, type InsulaUsageWatch, type InsulaUsageWindow, type InsulaWatchOptions, } from './insula.js';
|
|
11
11
|
export { acceptAccount, type DeclinedAccount, declineAccount, isDeclined, } from './interlock.js';
|
|
12
12
|
export { drainVaultQuota } from './quota-writer.js';
|
|
13
|
-
export { type AccountMapper, acceptVaultRoute, DEFAULT_ROUTE_PREFIX, declineVaultRoute, type MutateVaultRosterOptions, mutateVaultRoster, type ProjectionOptions, projectVaultRoster, type QuotaReceipt, readVaultRoster, recordVaultAccountQuota, recordVaultQuota, refreshVaultRoster, resolveVaultPrimary, type VaultPrimary, type VaultPrimaryBinding, type VaultPrimaryUnavailableReason, type VaultRosterFile, type VaultRosterLockContext, type VaultRosterRow, vaultRoutingRows, } from './roster.js';
|
|
13
|
+
export { type AccountMapper, acceptVaultRoute, DEFAULT_ROUTE_PREFIX, declineVaultRoute, type MutateVaultRosterOptions, mutateVaultRoster, type ProjectionOptions, projectVaultRoster, type QuotaReceipt, type ReorderVaultRosterOptions, type ReorderVaultRosterResult, readVaultRoster, recordVaultAccountQuota, recordVaultQuota, refreshVaultRoster, reorderVaultRoster, resolveVaultPrimary, type VaultPrimary, type VaultPrimaryBinding, type VaultPrimaryUnavailableReason, type VaultRosterFile, type VaultRosterLockContext, type VaultRosterRow, vaultRoutingRows, } from './roster.js';
|
|
14
14
|
export { type ChangeNotice, ChangeNoticeReader, RESUBSCRIBE_BACKOFF_MS, SAFETY_POLL_MS, ScopedRosterWatch, type ScopedRosterWatchOptions, type ScopedWatchEnd, type ScopedWatchHandle, type ScopedWatchNoticeShape, type ScopedWatchSource, WATCH_RETRY_MS, type WatchMode, } from './watch.js';
|
|
15
15
|
/**
|
|
16
16
|
* Connect the client that lists and fetches this consumer's vault credentials
|
|
@@ -119,6 +119,37 @@ export declare function mutateVaultRoster<T>(path: string, change: (current: Vau
|
|
|
119
119
|
export declare function declineVaultRoute(path: string, routeId: string): Promise<undefined>;
|
|
120
120
|
/** Lift the user's decline for a vault account and every alias of it. */
|
|
121
121
|
export declare function acceptVaultRoute(path: string, routeId: string): Promise<undefined>;
|
|
122
|
+
/** Options of `reorderVaultRoster`. */
|
|
123
|
+
export interface ReorderVaultRosterOptions {
|
|
124
|
+
/** Aborts the wait for the roster's write lock. */
|
|
125
|
+
signal?: AbortSignal;
|
|
126
|
+
}
|
|
127
|
+
export interface ReorderVaultRosterResult {
|
|
128
|
+
/** The route id order now on disk. */
|
|
129
|
+
ids: string[];
|
|
130
|
+
/** `unchanged` when the roster was already in this order; nothing was written. */
|
|
131
|
+
outcome: 'reordered' | 'unchanged';
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Set the order vault accounts are tried in. Routing reads that order from
|
|
135
|
+
* the roster's row order (`vaultRoutingRows`), and a later discovery keeps
|
|
136
|
+
* each account at its position, so the order lasts until the next reorder.
|
|
137
|
+
* The order is this host's routing preference: the vault has no order of its
|
|
138
|
+
* own, and this never contacts it.
|
|
139
|
+
*
|
|
140
|
+
* `routeIds` must name every roster route id exactly once; anything else
|
|
141
|
+
* (an unknown, missing or repeated id) refuses with `invalid-order` and
|
|
142
|
+
* writes nothing. It runs under the roster's write lock, like every other
|
|
143
|
+
* roster write, so a quota reading or decline committed meanwhile is kept.
|
|
144
|
+
* Each row is kept as the same object, so only the positions change: every
|
|
145
|
+
* row's fields and the rest of the file stay as they were. An order equal to
|
|
146
|
+
* the current one writes nothing. A host with no roster file yet has no
|
|
147
|
+
* routes, so only the empty order is accepted.
|
|
148
|
+
*
|
|
149
|
+
* A `ClaustrumConsumer` in the same process does not see the new order until
|
|
150
|
+
* it re-reads the file: call `consumer.reorder` instead to update both.
|
|
151
|
+
*/
|
|
152
|
+
export declare function reorderVaultRoster(path: string, routeIds: readonly string[], options?: ReorderVaultRosterOptions): Promise<ReorderVaultRosterResult>;
|
|
122
153
|
/**
|
|
123
154
|
* The receipt fields a quota or profile observation must carry to say which
|
|
124
155
|
* send (credential and account) it came from. Pass the
|
|
@@ -48,6 +48,43 @@ export interface RoutingSectionOptions {
|
|
|
48
48
|
*/
|
|
49
49
|
orderedVariants?: MenuChoice[];
|
|
50
50
|
formerMainId?: string;
|
|
51
|
+
/**
|
|
52
|
+
* The vault accounts' routing order, for a plugin that can run in vault
|
|
53
|
+
* mode. Called on each build; return the order while the plugin routes
|
|
54
|
+
* through vault accounts and undefined otherwise. While it returns one, the
|
|
55
|
+
* section shows and sets the vault accounts' order (by the name the menu
|
|
56
|
+
* shows for each account) instead of the pool roster's. Wire it to
|
|
57
|
+
* `ClaustrumConsumer.snapshot()` and `ClaustrumConsumer.reorder`, or to
|
|
58
|
+
* `readVaultRoster` and `reorderVaultRoster` for a plugin without a
|
|
59
|
+
* consumer.
|
|
60
|
+
*/
|
|
61
|
+
vaultOrder?: () => VaultRoutingOrder | undefined | Promise<VaultRoutingOrder | undefined>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The order vault accounts are tried in, as the Routing section reads and
|
|
65
|
+
* sets it in vault mode. Shaped after `/claustrum`'s roster, without
|
|
66
|
+
* importing it, so the menu does not need the vault client.
|
|
67
|
+
*/
|
|
68
|
+
export interface VaultRoutingOrder {
|
|
69
|
+
/**
|
|
70
|
+
* Every vault roster row, in roster (routing) order: its route id and the
|
|
71
|
+
* name the plugin shows for that account (the roster row's `label`, or its
|
|
72
|
+
* email). Route ids are long and opaque, so the section shows and accepts
|
|
73
|
+
* labels; a route id is still accepted wherever a label is.
|
|
74
|
+
*/
|
|
75
|
+
rows: readonly {
|
|
76
|
+
id: string;
|
|
77
|
+
label: string;
|
|
78
|
+
}[];
|
|
79
|
+
/**
|
|
80
|
+
* Sets the order. `ids` are route ids, every one exactly once. Wire it to
|
|
81
|
+
* `ClaustrumConsumer.reorder` or `reorderVaultRoster`, which refuse any
|
|
82
|
+
* other list without writing.
|
|
83
|
+
*/
|
|
84
|
+
reorder(ids: readonly string[]): Promise<{
|
|
85
|
+
ids: readonly string[];
|
|
86
|
+
outcome: 'reordered' | 'unchanged';
|
|
87
|
+
}>;
|
|
51
88
|
}
|
|
52
89
|
export interface LimitsSectionOptions {
|
|
53
90
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { AccountsSectionOptions, LimitsSectionOptions, LoginOutcome, QuotaSectionOptions, RoutingSectionOptions, } from './builtins.js';
|
|
1
|
+
export type { AccountsSectionOptions, LimitsSectionOptions, LoginOutcome, QuotaSectionOptions, RoutingSectionOptions, VaultRoutingOrder, } from './builtins.js';
|
|
2
2
|
export { MENU_DISABLED_REASON } from './builtins.js';
|
|
3
3
|
export type { CommandMenu, CommandMenuOptions, StoreSectionSlot, } from './menu.js';
|
|
4
4
|
export { createCommandMenu, parseApplyRequest } from './menu.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type MenuTerminal } from '../auth-menu/terminal.js';
|
|
2
2
|
import type { SetupHost, SetupIO, SetupPlugin } from './types.js';
|
|
3
3
|
/** Fixed text: no version, host or account detail, so it is safe to print anywhere. */
|
|
4
|
-
export declare const UNSUPPORTED_VAULT_MESSAGE = "This Claustrum vault
|
|
4
|
+
export declare const UNSUPPORTED_VAULT_MESSAGE = "This Claustrum vault is too old to connect plugins. Run ck upgrade to move to Claustrum v0.2.0 or newer (read \"Upgrading from v0.1.3\" in its release notes first), or run setup without --claustrum to use local accounts.";
|
|
5
5
|
/**
|
|
6
6
|
* The longest setup waits, in total, for the vault to create the enrollment
|
|
7
7
|
* request and for an operator to approve it. It matches the wait setup always
|
package/dist/native-custody.d.ts
CHANGED
|
@@ -203,7 +203,7 @@ export interface NativeCustodyInventory {
|
|
|
203
203
|
}[];
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
|
-
export type NativeCustodyErrorCode = 'closed' | 'not-enrolled' | 'invalid-token' | 'unavailable' | 'identity-changed' | 'identity-unasserted' | 'insufficient-validity' | 'invalid-material' | 'not-active' | 'route-unavailable' | 'route-declined' | 'no-receipt' | 'unsafe-file' | 'invalid-state' | 'wrong-consumer' | 'roster-busy' | 'host-slot-login' | 'host-slot-placeholder' | 'placeholder-refresh';
|
|
206
|
+
export type NativeCustodyErrorCode = 'closed' | 'not-enrolled' | 'invalid-token' | 'unavailable' | 'identity-changed' | 'identity-unasserted' | 'insufficient-validity' | 'invalid-material' | 'not-active' | 'route-unavailable' | 'route-declined' | 'no-receipt' | 'unsafe-file' | 'invalid-state' | 'wrong-consumer' | 'roster-busy' | 'host-slot-login' | 'host-slot-placeholder' | 'placeholder-refresh' | 'invalid-order';
|
|
207
207
|
export declare class NativeCustodyError extends Error {
|
|
208
208
|
readonly code: NativeCustodyErrorCode;
|
|
209
209
|
constructor(code: NativeCustodyErrorCode);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NativeAccountMetadataPatch } from './native-account-view.ts';
|
|
2
|
+
/** Share one replaceable bookkeeping mailbox per destination in this process, never for credential writes. */
|
|
3
|
+
export declare function acquireNativeLocalPublisher(key: string): {
|
|
4
|
+
release(): void;
|
|
5
|
+
run: <T>(write: () => Promise<T>) => Promise<T>;
|
|
6
|
+
enqueue(key: string, patch: NativeAccountMetadataPatch, write: (patch: NativeAccountMetadataPatch) => Promise<boolean>): Promise<boolean>;
|
|
7
|
+
drain(): Promise<void>;
|
|
8
|
+
};
|
|
@@ -7,6 +7,14 @@ export type NativeQuotaObservation = {
|
|
|
7
7
|
entry: QuotaEntry;
|
|
8
8
|
generalExhausted: boolean;
|
|
9
9
|
quotaErrorClearedAt?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Set on a response-header reading from a host that routes on native
|
|
12
|
+
* snapshots (OpenCode 2). The snapshot overlay then applies it while its
|
|
13
|
+
* detached save is still pending, as it does for poll readings. Header
|
|
14
|
+
* readings without it stay host-owned: OpenCode 1 applies its own routing
|
|
15
|
+
* and display rules to them.
|
|
16
|
+
*/
|
|
17
|
+
routeBeforeSave?: boolean;
|
|
10
18
|
};
|
|
11
19
|
export declare function nativePollQuotaGenerallyExhausted(quota: OAuthQuotaSnapshot): boolean;
|
|
12
20
|
export declare function nativeLocalQuotaLineage(binding: NativeLocalPoolBinding & {
|
|
@@ -22,5 +30,14 @@ export declare class NativeQuotaObservations extends Map<string, NativeQuotaObse
|
|
|
22
30
|
clearAt?: number;
|
|
23
31
|
} | undefined;
|
|
24
32
|
publish(accountId: string, observation: NativeQuotaObservation, write: () => Promise<boolean>): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Registers a response-header reading before its detached save starts, so
|
|
35
|
+
* routing in this process sees it while the save waits on file locks. A
|
|
36
|
+
* reading older than the one already held for the same credential lineage
|
|
37
|
+
* is not registered, so an older reading never replaces a newer one.
|
|
38
|
+
*/
|
|
39
|
+
hold(accountId: string, observation: NativeQuotaObservation): boolean;
|
|
40
|
+
/** Forgets a held reading whose save was refused, unless a later reading already replaced it. */
|
|
41
|
+
forget(accountId: string, observation: NativeQuotaObservation): void;
|
|
25
42
|
drain(): Promise<void>;
|
|
26
43
|
}
|
package/dist/native-runtime.d.ts
CHANGED
|
@@ -104,10 +104,10 @@ export interface NativeRuntimeWriteHooks {
|
|
|
104
104
|
/** This writer alone uses `<leaf>.native-runtime.<UUID v4>.partial`. */
|
|
105
105
|
export declare function isNativeRuntimeStagingName(path: string, name: string): boolean;
|
|
106
106
|
/**
|
|
107
|
-
* This file has its own update lock.
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
107
|
+
* This file has its own update lock. Advisory account publishers recheck the
|
|
108
|
+
* pool or roster binding without taking its lock immediately before replacement;
|
|
109
|
+
* readers reject metadata whose binding no longer matches. Credential writers
|
|
110
|
+
* retain their separate ownership and custody checks.
|
|
111
111
|
*/
|
|
112
112
|
export declare function updateNativeRuntime(path: string, storageId: string, change: (current: NativeRuntimeState) => NativeRuntimeState | Promise<NativeRuntimeState>, hooks?: NativeRuntimeWriteHooks): Promise<NativeRuntimeState>;
|
|
113
113
|
/**
|
package/dist/pool-store.d.ts
CHANGED
|
@@ -15,8 +15,9 @@ export interface NativePoolStoreOptions extends Pick<OpenPoolStoreOptions, 'now'
|
|
|
15
15
|
onLockEvent?: (event: NativeLockEvent) => void;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* These
|
|
19
|
-
*
|
|
18
|
+
* These locks serialize pool transactions, including credential changes and
|
|
19
|
+
* quota publication. Runtime bookkeeping has its own lock and binding checks.
|
|
20
|
+
* Legacy sidecar writers use different files and locks.
|
|
20
21
|
*/
|
|
21
22
|
export declare function nativePoolStoreLocks(paths: NativePoolStorePaths): readonly [{
|
|
22
23
|
readonly path: string;
|
package/dist/sticky-routing.d.ts
CHANGED
|
@@ -148,6 +148,8 @@ export declare class StickySessionRouter {
|
|
|
148
148
|
inputBytes: number;
|
|
149
149
|
preferredAccountId?: string;
|
|
150
150
|
excludeAccountIds?: ReadonlySet<string>;
|
|
151
|
+
/** Leave the current pin intact when selection cannot offer an account. */
|
|
152
|
+
preserveOnNoCandidate?: boolean;
|
|
151
153
|
}): Promise<StickyRouteResolution | null>;
|
|
152
154
|
clear(sessionId: string): Promise<void>;
|
|
153
155
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/anthropic-auth-core",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"prepublishOnly": "bun run build"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@cortexkit/common-auth": "0.
|
|
33
|
+
"@cortexkit/common-auth": "0.25.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@cortexkit/claustrum-client": "0.12.0",
|