@cortexkit/opencode-anthropic-auth 1.21.0 → 1.23.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/README.md +57 -12
- package/dist/billing-lineage.d.ts +51 -0
- package/dist/claustrum-enrollment-registry.d.ts +4 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +89 -6
- package/dist/custody-dimensions.d.ts +36 -0
- package/dist/custody-enrollment.d.ts +14 -0
- package/dist/custody-live.d.ts +111 -0
- package/dist/custody-manifest-watcher.d.ts +16 -0
- package/dist/custody-mode.d.ts +186 -0
- package/dist/effort-history.d.ts +62 -0
- package/dist/fable-fallback.d.ts +6 -7
- package/dist/{index-w9d41yy9.js → index-0jfsxyn9.js} +1 -1
- package/dist/index-1fq7h6yd.js +430 -0
- package/dist/{index-y0khnbt6.js → index-1gr8v4fn.js} +1 -1
- package/dist/index-797pmamn.js +2 -0
- package/dist/{index-1db17bc2.js → index-8y2t4mwz.js} +2 -2
- package/dist/index-a5d6v3ba.js +5 -0
- package/dist/index-n96q8pks.js +2 -0
- package/dist/index-xxhyhp98.js +2 -0
- package/dist/index-xzbhzz0a.js +2 -0
- package/dist/index-z7gfdyv3.js +1 -0
- package/dist/index.js +28 -21
- package/dist/local-login.d.ts +64 -0
- package/dist/{logger-ftk4bg3p.js → logger-kk9vyem9.js} +1 -1
- package/dist/prime-manager-registry.d.ts +5 -1
- package/dist/rpc/notifications.d.ts +3 -3
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +23 -1
- package/dist/rpc/protocol.js +1 -1
- package/dist/rpc/rpc-client.d.ts +1 -1
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/rpc/server-registry.d.ts +10 -0
- package/dist/setup/activation.d.ts +7 -0
- package/dist/setup/claustrum.d.ts +26 -0
- package/dist/setup/command-runner.d.ts +2 -0
- package/dist/setup/command.d.ts +8 -0
- package/dist/setup/detect.d.ts +5 -0
- package/dist/setup/jsonc.d.ts +9 -0
- package/dist/setup/opencode-config.d.ts +18 -0
- package/dist/setup/paths.d.ts +15 -0
- package/dist/setup/pi.d.ts +8 -0
- package/dist/setup/process-fence.d.ts +14 -0
- package/dist/setup/types.d.ts +37 -0
- package/dist/sidebar-state.d.ts +5 -2
- package/dist/sidebar-state.js +1 -1
- package/dist/transform.d.ts +17 -1
- package/dist/tui/command-dialogs.d.ts +28 -9
- package/dist/tui-preferences.js +2 -2
- package/package.json +10 -5
- package/src/rpc/notifications.ts +28 -24
- package/src/rpc/protocol.ts +34 -1
- package/src/rpc/rpc-client.ts +1 -1
- package/src/rpc/rpc-server.ts +18 -5
- package/src/rpc/server-registry.ts +61 -0
- package/src/sidebar-state.ts +42 -3
- package/src/tui/command-dialogs.tsx +301 -97
- package/src/tui-compiled/rpc/protocol.ts +34 -1
- package/src/tui-compiled/rpc/rpc-client.ts +1 -1
- package/src/tui-compiled/sidebar-state.ts +42 -3
- package/src/tui-compiled/tui/command-dialogs.tsx +240 -70
- package/src/tui-compiled/tui.tsx +8 -3
- package/src/tui.tsx +18 -3
- package/dist/index-4nz423bt.js +0 -4
- package/dist/index-4x20896x.js +0 -2
- package/dist/index-wezdyxst.js +0 -2
- package/dist/index-yskwj5e6.js +0 -428
package/src/rpc/notifications.ts
CHANGED
|
@@ -3,52 +3,56 @@ import type { OpenDialogPayload, RpcNotification } from './protocol'
|
|
|
3
3
|
const QUEUE_CAP = 100
|
|
4
4
|
const TUI_CONNECTED_WINDOW_MS = 3_000
|
|
5
5
|
|
|
6
|
+
// One queue serves every RPC server in the process, and a process can hold one
|
|
7
|
+
// server per project directory. Session ids are globally unique, so both
|
|
8
|
+
// producers and drains require one: an unscoped drain could otherwise expose
|
|
9
|
+
// another project's pending command dialog.
|
|
6
10
|
let queue: RpcNotification[] = []
|
|
7
11
|
let nextId = 1
|
|
8
|
-
let lastDrainAtAny = 0
|
|
9
12
|
const lastDrainAtBySession = new Map<string, number>()
|
|
10
13
|
|
|
14
|
+
function assertSessionId(sessionId: string): void {
|
|
15
|
+
if (typeof sessionId !== 'string' || sessionId.length === 0) {
|
|
16
|
+
throw new TypeError('sessionId is required')
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
11
20
|
export function pushNotification(
|
|
12
21
|
payload: OpenDialogPayload,
|
|
13
|
-
sessionId
|
|
22
|
+
sessionId: string,
|
|
14
23
|
): void {
|
|
24
|
+
assertSessionId(sessionId)
|
|
15
25
|
queue.push({ id: nextId++, type: 'open-dialog', payload, sessionId })
|
|
16
26
|
if (queue.length > QUEUE_CAP) queue = queue.slice(queue.length - QUEUE_CAP)
|
|
17
27
|
}
|
|
18
28
|
|
|
19
29
|
export function drainNotifications(
|
|
20
|
-
lastReceivedId
|
|
21
|
-
sessionId
|
|
30
|
+
lastReceivedId: number,
|
|
31
|
+
sessionId: string,
|
|
22
32
|
): RpcNotification[] {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (sessionId !== undefined) lastDrainAtBySession.set(sessionId, now)
|
|
26
|
-
const matches = (n: RpcNotification) =>
|
|
27
|
-
sessionId === undefined ||
|
|
28
|
-
n.sessionId === undefined ||
|
|
29
|
-
n.sessionId === sessionId
|
|
33
|
+
assertSessionId(sessionId)
|
|
34
|
+
lastDrainAtBySession.set(sessionId, Date.now())
|
|
30
35
|
if (lastReceivedId > 0) {
|
|
31
|
-
queue = queue.filter(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
queue = queue.filter(
|
|
37
|
+
(notification) =>
|
|
38
|
+
notification.id > lastReceivedId ||
|
|
39
|
+
notification.sessionId !== sessionId,
|
|
40
|
+
)
|
|
36
41
|
}
|
|
37
|
-
return queue.filter(
|
|
42
|
+
return queue.filter(
|
|
43
|
+
(notification) =>
|
|
44
|
+
notification.id > lastReceivedId && notification.sessionId === sessionId,
|
|
45
|
+
)
|
|
38
46
|
}
|
|
39
47
|
|
|
40
|
-
export function isTuiConnected(sessionId
|
|
48
|
+
export function isTuiConnected(sessionId: string): boolean {
|
|
41
49
|
const now = Date.now()
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS
|
|
45
|
-
}
|
|
46
|
-
return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS
|
|
50
|
+
const at = lastDrainAtBySession.get(sessionId) ?? 0
|
|
51
|
+
return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
export function resetNotificationsForTest(): void {
|
|
50
55
|
queue = []
|
|
51
56
|
nextId = 1
|
|
52
|
-
lastDrainAtAny = 0
|
|
53
57
|
lastDrainAtBySession.clear()
|
|
54
58
|
}
|
package/src/rpc/protocol.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ClaustrumEnrollmentStatus } from '@cortexkit/anthropic-auth-core'
|
|
2
|
+
|
|
1
3
|
export const COMMAND_MODAL_NAMES = [
|
|
2
4
|
'claude-account',
|
|
3
5
|
'claude-cache',
|
|
@@ -14,6 +16,37 @@ export const COMMAND_MODAL_NAMES = [
|
|
|
14
16
|
|
|
15
17
|
export type CommandModalName = (typeof COMMAND_MODAL_NAMES)[number]
|
|
16
18
|
|
|
19
|
+
export interface AccountDialogAccount {
|
|
20
|
+
id: string
|
|
21
|
+
label: string
|
|
22
|
+
role: 'main' | 'fallback'
|
|
23
|
+
enabled: boolean
|
|
24
|
+
quotaPercent: number | null
|
|
25
|
+
tierLabel?: string
|
|
26
|
+
claustrumGate: 'on' | 'off' | 'na'
|
|
27
|
+
vaultServed: boolean
|
|
28
|
+
vaultReauth: boolean
|
|
29
|
+
custodyState:
|
|
30
|
+
| 'na'
|
|
31
|
+
| 'off'
|
|
32
|
+
| 'on-vault-served'
|
|
33
|
+
| 'on-vault-reauth'
|
|
34
|
+
| 'on-cold'
|
|
35
|
+
| 'unknown-identity'
|
|
36
|
+
| 'on-identity-mismatch'
|
|
37
|
+
| 'on-corrupt-binding'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AccountDialogKnobs {
|
|
41
|
+
accounts: AccountDialogAccount[]
|
|
42
|
+
claustrumDetection: string
|
|
43
|
+
custodyMode?: 'local' | 'claustrum'
|
|
44
|
+
custodyModeKnown?: boolean
|
|
45
|
+
enrollmentStatus?: string
|
|
46
|
+
claustrumEnrollment?: ClaustrumEnrollmentStatus
|
|
47
|
+
[key: string]: unknown
|
|
48
|
+
}
|
|
49
|
+
|
|
17
50
|
export interface OpenDialogPayload {
|
|
18
51
|
command: CommandModalName
|
|
19
52
|
text: string
|
|
@@ -24,7 +57,7 @@ export interface RpcNotification {
|
|
|
24
57
|
id: number
|
|
25
58
|
type: 'open-dialog'
|
|
26
59
|
payload: OpenDialogPayload
|
|
27
|
-
sessionId
|
|
60
|
+
sessionId: string
|
|
28
61
|
}
|
|
29
62
|
|
|
30
63
|
export interface ApplyRequest {
|
package/src/rpc/rpc-client.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { ApplyRequest, ApplyResult, RpcNotification } from './protocol'
|
|
|
4
4
|
export interface RpcClient {
|
|
5
5
|
pending: (
|
|
6
6
|
lastReceivedId: number,
|
|
7
|
-
sessionId
|
|
7
|
+
sessionId: string,
|
|
8
8
|
) => Promise<RpcNotification[]>
|
|
9
9
|
apply: (request: ApplyRequest) => Promise<ApplyResult>
|
|
10
10
|
}
|
package/src/rpc/rpc-server.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes, timingSafeEqual } from 'node:crypto'
|
|
2
|
-
import { unlink } from 'node:fs/promises'
|
|
2
|
+
import { readFile, unlink } from 'node:fs/promises'
|
|
3
3
|
import {
|
|
4
4
|
createServer,
|
|
5
5
|
type IncomingMessage,
|
|
@@ -74,9 +74,15 @@ export async function startRpcServer(
|
|
|
74
74
|
unknown
|
|
75
75
|
>
|
|
76
76
|
if (method === 'pending-notifications') {
|
|
77
|
+
if (
|
|
78
|
+
typeof params.sessionId !== 'string' ||
|
|
79
|
+
params.sessionId.length === 0
|
|
80
|
+
) {
|
|
81
|
+
return json(400, { error: 'sessionId is required' })
|
|
82
|
+
}
|
|
77
83
|
const messages = options.drain(
|
|
78
84
|
Number(params.lastReceivedId ?? 0),
|
|
79
|
-
|
|
85
|
+
params.sessionId,
|
|
80
86
|
)
|
|
81
87
|
return json(200, { messages })
|
|
82
88
|
}
|
|
@@ -115,9 +121,16 @@ export async function startRpcServer(
|
|
|
115
121
|
token,
|
|
116
122
|
async stop() {
|
|
117
123
|
await new Promise<void>((resolve) => server.close(() => resolve()))
|
|
118
|
-
|
|
119
|
-
(
|
|
120
|
-
|
|
124
|
+
try {
|
|
125
|
+
const portFile = join(options.dir, `port-${process.pid}.json`)
|
|
126
|
+
const current = JSON.parse(await readFile(portFile, 'utf8')) as {
|
|
127
|
+
port?: unknown
|
|
128
|
+
pid?: unknown
|
|
129
|
+
}
|
|
130
|
+
if (current.port === port && current.pid === process.pid) {
|
|
131
|
+
await unlink(portFile)
|
|
132
|
+
}
|
|
133
|
+
} catch {}
|
|
121
134
|
},
|
|
122
135
|
}
|
|
123
136
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { RpcServerHandle } from './rpc-server'
|
|
2
|
+
|
|
3
|
+
type RpcServerGlobal = typeof globalThis & {
|
|
4
|
+
__anthropicAuthRpcServers?: Map<string, RpcServerHandle>
|
|
5
|
+
__anthropicAuthRpcServersPending?: Map<string, Promise<void>>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface RpcServerAdoption {
|
|
9
|
+
server: RpcServerHandle
|
|
10
|
+
release: () => Promise<void>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Serializes same-directory replacement and returns an identity-fenced lease.
|
|
15
|
+
* Distinct project directories remain independent.
|
|
16
|
+
*/
|
|
17
|
+
export async function adoptRpcServer(
|
|
18
|
+
rpcDir: string,
|
|
19
|
+
create: () => Promise<RpcServerHandle>,
|
|
20
|
+
): Promise<RpcServerAdoption> {
|
|
21
|
+
const rpcGlobal = globalThis as RpcServerGlobal
|
|
22
|
+
const servers =
|
|
23
|
+
rpcGlobal.__anthropicAuthRpcServers ?? new Map<string, RpcServerHandle>()
|
|
24
|
+
const pending =
|
|
25
|
+
rpcGlobal.__anthropicAuthRpcServersPending ??
|
|
26
|
+
new Map<string, Promise<void>>()
|
|
27
|
+
rpcGlobal.__anthropicAuthRpcServers = servers
|
|
28
|
+
rpcGlobal.__anthropicAuthRpcServersPending = pending
|
|
29
|
+
|
|
30
|
+
const predecessor = pending.get(rpcDir) ?? Promise.resolve()
|
|
31
|
+
let server: RpcServerHandle | undefined
|
|
32
|
+
const start = predecessor
|
|
33
|
+
.catch(() => {})
|
|
34
|
+
.then(async () => {
|
|
35
|
+
const previous = servers.get(rpcDir)
|
|
36
|
+
if (previous) {
|
|
37
|
+
await previous.stop()
|
|
38
|
+
if (servers.get(rpcDir) === previous) servers.delete(rpcDir)
|
|
39
|
+
}
|
|
40
|
+
server = await create()
|
|
41
|
+
servers.set(rpcDir, server)
|
|
42
|
+
})
|
|
43
|
+
pending.set(rpcDir, start)
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await start
|
|
47
|
+
} finally {
|
|
48
|
+
if (pending.get(rpcDir) === start) pending.delete(rpcDir)
|
|
49
|
+
}
|
|
50
|
+
const adoptedServer = server
|
|
51
|
+
if (!adoptedServer) throw new Error('RPC server failed to start')
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
server: adoptedServer,
|
|
55
|
+
release: async () => {
|
|
56
|
+
if (servers.get(rpcDir) !== adoptedServer) return
|
|
57
|
+
await adoptedServer.stop()
|
|
58
|
+
if (servers.get(rpcDir) === adoptedServer) servers.delete(rpcDir)
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/sidebar-state.ts
CHANGED
|
@@ -34,6 +34,18 @@ export interface SidebarAccountState {
|
|
|
34
34
|
// True when the account's refresh token is permanently dead (400
|
|
35
35
|
// invalid_grant) and it needs a re-login — distinct from a transient backoff.
|
|
36
36
|
needsReauth: boolean
|
|
37
|
+
// True when the vault copy needs re-importing while the sidecar remains usable.
|
|
38
|
+
vaultReauth?: boolean
|
|
39
|
+
// A gate alone does not prove the sidecar can serve a request.
|
|
40
|
+
vaultServed?: boolean
|
|
41
|
+
custodyState?:
|
|
42
|
+
| 'off'
|
|
43
|
+
| 'on-vault-served'
|
|
44
|
+
| 'on-vault-reauth'
|
|
45
|
+
| 'on-cold'
|
|
46
|
+
| 'unknown-identity'
|
|
47
|
+
| 'on-identity-mismatch'
|
|
48
|
+
| 'on-corrupt-binding'
|
|
37
49
|
tierLabel?: string
|
|
38
50
|
}
|
|
39
51
|
|
|
@@ -57,7 +69,7 @@ export interface PrimeSidebarAccountState {
|
|
|
57
69
|
label: string
|
|
58
70
|
nextDueAt?: number | null
|
|
59
71
|
lastPrimedAt?: number | null
|
|
60
|
-
lastResult?: 'ok' | 'error'
|
|
72
|
+
lastResult?: 'ok' | 'error' | 'skipped'
|
|
61
73
|
usage?: PrimeUsageCounters
|
|
62
74
|
estimatedCostUsd?: number
|
|
63
75
|
}
|
|
@@ -178,7 +190,11 @@ function normalizePrimeAccount(
|
|
|
178
190
|
} else if (isFiniteNumber(value.lastPrimedAt)) {
|
|
179
191
|
account.lastPrimedAt = value.lastPrimedAt
|
|
180
192
|
}
|
|
181
|
-
if (
|
|
193
|
+
if (
|
|
194
|
+
value.lastResult === 'ok' ||
|
|
195
|
+
value.lastResult === 'error' ||
|
|
196
|
+
value.lastResult === 'skipped'
|
|
197
|
+
) {
|
|
182
198
|
account.lastResult = value.lastResult
|
|
183
199
|
}
|
|
184
200
|
const usage = normalizePrimeUsage(value.usage)
|
|
@@ -335,6 +351,12 @@ export function normalizeSidebarState(raw: unknown): SidebarState {
|
|
|
335
351
|
enabled: typeof entry.enabled === 'boolean' ? entry.enabled : false,
|
|
336
352
|
needsReauth:
|
|
337
353
|
typeof entry.needsReauth === 'boolean' ? entry.needsReauth : false,
|
|
354
|
+
...(entry.vaultReauth === true && { vaultReauth: true }),
|
|
355
|
+
...(entry.vaultServed === true && { vaultServed: true }),
|
|
356
|
+
...(typeof entry.custodyState === 'string' && {
|
|
357
|
+
custodyState:
|
|
358
|
+
entry.custodyState as SidebarAccountState['custodyState'],
|
|
359
|
+
}),
|
|
338
360
|
tierLabel:
|
|
339
361
|
typeof entry.tierLabel === 'string' && entry.tierLabel.trim()
|
|
340
362
|
? entry.tierLabel.trim()
|
|
@@ -715,6 +737,8 @@ export async function setSidebarState(
|
|
|
715
737
|
lastUpdated: Math.max(current.lastUpdated, state.lastUpdated),
|
|
716
738
|
}
|
|
717
739
|
}
|
|
740
|
+
// The state file is a cross-process artifact; future account fields must fail closed.
|
|
741
|
+
stateToWrite = normalizeSidebarState(stateToWrite)
|
|
718
742
|
result = await writeSidebarStateAtomic(
|
|
719
743
|
stateFile,
|
|
720
744
|
stateToWrite,
|
|
@@ -795,13 +819,25 @@ export function formatScopedQuotaLabel(title: string) {
|
|
|
795
819
|
}
|
|
796
820
|
|
|
797
821
|
/**
|
|
798
|
-
* Pretty label for a recoverable-refusal source model (Fable 5 or Opus 5).
|
|
822
|
+
* Pretty label for a recoverable-refusal source model (Fable 5/5.1 or Opus 5).
|
|
799
823
|
* Falls back to the raw id for unrecognized ids so the sidebar never goes
|
|
800
824
|
* blank on a future model that has not yet been cataloged here.
|
|
801
825
|
*/
|
|
802
826
|
export function formatFallbackModelLabel(modelId: string | undefined): string {
|
|
827
|
+
if (
|
|
828
|
+
modelId === 'claude-mythos-5-1' ||
|
|
829
|
+
modelId?.startsWith('claude-mythos-5-1-')
|
|
830
|
+
)
|
|
831
|
+
return 'Mythos 5.1'
|
|
832
|
+
if (
|
|
833
|
+
modelId === 'claude-fable-5-1' ||
|
|
834
|
+
modelId?.startsWith('claude-fable-5-1-')
|
|
835
|
+
)
|
|
836
|
+
return 'Fable 5.1'
|
|
803
837
|
if (modelId === 'claude-fable-5' || modelId?.startsWith('claude-fable-5-'))
|
|
804
838
|
return 'Fable 5'
|
|
839
|
+
if (modelId === 'claude-opus-5-5' || modelId?.startsWith('claude-opus-5-5-'))
|
|
840
|
+
return 'Opus 5.5'
|
|
805
841
|
if (modelId === 'claude-opus-5' || modelId?.startsWith('claude-opus-5-'))
|
|
806
842
|
return 'Opus 5'
|
|
807
843
|
if (modelId === 'claude-opus-4-8' || modelId?.startsWith('claude-opus-4-8-'))
|
|
@@ -829,6 +865,9 @@ export function formatPrimeAccountValue(account: PrimeSidebarAccountState): {
|
|
|
829
865
|
if (account.lastResult === 'error') {
|
|
830
866
|
return { text: 'err', hasError: true }
|
|
831
867
|
}
|
|
868
|
+
if (account.lastResult === 'skipped') {
|
|
869
|
+
return { text: 'skip', hasError: false }
|
|
870
|
+
}
|
|
832
871
|
if (account.nextDueAt && account.nextDueAt > Date.now()) {
|
|
833
872
|
return { text: formatPrimeTime(account.nextDueAt), hasError: false }
|
|
834
873
|
}
|