sidekick-shared 0.24.5 → 0.26.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/CHANGELOG.md +92 -0
- package/README.md +205 -52
- package/dist/accountChangeSignal.d.ts +3 -0
- package/dist/accountChangeSignal.js +19 -0
- package/dist/accountChanges.d.ts +21 -0
- package/dist/accountChanges.js +170 -0
- package/dist/accountRegistry.d.ts +25 -2
- package/dist/accountRegistry.js +41 -13
- package/dist/accountStatus.d.ts +9 -1
- package/dist/accountStatus.js +3 -3
- package/dist/accounts.d.ts +3 -1
- package/dist/accounts.js +84 -64
- package/dist/aggregation/EventAggregator.d.ts +15 -2
- package/dist/aggregation/EventAggregator.js +54 -9
- package/dist/aggregation/costProvenance.d.ts +14 -0
- package/dist/aggregation/costProvenance.js +35 -0
- package/dist/aggregation/snapshot.d.ts +13 -2
- package/dist/aggregation/snapshot.js +57 -6
- package/dist/aggregation/types.d.ts +30 -1
- package/dist/analytics/compactionLedger.js +15 -5
- package/dist/analytics/qualityScore.js +2 -1
- package/dist/browser.d.ts +19 -4
- package/dist/browser.js +43 -1
- package/dist/codexProfiles.d.ts +3 -1
- package/dist/codexProfiles.js +4 -2
- package/dist/codexQuota.d.ts +2 -0
- package/dist/codexQuota.js +11 -66
- package/dist/codexQuotaWatcher.d.ts +6 -0
- package/dist/codexQuotaWatcher.js +101 -47
- package/dist/failingTools.d.ts +37 -0
- package/dist/failingTools.js +70 -0
- package/dist/formatters/sessionDump.js +13 -7
- package/dist/formatting.d.ts +13 -0
- package/dist/formatting.js +35 -0
- package/dist/historicalStore.d.ts +47 -0
- package/dist/historicalStore.js +312 -0
- package/dist/index.d.ts +67 -23
- package/dist/index.js +138 -26
- package/dist/modelAliases.d.ts +13 -0
- package/dist/modelAliases.js +58 -0
- package/dist/modelCatalog.d.ts +32 -0
- package/dist/modelCatalog.js +145 -0
- package/dist/modelContext.d.ts +36 -3
- package/dist/modelContext.js +109 -34
- package/dist/modelInfo.d.ts +30 -1
- package/dist/modelInfo.js +116 -26
- package/dist/multiProviderQuotaService.d.ts +8 -0
- package/dist/multiProviderQuotaService.js +64 -3
- package/dist/node.d.ts +23 -4
- package/dist/node.js +50 -1
- package/dist/observedContextWindows.d.ts +7 -0
- package/dist/observedContextWindows.js +17 -2
- package/dist/observedSessionCollector.d.ts +72 -6
- package/dist/observedSessionCollector.js +440 -47
- package/dist/parsers/codexHistory.d.ts +4 -2
- package/dist/parsers/codexHistory.js +9 -2
- package/dist/parsers/sessionActivityDetector.d.ts +28 -0
- package/dist/parsers/sessionActivityDetector.js +110 -15
- package/dist/parsers/sessionPathResolver.d.ts +11 -0
- package/dist/parsers/sessionPathResolver.js +13 -10
- package/dist/peakHours.d.ts +34 -3
- package/dist/peakHours.js +92 -7
- package/dist/pricingCatalog.d.ts +7 -0
- package/dist/pricingCatalog.js +8 -5
- package/dist/providerIds.d.ts +11 -0
- package/dist/providerIds.js +9 -0
- package/dist/providerQuota.d.ts +3 -1
- package/dist/providerQuota.js +3 -0
- package/dist/providers/claudeCode.d.ts +10 -1
- package/dist/providers/claudeCode.js +211 -131
- package/dist/providers/codex.d.ts +20 -1
- package/dist/providers/codex.js +308 -244
- package/dist/providers/codexDatabase.d.ts +9 -0
- package/dist/providers/codexDatabase.js +107 -2
- package/dist/providers/detect.d.ts +2 -0
- package/dist/providers/detect.js +29 -24
- package/dist/providers/diagnostics.d.ts +13 -0
- package/dist/providers/diagnostics.js +88 -0
- package/dist/providers/factory.d.ts +20 -0
- package/dist/providers/factory.js +57 -0
- package/dist/providers/openCode.d.ts +22 -1
- package/dist/providers/openCode.js +460 -126
- package/dist/providers/openCodeDatabase.d.ts +10 -0
- package/dist/providers/openCodeDatabase.js +69 -0
- package/dist/providers/rolloutWalker.d.ts +53 -0
- package/dist/providers/rolloutWalker.js +209 -0
- package/dist/providers/types.d.ts +82 -2
- package/dist/providers/types.js +4 -0
- package/dist/quota.d.ts +34 -4
- package/dist/quota.js +37 -9
- package/dist/quotaHistory.d.ts +3 -2
- package/dist/quotaHistory.js +36 -35
- package/dist/quotaPoller.d.ts +13 -1
- package/dist/quotaPoller.js +65 -3
- package/dist/quotaResolver.d.ts +81 -0
- package/dist/quotaResolver.js +213 -0
- package/dist/quotaSnapshots.d.ts +15 -2
- package/dist/quotaSnapshots.js +25 -3
- package/dist/quotaThresholds.d.ts +60 -0
- package/dist/quotaThresholds.js +90 -0
- package/dist/report/htmlReportGenerator.js +10 -4
- package/dist/report/sessionReportInputs.d.ts +23 -0
- package/dist/report/sessionReportInputs.js +34 -0
- package/dist/schemas/accountManager.d.ts +2 -2
- package/dist/schemas/accountManager.js +2 -4
- package/dist/schemas/accountStatus.d.ts +3 -0
- package/dist/schemas/accountStatus.js +1 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.js +16 -2
- package/dist/schemas/observedSessionV1.d.ts +27 -26
- package/dist/schemas/observedSessionV1.js +6 -4
- package/dist/schemas/quota.d.ts +141 -2
- package/dist/schemas/quota.js +20 -8
- package/dist/schemas/quotaHistory.d.ts +3 -2
- package/dist/schemas/quotaHistory.js +1 -1
- package/dist/schemas/sessionEvent.d.ts +6 -0
- package/dist/schemas/sessionEvent.js +1 -0
- package/dist/schemas/stateFile.d.ts +198 -0
- package/dist/schemas/stateFile.js +77 -0
- package/dist/schemas/tokenEstimation.d.ts +1 -1
- package/dist/schemas/transcript.js +2 -0
- package/dist/sessionFingerprint.d.ts +25 -0
- package/dist/sessionFingerprint.js +46 -0
- package/dist/sessionMonitor.d.ts +15 -0
- package/dist/sessionMonitor.js +142 -0
- package/dist/sessionPreviews.d.ts +33 -2
- package/dist/sessionPreviews.js +333 -6
- package/dist/sessionStats.d.ts +51 -0
- package/dist/sessionStats.js +166 -0
- package/dist/stateFile.d.ts +113 -0
- package/dist/stateFile.js +160 -0
- package/dist/statusline/claudeStatuslinePayload.d.ts +89 -0
- package/dist/statusline/claudeStatuslinePayload.js +152 -0
- package/dist/statusline/formatter.d.ts +13 -0
- package/dist/statusline/formatter.js +48 -6
- package/dist/statusline/index.d.ts +10 -2
- package/dist/statusline/index.js +19 -1
- package/dist/tokenSummary.d.ts +62 -0
- package/dist/tokenSummary.js +72 -0
- package/dist/transcript.d.ts +5 -0
- package/dist/transcript.js +2 -0
- package/dist/types/observedSessionV1.d.ts +8 -2
- package/dist/types/observedSessionV1.js +88 -25
- package/dist/types/sessionEvent.d.ts +2 -0
- package/dist/usage/billingBlocks.d.ts +85 -0
- package/dist/usage/billingBlocks.js +153 -0
- package/dist/usage/importSessionHistory.d.ts +53 -0
- package/dist/usage/importSessionHistory.js +88 -0
- package/dist/usage/usageEvents.d.ts +92 -0
- package/dist/usage/usageEvents.js +322 -0
- package/dist/usage/usageReports.d.ts +76 -0
- package/dist/usage/usageReports.js +208 -0
- package/dist/watchers/factory.d.ts +6 -0
- package/dist/watchers/factory.js +30 -25
- package/dist/zaiQuotaApi.d.ts +2 -0
- package/dist/zaiQuotaApi.js +2 -0
- package/package.json +22 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._onRawAccountsChanged = _onRawAccountsChanged;
|
|
4
|
+
exports._emitAccountsChanged = _emitAccountsChanged;
|
|
5
|
+
const listeners = new Set();
|
|
6
|
+
function _onRawAccountsChanged(listener) {
|
|
7
|
+
listeners.add(listener);
|
|
8
|
+
return () => listeners.delete(listener);
|
|
9
|
+
}
|
|
10
|
+
function _emitAccountsChanged(reason) {
|
|
11
|
+
for (const listener of listeners) {
|
|
12
|
+
try {
|
|
13
|
+
listener(reason);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// Account notifications are observational and cannot break mutations.
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AccountChangeReason } from './accountChangeSignal';
|
|
2
|
+
import { type ActiveAccountStatus } from './accountStatus';
|
|
3
|
+
import type { Disposable } from './quotaPoller';
|
|
4
|
+
export interface AccountsChangedEvent {
|
|
5
|
+
reason: AccountChangeReason;
|
|
6
|
+
status: ActiveAccountStatus;
|
|
7
|
+
}
|
|
8
|
+
export interface OnAccountsChangedOptions {
|
|
9
|
+
/** Emit the current status immediately after subscribing. */
|
|
10
|
+
emitCurrent?: boolean;
|
|
11
|
+
/** Catch-up poll for filesystems where watch delivery is unreliable. Default: 30s. */
|
|
12
|
+
pollIntervalMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Observe login, logout, and active-account switches. Local Sidekick mutations
|
|
16
|
+
* signal immediately; filesystem watchers cover external CLIs; a low-frequency
|
|
17
|
+
* catch-up poll protects network mounts and watcher-hostile filesystems.
|
|
18
|
+
*/
|
|
19
|
+
export declare function onAccountsChanged(listener: (event: AccountsChangedEvent) => void, options?: OnAccountsChangedOptions): Disposable;
|
|
20
|
+
/** Internal bridge for account mutations outside the registry module. */
|
|
21
|
+
export declare function _notifyAccountsChanged(): void;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.onAccountsChanged = onAccountsChanged;
|
|
37
|
+
exports._notifyAccountsChanged = _notifyAccountsChanged;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const accountChangeSignal_1 = require("./accountChangeSignal");
|
|
42
|
+
const accountStatus_1 = require("./accountStatus");
|
|
43
|
+
const accountRegistry_1 = require("./accountRegistry");
|
|
44
|
+
const codexProfiles_1 = require("./codexProfiles");
|
|
45
|
+
const listeners = new Set();
|
|
46
|
+
let watchers = [];
|
|
47
|
+
let pollTimer;
|
|
48
|
+
let stopRawListener;
|
|
49
|
+
let lastFingerprint;
|
|
50
|
+
let debounceTimer;
|
|
51
|
+
const DEFAULT_POLL_INTERVAL_MS = 30_000;
|
|
52
|
+
let activePollIntervalMs = DEFAULT_POLL_INTERVAL_MS;
|
|
53
|
+
/**
|
|
54
|
+
* Observe login, logout, and active-account switches. Local Sidekick mutations
|
|
55
|
+
* signal immediately; filesystem watchers cover external CLIs; a low-frequency
|
|
56
|
+
* catch-up poll protects network mounts and watcher-hostile filesystems.
|
|
57
|
+
*/
|
|
58
|
+
function onAccountsChanged(listener, options = {}) {
|
|
59
|
+
listeners.add(listener);
|
|
60
|
+
// The fastest interval any live subscriber asked for wins; a later, faster
|
|
61
|
+
// subscriber restarts the timer rather than being silently ignored.
|
|
62
|
+
const requestedPollMs = Math.max(1_000, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
63
|
+
if (!stopRawListener) {
|
|
64
|
+
activePollIntervalMs = requestedPollMs;
|
|
65
|
+
ensureMonitoring();
|
|
66
|
+
}
|
|
67
|
+
else if (requestedPollMs < activePollIntervalMs) {
|
|
68
|
+
activePollIntervalMs = requestedPollMs;
|
|
69
|
+
restartPollTimer();
|
|
70
|
+
}
|
|
71
|
+
if (options.emitCurrent)
|
|
72
|
+
emitIfChanged('poll', true, listener);
|
|
73
|
+
return {
|
|
74
|
+
dispose: () => {
|
|
75
|
+
listeners.delete(listener);
|
|
76
|
+
if (listeners.size === 0)
|
|
77
|
+
stopMonitoring();
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function ensureMonitoring() {
|
|
82
|
+
if (stopRawListener)
|
|
83
|
+
return;
|
|
84
|
+
lastFingerprint = fingerprint((0, accountStatus_1.getActiveAccountStatus)());
|
|
85
|
+
stopRawListener = (0, accountChangeSignal_1._onRawAccountsChanged)((reason) => scheduleCheck(reason));
|
|
86
|
+
for (const target of watchTargets()) {
|
|
87
|
+
try {
|
|
88
|
+
const watcher = fs.watch(target, { persistent: false }, () => {
|
|
89
|
+
scheduleCheck('filesystem');
|
|
90
|
+
});
|
|
91
|
+
watcher.on('error', () => {
|
|
92
|
+
watchers = watchers.filter((candidate) => candidate !== watcher);
|
|
93
|
+
try {
|
|
94
|
+
watcher.close();
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// The catch-up poll remains active.
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
watchers.push(watcher);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Missing/unwatchable directories are covered by the catch-up poll.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
startPollTimer();
|
|
107
|
+
}
|
|
108
|
+
function startPollTimer() {
|
|
109
|
+
pollTimer = setInterval(() => emitIfChanged('poll'), activePollIntervalMs);
|
|
110
|
+
pollTimer.unref?.();
|
|
111
|
+
}
|
|
112
|
+
function restartPollTimer() {
|
|
113
|
+
if (pollTimer)
|
|
114
|
+
clearInterval(pollTimer);
|
|
115
|
+
startPollTimer();
|
|
116
|
+
}
|
|
117
|
+
function stopMonitoring() {
|
|
118
|
+
stopRawListener?.();
|
|
119
|
+
stopRawListener = undefined;
|
|
120
|
+
for (const watcher of watchers)
|
|
121
|
+
watcher.close();
|
|
122
|
+
watchers = [];
|
|
123
|
+
if (pollTimer)
|
|
124
|
+
clearInterval(pollTimer);
|
|
125
|
+
pollTimer = undefined;
|
|
126
|
+
if (debounceTimer)
|
|
127
|
+
clearTimeout(debounceTimer);
|
|
128
|
+
debounceTimer = undefined;
|
|
129
|
+
lastFingerprint = undefined;
|
|
130
|
+
activePollIntervalMs = DEFAULT_POLL_INTERVAL_MS;
|
|
131
|
+
}
|
|
132
|
+
function watchTargets() {
|
|
133
|
+
return Array.from(new Set([(0, accountRegistry_1.getAccountsDir)(), path.join(os.homedir(), '.claude'), (0, codexProfiles_1.getSystemCodexHome)()]));
|
|
134
|
+
}
|
|
135
|
+
function scheduleCheck(reason) {
|
|
136
|
+
if (debounceTimer)
|
|
137
|
+
clearTimeout(debounceTimer);
|
|
138
|
+
debounceTimer = setTimeout(() => {
|
|
139
|
+
debounceTimer = undefined;
|
|
140
|
+
// Never force: watch targets include ~/.claude and the Codex home, whose
|
|
141
|
+
// history files churn on every prompt. Only a real status change may wake
|
|
142
|
+
// subscribers, or dormant quota pollers would poll on unrelated writes.
|
|
143
|
+
emitIfChanged(reason);
|
|
144
|
+
}, 25);
|
|
145
|
+
debounceTimer.unref?.();
|
|
146
|
+
}
|
|
147
|
+
function emitIfChanged(reason, force = false, singleListener) {
|
|
148
|
+
const status = (0, accountStatus_1.getActiveAccountStatus)();
|
|
149
|
+
const nextFingerprint = fingerprint(status);
|
|
150
|
+
if (!force && nextFingerprint === lastFingerprint)
|
|
151
|
+
return;
|
|
152
|
+
lastFingerprint = nextFingerprint;
|
|
153
|
+
const event = { reason, status };
|
|
154
|
+
const targets = singleListener ? [singleListener] : [...listeners];
|
|
155
|
+
for (const listener of targets) {
|
|
156
|
+
try {
|
|
157
|
+
listener(event);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// A host callback cannot stop monitoring.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function fingerprint(status) {
|
|
165
|
+
return JSON.stringify(status);
|
|
166
|
+
}
|
|
167
|
+
/** Internal bridge for account mutations outside the registry module. */
|
|
168
|
+
function _notifyAccountsChanged() {
|
|
169
|
+
(0, accountChangeSignal_1._emitAccountsChanged)('local');
|
|
170
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { AccountProviderId } from './providerIds';
|
|
2
|
+
export { ACCOUNT_PROVIDER_IDS } from './providerIds';
|
|
3
|
+
export type { AccountProviderId } from './providerIds';
|
|
2
4
|
export interface AccountIdentityMetadata {
|
|
3
5
|
email?: string;
|
|
4
6
|
workspaceId?: string;
|
|
@@ -39,11 +41,32 @@ export interface ResolvedActiveAccount {
|
|
|
39
41
|
source: 'live' | 'registry' | 'none';
|
|
40
42
|
}
|
|
41
43
|
export declare function getAccountsDir(): string;
|
|
44
|
+
interface RegistryWriteOptions {
|
|
45
|
+
/**
|
|
46
|
+
* Skip the local change notification. Only for writes that change no
|
|
47
|
+
* observable account state, such as re-pointing the active pointer at the
|
|
48
|
+
* profile that already matches the live login (self-heal): a read path
|
|
49
|
+
* must not wake every subscriber.
|
|
50
|
+
*/
|
|
51
|
+
silent?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Run `operation` while holding the registry lock. For callers that need a
|
|
55
|
+
* read-modify-write across several steps (the legacy Claude facade's
|
|
56
|
+
* add/remove); inside it use the `*Unlocked` writers, never the locking ones:
|
|
57
|
+
* the lock is not re-entrant.
|
|
58
|
+
*/
|
|
59
|
+
export declare function withSavedAccountRegistryLock<T>(operation: () => T): T;
|
|
42
60
|
export declare function readSavedAccountRegistry(): SavedAccountRegistry | null;
|
|
43
61
|
export declare function writeSavedAccountRegistry(registry: SavedAccountRegistry): void;
|
|
44
62
|
export declare function listSavedAccountProfiles(providerId?: AccountProviderId): SavedAccountProfile[];
|
|
45
63
|
export declare function getActiveSavedAccount(providerId: AccountProviderId): SavedAccountProfile | null;
|
|
46
64
|
export declare function upsertSavedAccountProfile(profile: SavedAccountProfile): SavedAccountRegistry;
|
|
47
|
-
export declare function setActiveSavedAccount(providerId: AccountProviderId, accountId: string | null): SavedAccountRegistry;
|
|
65
|
+
export declare function setActiveSavedAccount(providerId: AccountProviderId, accountId: string | null, options?: RegistryWriteOptions): SavedAccountRegistry;
|
|
48
66
|
export declare function replaceSavedAccountProfiles(providerId: AccountProviderId, accounts: SavedAccountProfile[], activeId: string | null): SavedAccountRegistry;
|
|
67
|
+
/**
|
|
68
|
+
* Same as {@link replaceSavedAccountProfiles} without taking the registry
|
|
69
|
+
* lock: for callers already inside {@link withSavedAccountRegistryLock}.
|
|
70
|
+
*/
|
|
71
|
+
export declare function replaceSavedAccountProfilesUnlocked(providerId: AccountProviderId, accounts: SavedAccountProfile[], activeId: string | null): SavedAccountRegistry;
|
|
49
72
|
export declare function removeSavedAccountProfile(providerId: AccountProviderId, accountId: string): SavedAccountProfile | null;
|
package/dist/accountRegistry.js
CHANGED
|
@@ -33,7 +33,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ACCOUNT_PROVIDER_IDS = void 0;
|
|
36
37
|
exports.getAccountsDir = getAccountsDir;
|
|
38
|
+
exports.withSavedAccountRegistryLock = withSavedAccountRegistryLock;
|
|
37
39
|
exports.readSavedAccountRegistry = readSavedAccountRegistry;
|
|
38
40
|
exports.writeSavedAccountRegistry = writeSavedAccountRegistry;
|
|
39
41
|
exports.listSavedAccountProfiles = listSavedAccountProfiles;
|
|
@@ -41,11 +43,15 @@ exports.getActiveSavedAccount = getActiveSavedAccount;
|
|
|
41
43
|
exports.upsertSavedAccountProfile = upsertSavedAccountProfile;
|
|
42
44
|
exports.setActiveSavedAccount = setActiveSavedAccount;
|
|
43
45
|
exports.replaceSavedAccountProfiles = replaceSavedAccountProfiles;
|
|
46
|
+
exports.replaceSavedAccountProfilesUnlocked = replaceSavedAccountProfilesUnlocked;
|
|
44
47
|
exports.removeSavedAccountProfile = removeSavedAccountProfile;
|
|
45
48
|
const fs = __importStar(require("fs"));
|
|
46
49
|
const path = __importStar(require("path"));
|
|
47
50
|
const paths_1 = require("./paths");
|
|
48
51
|
const atomic_1 = require("./writers/atomic");
|
|
52
|
+
const accountChangeSignal_1 = require("./accountChangeSignal");
|
|
53
|
+
var providerIds_1 = require("./providerIds");
|
|
54
|
+
Object.defineProperty(exports, "ACCOUNT_PROVIDER_IDS", { enumerable: true, get: function () { return providerIds_1.ACCOUNT_PROVIDER_IDS; } });
|
|
49
55
|
function getAccountsDir() {
|
|
50
56
|
return path.join((0, paths_1.getConfigDir)(), 'accounts');
|
|
51
57
|
}
|
|
@@ -58,8 +64,20 @@ function getRegistryLockPath() {
|
|
|
58
64
|
function ensureAccountsDir() {
|
|
59
65
|
fs.mkdirSync(getAccountsDir(), { recursive: true, mode: 0o700 });
|
|
60
66
|
}
|
|
61
|
-
function writeRegistryUnlocked(registry) {
|
|
67
|
+
function writeRegistryUnlocked(registry, options = {}) {
|
|
62
68
|
(0, atomic_1.atomicWriteJsonSync)(getRegistryPath(), normalizeRegistry(registry));
|
|
69
|
+
if (!options.silent)
|
|
70
|
+
(0, accountChangeSignal_1._emitAccountsChanged)('local');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run `operation` while holding the registry lock. For callers that need a
|
|
74
|
+
* read-modify-write across several steps (the legacy Claude facade's
|
|
75
|
+
* add/remove); inside it use the `*Unlocked` writers, never the locking ones:
|
|
76
|
+
* the lock is not re-entrant.
|
|
77
|
+
*/
|
|
78
|
+
function withSavedAccountRegistryLock(operation) {
|
|
79
|
+
ensureAccountsDir();
|
|
80
|
+
return (0, atomic_1.withFileLockSync)(getRegistryLockPath(), operation);
|
|
63
81
|
}
|
|
64
82
|
/**
|
|
65
83
|
* Serializes registry read-modify-write cycles across processes. Without the
|
|
@@ -68,13 +86,13 @@ function writeRegistryUnlocked(registry) {
|
|
|
68
86
|
* mutation. Lock ordering: callers that also hold a provider auth-swap lock
|
|
69
87
|
* take that lock first and this one inside it, never the reverse.
|
|
70
88
|
*/
|
|
71
|
-
function mutateSavedAccountRegistry(mutator) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
function mutateSavedAccountRegistry(mutator, options = {}) {
|
|
90
|
+
return withSavedAccountRegistryLock(() => mutateSavedAccountRegistryUnlocked(mutator, options));
|
|
91
|
+
}
|
|
92
|
+
function mutateSavedAccountRegistryUnlocked(mutator, options = {}) {
|
|
93
|
+
const next = mutator(readSavedAccountRegistry() ?? createEmptyRegistry());
|
|
94
|
+
writeRegistryUnlocked(next, options);
|
|
95
|
+
return next;
|
|
78
96
|
}
|
|
79
97
|
function createEmptyRegistry() {
|
|
80
98
|
return {
|
|
@@ -182,20 +200,30 @@ function upsertSavedAccountProfile(profile) {
|
|
|
182
200
|
return registry;
|
|
183
201
|
});
|
|
184
202
|
}
|
|
185
|
-
function setActiveSavedAccount(providerId, accountId) {
|
|
203
|
+
function setActiveSavedAccount(providerId, accountId, options = {}) {
|
|
186
204
|
return mutateSavedAccountRegistry((registry) => {
|
|
187
205
|
registry.activeByProvider[providerId] = accountId;
|
|
188
206
|
return registry;
|
|
189
|
-
});
|
|
207
|
+
}, options);
|
|
190
208
|
}
|
|
191
|
-
function
|
|
192
|
-
return
|
|
209
|
+
function replaceProfiles(providerId, accounts, activeId) {
|
|
210
|
+
return (registry) => {
|
|
193
211
|
registry.accounts = registry.accounts
|
|
194
212
|
.filter((account) => account.providerId !== providerId)
|
|
195
213
|
.concat(accounts);
|
|
196
214
|
registry.activeByProvider[providerId] = activeId;
|
|
197
215
|
return registry;
|
|
198
|
-
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function replaceSavedAccountProfiles(providerId, accounts, activeId) {
|
|
219
|
+
return mutateSavedAccountRegistry(replaceProfiles(providerId, accounts, activeId));
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Same as {@link replaceSavedAccountProfiles} without taking the registry
|
|
223
|
+
* lock: for callers already inside {@link withSavedAccountRegistryLock}.
|
|
224
|
+
*/
|
|
225
|
+
function replaceSavedAccountProfilesUnlocked(providerId, accounts, activeId) {
|
|
226
|
+
return mutateSavedAccountRegistryUnlocked(replaceProfiles(providerId, accounts, activeId));
|
|
199
227
|
}
|
|
200
228
|
function removeSavedAccountProfile(providerId, accountId) {
|
|
201
229
|
ensureAccountsDir();
|
package/dist/accountStatus.d.ts
CHANGED
|
@@ -10,10 +10,18 @@ export interface ActiveAccountStatus {
|
|
|
10
10
|
codex: ActiveProviderAccountStatus;
|
|
11
11
|
error?: string;
|
|
12
12
|
}
|
|
13
|
+
export interface ActiveAccountStatusOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Re-point a stale saved-account pointer at the live identity when they
|
|
16
|
+
* disagree (default true). Hot paths such as the status line pass `false`
|
|
17
|
+
* so they never write; an ordinary command repairs the pointer later.
|
|
18
|
+
*/
|
|
19
|
+
selfHeal?: boolean;
|
|
20
|
+
}
|
|
13
21
|
/**
|
|
14
22
|
* Reads active Claude Code and Codex account status in one filesystem pass.
|
|
15
23
|
*
|
|
16
24
|
* If reading either provider throws, the result still has provider-shaped
|
|
17
25
|
* fields so startup flows can render a consistent "not configured" state.
|
|
18
26
|
*/
|
|
19
|
-
export declare function getActiveAccountStatus(error?: string): ActiveAccountStatus;
|
|
27
|
+
export declare function getActiveAccountStatus(error?: string, options?: ActiveAccountStatusOptions): ActiveAccountStatus;
|
package/dist/accountStatus.js
CHANGED
|
@@ -12,10 +12,10 @@ function errorMessage(error) {
|
|
|
12
12
|
* If reading either provider throws, the result still has provider-shaped
|
|
13
13
|
* fields so startup flows can render a consistent "not configured" state.
|
|
14
14
|
*/
|
|
15
|
-
function getActiveAccountStatus(error) {
|
|
15
|
+
function getActiveAccountStatus(error, options = {}) {
|
|
16
16
|
try {
|
|
17
|
-
const claudeAccount = (0, accounts_1.resolveActiveClaudeAccount)();
|
|
18
|
-
const codexAccount = (0, codexProfiles_1.resolveActiveCodexAccount)();
|
|
17
|
+
const claudeAccount = (0, accounts_1.resolveActiveClaudeAccount)(options);
|
|
18
|
+
const codexAccount = (0, codexProfiles_1.resolveActiveCodexAccount)(options);
|
|
19
19
|
const claude = claudeAccount.source !== 'none'
|
|
20
20
|
? {
|
|
21
21
|
present: true,
|
package/dist/accounts.d.ts
CHANGED
|
@@ -50,5 +50,7 @@ export declare function getActiveAccount(): AccountEntry | null;
|
|
|
50
50
|
* re-pointed so registry-keyed data tracks reality too. Never creates or deletes
|
|
51
51
|
* profiles; an unknown live account is shown as-is with no label and no write.
|
|
52
52
|
*/
|
|
53
|
-
export declare function resolveActiveClaudeAccount(
|
|
53
|
+
export declare function resolveActiveClaudeAccount(options?: {
|
|
54
|
+
selfHeal?: boolean;
|
|
55
|
+
}): ResolvedActiveAccount;
|
|
54
56
|
export declare function isMultiAccountEnabled(): boolean;
|
package/dist/accounts.js
CHANGED
|
@@ -201,7 +201,14 @@ function readAccountRegistry() {
|
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
function writeAccountRegistry(registry) {
|
|
204
|
-
|
|
204
|
+
(0, accountRegistry_1.replaceSavedAccountProfiles)('claude-code', mapClaudeProfiles(registry), registry.activeAccountUuid);
|
|
205
|
+
}
|
|
206
|
+
/** For callers already holding the registry lock (see withSavedAccountRegistryLock). */
|
|
207
|
+
function writeAccountRegistryUnlocked(registry) {
|
|
208
|
+
(0, accountRegistry_1.replaceSavedAccountProfilesUnlocked)('claude-code', mapClaudeProfiles(registry), registry.activeAccountUuid);
|
|
209
|
+
}
|
|
210
|
+
function mapClaudeProfiles(registry) {
|
|
211
|
+
return registry.accounts.map((account) => ({
|
|
205
212
|
id: account.uuid,
|
|
206
213
|
providerId: 'claude-code',
|
|
207
214
|
providerAccountId: account.uuid,
|
|
@@ -212,7 +219,6 @@ function writeAccountRegistry(registry) {
|
|
|
212
219
|
email: account.email,
|
|
213
220
|
},
|
|
214
221
|
}));
|
|
215
|
-
(0, accountRegistry_1.replaceSavedAccountProfiles)('claude-code', mappedClaudeProfiles, registry.activeAccountUuid);
|
|
216
222
|
}
|
|
217
223
|
// ── Read active Claude account ───────────────────────────────────────────
|
|
218
224
|
function readActiveClaudeAccount() {
|
|
@@ -254,17 +260,34 @@ function addCurrentAccount(label) {
|
|
|
254
260
|
return { success: false, error: 'Could not read Claude config file.' };
|
|
255
261
|
}
|
|
256
262
|
ensureDirs();
|
|
257
|
-
// 4
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
263
|
+
// 4–7 are one read-modify-write of the registry: hold the registry lock
|
|
264
|
+
// across them so a concurrent add from another process is not dropped
|
|
265
|
+
// when this write replaces the whole Claude profile set.
|
|
266
|
+
return (0, accountRegistry_1.withSavedAccountRegistryLock)(() => {
|
|
267
|
+
// 4. Load or create registry
|
|
268
|
+
const registry = readAccountRegistry() ?? {
|
|
269
|
+
version: 1,
|
|
270
|
+
activeAccountUuid: null,
|
|
271
|
+
accounts: [],
|
|
272
|
+
};
|
|
273
|
+
// 5. Check if account already exists — update credentials + label
|
|
274
|
+
const existing = registry.accounts.find((a) => a.uuid === active.uuid);
|
|
275
|
+
if (existing) {
|
|
276
|
+
if (label !== undefined)
|
|
277
|
+
existing.label = label;
|
|
278
|
+
(0, atomic_1.atomicWriteJsonSync)(path.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
|
|
279
|
+
(0, atomic_1.atomicWriteJsonSync)(path.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
|
|
280
|
+
try {
|
|
281
|
+
writeClaudeProfileMirror(active.uuid, credBlob, configBlob);
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
/* flat backups remain available */
|
|
285
|
+
}
|
|
286
|
+
registry.activeAccountUuid = active.uuid;
|
|
287
|
+
writeAccountRegistryUnlocked(registry);
|
|
288
|
+
return { success: true };
|
|
289
|
+
}
|
|
290
|
+
// 6. New account — back up credentials
|
|
268
291
|
(0, atomic_1.atomicWriteJsonSync)(path.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
|
|
269
292
|
(0, atomic_1.atomicWriteJsonSync)(path.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
|
|
270
293
|
try {
|
|
@@ -273,29 +296,17 @@ function addCurrentAccount(label) {
|
|
|
273
296
|
catch {
|
|
274
297
|
/* flat backups remain available */
|
|
275
298
|
}
|
|
299
|
+
// 7. Add entry
|
|
300
|
+
registry.accounts.push({
|
|
301
|
+
uuid: active.uuid,
|
|
302
|
+
email: active.email,
|
|
303
|
+
label,
|
|
304
|
+
addedAt: new Date().toISOString(),
|
|
305
|
+
});
|
|
276
306
|
registry.activeAccountUuid = active.uuid;
|
|
277
|
-
|
|
307
|
+
writeAccountRegistryUnlocked(registry);
|
|
278
308
|
return { success: true };
|
|
279
|
-
}
|
|
280
|
-
// 6. New account — back up credentials
|
|
281
|
-
(0, atomic_1.atomicWriteJsonSync)(path.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
|
|
282
|
-
(0, atomic_1.atomicWriteJsonSync)(path.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
|
|
283
|
-
try {
|
|
284
|
-
writeClaudeProfileMirror(active.uuid, credBlob, configBlob);
|
|
285
|
-
}
|
|
286
|
-
catch {
|
|
287
|
-
/* flat backups remain available */
|
|
288
|
-
}
|
|
289
|
-
// 7. Add entry
|
|
290
|
-
registry.accounts.push({
|
|
291
|
-
uuid: active.uuid,
|
|
292
|
-
email: active.email,
|
|
293
|
-
label,
|
|
294
|
-
addedAt: new Date().toISOString(),
|
|
295
309
|
});
|
|
296
|
-
registry.activeAccountUuid = active.uuid;
|
|
297
|
-
writeAccountRegistry(registry);
|
|
298
|
-
return { success: true };
|
|
299
310
|
}
|
|
300
311
|
// ── Switch to account ────────────────────────────────────────────────────
|
|
301
312
|
/**
|
|
@@ -486,34 +497,37 @@ function reconcileClaudeAuthState() {
|
|
|
486
497
|
}
|
|
487
498
|
// ── Remove account ───────────────────────────────────────────────────────
|
|
488
499
|
function removeAccount(uuid) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
registry.
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
500
|
+
// One read-modify-write under the registry lock (see addCurrentAccount).
|
|
501
|
+
return (0, accountRegistry_1.withSavedAccountRegistryLock)(() => {
|
|
502
|
+
const registry = readAccountRegistry();
|
|
503
|
+
if (!registry) {
|
|
504
|
+
return { success: false, error: 'No account registry found.' };
|
|
505
|
+
}
|
|
506
|
+
const idx = registry.accounts.findIndex((a) => a.uuid === uuid);
|
|
507
|
+
if (idx === -1) {
|
|
508
|
+
return { success: false, error: `Account ${uuid} not found.` };
|
|
509
|
+
}
|
|
510
|
+
// Remove backed-up files
|
|
511
|
+
try {
|
|
512
|
+
fs.unlinkSync(path.join(getCredentialsDir(), `${uuid}.credentials.json`));
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
/* ok */
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
fs.unlinkSync(path.join(getConfigsDir(), `${uuid}.config.json`));
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
/* ok */
|
|
522
|
+
}
|
|
523
|
+
// Remove from registry
|
|
524
|
+
registry.accounts.splice(idx, 1);
|
|
525
|
+
if (registry.activeAccountUuid === uuid) {
|
|
526
|
+
registry.activeAccountUuid = registry.accounts[0]?.uuid ?? null;
|
|
527
|
+
}
|
|
528
|
+
writeAccountRegistryUnlocked(registry);
|
|
529
|
+
return { success: true };
|
|
530
|
+
});
|
|
517
531
|
}
|
|
518
532
|
// ── Query helpers ────────────────────────────────────────────────────────
|
|
519
533
|
function listAccounts() {
|
|
@@ -546,7 +560,8 @@ function getActiveAccount() {
|
|
|
546
560
|
* re-pointed so registry-keyed data tracks reality too. Never creates or deletes
|
|
547
561
|
* profiles; an unknown live account is shown as-is with no label and no write.
|
|
548
562
|
*/
|
|
549
|
-
function resolveActiveClaudeAccount() {
|
|
563
|
+
function resolveActiveClaudeAccount(options = {}) {
|
|
564
|
+
const selfHeal = options.selfHeal ?? true;
|
|
550
565
|
const live = readActiveClaudeAccount();
|
|
551
566
|
if (live) {
|
|
552
567
|
const profiles = (0, accountRegistry_1.listSavedAccountProfiles)('claude-code');
|
|
@@ -554,12 +569,17 @@ function resolveActiveClaudeAccount() {
|
|
|
554
569
|
profiles.find((p) => (p.email ?? p.metadata?.email) === live.email);
|
|
555
570
|
if (match) {
|
|
556
571
|
const active = (0, accountRegistry_1.getActiveSavedAccount)('claude-code');
|
|
557
|
-
|
|
572
|
+
// Hot paths (the status line runs on every prompt) pass selfHeal: false
|
|
573
|
+
// so a persistently failing registry write never becomes a write per
|
|
574
|
+
// prompt; the next ordinary command repairs the pointer instead.
|
|
575
|
+
if (selfHeal && (!active || active.id !== match.id)) {
|
|
558
576
|
// Self-heal is best-effort: a registry write failure (read-only/full
|
|
559
577
|
// disk) must never break display or extension activation. We still
|
|
560
578
|
// return the correct live identity below.
|
|
579
|
+
// Silent: this is a read path repairing a pointer to the profile that
|
|
580
|
+
// already matches the live login, so subscribers have nothing new to see.
|
|
561
581
|
try {
|
|
562
|
-
(0, accountRegistry_1.setActiveSavedAccount)('claude-code', match.id);
|
|
582
|
+
(0, accountRegistry_1.setActiveSavedAccount)('claude-code', match.id, { silent: true });
|
|
563
583
|
}
|
|
564
584
|
catch {
|
|
565
585
|
/* keep going with the live identity */
|
|
@@ -14,7 +14,7 @@ import type { SerializedFrequencyState } from './FrequencyTracker';
|
|
|
14
14
|
import type { SerializedHeatmapState } from './HeatmapTracker';
|
|
15
15
|
import type { SerializedPatternState } from './PatternExtractor';
|
|
16
16
|
/** Schema version for serialized snapshots. */
|
|
17
|
-
export declare const SNAPSHOT_SCHEMA_VERSION =
|
|
17
|
+
export declare const SNAPSHOT_SCHEMA_VERSION = 5;
|
|
18
18
|
/**
|
|
19
19
|
* JSON-serializable snapshot of EventAggregator state.
|
|
20
20
|
* Used by session snapshot sidecar files for fast resume.
|
|
@@ -28,7 +28,14 @@ export interface SerializedAggregatorState {
|
|
|
28
28
|
cacheRead: number;
|
|
29
29
|
reasoning: number;
|
|
30
30
|
total: number;
|
|
31
|
+
/** @deprecated Mirrors `costUsd`; kept so older readers of the field keep working. */
|
|
31
32
|
reportedCost: number;
|
|
33
|
+
costUsd: number;
|
|
34
|
+
reportedCostUsd: number;
|
|
35
|
+
estimatedCostUsd: number;
|
|
36
|
+
reportedCalls: number;
|
|
37
|
+
estimatedCalls: number;
|
|
38
|
+
unpricedCalls: number;
|
|
32
39
|
};
|
|
33
40
|
modelUsage: Array<[
|
|
34
41
|
string,
|
|
@@ -100,7 +107,13 @@ export declare class EventAggregator {
|
|
|
100
107
|
private cacheReadTokens;
|
|
101
108
|
private reasoningTokens;
|
|
102
109
|
private totalTokens;
|
|
103
|
-
|
|
110
|
+
/** Running cost in USD (reported + estimated). */
|
|
111
|
+
private costUsd;
|
|
112
|
+
private reportedCostUsd;
|
|
113
|
+
private estimatedCostUsd;
|
|
114
|
+
private reportedCalls;
|
|
115
|
+
private estimatedCalls;
|
|
116
|
+
private unpricedCalls;
|
|
104
117
|
private modelUsage;
|
|
105
118
|
private currentContextSize;
|
|
106
119
|
private previousContextSize;
|