sidekick-shared 0.21.2 → 0.21.3
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/README.md +45 -45
- package/dist/accountManager.js +9 -7
- package/dist/accountRegistry.js +18 -14
- package/dist/accountStatus.js +4 -1
- package/dist/accounts.js +60 -25
- package/dist/aggregation/EventAggregator.d.ts +14 -11
- package/dist/aggregation/EventAggregator.js +68 -31
- package/dist/aggregation/PatternExtractor.js +5 -4
- package/dist/autoSwitch.js +6 -12
- package/dist/claudeProfiles.js +3 -3
- package/dist/codexProfiles.js +45 -22
- package/dist/codexQuota.js +44 -16
- package/dist/codexQuotaWatcher.js +10 -3
- package/dist/context/composer.js +8 -6
- package/dist/context/sessionContext.js +12 -9
- package/dist/credentialIO.js +10 -8
- package/dist/extractors/sessionAssets.js +9 -1
- package/dist/extractors/sources/claudeAssets.js +7 -1
- package/dist/extractors/sources/codexAssets.js +14 -3
- package/dist/extractors/toolCall.js +1 -4
- package/dist/formatters/eventHighlighter.js +148 -28
- package/dist/formatters/noiseClassifier.js +3 -11
- package/dist/formatters/sessionDump.js +22 -13
- package/dist/formatters/toolSummary.js +1 -3
- package/dist/index.d.ts +23 -23
- package/dist/index.js +7 -3
- package/dist/modelContext.js +10 -8
- package/dist/modelInfo.js +11 -6
- package/dist/multiProviderQuotaService.js +10 -7
- package/dist/parsers/codexParser.js +170 -103
- package/dist/parsers/debugLogParser.js +5 -4
- package/dist/parsers/openCodeParser.js +66 -48
- package/dist/parsers/planExtractor.js +3 -3
- package/dist/parsers/sessionActivityDetector.js +10 -6
- package/dist/parsers/sessionPathResolver.js +29 -34
- package/dist/parsers/subagentScanner.js +21 -20
- package/dist/parsers/subagentTraceParser.js +7 -4
- package/dist/phrases.js +11 -11
- package/dist/providerStatus.js +3 -4
- package/dist/providers/claudeCode.js +3 -1
- package/dist/providers/codex.js +35 -14
- package/dist/providers/codexDatabase.js +14 -5
- package/dist/providers/detect.js +10 -6
- package/dist/providers/openCode.js +91 -63
- package/dist/providers/openCodeDatabase.js +44 -17
- package/dist/quota.d.ts +19 -0
- package/dist/quota.js +50 -8
- package/dist/quotaHistory.js +10 -3
- package/dist/quotaSnapshots.js +2 -2
- package/dist/readers/decisions.js +1 -1
- package/dist/readers/notes.js +5 -3
- package/dist/readers/plans.js +43 -23
- package/dist/readers/tasks.js +1 -1
- package/dist/report/htmlHelpers.js +135 -16
- package/dist/report/htmlReportGenerator.js +38 -12
- package/dist/report/transcriptParser.js +8 -2
- package/dist/schemas/index.d.ts +4 -4
- package/dist/schemas/quota.js +2 -10
- package/dist/schemas/sessionEvent.js +20 -10
- package/dist/search/advancedFilter.js +3 -3
- package/dist/search/sessionSearch.js +10 -4
- package/dist/terminalSync.js +4 -2
- package/dist/turns/assistantTurn.js +5 -1
- package/dist/watchers/eventBridge.js +56 -26
- package/dist/watchers/factory.js +5 -2
- package/dist/watchers/jsonlTail.js +3 -1
- package/dist/watchers/jsonlWatcher.js +128 -52
- package/dist/watchers/providerReaderWatcher.js +3 -1
- package/dist/watchers/sqliteWatcher.js +52 -22
- package/dist/zaiQuota.js +6 -10
- package/dist/zaiQuotaApi.js +13 -5
- package/dist/zaiQuotaWatcher.js +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -25,49 +25,49 @@ npm install sidekick-shared
|
|
|
25
25
|
|
|
26
26
|
## API Overview
|
|
27
27
|
|
|
28
|
-
| Module
|
|
29
|
-
|
|
30
|
-
| **Types**
|
|
31
|
-
| **Paths**
|
|
32
|
-
| **Readers**
|
|
33
|
-
| **Providers**
|
|
34
|
-
| **Parsers**
|
|
35
|
-
| **Watchers**
|
|
36
|
-
| **Formatters**
|
|
37
|
-
| **Search**
|
|
38
|
-
| **Aggregation**
|
|
39
|
-
| **Session Context**
|
|
40
|
-
| **Assistant Turns**
|
|
41
|
-
| **Report**
|
|
42
|
-
| **Credentials**
|
|
43
|
-
| **Quota**
|
|
44
|
-
| **Provider Status**
|
|
45
|
-
| **Schemas**
|
|
46
|
-
| **Extractors**
|
|
47
|
-
| **Model Info & Pricing**
|
|
48
|
-
| **Quota Polling**
|
|
49
|
-
| **Multi-Provider Quota**
|
|
50
|
-
| **Accounts**
|
|
51
|
-
| **Account Management 2.0**
|
|
52
|
-
| **Terminal sync & auto-switch** | Opt-in terminal profile pointers, shell-hook/launcher helpers, and a default-off `AutoSwitchController` (`decideAutoSwitch()`) that moves to a healthier saved account when quota crosses a configured threshold
|
|
53
|
-
| **Codex Profiles**
|
|
54
|
-
| **Quota Snapshots**
|
|
55
|
-
| **Phrases**
|
|
28
|
+
| Module | Description |
|
|
29
|
+
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| **Types** | Session events, OpenCode/Codex format types, persistence schemas (tasks, decisions, notes, plans, historical data) |
|
|
31
|
+
| **Paths** | Config directory resolution, project data paths, workspace encoding |
|
|
32
|
+
| **Readers** | Read tasks, decisions, notes, history, handoff, and plans from `~/.config/sidekick/` |
|
|
33
|
+
| **Providers** | Session provider abstraction with Claude Code, OpenCode, and Codex implementations; auto-detection via filesystem |
|
|
34
|
+
| **Parsers** | JSONL event parsing, OpenCode/Codex format normalization, subagent scanning, session path resolution, debug log parsing |
|
|
35
|
+
| **Watchers** | Live session file watching with event bridging, plus `createJsonlTail()` for raw incremental JSONL consumers |
|
|
36
|
+
| **Formatters** | Display helpers (`formatTokenCount()`, `formatDurationMs()`), tool summary, noise classification, session dump (text/markdown/JSON), event highlighting |
|
|
37
|
+
| **Search** | Cross-session full-text search, advanced filtering (substring, fuzzy, regex, date) |
|
|
38
|
+
| **Aggregation** | Event aggregation, frequency tracking, activity heatmaps, pattern extraction |
|
|
39
|
+
| **Session Context** | Provider-neutral context evidence snapshots (`buildSessionContextSnapshot()`, `calculateSessionContextPressure()`, `createSessionContextProjector()`, `readSessionContextSnapshot()`): layered evidence sources, low/medium/high context pressure, and observed capabilities (tools, MCP servers, permission mode, rate limits) |
|
|
40
|
+
| **Assistant Turns** | Browser-safe timeline/process/answer projection for provider-normalized assistant turns (`segmentAssistantTurn()`, `assistantTurnEventsFromSessionEvents()`), including interleaved reasoning, compact tool groups, and Claude `Task` subagent refs without prompt leakage |
|
|
41
|
+
| **Report** | Self-contained HTML session report generation |
|
|
42
|
+
| **Credentials** | Claude Max OAuth credential reading from `~/.claude/.credentials.json` |
|
|
43
|
+
| **Quota** | Claude Max subscription quota fetching (5-hour and 7-day windows), Codex rate-limit extraction from event streams, and authoritative z.ai Coding Plan quota fetching via `resolveZaiQuota()` / `fetchZaiQuotaFromApi()` (reads z.ai's `api/monitor/usage/quota/limit` endpoint with cached-snapshot fallback). The deprecated observed-traffic z.ai estimator helpers remain exported for backward compatibility only |
|
|
44
|
+
| **Provider Status** | API health checking via status.claude.com and status.openai.com (indicator, components, incidents) |
|
|
45
|
+
| **Schemas** | Zod schemas for runtime validation of data crossing process/IPC boundaries — JSONL session events (`sessionEventSchema`, `messageUsageSchema`, `sessionMessageSchema`), assistant turns, quota, account status, and quota history — plus `extractSessionEvents()` to unwrap `progress`-wrapped events. Also published fs-free via the [`sidekick-shared/schemas`](#supported-import-paths) subpath |
|
|
46
|
+
| **Extractors** | Pure functions for single-event processing: `extractTokenUsage()`, `extractToolCall()` (top-level `tool_use`), `extractToolCalls()` (assistant content blocks), plus Node-only session asset extraction (`gatherAssetsForCwd()`, `readClaudeAssets()`, `readCodexAssets()`) for URLs, file paths, commands, and plans |
|
|
47
|
+
| **Model Info & Pricing** | Model family parsing (Anthropic / OpenAI / Google, including legacy `claude-3-opus-…` and `claude-3-5-sonnet-…` IDs), context-window lookup (including Fable 5 / Opus 4.8 / Opus 4.7 / Sonnet 4.7 1M and GPT-5.x variants), pricing tables with optional LiteLLM hydration, null-aware cost (`calculateCost()`), provenance-preserving cost (`calculateCostWithProvenance()`, `mergeCostSources()`), and display helpers (`shortModelName()`, `getModelDisplayInfo()`, `compareModelIds()`, `sortModelIds()`, `formatCost()`) |
|
|
48
|
+
| **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
|
|
49
|
+
| **Multi-Provider Quota** | `MultiProviderQuotaService` orchestrates Claude polling + peak-hours + account labels + Codex/z.ai quota updates behind one typed event stream. `CodexQuotaWatcher` watches the active Codex rollout for live rate limits with snapshot fallback; z.ai quota uses the API resolver with snapshot fallback |
|
|
50
|
+
| **Accounts** | Multi-provider account registry (v2) with per-provider active account, save/switch/remove, v1 migration, `ensureDefaultAccounts()` for first-run bootstrap of the active system Claude/Codex credentials as a "Default" saved account, and `getActiveAccountStatus()` for a single-pass active-account read across providers |
|
|
51
|
+
| **Account Management 2.0** | Provider-neutral acquisition + switching: `beginAccountLogin()` / `getAccountLoginStatus()` / `finalizeAccountLogin()` / `spawnAccountLogin()` drive a TTY-less, profile-isolated login that doesn't disturb the active account until finalization; `listAllAccounts()` and `switchAccount()` expose a shared switcher across Claude and Codex. Claude accounts get canonical profile homes with account-scoped macOS keychain suffixes and startup migration from legacy flat backups |
|
|
52
|
+
| **Terminal sync & auto-switch** | Opt-in terminal profile pointers, shell-hook/launcher helpers, and a default-off `AutoSwitchController` (`decideAutoSwitch()`) that moves to a healthier saved account when quota crosses a configured threshold |
|
|
53
|
+
| **Codex Profiles** | Codex account lifecycle — prepare, finalize, switch, remove — switching atomically swaps the profile's backed-up credentials into the system `~/.codex/auth.json`, with rotated-token staleness protection, one-time dual-home migration, and legacy multi-home session monitoring |
|
|
54
|
+
| **Quota Snapshots** | Persistent quota caching per provider/account for offline fallback |
|
|
55
|
+
| **Phrases** | Curated humorous phrases for loading/idle states, available as a flat `ALL_PHRASES` array or grouped via `PHRASE_CATEGORIES` for category-aware UI |
|
|
56
56
|
|
|
57
57
|
## Supported import paths
|
|
58
58
|
|
|
59
59
|
`sidekick-shared` ships three public entry points plus a few convenience subpaths. Pick the one that matches your runtime.
|
|
60
60
|
|
|
61
|
-
| Path
|
|
62
|
-
|
|
63
|
-
| `sidekick-shared`
|
|
64
|
-
| `sidekick-shared/browser`
|
|
65
|
-
| `sidekick-shared/node`
|
|
66
|
-
| `sidekick-shared/phrases`
|
|
67
|
-
| `sidekick-shared/modelContext`
|
|
68
|
-
| `sidekick-shared/modelInfo`
|
|
69
|
-
| `sidekick-shared/formatting`
|
|
70
|
-
| `sidekick-shared/schemas`
|
|
61
|
+
| Path | Runtime | What it exposes |
|
|
62
|
+
| ------------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
63
|
+
| `sidekick-shared` | Node (CLI, extension host) | Full public API (readers, providers, parsers, pricing, …). |
|
|
64
|
+
| `sidekick-shared/browser` | **Browser / webview** | Pure helpers: context-window lookup, model parsing, cost math, assistant turn projection. |
|
|
65
|
+
| `sidekick-shared/node` | Node only | LiteLLM pricing catalog hydration (`fs` + `path`). |
|
|
66
|
+
| `sidekick-shared/phrases` | Any runtime | Phrase arrays + `getRandomPhrase()`. |
|
|
67
|
+
| `sidekick-shared/modelContext` | Any runtime | Direct access to the context-window module. |
|
|
68
|
+
| `sidekick-shared/modelInfo` | Any runtime | Direct access to model parsing and cost math. |
|
|
69
|
+
| `sidekick-shared/formatting` | Any runtime | Direct access to pure token and duration display helpers. |
|
|
70
|
+
| `sidekick-shared/schemas` | Any runtime | Pure Zod boundary schemas (session events, assistant turns, quota, account status, quota history) — fs-free, no Node builtins. |
|
|
71
71
|
|
|
72
72
|
### Browser / webview runtimes
|
|
73
73
|
|
|
@@ -191,9 +191,9 @@ console.log(formatCost(cost)); // "$0.0045"
|
|
|
191
191
|
```typescript
|
|
192
192
|
import { extractTokenUsage, extractToolCall, extractToolCalls } from 'sidekick-shared';
|
|
193
193
|
|
|
194
|
-
const usage = extractTokenUsage(event);
|
|
195
|
-
const tools = extractToolCalls(event);
|
|
196
|
-
const toolFromEvent = extractToolCall(event);
|
|
194
|
+
const usage = extractTokenUsage(event); // TokenUsage | null
|
|
195
|
+
const tools = extractToolCalls(event); // ToolCall[] — assistant content blocks
|
|
196
|
+
const toolFromEvent = extractToolCall(event); // ToolCall | null — top-level `tool_use` events
|
|
197
197
|
```
|
|
198
198
|
|
|
199
199
|
### Extract actionable assets from recent sessions
|
|
@@ -234,7 +234,7 @@ const provider = await detectProvider('/path/to/project');
|
|
|
234
234
|
if (provider) {
|
|
235
235
|
const snapshot = readSessionContextSnapshot(provider, '/path/to/session.jsonl');
|
|
236
236
|
|
|
237
|
-
console.log(snapshot.pressure);
|
|
237
|
+
console.log(snapshot.pressure); // 'low' | 'medium' | 'high'
|
|
238
238
|
console.log(snapshot.contextTokens, '/', snapshot.contextWindow);
|
|
239
239
|
console.log(snapshot.capabilities.tools, snapshot.capabilities.mcpServers);
|
|
240
240
|
console.log(snapshot.sources.length, 'evidence sources');
|
|
@@ -295,9 +295,9 @@ import { createJsonlTail, sessionEventSchema } from 'sidekick-shared';
|
|
|
295
295
|
const tail = createJsonlTail({
|
|
296
296
|
path: '/path/to/session.jsonl',
|
|
297
297
|
schema: sessionEventSchema,
|
|
298
|
-
onEvent: event => aggregator.processEvent(event),
|
|
298
|
+
onEvent: (event) => aggregator.processEvent(event),
|
|
299
299
|
onBatchComplete: () => renderMetrics(aggregator.getMetrics()),
|
|
300
|
-
onError: error => console.warn(error.message),
|
|
300
|
+
onError: (error) => console.warn(error.message),
|
|
301
301
|
});
|
|
302
302
|
|
|
303
303
|
tail.start();
|
|
@@ -329,7 +329,7 @@ const service = new MultiProviderQuotaService({
|
|
|
329
329
|
|
|
330
330
|
service.onUpdate(({ claude, codex }) => {
|
|
331
331
|
if (claude) console.log('Claude:', claude.fiveHour.utilization, claude.peakHours?.label);
|
|
332
|
-
if (codex)
|
|
332
|
+
if (codex) console.log('Codex:', codex.fiveHour.utilization, codex.accountLabel);
|
|
333
333
|
});
|
|
334
334
|
|
|
335
335
|
service.startPolling();
|
package/dist/accountManager.js
CHANGED
|
@@ -68,7 +68,9 @@ function atomicWriteJson(filePath, data, mode = 0o600) {
|
|
|
68
68
|
try {
|
|
69
69
|
fs.unlinkSync(tmp);
|
|
70
70
|
}
|
|
71
|
-
catch {
|
|
71
|
+
catch {
|
|
72
|
+
/* nothing to clean up */
|
|
73
|
+
}
|
|
72
74
|
throw err;
|
|
73
75
|
}
|
|
74
76
|
}
|
|
@@ -88,7 +90,9 @@ function removePendingClaudeProfile(loginId) {
|
|
|
88
90
|
try {
|
|
89
91
|
fs.rmSync(getPendingClaudeProfilePath(loginId), { force: true });
|
|
90
92
|
}
|
|
91
|
-
catch {
|
|
93
|
+
catch {
|
|
94
|
+
/* best effort */
|
|
95
|
+
}
|
|
92
96
|
}
|
|
93
97
|
function getClaudeCredentialsBackupPath(uuid) {
|
|
94
98
|
return path.join((0, accountRegistry_1.getAccountsDir)(), 'credentials', `${uuid}.credentials.json`);
|
|
@@ -246,7 +250,7 @@ function waitForNextPoll(ms, signal) {
|
|
|
246
250
|
return Promise.resolve(true);
|
|
247
251
|
if (signal?.aborted)
|
|
248
252
|
return Promise.resolve(false);
|
|
249
|
-
return new Promise(resolve => {
|
|
253
|
+
return new Promise((resolve) => {
|
|
250
254
|
const timer = setTimeout(() => {
|
|
251
255
|
cleanup();
|
|
252
256
|
resolve(true);
|
|
@@ -289,7 +293,7 @@ async function spawnAccountLogin(provider, label, opts = {}) {
|
|
|
289
293
|
catch (err) {
|
|
290
294
|
return { success: false, error: `Could not spawn account login: ${err}` };
|
|
291
295
|
}
|
|
292
|
-
child.on('exit', code => {
|
|
296
|
+
child.on('exit', (code) => {
|
|
293
297
|
childExited = true;
|
|
294
298
|
childExitCode = code;
|
|
295
299
|
});
|
|
@@ -328,9 +332,7 @@ async function spawnAccountLogin(provider, label, opts = {}) {
|
|
|
328
332
|
}
|
|
329
333
|
}
|
|
330
334
|
function switchAccount(provider, id) {
|
|
331
|
-
return provider === 'codex'
|
|
332
|
-
? (0, codexProfiles_1.switchToCodexAccount)(id)
|
|
333
|
-
: (0, accounts_1.switchToAccount)(id);
|
|
335
|
+
return provider === 'codex' ? (0, codexProfiles_1.switchToCodexAccount)(id) : (0, accounts_1.switchToAccount)(id);
|
|
334
336
|
}
|
|
335
337
|
function listAllAccounts() {
|
|
336
338
|
return {
|
package/dist/accountRegistry.js
CHANGED
|
@@ -81,17 +81,19 @@ function normalizeRegistry(registry) {
|
|
|
81
81
|
(account.providerId === 'claude-code' || account.providerId === 'codex') &&
|
|
82
82
|
typeof account.id === 'string' &&
|
|
83
83
|
typeof account.addedAt === 'string')
|
|
84
|
-
.map(account => ({
|
|
84
|
+
.map((account) => ({
|
|
85
85
|
...account,
|
|
86
86
|
label: account.label || undefined,
|
|
87
87
|
email: account.email || undefined,
|
|
88
88
|
providerAccountId: account.providerAccountId || undefined,
|
|
89
|
-
metadata: account.metadata
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
metadata: account.metadata
|
|
90
|
+
? {
|
|
91
|
+
email: account.metadata.email || undefined,
|
|
92
|
+
workspaceId: account.metadata.workspaceId || undefined,
|
|
93
|
+
planType: account.metadata.planType || undefined,
|
|
94
|
+
authMode: account.metadata.authMode || undefined,
|
|
95
|
+
}
|
|
96
|
+
: undefined,
|
|
95
97
|
}))
|
|
96
98
|
: [];
|
|
97
99
|
return normalized;
|
|
@@ -103,7 +105,7 @@ function migrateLegacyRegistry(legacy) {
|
|
|
103
105
|
'claude-code': legacy.activeAccountUuid,
|
|
104
106
|
codex: null,
|
|
105
107
|
},
|
|
106
|
-
accounts: legacy.accounts.map(account => ({
|
|
108
|
+
accounts: legacy.accounts.map((account) => ({
|
|
107
109
|
id: account.uuid,
|
|
108
110
|
providerId: 'claude-code',
|
|
109
111
|
providerAccountId: account.uuid,
|
|
@@ -141,7 +143,7 @@ function listSavedAccountProfiles(providerId) {
|
|
|
141
143
|
if (!registry)
|
|
142
144
|
return [];
|
|
143
145
|
return providerId
|
|
144
|
-
? registry.accounts.filter(account => account.providerId === providerId)
|
|
146
|
+
? registry.accounts.filter((account) => account.providerId === providerId)
|
|
145
147
|
: registry.accounts;
|
|
146
148
|
}
|
|
147
149
|
function getActiveSavedAccount(providerId) {
|
|
@@ -151,11 +153,11 @@ function getActiveSavedAccount(providerId) {
|
|
|
151
153
|
const activeId = registry.activeByProvider[providerId];
|
|
152
154
|
if (!activeId)
|
|
153
155
|
return null;
|
|
154
|
-
return registry.accounts.find(account => account.providerId === providerId && account.id === activeId) ?? null;
|
|
156
|
+
return (registry.accounts.find((account) => account.providerId === providerId && account.id === activeId) ?? null);
|
|
155
157
|
}
|
|
156
158
|
function upsertSavedAccountProfile(profile) {
|
|
157
159
|
const registry = readSavedAccountRegistry() ?? createEmptyRegistry();
|
|
158
|
-
const index = registry.accounts.findIndex(account => account.providerId === profile.providerId && account.id === profile.id);
|
|
160
|
+
const index = registry.accounts.findIndex((account) => account.providerId === profile.providerId && account.id === profile.id);
|
|
159
161
|
if (index >= 0) {
|
|
160
162
|
registry.accounts[index] = profile;
|
|
161
163
|
}
|
|
@@ -173,7 +175,9 @@ function setActiveSavedAccount(providerId, accountId) {
|
|
|
173
175
|
}
|
|
174
176
|
function replaceSavedAccountProfiles(providerId, accounts, activeId) {
|
|
175
177
|
const registry = readSavedAccountRegistry() ?? createEmptyRegistry();
|
|
176
|
-
registry.accounts = registry.accounts
|
|
178
|
+
registry.accounts = registry.accounts
|
|
179
|
+
.filter((account) => account.providerId !== providerId)
|
|
180
|
+
.concat(accounts);
|
|
177
181
|
registry.activeByProvider[providerId] = activeId;
|
|
178
182
|
writeSavedAccountRegistry(registry);
|
|
179
183
|
return registry;
|
|
@@ -182,13 +186,13 @@ function removeSavedAccountProfile(providerId, accountId) {
|
|
|
182
186
|
const registry = readSavedAccountRegistry();
|
|
183
187
|
if (!registry)
|
|
184
188
|
return null;
|
|
185
|
-
const index = registry.accounts.findIndex(account => account.providerId === providerId && account.id === accountId);
|
|
189
|
+
const index = registry.accounts.findIndex((account) => account.providerId === providerId && account.id === accountId);
|
|
186
190
|
if (index === -1)
|
|
187
191
|
return null;
|
|
188
192
|
const [removed] = registry.accounts.splice(index, 1);
|
|
189
193
|
if (registry.activeByProvider[providerId] === accountId) {
|
|
190
194
|
registry.activeByProvider[providerId] =
|
|
191
|
-
registry.accounts.find(account => account.providerId === providerId)?.id ?? null;
|
|
195
|
+
registry.accounts.find((account) => account.providerId === providerId)?.id ?? null;
|
|
192
196
|
}
|
|
193
197
|
writeSavedAccountRegistry(registry);
|
|
194
198
|
return removed;
|
package/dist/accountStatus.js
CHANGED
|
@@ -27,7 +27,10 @@ function getActiveAccountStatus(error) {
|
|
|
27
27
|
? {
|
|
28
28
|
present: true,
|
|
29
29
|
email: codexAccount.email ?? codexAccount.metadata?.email,
|
|
30
|
-
label: codexAccount.label ??
|
|
30
|
+
label: codexAccount.label ??
|
|
31
|
+
codexAccount.email ??
|
|
32
|
+
codexAccount.metadata?.email ??
|
|
33
|
+
codexAccount.id,
|
|
31
34
|
}
|
|
32
35
|
: { present: false };
|
|
33
36
|
return {
|
package/dist/accounts.js
CHANGED
|
@@ -69,7 +69,9 @@ function getClaudeConfigPath() {
|
|
|
69
69
|
if (data?.oauthAccount)
|
|
70
70
|
return primary;
|
|
71
71
|
}
|
|
72
|
-
catch {
|
|
72
|
+
catch {
|
|
73
|
+
/* fall through */
|
|
74
|
+
}
|
|
73
75
|
// Fallback: ~/.claude.json (some Claude Code versions store config here)
|
|
74
76
|
return path.join(os.homedir(), '.claude.json');
|
|
75
77
|
}
|
|
@@ -193,8 +195,8 @@ function readAccountRegistry() {
|
|
|
193
195
|
if (!registry)
|
|
194
196
|
return null;
|
|
195
197
|
const claudeAccounts = registry.accounts
|
|
196
|
-
.filter(account => account.providerId === 'claude-code')
|
|
197
|
-
.map(account => ({
|
|
198
|
+
.filter((account) => account.providerId === 'claude-code')
|
|
199
|
+
.map((account) => ({
|
|
198
200
|
uuid: account.providerAccountId ?? account.id,
|
|
199
201
|
email: account.email ?? account.metadata?.email ?? 'unknown',
|
|
200
202
|
label: account.label,
|
|
@@ -207,7 +209,7 @@ function readAccountRegistry() {
|
|
|
207
209
|
};
|
|
208
210
|
}
|
|
209
211
|
function writeAccountRegistry(registry) {
|
|
210
|
-
const mappedClaudeProfiles = registry.accounts.map(account => ({
|
|
212
|
+
const mappedClaudeProfiles = registry.accounts.map((account) => ({
|
|
211
213
|
id: account.uuid,
|
|
212
214
|
providerId: 'claude-code',
|
|
213
215
|
providerAccountId: account.uuid,
|
|
@@ -240,7 +242,10 @@ function addCurrentAccount(label) {
|
|
|
240
242
|
// 1. Read current Claude identity
|
|
241
243
|
const active = readActiveClaudeAccount();
|
|
242
244
|
if (!active) {
|
|
243
|
-
return {
|
|
245
|
+
return {
|
|
246
|
+
success: false,
|
|
247
|
+
error: 'No active Claude account found. Sign in with `claude` first.',
|
|
248
|
+
};
|
|
244
249
|
}
|
|
245
250
|
// 2. Read current credentials (platform-aware: Keychain on macOS, file on Linux)
|
|
246
251
|
const credBlob = (0, credentialIO_1.readActiveCredentials)();
|
|
@@ -264,7 +269,7 @@ function addCurrentAccount(label) {
|
|
|
264
269
|
accounts: [],
|
|
265
270
|
};
|
|
266
271
|
// 5. Check if account already exists — update credentials + label
|
|
267
|
-
const existing = registry.accounts.find(a => a.uuid === active.uuid);
|
|
272
|
+
const existing = registry.accounts.find((a) => a.uuid === active.uuid);
|
|
268
273
|
if (existing) {
|
|
269
274
|
if (label !== undefined)
|
|
270
275
|
existing.label = label;
|
|
@@ -273,7 +278,9 @@ function addCurrentAccount(label) {
|
|
|
273
278
|
try {
|
|
274
279
|
writeClaudeProfileMirror(active.uuid, credBlob, configBlob);
|
|
275
280
|
}
|
|
276
|
-
catch {
|
|
281
|
+
catch {
|
|
282
|
+
/* flat backups remain available */
|
|
283
|
+
}
|
|
277
284
|
registry.activeAccountUuid = active.uuid;
|
|
278
285
|
writeAccountRegistry(registry);
|
|
279
286
|
return { success: true };
|
|
@@ -284,7 +291,9 @@ function addCurrentAccount(label) {
|
|
|
284
291
|
try {
|
|
285
292
|
writeClaudeProfileMirror(active.uuid, credBlob, configBlob);
|
|
286
293
|
}
|
|
287
|
-
catch {
|
|
294
|
+
catch {
|
|
295
|
+
/* flat backups remain available */
|
|
296
|
+
}
|
|
288
297
|
// 7. Add entry
|
|
289
298
|
registry.accounts.push({
|
|
290
299
|
uuid: active.uuid,
|
|
@@ -302,7 +311,7 @@ function switchToAccount(uuid) {
|
|
|
302
311
|
if (!registry) {
|
|
303
312
|
return { success: false, error: 'No account registry found. Save an account first.' };
|
|
304
313
|
}
|
|
305
|
-
const target = registry.accounts.find(a => a.uuid === uuid);
|
|
314
|
+
const target = registry.accounts.find((a) => a.uuid === uuid);
|
|
306
315
|
if (!target) {
|
|
307
316
|
return { success: false, error: `Account ${uuid} not found in registry.` };
|
|
308
317
|
}
|
|
@@ -313,11 +322,15 @@ function switchToAccount(uuid) {
|
|
|
313
322
|
try {
|
|
314
323
|
originalConfig = fs.readFileSync(getClaudeConfigPath(), 'utf8');
|
|
315
324
|
}
|
|
316
|
-
catch {
|
|
325
|
+
catch {
|
|
326
|
+
/* no existing config to back up */
|
|
327
|
+
}
|
|
317
328
|
try {
|
|
318
329
|
backupCurrentClaudeLiveHome();
|
|
319
330
|
}
|
|
320
|
-
catch {
|
|
331
|
+
catch {
|
|
332
|
+
/* best effort */
|
|
333
|
+
}
|
|
321
334
|
try {
|
|
322
335
|
(0, accountRegistry_1.setActiveSavedAccount)('claude-code', uuid);
|
|
323
336
|
}
|
|
@@ -329,18 +342,24 @@ function switchToAccount(uuid) {
|
|
|
329
342
|
try {
|
|
330
343
|
(0, accountRegistry_1.setActiveSavedAccount)('claude-code', registry.activeAccountUuid);
|
|
331
344
|
}
|
|
332
|
-
catch {
|
|
345
|
+
catch {
|
|
346
|
+
/* rollback failed */
|
|
347
|
+
}
|
|
333
348
|
if (originalCreds) {
|
|
334
349
|
try {
|
|
335
350
|
(0, credentialIO_1.writeActiveCredentials)(originalCreds);
|
|
336
351
|
}
|
|
337
|
-
catch {
|
|
352
|
+
catch {
|
|
353
|
+
/* rollback failed */
|
|
354
|
+
}
|
|
338
355
|
}
|
|
339
356
|
if (originalConfig) {
|
|
340
357
|
try {
|
|
341
358
|
fs.writeFileSync(getClaudeConfigPath(), originalConfig);
|
|
342
359
|
}
|
|
343
|
-
catch {
|
|
360
|
+
catch {
|
|
361
|
+
/* rollback failed */
|
|
362
|
+
}
|
|
344
363
|
}
|
|
345
364
|
return applied;
|
|
346
365
|
}
|
|
@@ -367,7 +386,9 @@ function applyActiveClaudeToLiveHome() {
|
|
|
367
386
|
try {
|
|
368
387
|
originalConfig = fs.readFileSync(getClaudeConfigPath(), 'utf8');
|
|
369
388
|
}
|
|
370
|
-
catch {
|
|
389
|
+
catch {
|
|
390
|
+
/* no existing config to back up */
|
|
391
|
+
}
|
|
371
392
|
try {
|
|
372
393
|
(0, credentialIO_1.writeActiveCredentials)(stored.credentials);
|
|
373
394
|
}
|
|
@@ -376,7 +397,9 @@ function applyActiveClaudeToLiveHome() {
|
|
|
376
397
|
try {
|
|
377
398
|
(0, credentialIO_1.writeActiveCredentials)(originalCreds);
|
|
378
399
|
}
|
|
379
|
-
catch {
|
|
400
|
+
catch {
|
|
401
|
+
/* rollback failed */
|
|
402
|
+
}
|
|
380
403
|
}
|
|
381
404
|
return { success: false, error: `Failed to write credentials: ${err}` };
|
|
382
405
|
}
|
|
@@ -388,13 +411,17 @@ function applyActiveClaudeToLiveHome() {
|
|
|
388
411
|
try {
|
|
389
412
|
(0, credentialIO_1.writeActiveCredentials)(originalCreds);
|
|
390
413
|
}
|
|
391
|
-
catch {
|
|
414
|
+
catch {
|
|
415
|
+
/* rollback failed */
|
|
416
|
+
}
|
|
392
417
|
}
|
|
393
418
|
if (originalConfig) {
|
|
394
419
|
try {
|
|
395
420
|
fs.writeFileSync(getClaudeConfigPath(), originalConfig);
|
|
396
421
|
}
|
|
397
|
-
catch {
|
|
422
|
+
catch {
|
|
423
|
+
/* rollback failed */
|
|
424
|
+
}
|
|
398
425
|
}
|
|
399
426
|
return { success: false, error: `Failed to write config: ${err}` };
|
|
400
427
|
}
|
|
@@ -407,10 +434,11 @@ function reconcileClaudeAuthState() {
|
|
|
407
434
|
return;
|
|
408
435
|
const registry = (0, accountRegistry_1.readSavedAccountRegistry)();
|
|
409
436
|
if (registry) {
|
|
410
|
-
for (const profile of registry.accounts.filter(account => account.providerId === 'claude-code')) {
|
|
437
|
+
for (const profile of registry.accounts.filter((account) => account.providerId === 'claude-code')) {
|
|
411
438
|
try {
|
|
412
439
|
const uuid = profile.providerAccountId ?? profile.id;
|
|
413
|
-
if ((0, credentialIO_1.readActiveCredentials)((0, claudeProfiles_1.getClaudeProfileHome)(uuid)) &&
|
|
440
|
+
if ((0, credentialIO_1.readActiveCredentials)((0, claudeProfiles_1.getClaudeProfileHome)(uuid)) &&
|
|
441
|
+
readProfileClaudeOauthAccount(uuid)) {
|
|
414
442
|
continue;
|
|
415
443
|
}
|
|
416
444
|
const credentials = readFlatClaudeCredentials(uuid);
|
|
@@ -425,7 +453,10 @@ function reconcileClaudeAuthState() {
|
|
|
425
453
|
}
|
|
426
454
|
}
|
|
427
455
|
fs.mkdirSync(path.dirname(markerPath), { recursive: true, mode: 0o700 });
|
|
428
|
-
fs.writeFileSync(markerPath, new Date().toISOString() + '\n', {
|
|
456
|
+
fs.writeFileSync(markerPath, new Date().toISOString() + '\n', {
|
|
457
|
+
encoding: 'utf8',
|
|
458
|
+
mode: 0o600,
|
|
459
|
+
});
|
|
429
460
|
}
|
|
430
461
|
catch {
|
|
431
462
|
// Reconciliation must never break startup.
|
|
@@ -437,7 +468,7 @@ function removeAccount(uuid) {
|
|
|
437
468
|
if (!registry) {
|
|
438
469
|
return { success: false, error: 'No account registry found.' };
|
|
439
470
|
}
|
|
440
|
-
const idx = registry.accounts.findIndex(a => a.uuid === uuid);
|
|
471
|
+
const idx = registry.accounts.findIndex((a) => a.uuid === uuid);
|
|
441
472
|
if (idx === -1) {
|
|
442
473
|
return { success: false, error: `Account ${uuid} not found.` };
|
|
443
474
|
}
|
|
@@ -445,11 +476,15 @@ function removeAccount(uuid) {
|
|
|
445
476
|
try {
|
|
446
477
|
fs.unlinkSync(path.join(getCredentialsDir(), `${uuid}.credentials.json`));
|
|
447
478
|
}
|
|
448
|
-
catch {
|
|
479
|
+
catch {
|
|
480
|
+
/* ok */
|
|
481
|
+
}
|
|
449
482
|
try {
|
|
450
483
|
fs.unlinkSync(path.join(getConfigsDir(), `${uuid}.config.json`));
|
|
451
484
|
}
|
|
452
|
-
catch {
|
|
485
|
+
catch {
|
|
486
|
+
/* ok */
|
|
487
|
+
}
|
|
453
488
|
// Remove from registry
|
|
454
489
|
registry.accounts.splice(idx, 1);
|
|
455
490
|
if (registry.activeAccountUuid === uuid) {
|
|
@@ -460,7 +495,7 @@ function removeAccount(uuid) {
|
|
|
460
495
|
}
|
|
461
496
|
// ── Query helpers ────────────────────────────────────────────────────────
|
|
462
497
|
function listAccounts() {
|
|
463
|
-
return (0, accountRegistry_1.listSavedAccountProfiles)('claude-code').map(account => ({
|
|
498
|
+
return (0, accountRegistry_1.listSavedAccountProfiles)('claude-code').map((account) => ({
|
|
464
499
|
uuid: account.providerAccountId ?? account.id,
|
|
465
500
|
email: account.email ?? account.metadata?.email ?? 'unknown',
|
|
466
501
|
label: account.label,
|
|
@@ -26,17 +26,20 @@ export interface SerializedAggregatorState {
|
|
|
26
26
|
cacheRead: number;
|
|
27
27
|
reportedCost: number;
|
|
28
28
|
};
|
|
29
|
-
modelUsage: Array<[
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
modelUsage: Array<[
|
|
30
|
+
string,
|
|
31
|
+
{
|
|
32
|
+
calls: number;
|
|
33
|
+
tokens: number;
|
|
34
|
+
inputTokens: number;
|
|
35
|
+
outputTokens: number;
|
|
36
|
+
cacheWriteTokens: number;
|
|
37
|
+
cacheReadTokens: number;
|
|
38
|
+
cost: number;
|
|
39
|
+
reasoningTokens?: number;
|
|
40
|
+
priced?: boolean;
|
|
41
|
+
}
|
|
42
|
+
]>;
|
|
40
43
|
contextSize: number;
|
|
41
44
|
previousContextSize: number;
|
|
42
45
|
compactionEvents: CompactionEvent[];
|