@cortexkit/antigravity-auth-core 3.0.19 → 3.0.21
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/common-auth-embedded/claustrum/consumer.d.ts +10 -0
- package/dist/common-auth-embedded/claustrum/consumer.js +25 -0
- package/dist/common-auth-embedded/claustrum/custody.d.ts +6 -2
- package/dist/common-auth-embedded/claustrum/custody.js +45 -6
- package/dist/common-auth-embedded/claustrum/hub.d.ts +3 -1
- package/dist/common-auth-embedded/claustrum/hub.js +75 -6
- package/dist/common-auth-embedded/claustrum/insula.js +6 -0
- package/dist/common-auth-embedded/claustrum/roster.js +1 -1
- package/dist/common-auth-embedded/fs/refresh-file-lock.d.ts +2 -1
- package/dist/common-auth-embedded/fs/refresh-file-lock.js +3 -1
- package/dist/common-auth-embedded/fs/with-lock.js +1 -1
- package/dist/common-auth-embedded/source-output.json +40 -40
- package/package.json +1 -1
|
@@ -50,6 +50,12 @@ export interface ClaustrumConsumerOptions<Q = QuotaObservation> {
|
|
|
50
50
|
* routine token refresh, which never moves the view.
|
|
51
51
|
*/
|
|
52
52
|
onRoster?: (roster: VaultRosterFile) => void;
|
|
53
|
+
/**
|
|
54
|
+
* Called once when a coalesced discovery starts, with its completion promise.
|
|
55
|
+
* Covers explicit refresh, poll, watch and send-triggered discovery, even when
|
|
56
|
+
* the view does not change. Observe rejection when attaching async work.
|
|
57
|
+
*/
|
|
58
|
+
onDiscovery?: (promise: Promise<VaultRosterFile | undefined>) => void;
|
|
53
59
|
onError?: (error: unknown) => void;
|
|
54
60
|
/**
|
|
55
61
|
* How often `start()` ticks. A tick lists the vault when no vault watch is
|
|
@@ -104,6 +110,8 @@ export interface HubMemberPort {
|
|
|
104
110
|
keptReceiptState(): KeptReceiptState;
|
|
105
111
|
/** Apply the union of every member's roster in memory to kept receipts. */
|
|
106
112
|
retainUnion(): void;
|
|
113
|
+
/** Share a committed preference change with live members using this roster file. */
|
|
114
|
+
publishRoster(roster: VaultRosterFile): void;
|
|
107
115
|
forget(credentialId: string): void;
|
|
108
116
|
/** The member closed: the hub stops committing and pushing to it. */
|
|
109
117
|
detach(): void;
|
|
@@ -113,6 +121,8 @@ export interface HubRefreshOutcome {
|
|
|
113
121
|
}
|
|
114
122
|
/** What the hub may do to one member's memory. Package-internal. */
|
|
115
123
|
export interface HubMemberHooks {
|
|
124
|
+
/** Observe a hub discovery without relying on member refresh overrides. */
|
|
125
|
+
discovery(promise: Promise<HubRefreshOutcome | undefined>): void;
|
|
116
126
|
/**
|
|
117
127
|
* Set the roster in memory as a consumer's own refresh does (pending quota
|
|
118
128
|
* readings included) and notify `onRoster` when its view changed.
|
|
@@ -314,8 +314,17 @@ export class ClaustrumConsumer {
|
|
|
314
314
|
this.#refreshing = undefined;
|
|
315
315
|
});
|
|
316
316
|
this.#track(this.#refreshing);
|
|
317
|
+
this.#notifyDiscovery(this.#refreshing);
|
|
317
318
|
return this.#refreshing;
|
|
318
319
|
}
|
|
320
|
+
#notifyDiscovery(promise) {
|
|
321
|
+
try {
|
|
322
|
+
this.#options.onDiscovery?.(promise);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
this.#options.onError?.(error);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
319
328
|
/**
|
|
320
329
|
* Poll in the background and open the vault and insula watches. Until the
|
|
321
330
|
* vault watch is accepted, and whenever it is unavailable, every tick lists
|
|
@@ -571,6 +580,13 @@ export class ClaustrumConsumer {
|
|
|
571
580
|
/** The hub's handle on this member's memory. */
|
|
572
581
|
#hubHooks() {
|
|
573
582
|
return {
|
|
583
|
+
discovery: (promise) => {
|
|
584
|
+
const hub = this.#hub;
|
|
585
|
+
if (!hub || !this.#options.onDiscovery || this.#shutdown.signal.aborted)
|
|
586
|
+
return;
|
|
587
|
+
const discovery = this.#track(promise.then((outcome) => this.#hubOutcome(hub, outcome)));
|
|
588
|
+
this.#notifyDiscovery(discovery);
|
|
589
|
+
},
|
|
574
590
|
install: (roster) => {
|
|
575
591
|
if (this.#shutdown.signal.aborted)
|
|
576
592
|
return;
|
|
@@ -607,6 +623,9 @@ export class ClaustrumConsumer {
|
|
|
607
623
|
*/
|
|
608
624
|
async #hubRefresh(hub) {
|
|
609
625
|
const outcome = await hub.refresh();
|
|
626
|
+
return this.#hubOutcome(hub, outcome);
|
|
627
|
+
}
|
|
628
|
+
#hubOutcome(hub, outcome) {
|
|
610
629
|
this.#assertOpen();
|
|
611
630
|
if (!outcome)
|
|
612
631
|
return undefined;
|
|
@@ -788,6 +807,8 @@ export class ClaustrumConsumer {
|
|
|
788
807
|
this.#assertOpen();
|
|
789
808
|
await declineVaultRoute(this.#options.rosterPath, routeId);
|
|
790
809
|
this.#roster = pendingVaultQuota(this.#options.rosterPath, await readVaultRoster(this.#options.rosterPath));
|
|
810
|
+
if (this.#roster)
|
|
811
|
+
this.#hub?.publishRoster(this.#roster);
|
|
791
812
|
// A hub member shares kept receipts with every other member, so they are
|
|
792
813
|
// retained against all members' rosters, never against this one alone.
|
|
793
814
|
if (this.#hub)
|
|
@@ -799,6 +820,8 @@ export class ClaustrumConsumer {
|
|
|
799
820
|
this.#assertOpen();
|
|
800
821
|
await acceptVaultRoute(this.#options.rosterPath, routeId);
|
|
801
822
|
this.#roster = pendingVaultQuota(this.#options.rosterPath, await readVaultRoster(this.#options.rosterPath));
|
|
823
|
+
if (this.#roster)
|
|
824
|
+
this.#hub?.publishRoster(this.#roster);
|
|
802
825
|
}
|
|
803
826
|
/**
|
|
804
827
|
* Set the order vault accounts are tried in (`reorderVaultRoster`), then
|
|
@@ -809,6 +832,8 @@ export class ClaustrumConsumer {
|
|
|
809
832
|
this.#assertOpen();
|
|
810
833
|
const result = await reorderVaultRoster(this.#options.rosterPath, routeIds);
|
|
811
834
|
this.#roster = pendingVaultQuota(this.#options.rosterPath, await readVaultRoster(this.#options.rosterPath));
|
|
835
|
+
if (this.#roster)
|
|
836
|
+
this.#hub?.publishRoster(this.#roster);
|
|
812
837
|
return result;
|
|
813
838
|
}
|
|
814
839
|
/**
|
|
@@ -25,7 +25,8 @@ export interface ClaustrumFamily {
|
|
|
25
25
|
/** Non-secret classification facts, including rows decoded by older clients. */
|
|
26
26
|
export interface VaultRowFacts {
|
|
27
27
|
readonly credentialId: string;
|
|
28
|
-
|
|
28
|
+
/** Internal serving type derived from `authMethod`, not the id's type hint. */
|
|
29
|
+
readonly credentialType: VaultCredentialType;
|
|
29
30
|
readonly authMethod?: string;
|
|
30
31
|
readonly refreshAdapter?: string;
|
|
31
32
|
readonly providerIds?: readonly string[];
|
|
@@ -108,7 +109,10 @@ export interface VaultInventory {
|
|
|
108
109
|
readonly view: string;
|
|
109
110
|
readonly credentials: readonly VaultCredential[];
|
|
110
111
|
readonly skipped: readonly SkippedVaultRecord[];
|
|
111
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Classifying families only: declared policy codes or the fixed library codes
|
|
114
|
+
* `unknown-auth-method`, `unmapped` and `unclassified`, never classifier errors.
|
|
115
|
+
*/
|
|
112
116
|
readonly refused?: readonly {
|
|
113
117
|
credentialId?: string;
|
|
114
118
|
code: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { channel } from 'node:diagnostics_channel';
|
|
1
2
|
import { ClaustrumCredentialError, } from '@cortexkit/claustrum-client';
|
|
2
3
|
import { createLogger } from '../logger/index.js';
|
|
3
4
|
import { ClaustrumUnreachableError, classifyVaultFailure, } from './availability.js';
|
|
@@ -27,6 +28,8 @@ function scopedRetryReason(served, current) {
|
|
|
27
28
|
return 'rotated';
|
|
28
29
|
}
|
|
29
30
|
const defaultLogger = createLogger('claustrum');
|
|
31
|
+
// Opt-in diagnostics let tests retain wait phases without logging each vault read.
|
|
32
|
+
const phaseDiagnostics = channel('common-auth.claustrum.phases');
|
|
30
33
|
/**
|
|
31
34
|
* Decide whether a request that got a 401 should retry with the freshly
|
|
32
35
|
* re-authorized receipt, and log the decision. The vault can refresh a
|
|
@@ -85,16 +88,28 @@ function credentialTypeOf(row, family) {
|
|
|
85
88
|
return notMine;
|
|
86
89
|
if (!row.categories.includes(family.category))
|
|
87
90
|
return notMine;
|
|
88
|
-
if (row.credentialType !== 'oauth' && row.credentialType !== 'api_key')
|
|
89
|
-
return notMine;
|
|
90
91
|
if ('classify' in family) {
|
|
91
|
-
|
|
92
|
+
const metadata = rowFactsOf(row);
|
|
93
|
+
let type;
|
|
94
|
+
// The id's type is only a hint; the vault's auth method determines serving.
|
|
95
|
+
switch (metadata.authMethod) {
|
|
96
|
+
case 'apikey':
|
|
97
|
+
type = 'api_key';
|
|
98
|
+
break;
|
|
99
|
+
case 'chatgpt':
|
|
100
|
+
case 'antigravity':
|
|
101
|
+
case 'oauth':
|
|
102
|
+
type = 'oauth';
|
|
103
|
+
break;
|
|
104
|
+
default:
|
|
105
|
+
return { kind: 'refused', code: 'unknown-auth-method' };
|
|
106
|
+
}
|
|
92
107
|
const facts = {
|
|
93
108
|
credentialId: row.id,
|
|
94
|
-
credentialType:
|
|
109
|
+
credentialType: type,
|
|
95
110
|
categories: row.categories,
|
|
96
111
|
refreshAdapter: row.refreshAdapter,
|
|
97
|
-
...
|
|
112
|
+
...metadata,
|
|
98
113
|
};
|
|
99
114
|
if (facts.providerIds === undefined)
|
|
100
115
|
return { kind: 'refused', code: 'unmapped' };
|
|
@@ -103,7 +118,7 @@ function credentialTypeOf(row, family) {
|
|
|
103
118
|
if (result.kind === 'not-mine')
|
|
104
119
|
return notMine;
|
|
105
120
|
if (result.kind === 'mine' && typeof result.member === 'string')
|
|
106
|
-
return { kind: 'mine', type
|
|
121
|
+
return { kind: 'mine', type, member: result.member };
|
|
107
122
|
if (result.kind === 'refused' &&
|
|
108
123
|
typeof result.code === 'string' &&
|
|
109
124
|
/^[a-z][a-z0-9-]{0,31}$/.test(result.code) &&
|
|
@@ -116,6 +131,8 @@ function credentialTypeOf(row, family) {
|
|
|
116
131
|
}
|
|
117
132
|
return { kind: 'refused', code: 'unclassified' };
|
|
118
133
|
}
|
|
134
|
+
if (row.credentialType !== 'oauth' && row.credentialType !== 'api_key')
|
|
135
|
+
return notMine;
|
|
119
136
|
if (row.credentialType === 'oauth')
|
|
120
137
|
return row.refreshAdapter === family.refreshAdapter
|
|
121
138
|
? { kind: 'mine', type: 'oauth' }
|
|
@@ -266,6 +283,11 @@ export class ClaustrumScopedCustody {
|
|
|
266
283
|
}
|
|
267
284
|
async #token(signal) {
|
|
268
285
|
this.#check(signal);
|
|
286
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
287
|
+
phaseDiagnostics.publish({
|
|
288
|
+
logger: this.#logger,
|
|
289
|
+
phase: 'token read begin',
|
|
290
|
+
});
|
|
269
291
|
const pending = this.#readToken();
|
|
270
292
|
this.#tokenReads.add(pending);
|
|
271
293
|
let value;
|
|
@@ -274,6 +296,11 @@ export class ClaustrumScopedCustody {
|
|
|
274
296
|
}
|
|
275
297
|
finally {
|
|
276
298
|
this.#tokenReads.delete(pending);
|
|
299
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
300
|
+
phaseDiagnostics.publish({
|
|
301
|
+
logger: this.#logger,
|
|
302
|
+
phase: 'token read end',
|
|
303
|
+
});
|
|
277
304
|
}
|
|
278
305
|
this.#check(signal);
|
|
279
306
|
if (!/^[0-9a-f]{64}$/.test(value.token) ||
|
|
@@ -345,6 +372,11 @@ export class ClaustrumScopedCustody {
|
|
|
345
372
|
this.#kept.clear();
|
|
346
373
|
throw error;
|
|
347
374
|
}
|
|
375
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
376
|
+
phaseDiagnostics.publish({
|
|
377
|
+
logger: this.#logger,
|
|
378
|
+
phase: 'list scoped begin',
|
|
379
|
+
});
|
|
348
380
|
try {
|
|
349
381
|
inventory = await this.#call(() => this.#client.listScoped(token), signal);
|
|
350
382
|
}
|
|
@@ -352,6 +384,13 @@ export class ClaustrumScopedCustody {
|
|
|
352
384
|
this.listFailed(error);
|
|
353
385
|
throw error;
|
|
354
386
|
}
|
|
387
|
+
finally {
|
|
388
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
389
|
+
phaseDiagnostics.publish({
|
|
390
|
+
logger: this.#logger,
|
|
391
|
+
phase: 'list scoped end',
|
|
392
|
+
});
|
|
393
|
+
}
|
|
355
394
|
const skipped = [];
|
|
356
395
|
const refused = [];
|
|
357
396
|
let notMine = 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { QuotaObservation } from '../quota/index.js';
|
|
2
|
-
import { type ClaustrumConsumer } from './consumer.js';
|
|
2
|
+
import { type ClaustrumConsumer, type ClaustrumConsumerOptions } from './consumer.js';
|
|
3
3
|
import { type ClassifyingClaustrumFamily, type ClaustrumScopedClient, ClaustrumScopedCustody, type IdentityParser, type KeptReceiptState, type VaultGrantReach } from './custody.js';
|
|
4
4
|
import { type ClaustrumLogger } from './errors.js';
|
|
5
5
|
import { type AccountMapper, type VaultRosterFile } from './roster.js';
|
|
@@ -100,6 +100,8 @@ export interface ClaustrumHubMemberOptions<_Q = QuotaObservation> {
|
|
|
100
100
|
reservedRouteIds?: () => Iterable<string>;
|
|
101
101
|
/** As `ClaustrumConsumerOptions.onRoster`: once per change of view. */
|
|
102
102
|
onRoster?: (roster: VaultRosterFile) => void;
|
|
103
|
+
/** Observe every hub discovery's member result, including background work. */
|
|
104
|
+
onDiscovery?: ClaustrumConsumerOptions<_Q>['onDiscovery'];
|
|
103
105
|
/** This member's own failures, such as a roster commit that failed. */
|
|
104
106
|
onError?: (error: unknown) => void;
|
|
105
107
|
logger?: ClaustrumLogger;
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
// different processes already share, plus the base-view rule, so a commit
|
|
12
12
|
// never replaces a view another process committed after this hub listed.
|
|
13
13
|
import { createHash } from 'node:crypto';
|
|
14
|
+
import { channel } from 'node:diagnostics_channel';
|
|
14
15
|
import { readdir, stat } from 'node:fs/promises';
|
|
15
|
-
import { dirname, join } from 'node:path';
|
|
16
|
+
import { dirname, join, resolve } from 'node:path';
|
|
16
17
|
import { createLogger } from '../logger/index.js';
|
|
17
18
|
import { combineAbortSignals } from '../util/any-signal.js';
|
|
18
19
|
import { ClaustrumUnreachableError } from './availability.js';
|
|
@@ -23,6 +24,8 @@ import { ClaustrumConsumerError } from './errors.js';
|
|
|
23
24
|
import { DEFAULT_ROUTE_PREFIX, decodedVaultRoster, forgetPendingVaultDiscovery, isVaultRosterLockRefusal, readVaultRoster, refreshVaultRoster, vaultRosterSeed, } from './roster.js';
|
|
24
25
|
import { drainShutdown, shutdownWarning } from './shutdown.js';
|
|
25
26
|
import { SAFETY_POLL_MS } from './watch.js';
|
|
27
|
+
/** Published rosters are shared only in memory; file locks still order commits. */
|
|
28
|
+
const sharedRosters = new Map();
|
|
26
29
|
/** Lowercase ASCII, safe as a file name on every OS. */
|
|
27
30
|
const FILE_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
28
31
|
/** Device names Windows reserves in every directory, with any extension. */
|
|
@@ -250,9 +253,11 @@ export class ClaustrumDiscoveryHub {
|
|
|
250
253
|
watchMode: () => this.watchState(),
|
|
251
254
|
keptReceiptState: () => this.keptReceiptState(),
|
|
252
255
|
retainUnion: () => this.#retainUnion(),
|
|
256
|
+
publishRoster: (roster) => this.#publishRoster(entry, roster),
|
|
253
257
|
forget: (credentialId) => this.#current?.custody?.forget(credentialId),
|
|
254
258
|
detach: () => {
|
|
255
259
|
entry.closed = true;
|
|
260
|
+
entry.releaseRoster?.();
|
|
256
261
|
},
|
|
257
262
|
};
|
|
258
263
|
const consumerOptions = {
|
|
@@ -265,14 +270,58 @@ export class ClaustrumDiscoveryHub {
|
|
|
265
270
|
}),
|
|
266
271
|
...(this.#options.mapAccount && { mapAccount: this.#options.mapAccount }),
|
|
267
272
|
...(options.onRoster && { onRoster: options.onRoster }),
|
|
273
|
+
...(options.onDiscovery && { onDiscovery: options.onDiscovery }),
|
|
268
274
|
...(options.onError && { onError: options.onError }),
|
|
269
275
|
logger: options.logger ?? this.#logger,
|
|
270
276
|
...(this.#options.now && { now: this.#options.now }),
|
|
271
277
|
};
|
|
272
278
|
const { consumer, hooks } = createHubMember(port, consumerOptions);
|
|
273
279
|
entry.hooks = hooks;
|
|
280
|
+
const path = resolve(entry.path);
|
|
281
|
+
const subscribers = sharedRosters.get(path) ?? new Set();
|
|
282
|
+
const install = (roster) => {
|
|
283
|
+
// A peer cannot reactivate an off or closed member, or install routes
|
|
284
|
+
// that collide with this plugin instance's local pool.
|
|
285
|
+
if (entry.closed ||
|
|
286
|
+
this.#active !== true ||
|
|
287
|
+
!this.#current ||
|
|
288
|
+
!this.#live(this.#current) ||
|
|
289
|
+
!hooks.roster() ||
|
|
290
|
+
this.#collides(hooks, roster))
|
|
291
|
+
return;
|
|
292
|
+
entry.seen = JSON.stringify(decodedVaultRoster(roster));
|
|
293
|
+
hooks.install(roster);
|
|
294
|
+
if (roster.view !== this.#committed?.view)
|
|
295
|
+
entry.pending ??= { failures: 0, wait: 0 };
|
|
296
|
+
this.#retainUnion();
|
|
297
|
+
};
|
|
298
|
+
subscribers.add(install);
|
|
299
|
+
entry.receiveRoster = install;
|
|
300
|
+
sharedRosters.set(path, subscribers);
|
|
301
|
+
let subscribed = true;
|
|
302
|
+
entry.releaseRoster = () => {
|
|
303
|
+
if (!subscribed)
|
|
304
|
+
return;
|
|
305
|
+
subscribed = false;
|
|
306
|
+
subscribers.delete(install);
|
|
307
|
+
if (!subscribers.size)
|
|
308
|
+
sharedRosters.delete(path);
|
|
309
|
+
};
|
|
274
310
|
return consumer;
|
|
275
311
|
}
|
|
312
|
+
#publishRoster(entry, roster) {
|
|
313
|
+
const generation = this.#current;
|
|
314
|
+
if (entry.closed || !generation || !this.#live(generation))
|
|
315
|
+
return;
|
|
316
|
+
entry.seen = JSON.stringify(decodedVaultRoster(roster));
|
|
317
|
+
entry.hooks?.install(roster);
|
|
318
|
+
for (const install of sharedRosters.get(resolve(entry.path)) ?? []) {
|
|
319
|
+
if (!this.#live(generation))
|
|
320
|
+
return;
|
|
321
|
+
if (install !== entry.receiveRoster)
|
|
322
|
+
install(roster);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
276
325
|
// --- leases ---
|
|
277
326
|
#startGeneration() {
|
|
278
327
|
const generation = {
|
|
@@ -582,6 +631,8 @@ export class ClaustrumDiscoveryHub {
|
|
|
582
631
|
}));
|
|
583
632
|
run.promise = promise;
|
|
584
633
|
this.#run = run;
|
|
634
|
+
for (const entry of this.#open())
|
|
635
|
+
entry.hooks?.discovery(promise);
|
|
585
636
|
return promise;
|
|
586
637
|
}
|
|
587
638
|
/**
|
|
@@ -681,9 +732,7 @@ export class ClaustrumDiscoveryHub {
|
|
|
681
732
|
continue;
|
|
682
733
|
}
|
|
683
734
|
// The push: memory holds the committed roster before refresh resolves.
|
|
684
|
-
entry
|
|
685
|
-
if (!entry.closed)
|
|
686
|
-
hooks.install(roster);
|
|
735
|
+
this.#publishRoster(entry, roster);
|
|
687
736
|
}
|
|
688
737
|
if (!this.#live(generation))
|
|
689
738
|
return undefined;
|
|
@@ -718,7 +767,25 @@ export class ClaustrumDiscoveryHub {
|
|
|
718
767
|
throw base;
|
|
719
768
|
// A member's first roster under this key carries the declines and
|
|
720
769
|
// order of its newest roster under an older key.
|
|
721
|
-
|
|
770
|
+
let seed;
|
|
771
|
+
if (!base.file) {
|
|
772
|
+
const phaseDiagnostics = channel('common-auth.claustrum.phases');
|
|
773
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
774
|
+
phaseDiagnostics.publish({
|
|
775
|
+
logger: this.#logger,
|
|
776
|
+
phase: `seed read ${entry.provider} begin`,
|
|
777
|
+
});
|
|
778
|
+
try {
|
|
779
|
+
seed = await this.#seed(entry);
|
|
780
|
+
}
|
|
781
|
+
finally {
|
|
782
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
783
|
+
phaseDiagnostics.publish({
|
|
784
|
+
logger: this.#logger,
|
|
785
|
+
phase: `seed read ${entry.provider} end`,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
722
789
|
const roster = await refreshVaultRoster({
|
|
723
790
|
path: entry.path,
|
|
724
791
|
custody: { discover: async () => share },
|
|
@@ -850,7 +917,7 @@ export class ClaustrumDiscoveryHub {
|
|
|
850
917
|
}
|
|
851
918
|
const before = enabledRows(memory);
|
|
852
919
|
entry.seen = content;
|
|
853
|
-
|
|
920
|
+
this.#publishRoster(entry, file);
|
|
854
921
|
if (enabledRows(hooks.roster()) !== before)
|
|
855
922
|
this.#retainUnion();
|
|
856
923
|
}
|
|
@@ -1013,6 +1080,8 @@ export class ClaustrumDiscoveryHub {
|
|
|
1013
1080
|
if (this.#shutdown.signal.aborted)
|
|
1014
1081
|
return;
|
|
1015
1082
|
this.#shutdown.abort(closed('Claustrum discovery hub is closed'));
|
|
1083
|
+
for (const entry of this.#members.values())
|
|
1084
|
+
entry.releaseRoster?.();
|
|
1016
1085
|
if (this.#timer)
|
|
1017
1086
|
(this.#options.clearTimeoutImpl ?? clearTimeout)(this.#timer);
|
|
1018
1087
|
this.#timer = undefined;
|
|
@@ -443,6 +443,12 @@ export class InsulaQuotaSource {
|
|
|
443
443
|
...(typeof entry.source === 'string' && { source: entry.source }),
|
|
444
444
|
},
|
|
445
445
|
};
|
|
446
|
+
// Kept usage must not trigger a provider poll after a non-local failure.
|
|
447
|
+
// Only an explicitly local failure permits fallback; unknown causes do not.
|
|
448
|
+
if (entry.stale !== undefined &&
|
|
449
|
+
(!record(entry.stale) ||
|
|
450
|
+
entry.stale.class !== 'local_source_unavailable'))
|
|
451
|
+
return { miss: 'upstream-failed', reading };
|
|
446
452
|
return now >= fetchedAt + MAX_AGE || entry.stale !== undefined
|
|
447
453
|
? { miss: 'stale', reading }
|
|
448
454
|
: { reading };
|
|
@@ -21,7 +21,8 @@ export interface LockLoss {
|
|
|
21
21
|
export interface RefreshFileLock {
|
|
22
22
|
readonly ownerId: string;
|
|
23
23
|
assertOwned(): Promise<void>;
|
|
24
|
-
release
|
|
24
|
+
/** Abort stops release retries; the unrenewed lease is left to expire by TTL. */
|
|
25
|
+
release(signal?: AbortSignal): Promise<void>;
|
|
25
26
|
/** Resolves once on detected loss; remains pending after an owner's release. */
|
|
26
27
|
whenLost(): Promise<LockLoss>;
|
|
27
28
|
hasLost(): boolean;
|
|
@@ -836,7 +836,7 @@ export async function acquireRefreshFileLockWithClock(options) {
|
|
|
836
836
|
: {}),
|
|
837
837
|
});
|
|
838
838
|
},
|
|
839
|
-
release: async () => {
|
|
839
|
+
release: async (signal) => {
|
|
840
840
|
if (windowsPrivateFiles && releaseConfirmed)
|
|
841
841
|
return;
|
|
842
842
|
released = true;
|
|
@@ -882,6 +882,8 @@ export async function acquireRefreshFileLockWithClock(options) {
|
|
|
882
882
|
releaseConfirmed = removalConfirmed;
|
|
883
883
|
return;
|
|
884
884
|
}
|
|
885
|
+
if (signal?.aborted)
|
|
886
|
+
return;
|
|
885
887
|
await recoverStaleEvictionMarker();
|
|
886
888
|
}
|
|
887
889
|
catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"package": "@cortexkit/common-auth",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.32.1",
|
|
5
5
|
"artifactStatus": "published",
|
|
6
6
|
"publicRoots": {
|
|
7
7
|
"./store": {
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"import": "./dist/pi-slot/index.js"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
-
"input": "tools/common-auth-build/inputs/cortexkit-common-auth-0.
|
|
41
|
-
"tarballSha256": "
|
|
42
|
-
"sri": "sha512-
|
|
40
|
+
"input": "tools/common-auth-build/inputs/cortexkit-common-auth-0.32.1.tgz",
|
|
41
|
+
"tarballSha256": "d25d2eb670bc5ac48e75bf31cd633bb8b56fd807bf4467d69dbf62a00e1e6717",
|
|
42
|
+
"sri": "sha512-Y3SmHTnkDjdvo8d6MsQTVr+GlR3q6Ro8HYDqEFuotSUbrv8GwJvjRkYraN8ahEAT8uRHFmfyWGuogfYLzz65DA==",
|
|
43
43
|
"canonicalFiles": 147,
|
|
44
|
-
"canonicalBytes":
|
|
44
|
+
"canonicalBytes": 1000963,
|
|
45
45
|
"generator": {
|
|
46
46
|
"path": "packages/opencode/scripts/embed-common-auth.ts",
|
|
47
|
-
"version":
|
|
48
|
-
"sha256": "
|
|
47
|
+
"version": 4,
|
|
48
|
+
"sha256": "d0920ba672e4d2a4ab37cd827d0a8098e9253d23678b88d051750373787af65d"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
{
|
|
@@ -227,33 +227,33 @@
|
|
|
227
227
|
{
|
|
228
228
|
"source": "package/dist/claustrum/consumer.d.ts",
|
|
229
229
|
"output": "claustrum/consumer.d.ts",
|
|
230
|
-
"bytes":
|
|
231
|
-
"sourceSha256": "
|
|
232
|
-
"outputSha256": "
|
|
230
|
+
"bytes": 13934,
|
|
231
|
+
"sourceSha256": "97e5fb096153912483b45f75831e4652db19457d94ad76ba8363b1fd232e6fd7",
|
|
232
|
+
"outputSha256": "97e5fb096153912483b45f75831e4652db19457d94ad76ba8363b1fd232e6fd7",
|
|
233
233
|
"transform": "verbatim"
|
|
234
234
|
},
|
|
235
235
|
{
|
|
236
236
|
"source": "package/dist/claustrum/consumer.js",
|
|
237
237
|
"output": "claustrum/consumer.js",
|
|
238
|
-
"bytes":
|
|
239
|
-
"sourceSha256": "
|
|
240
|
-
"outputSha256": "
|
|
238
|
+
"bytes": 39439,
|
|
239
|
+
"sourceSha256": "3b4cf06be8732f3183ac97721dfffb8fc489d4be1044260742a72a9995324391",
|
|
240
|
+
"outputSha256": "3b4cf06be8732f3183ac97721dfffb8fc489d4be1044260742a72a9995324391",
|
|
241
241
|
"transform": "verbatim"
|
|
242
242
|
},
|
|
243
243
|
{
|
|
244
244
|
"source": "package/dist/claustrum/custody.d.ts",
|
|
245
245
|
"output": "claustrum/custody.d.ts",
|
|
246
|
-
"bytes":
|
|
247
|
-
"sourceSha256": "
|
|
248
|
-
"outputSha256": "
|
|
246
|
+
"bytes": 16390,
|
|
247
|
+
"sourceSha256": "3bbbfc2d700cf6c25356339c64d3bef5cee5e00336d141de18bb7522c8a5ceaa",
|
|
248
|
+
"outputSha256": "3bbbfc2d700cf6c25356339c64d3bef5cee5e00336d141de18bb7522c8a5ceaa",
|
|
249
249
|
"transform": "verbatim"
|
|
250
250
|
},
|
|
251
251
|
{
|
|
252
252
|
"source": "package/dist/claustrum/custody.js",
|
|
253
253
|
"output": "claustrum/custody.js",
|
|
254
|
-
"bytes":
|
|
255
|
-
"sourceSha256": "
|
|
256
|
-
"outputSha256": "
|
|
254
|
+
"bytes": 31616,
|
|
255
|
+
"sourceSha256": "5ed70e65b78a04ceec3b3430908f46c860ea768b779b6af0f925681fd786593d",
|
|
256
|
+
"outputSha256": "5ed70e65b78a04ceec3b3430908f46c860ea768b779b6af0f925681fd786593d",
|
|
257
257
|
"transform": "verbatim"
|
|
258
258
|
},
|
|
259
259
|
{
|
|
@@ -307,17 +307,17 @@
|
|
|
307
307
|
{
|
|
308
308
|
"source": "package/dist/claustrum/hub.d.ts",
|
|
309
309
|
"output": "claustrum/hub.d.ts",
|
|
310
|
-
"bytes":
|
|
311
|
-
"sourceSha256": "
|
|
312
|
-
"outputSha256": "
|
|
310
|
+
"bytes": 9346,
|
|
311
|
+
"sourceSha256": "4c2cbd91b3cc9691ee765f766b7c80d27cf28b38494a0a53ce9410c184e7ca1d",
|
|
312
|
+
"outputSha256": "4c2cbd91b3cc9691ee765f766b7c80d27cf28b38494a0a53ce9410c184e7ca1d",
|
|
313
313
|
"transform": "verbatim"
|
|
314
314
|
},
|
|
315
315
|
{
|
|
316
316
|
"source": "package/dist/claustrum/hub.js",
|
|
317
317
|
"output": "claustrum/hub.js",
|
|
318
|
-
"bytes":
|
|
319
|
-
"sourceSha256": "
|
|
320
|
-
"outputSha256": "
|
|
318
|
+
"bytes": 45993,
|
|
319
|
+
"sourceSha256": "e50ebf6bcf840f8a4229d219ecb20fc1422c74b5b3aa2034dff0b73852380b79",
|
|
320
|
+
"outputSha256": "e50ebf6bcf840f8a4229d219ecb20fc1422c74b5b3aa2034dff0b73852380b79",
|
|
321
321
|
"transform": "verbatim"
|
|
322
322
|
},
|
|
323
323
|
{
|
|
@@ -347,9 +347,9 @@
|
|
|
347
347
|
{
|
|
348
348
|
"source": "package/dist/claustrum/insula.js",
|
|
349
349
|
"output": "claustrum/insula.js",
|
|
350
|
-
"bytes":
|
|
351
|
-
"sourceSha256": "
|
|
352
|
-
"outputSha256": "
|
|
350
|
+
"bytes": 20137,
|
|
351
|
+
"sourceSha256": "e2a2625cdad490cf24aa0badf66c842becd806738f8b137ebcf2b01e296eb0d2",
|
|
352
|
+
"outputSha256": "e2a2625cdad490cf24aa0badf66c842becd806738f8b137ebcf2b01e296eb0d2",
|
|
353
353
|
"transform": "verbatim"
|
|
354
354
|
},
|
|
355
355
|
{
|
|
@@ -395,9 +395,9 @@
|
|
|
395
395
|
{
|
|
396
396
|
"source": "package/dist/claustrum/roster.js",
|
|
397
397
|
"output": "claustrum/roster.js",
|
|
398
|
-
"bytes":
|
|
399
|
-
"sourceSha256": "
|
|
400
|
-
"outputSha256": "
|
|
398
|
+
"bytes": 39340,
|
|
399
|
+
"sourceSha256": "6919aa948f9022f76ce2b7aac83733bd7a11a8c21b8511191cf7fbf6bc1a2b30",
|
|
400
|
+
"outputSha256": "6919aa948f9022f76ce2b7aac83733bd7a11a8c21b8511191cf7fbf6bc1a2b30",
|
|
401
401
|
"transform": "verbatim"
|
|
402
402
|
},
|
|
403
403
|
{
|
|
@@ -595,17 +595,17 @@
|
|
|
595
595
|
{
|
|
596
596
|
"source": "package/dist/fs/refresh-file-lock.d.ts",
|
|
597
597
|
"output": "fs/refresh-file-lock.d.ts",
|
|
598
|
-
"bytes":
|
|
599
|
-
"sourceSha256": "
|
|
600
|
-
"outputSha256": "
|
|
598
|
+
"bytes": 3052,
|
|
599
|
+
"sourceSha256": "31c23ba9135ddc57ea0196ad56280bd12eab2c9524d33bd43d78112da27214d2",
|
|
600
|
+
"outputSha256": "31c23ba9135ddc57ea0196ad56280bd12eab2c9524d33bd43d78112da27214d2",
|
|
601
601
|
"transform": "verbatim"
|
|
602
602
|
},
|
|
603
603
|
{
|
|
604
604
|
"source": "package/dist/fs/refresh-file-lock.js",
|
|
605
605
|
"output": "fs/refresh-file-lock.js",
|
|
606
|
-
"bytes":
|
|
607
|
-
"sourceSha256": "
|
|
608
|
-
"outputSha256": "
|
|
606
|
+
"bytes": 37832,
|
|
607
|
+
"sourceSha256": "07b261b409c5c6cc9b35cb732df44486d372413ce6ae078be4b211b9b5a5c1c7",
|
|
608
|
+
"outputSha256": "07b261b409c5c6cc9b35cb732df44486d372413ce6ae078be4b211b9b5a5c1c7",
|
|
609
609
|
"transform": "verbatim"
|
|
610
610
|
},
|
|
611
611
|
{
|
|
@@ -619,9 +619,9 @@
|
|
|
619
619
|
{
|
|
620
620
|
"source": "package/dist/fs/with-lock.js",
|
|
621
621
|
"output": "fs/with-lock.js",
|
|
622
|
-
"bytes":
|
|
623
|
-
"sourceSha256": "
|
|
624
|
-
"outputSha256": "
|
|
622
|
+
"bytes": 1803,
|
|
623
|
+
"sourceSha256": "f31ff1935e18563e465ab235202de3932255d3b1fc72b5d0baf6d3502931fc8e",
|
|
624
|
+
"outputSha256": "f31ff1935e18563e465ab235202de3932255d3b1fc72b5d0baf6d3502931fc8e",
|
|
625
625
|
"transform": "verbatim"
|
|
626
626
|
},
|
|
627
627
|
{
|
package/package.json
CHANGED