@cortexkit/common-auth 0.9.5 → 0.11.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/auth-menu/ansi.d.ts +4 -2
- package/dist/auth-menu/ansi.js +44 -13
- package/dist/auth-menu/index.d.ts +1 -1
- package/dist/auth-menu/index.js +1 -1
- package/dist/auth-menu/select.js +30 -24
- package/dist/fs/refresh-file-lock.d.ts +5 -1
- package/dist/fs/refresh-file-lock.js +53 -14
- package/dist/rpc/rpc-server.js +29 -7
- package/dist/store/refresh-lock.d.ts +1 -1
- package/dist/store/refresh-lock.js +21 -10
- package/package.json +2 -2
package/dist/auth-menu/ansi.d.ts
CHANGED
|
@@ -12,8 +12,10 @@ export declare const ANSI: {
|
|
|
12
12
|
readonly dim: '\u001B[2m';
|
|
13
13
|
readonly reset: '\u001B[0m';
|
|
14
14
|
};
|
|
15
|
-
export type KeyAction = 'up' | 'down' | 'enter' | 'escape' | 'escape-start' | null;
|
|
16
|
-
/**
|
|
15
|
+
export type KeyAction = 'up' | 'down' | 'left' | 'right' | 'enter' | 'escape' | 'escape-start' | 'char' | null;
|
|
16
|
+
/** Tokenize complete keys in one read; incomplete escape sequences are ignored. */
|
|
17
|
+
export declare function parseKeys(data: Buffer | string): KeyAction[];
|
|
18
|
+
/** Preserve the single-key parser for callers that only need the first token. */
|
|
17
19
|
export declare function parseKey(data: Buffer | string): KeyAction;
|
|
18
20
|
/** Remove colour codes, leaving the text an operator sees. */
|
|
19
21
|
export declare function stripAnsi(input: string): string;
|
package/dist/auth-menu/ansi.js
CHANGED
|
@@ -12,20 +12,51 @@ export const ANSI = {
|
|
|
12
12
|
dim: '\x1b[2m',
|
|
13
13
|
reset: '\x1b[0m',
|
|
14
14
|
};
|
|
15
|
-
/**
|
|
16
|
-
export function
|
|
15
|
+
/** Tokenize complete keys in one read; incomplete escape sequences are ignored. */
|
|
16
|
+
export function parseKeys(data) {
|
|
17
17
|
const value = data.toString();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
const keys = [];
|
|
19
|
+
for (let index = 0; index < value.length;) {
|
|
20
|
+
const char = value[index];
|
|
21
|
+
if (char === '\x1b') {
|
|
22
|
+
const sequence = value.slice(index, index + 3);
|
|
23
|
+
const arrow = sequence[1] === '[' || sequence[1] === 'O' ? sequence[2] : undefined;
|
|
24
|
+
if (arrow && 'ABCD'.includes(arrow)) {
|
|
25
|
+
const action = { A: 'up', B: 'down', C: 'right', D: 'left' };
|
|
26
|
+
const parsed = action[arrow];
|
|
27
|
+
if (parsed)
|
|
28
|
+
keys.push(parsed);
|
|
29
|
+
index += 3;
|
|
30
|
+
}
|
|
31
|
+
else if (index + 1 === value.length) {
|
|
32
|
+
// Only a chunk consisting solely of ESC gets the legacy Escape timeout.
|
|
33
|
+
if (index === 0)
|
|
34
|
+
keys.push('escape-start');
|
|
35
|
+
index += 1;
|
|
36
|
+
}
|
|
37
|
+
else if ((value[index + 1] === '[' || value[index + 1] === 'O') &&
|
|
38
|
+
index + 2 >= value.length) {
|
|
39
|
+
// An incomplete escape prefix at the chunk boundary is not a key.
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
index += 1;
|
|
44
|
+
}
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (char === '\r' || char === '\n')
|
|
48
|
+
keys.push('enter');
|
|
49
|
+
else if (char === '\x03')
|
|
50
|
+
keys.push('escape');
|
|
51
|
+
else if (char && char >= ' ')
|
|
52
|
+
keys.push('char');
|
|
53
|
+
index += 1;
|
|
54
|
+
}
|
|
55
|
+
return keys;
|
|
56
|
+
}
|
|
57
|
+
/** Preserve the single-key parser for callers that only need the first token. */
|
|
58
|
+
export function parseKey(data) {
|
|
59
|
+
return parseKeys(data)[0] ?? null;
|
|
29
60
|
}
|
|
30
61
|
const ANSI_PATTERN = `${String.fromCharCode(27)}\\[[0-9;]*m`;
|
|
31
62
|
const ANSI_REGEX = new RegExp(ANSI_PATTERN, 'g');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { AccountMenuOptions } from './accounts.js';
|
|
2
2
|
export { accountMenuActions, addAccountAction, checkQuotasAction, deleteAllAction, listAccountsAction, MENU_DISABLE_REASON, poolHasCredential, quotaLines, reauthenticateAction, removeAccountAction, runAccountMenu, toggleAccountAction, } from './accounts.js';
|
|
3
3
|
export type { KeyAction } from './ansi.js';
|
|
4
|
-
export { ANSI, parseKey, stripAnsi, truncateAnsi } from './ansi.js';
|
|
4
|
+
export { ANSI, parseKey, parseKeys, stripAnsi, truncateAnsi } from './ansi.js';
|
|
5
5
|
export { confirm } from './confirm.js';
|
|
6
6
|
export type { DoctorActionOptions, DoctorCheck, DoctorFinding, DoctorRepair, DoctorReport, RepairOutcome, } from './doctor.js';
|
|
7
7
|
export { applyChosenRepairs, DOCTOR_CHECK_FAILED, doctorAction, formatDoctorReport, runDoctorChecks, } from './doctor.js';
|
package/dist/auth-menu/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// runtime, the OpenCode v1 `authorize` contract around it, and the account
|
|
3
3
|
// actions built over the `/store` pool.
|
|
4
4
|
export { accountMenuActions, addAccountAction, checkQuotasAction, deleteAllAction, listAccountsAction, MENU_DISABLE_REASON, poolHasCredential, quotaLines, reauthenticateAction, removeAccountAction, runAccountMenu, toggleAccountAction, } from './accounts.js';
|
|
5
|
-
export { ANSI, parseKey, stripAnsi, truncateAnsi } from './ansi.js';
|
|
5
|
+
export { ANSI, parseKey, parseKeys, stripAnsi, truncateAnsi } from './ansi.js';
|
|
6
6
|
export { confirm } from './confirm.js';
|
|
7
7
|
export { applyChosenRepairs, DOCTOR_CHECK_FAILED, doctorAction, formatDoctorReport, runDoctorChecks, } from './doctor.js';
|
|
8
8
|
export { openBrowserForMenu, runMenuLogin } from './login.js';
|
package/dist/auth-menu/select.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ANSI,
|
|
1
|
+
import { ANSI, parseKeys, truncateAnsi } from './ansi.js';
|
|
2
2
|
import { isInteractive } from './terminal.js';
|
|
3
3
|
/** How long a lone Escape byte waits for the rest of an arrow-key sequence. */
|
|
4
4
|
const ESCAPE_TIMEOUT_MS = 50;
|
|
@@ -109,30 +109,36 @@ export async function select(terminal, items, options) {
|
|
|
109
109
|
};
|
|
110
110
|
const onSignal = () => finish(null);
|
|
111
111
|
const onKey = (data) => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
escapeTimeout = null;
|
|
115
|
-
}
|
|
116
|
-
switch (parseKey(data)) {
|
|
117
|
-
case 'up':
|
|
118
|
-
cursor = (cursor - 1 + items.length) % items.length;
|
|
119
|
-
render();
|
|
120
|
-
break;
|
|
121
|
-
case 'down':
|
|
122
|
-
cursor = (cursor + 1) % items.length;
|
|
123
|
-
render();
|
|
124
|
-
break;
|
|
125
|
-
case 'enter':
|
|
126
|
-
finish(items[cursor]?.value ?? null);
|
|
127
|
-
break;
|
|
128
|
-
case 'escape':
|
|
129
|
-
finish(null);
|
|
130
|
-
break;
|
|
131
|
-
case 'escape-start':
|
|
132
|
-
// A bare Escape byte is also the start of an arrow-key sequence
|
|
133
|
-
// that may arrive split across reads; only a lone one cancels.
|
|
134
|
-
escapeTimeout = setTimeout(() => finish(null), ESCAPE_TIMEOUT_MS);
|
|
112
|
+
for (const key of parseKeys(data)) {
|
|
113
|
+
if (cleaned)
|
|
135
114
|
break;
|
|
115
|
+
if (escapeTimeout) {
|
|
116
|
+
clearTimeout(escapeTimeout);
|
|
117
|
+
escapeTimeout = null;
|
|
118
|
+
}
|
|
119
|
+
switch (key) {
|
|
120
|
+
case 'up':
|
|
121
|
+
cursor = (cursor - 1 + items.length) % items.length;
|
|
122
|
+
render();
|
|
123
|
+
break;
|
|
124
|
+
case 'down':
|
|
125
|
+
cursor = (cursor + 1) % items.length;
|
|
126
|
+
render();
|
|
127
|
+
break;
|
|
128
|
+
case 'enter':
|
|
129
|
+
finish(items[cursor]?.value ?? null);
|
|
130
|
+
break;
|
|
131
|
+
case 'escape':
|
|
132
|
+
finish(null);
|
|
133
|
+
break;
|
|
134
|
+
case 'escape-start':
|
|
135
|
+
// A bare Escape byte is also the start of an arrow-key sequence
|
|
136
|
+
// that may arrive split across reads; only a lone one cancels.
|
|
137
|
+
escapeTimeout = setTimeout(() => finish(null), ESCAPE_TIMEOUT_MS);
|
|
138
|
+
break;
|
|
139
|
+
default:
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
136
142
|
}
|
|
137
143
|
};
|
|
138
144
|
signals?.once('SIGINT', onSignal);
|
|
@@ -25,5 +25,9 @@ export declare function acquireRefreshFileLock(options: {
|
|
|
25
25
|
now?: () => number;
|
|
26
26
|
renew?: boolean;
|
|
27
27
|
renewIntervalMs?: number;
|
|
28
|
-
|
|
28
|
+
/** Notifies a live-owner refusal without awaiting; observer failures are ignored. */
|
|
29
|
+
onContended?: () => void;
|
|
30
|
+
/** Synchronous, never awaited; throws and returned promise/thenable rejections are isolated. */
|
|
31
|
+
onRenewalTimer?: (event: 'scheduled' | 'cancelled') => void;
|
|
32
|
+
onStep?: (step: 'stale-marker-stat' | 'stale-marker-claimed' | 'stale-lock-observed' | 'stale-lock-confirmed' | 'stale-lock-removed' | 'stale-lock-recreated' | 'eviction-marker-acquired' | 'renewal-owner-confirmed' | 'renewal-marker-unavailable' | 'renewal-write-fenced' | 'renewal-write-ready' | 'relinquish-read' | 'renewal-finished' | 'release-owner-confirmed') => void | Promise<void>;
|
|
29
33
|
}): Promise<RefreshFileLock | null>;
|
|
@@ -24,6 +24,27 @@ export async function acquireRefreshFileLock(options) {
|
|
|
24
24
|
const lostPromise = new Promise((resolve) => {
|
|
25
25
|
resolveLoss = resolve;
|
|
26
26
|
});
|
|
27
|
+
function notifyTimerStep(event) {
|
|
28
|
+
try {
|
|
29
|
+
const result = options.onRenewalTimer?.(event);
|
|
30
|
+
if (result &&
|
|
31
|
+
(typeof result === 'object' || typeof result === 'function') &&
|
|
32
|
+
'then' in result &&
|
|
33
|
+
typeof result.then === 'function') {
|
|
34
|
+
void Promise.resolve(result).catch(() => { });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Timer observer failures must not replace the lock operation result.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function cancelRenewalTimer() {
|
|
42
|
+
if (!renewTimer)
|
|
43
|
+
return;
|
|
44
|
+
clearRefreshLockRenewalTimeout(renewTimer);
|
|
45
|
+
renewTimer = null;
|
|
46
|
+
notifyTimerStep('cancelled');
|
|
47
|
+
}
|
|
27
48
|
function recordLoss(reason, owner) {
|
|
28
49
|
if (loss || released)
|
|
29
50
|
return;
|
|
@@ -37,10 +58,7 @@ export async function acquireRefreshFileLock(options) {
|
|
|
37
58
|
? { observedExpiresAt: owner.expiresAt }
|
|
38
59
|
: {}),
|
|
39
60
|
});
|
|
40
|
-
|
|
41
|
-
clearRefreshLockRenewalTimeout(renewTimer);
|
|
42
|
-
renewTimer = null;
|
|
43
|
-
}
|
|
61
|
+
cancelRenewalTimer();
|
|
44
62
|
resolveLoss(loss);
|
|
45
63
|
}
|
|
46
64
|
let renewalInFlight = null;
|
|
@@ -211,9 +229,10 @@ export async function acquireRefreshFileLock(options) {
|
|
|
211
229
|
}
|
|
212
230
|
return true;
|
|
213
231
|
}
|
|
214
|
-
// Marker loss after a write may mean
|
|
215
|
-
// Delete only a record still owned by us
|
|
216
|
-
//
|
|
232
|
+
// Marker loss after a write may mean another contender has taken over.
|
|
233
|
+
// Delete only a record still owned by us, so an already-installed
|
|
234
|
+
// successor is preserved. The owner read and the removal are not atomic:
|
|
235
|
+
// a successor record written between them can still be deleted.
|
|
217
236
|
async function relinquishLockAfterMarkerLoss() {
|
|
218
237
|
for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
|
|
219
238
|
if (options.onStep)
|
|
@@ -241,6 +260,7 @@ export async function acquireRefreshFileLock(options) {
|
|
|
241
260
|
return;
|
|
242
261
|
const intervalMs = options.renewIntervalMs ?? Math.max(1_000, Math.floor(options.ttlMs / 3));
|
|
243
262
|
renewTimer = setRefreshLockRenewalTimeout(() => {
|
|
263
|
+
renewTimer = null;
|
|
244
264
|
const renewal = (async () => {
|
|
245
265
|
let shouldReschedule = !released;
|
|
246
266
|
try {
|
|
@@ -333,6 +353,22 @@ export async function acquireRefreshFileLock(options) {
|
|
|
333
353
|
}, intervalMs);
|
|
334
354
|
if ('unref' in renewTimer)
|
|
335
355
|
renewTimer.unref();
|
|
356
|
+
notifyTimerStep('scheduled');
|
|
357
|
+
}
|
|
358
|
+
function contended() {
|
|
359
|
+
try {
|
|
360
|
+
const result = options.onContended?.();
|
|
361
|
+
if (result &&
|
|
362
|
+
(typeof result === 'object' || typeof result === 'function') &&
|
|
363
|
+
'then' in result &&
|
|
364
|
+
typeof result.then === 'function') {
|
|
365
|
+
void Promise.resolve(result).catch(() => { });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
// Observers cannot change whether the lock is acquired.
|
|
370
|
+
}
|
|
371
|
+
return null;
|
|
336
372
|
}
|
|
337
373
|
let acquired = await tryAcquire();
|
|
338
374
|
if (!acquired) {
|
|
@@ -341,7 +377,9 @@ export async function acquireRefreshFileLock(options) {
|
|
|
341
377
|
if (acquired)
|
|
342
378
|
break;
|
|
343
379
|
if (await lockIsLive())
|
|
344
|
-
return
|
|
380
|
+
return contended();
|
|
381
|
+
if (options.onStep)
|
|
382
|
+
await options.onStep('stale-lock-observed');
|
|
345
383
|
try {
|
|
346
384
|
if (!(await tryAcquireEvictionMarker())) {
|
|
347
385
|
await backoff();
|
|
@@ -360,7 +398,7 @@ export async function acquireRefreshFileLock(options) {
|
|
|
360
398
|
}
|
|
361
399
|
try {
|
|
362
400
|
if (await lockIsLive())
|
|
363
|
-
return
|
|
401
|
+
return contended();
|
|
364
402
|
// Verify marker ownership before removing a lock found not live.
|
|
365
403
|
if (!(await ownsEvictionMarker()))
|
|
366
404
|
return null;
|
|
@@ -374,14 +412,18 @@ export async function acquireRefreshFileLock(options) {
|
|
|
374
412
|
// Fence check 3: re-verify ownership after removing the stale lock.
|
|
375
413
|
if (!(await ownsEvictionMarker()))
|
|
376
414
|
return null;
|
|
415
|
+
if (options.onStep)
|
|
416
|
+
await options.onStep('stale-lock-removed');
|
|
377
417
|
acquired = await tryAcquire();
|
|
378
418
|
if (!acquired)
|
|
379
419
|
return null;
|
|
420
|
+
if (options.onStep)
|
|
421
|
+
await options.onStep('stale-lock-recreated');
|
|
380
422
|
// Fence check 4: re-verify ownership after acquiring the lock. If the
|
|
381
423
|
// marker was stolen between tryAcquire and this check, release the
|
|
382
424
|
// just-acquired lock and return null (fail-closed).
|
|
383
425
|
if (!(await ownsEvictionMarker())) {
|
|
384
|
-
await
|
|
426
|
+
await relinquishLockAfterMarkerLoss();
|
|
385
427
|
acquired = false;
|
|
386
428
|
return null;
|
|
387
429
|
}
|
|
@@ -429,10 +471,7 @@ export async function acquireRefreshFileLock(options) {
|
|
|
429
471
|
},
|
|
430
472
|
release: async () => {
|
|
431
473
|
released = true;
|
|
432
|
-
|
|
433
|
-
clearRefreshLockRenewalTimeout(renewTimer);
|
|
434
|
-
renewTimer = null;
|
|
435
|
-
}
|
|
474
|
+
cancelRenewalTimer();
|
|
436
475
|
await renewalInFlight;
|
|
437
476
|
for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
|
|
438
477
|
try {
|
package/dist/rpc/rpc-server.js
CHANGED
|
@@ -24,20 +24,29 @@ export class RpcRequestError extends Error {
|
|
|
24
24
|
const MAX_BODY_BYTES = 1_000_000;
|
|
25
25
|
/** The request body exceeded the cap; answered 413. */
|
|
26
26
|
class BodyTooLargeError extends Error {
|
|
27
|
+
declaredOversize;
|
|
28
|
+
constructor(declaredOversize) {
|
|
29
|
+
super('body too large');
|
|
30
|
+
this.declaredOversize = declaredOversize;
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
function readBody(req) {
|
|
29
34
|
return new Promise((resolve, reject) => {
|
|
30
|
-
const tooLarge = () => {
|
|
31
|
-
//
|
|
32
|
-
//
|
|
35
|
+
const tooLarge = (declaredOversize = false) => {
|
|
36
|
+
// Streamed overflow drains without closing at response finish: doing so
|
|
37
|
+
// caused EPIPE on a Bun 1.4.2 uploader. Declared oversize also drains, but
|
|
38
|
+
// half-closes after the 413: Bun 1.3.14 reused refused fetch connections
|
|
39
|
+
// despite Connection: close. Half-close preserved complete 413 JSON for
|
|
40
|
+
// tested fetch and slow declared-length uploaders on both runtimes (see
|
|
41
|
+
// research/load-probe/RPC-413-CLOSE.md for the delivery/lifecycle samples).
|
|
33
42
|
req.removeAllListeners('data');
|
|
34
43
|
req.on('data', () => { });
|
|
35
44
|
req.resume();
|
|
36
|
-
reject(new BodyTooLargeError(
|
|
45
|
+
reject(new BodyTooLargeError(declaredOversize));
|
|
37
46
|
};
|
|
38
47
|
const declared = Number(req.headers['content-length']);
|
|
39
48
|
if (Number.isFinite(declared) && declared > MAX_BODY_BYTES) {
|
|
40
|
-
tooLarge();
|
|
49
|
+
tooLarge(true);
|
|
41
50
|
return;
|
|
42
51
|
}
|
|
43
52
|
const chunks = [];
|
|
@@ -133,8 +142,21 @@ export async function startRpcServer(options) {
|
|
|
133
142
|
catch (error) {
|
|
134
143
|
if (!(error instanceof BodyTooLargeError))
|
|
135
144
|
throw error;
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
if (error.declaredOversize) {
|
|
146
|
+
const socket = req.socket;
|
|
147
|
+
res.once('finish', () => {
|
|
148
|
+
socket.end();
|
|
149
|
+
// Bound an abandoned upload to two seconds, matching the server's
|
|
150
|
+
// default request-delivery timeout. end() alone left Bun 1.3.14
|
|
151
|
+
// compatibility sockets open.
|
|
152
|
+
const timer = setTimeout(() => socket.destroy(), 2_000);
|
|
153
|
+
timer.unref();
|
|
154
|
+
req.once('end', () => socket.destroy());
|
|
155
|
+
socket.once('close', () => clearTimeout(timer));
|
|
156
|
+
if (req.complete)
|
|
157
|
+
socket.destroy();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
138
160
|
return json(413, { error: 'body too large' }, { connection: 'close' });
|
|
139
161
|
}
|
|
140
162
|
let params;
|
|
@@ -19,7 +19,7 @@ export interface PoolLockSpec extends Partial<PoolLockOptions> {
|
|
|
19
19
|
*/
|
|
20
20
|
export declare const POOL_LOCK_DEFAULTS: Readonly<PoolLockOptions>;
|
|
21
21
|
export type LockEvent = {
|
|
22
|
-
type: 'acquired' | 'released';
|
|
22
|
+
type: 'acquired' | 'released' | 'contended';
|
|
23
23
|
name: string;
|
|
24
24
|
path: string;
|
|
25
25
|
};
|
|
@@ -17,6 +17,24 @@ export const POOL_LOCK_DEFAULTS = Object.freeze({
|
|
|
17
17
|
* expired during the wait.
|
|
18
18
|
*/
|
|
19
19
|
export async function acquirePoolLock(spec, defaults, env) {
|
|
20
|
+
const emit = (type) => {
|
|
21
|
+
try {
|
|
22
|
+
const result = env.onLockEvent?.({
|
|
23
|
+
type,
|
|
24
|
+
name: spec.name,
|
|
25
|
+
path: spec.path,
|
|
26
|
+
});
|
|
27
|
+
if (result &&
|
|
28
|
+
(typeof result === 'object' || typeof result === 'function') &&
|
|
29
|
+
'then' in result &&
|
|
30
|
+
typeof result.then === 'function') {
|
|
31
|
+
void Promise.resolve(result).catch(() => { });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Lock observers must not affect acquisition or release.
|
|
36
|
+
}
|
|
37
|
+
};
|
|
20
38
|
const options = { ...defaults, ...definedOnly(spec) };
|
|
21
39
|
const started = performance.now();
|
|
22
40
|
for (;;) {
|
|
@@ -26,6 +44,7 @@ export async function acquirePoolLock(spec, defaults, env) {
|
|
|
26
44
|
ttlMs: options.ttlMs,
|
|
27
45
|
now: env.now,
|
|
28
46
|
renew: options.renew,
|
|
47
|
+
onContended: () => emit('contended'),
|
|
29
48
|
...(options.renewIntervalMs !== undefined
|
|
30
49
|
? { renewIntervalMs: options.renewIntervalMs }
|
|
31
50
|
: {}),
|
|
@@ -36,22 +55,14 @@ export async function acquirePoolLock(spec, defaults, env) {
|
|
|
36
55
|
: {}),
|
|
37
56
|
});
|
|
38
57
|
if (lock) {
|
|
39
|
-
|
|
40
|
-
type: 'acquired',
|
|
41
|
-
name: spec.name,
|
|
42
|
-
path: spec.path,
|
|
43
|
-
});
|
|
58
|
+
emit('acquired');
|
|
44
59
|
const held = {
|
|
45
60
|
name: spec.name,
|
|
46
61
|
path: spec.path,
|
|
47
62
|
assertOwned: () => lock.assertOwned(),
|
|
48
63
|
release: async () => {
|
|
49
64
|
await lock.release();
|
|
50
|
-
|
|
51
|
-
type: 'released',
|
|
52
|
-
name: spec.name,
|
|
53
|
-
path: spec.path,
|
|
54
|
-
});
|
|
65
|
+
emit('released');
|
|
55
66
|
},
|
|
56
67
|
};
|
|
57
68
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/common-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Shared code for the CortexKit auth plugins: account pool, quota and routing, commands and auth menu, OpenCode 2 hooks, Claustrum custody, and plumbing (loopback RPC, file locks, logger, sidebar state, TUI preferences and build).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"dist/"
|
|
86
86
|
],
|
|
87
87
|
"scripts": {
|
|
88
|
-
"build": "bun scripts/check-installed-ranges.mjs && tsc -p tsconfig.build.json",
|
|
88
|
+
"build": "bun scripts/check-local-deps.mjs && bun scripts/check-installed-ranges.mjs && tsc -p tsconfig.build.json",
|
|
89
89
|
"test": "bun test",
|
|
90
90
|
"typecheck": "tsc --noEmit",
|
|
91
91
|
"types": "bun run typecheck",
|