@brimveyn/aimux 1.22.7 → 1.22.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brimveyn/aimux",
3
- "version": "1.22.7",
3
+ "version": "1.22.8",
4
4
  "description": "A terminal multiplexer for AI CLIs. Run Claude, Codex, OpenCode, Kimi side-by-side with tabbed navigation, split panes, and persistent sessions.",
5
5
  "keywords": [
6
6
  "ai",
@@ -1,5 +1,9 @@
1
1
  import type { AIUsageToolConfig } from '@brimveyn/aimux-config'
2
2
 
3
+ import { readFile } from 'node:fs/promises'
4
+ import { homedir } from 'node:os'
5
+ import { join } from 'node:path'
6
+
3
7
  import type { UsageSnapshot, UsageWindow, UsageWindowKind } from '../types'
4
8
 
5
9
  import { computePace, formatTimeRemaining } from '../pace'
@@ -11,7 +15,7 @@ interface ClaudeOAuthCreds {
11
15
  planTier: string | null
12
16
  }
13
17
 
14
- interface ClaudeKeychainPayload {
18
+ interface ClaudeCredentialsPayload {
15
19
  claudeAiOauth?: {
16
20
  accessToken?: string
17
21
  expiresAt?: number
@@ -59,7 +63,7 @@ function normalizePlanTier(raw: string | undefined | null): string | null {
59
63
  return trimmed.charAt(0).toUpperCase() + trimmed.slice(1)
60
64
  }
61
65
 
62
- function planTierFromPayload(payload: ClaudeKeychainPayload): string | null {
66
+ function planTierFromPayload(payload: ClaudeCredentialsPayload): string | null {
63
67
  const o = payload.claudeAiOauth
64
68
  if (!o) return null
65
69
  const raw = o.rateLimitTier ?? o.rate_limit_tier ?? o.subscriptionType
@@ -77,6 +81,33 @@ function planTierFromPayload(payload: ClaudeKeychainPayload): string | null {
77
81
  return null
78
82
  }
79
83
 
84
+ function claudeConfigDir(): string {
85
+ const env = process.env.CLAUDE_CONFIG_DIR?.trim()
86
+ if (env != null && env !== '') return env
87
+ return join(homedir(), '.claude')
88
+ }
89
+
90
+ async function readCredsFromFile(): Promise<string | null> {
91
+ try {
92
+ const raw = await readFile(join(claudeConfigDir(), '.credentials.json'), 'utf8')
93
+ return raw.trim() || null
94
+ } catch {
95
+ return null
96
+ }
97
+ }
98
+
99
+ async function readCredsFromKeychain(): Promise<string | null> {
100
+ if (process.platform !== 'darwin') return null
101
+ const result = await runCli('security', [
102
+ 'find-generic-password',
103
+ '-s',
104
+ 'Claude Code-credentials',
105
+ '-w',
106
+ ])
107
+ if (!result.ok) return null
108
+ return result.stdout.trim() || null
109
+ }
110
+
80
111
  async function readClaudeCreds(): Promise<ClaudeOAuthCreds> {
81
112
  const now = Date.now()
82
113
  if (
@@ -87,22 +118,27 @@ async function readClaudeCreds(): Promise<ClaudeOAuthCreds> {
87
118
  return cachedCreds
88
119
  }
89
120
 
90
- if (process.platform !== 'darwin') {
91
- throw new Error('claude usage requires macOS keychain (darwin only)')
121
+ // macOS keeps the OAuth blob in the keychain; Linux (incl. WSL) and Windows
122
+ // write the same JSON to ~/.claude/.credentials.json. Try the platform's
123
+ // primary store first, then the other one — a macOS install with the keychain
124
+ // unavailable falls back to the file too.
125
+ const raw =
126
+ process.platform === 'darwin'
127
+ ? ((await readCredsFromKeychain()) ?? (await readCredsFromFile()))
128
+ : ((await readCredsFromFile()) ?? (await readCredsFromKeychain()))
129
+ if (raw === null) {
130
+ throw new Error('no Claude credentials found — run `claude` to sign in')
92
131
  }
93
- const result = await runCli('security', [
94
- 'find-generic-password',
95
- '-s',
96
- 'Claude Code-credentials',
97
- '-w',
98
- ])
99
- if (!result.ok) {
100
- throw new Error(`keychain read failed — run \`claude\` to sign in`)
132
+
133
+ let parsed: ClaudeCredentialsPayload
134
+ try {
135
+ parsed = JSON.parse(raw) as ClaudeCredentialsPayload
136
+ } catch {
137
+ throw new Error('claude credentials unreadable — run `claude` to sign in')
101
138
  }
102
- const parsed = JSON.parse(result.stdout.trim()) as ClaudeKeychainPayload
103
139
  const access = parsed.claudeAiOauth?.accessToken
104
140
  if (!(access != null && access !== '')) {
105
- throw new Error('no accessToken in Claude Code keychain')
141
+ throw new Error('no accessToken in Claude credentials — run `claude` to sign in')
106
142
  }
107
143
  cachedCreds = {
108
144
  accessToken: access,