@cortexkit/opencode-openai-auth 0.7.2 → 0.9.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 +9 -5
- package/dist/index-301y4qa1.js +2 -0
- package/dist/index-338z7171.js +211 -0
- package/dist/index-5y4vzdf8.js +2 -0
- package/dist/{index-zxqb5ydj.js → index-9ehhb051.js} +1 -1
- package/dist/{index-zg1x8sty.js → index-jwn4r3sw.js} +1 -1
- package/dist/{index-9jpc8yct.js → index-sdt2d2ra.js} +1 -1
- package/dist/index-w42s0g6m.js +2 -0
- package/dist/index.d.ts +213 -36
- package/dist/index.js +59 -154
- package/dist/internal-Y7QNVDJT.d.ts +908 -0
- package/dist/quota-normalize-3qffywcm.js +1 -0
- package/dist/rpc/port-file.d.ts +11 -9
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +2 -21
- package/dist/rpc/rpc-client.d.ts +7 -4
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.d.ts +7 -5
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/sidebar-state-B98sERYt.d.ts +219 -0
- package/dist/sidebar-state.d.ts +2 -171
- package/dist/sidebar-state.js +1 -1
- package/dist/{token-bvp0y166.js → token-9823y0pc.js} +1 -1
- package/dist/token-util-ypgz7rge.js +1 -0
- package/dist/tui-preferences.d.ts +27 -26
- package/dist/tui-preferences.js +1 -1
- package/dist/tui.d.ts +42 -19
- package/package.json +8 -14
- package/src/tui/entry.mjs +2 -2
- package/src/tui-compiled/{core/account-paths.ts → raw/core-account-paths.ts} +29 -18
- package/src/tui-compiled/raw/core-custody-state.ts +243 -0
- package/src/tui-compiled/raw/logger.ts +53 -0
- package/src/tui-compiled/{rpc/port-file.ts → raw/rpc-port-file.ts} +1 -1
- package/src/tui-compiled/raw/rpc-protocol.ts +12 -0
- package/src/{rpc/rpc-client.ts → tui-compiled/raw/rpc-rpc-client.ts} +2 -2
- package/src/{sidebar-state.ts → tui-compiled/raw/sidebar-state.ts} +142 -13
- package/src/{tui/command-dialogs.tsx → tui-compiled/raw/tui-command-dialogs.tsx} +94 -44
- package/src/{tui.tsx → tui-compiled/raw/tui.tsx} +149 -28
- package/src/{core/account-paths.ts → tui-compiled/runtime/core-account-paths.ts} +29 -18
- package/src/tui-compiled/runtime/core-custody-state.ts +243 -0
- package/src/tui-compiled/runtime/logger.ts +53 -0
- package/src/{rpc/port-file.ts → tui-compiled/runtime/rpc-port-file.ts} +1 -1
- package/src/tui-compiled/runtime/rpc-protocol.ts +12 -0
- package/src/tui-compiled/{rpc/rpc-client.ts → runtime/rpc-rpc-client.ts} +2 -2
- package/src/tui-compiled/{sidebar-state.ts → runtime/sidebar-state.ts} +142 -13
- package/src/tui-compiled/{tui/command-dialogs.tsx → runtime/tui-command-dialogs.tsx} +73 -37
- package/src/tui-compiled/{tui.tsx → runtime/tui.tsx} +251 -153
- package/src/tui-compiled/shared/internal.ts +6 -0
- package/src/{logger.ts → tui-compiled/shared/logger.ts} +91 -23
- package/src/tui-compiled/shared/paths.ts +33 -0
- package/src/{core → tui-compiled/shared}/refresh-file-lock.ts +7 -4
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -15
- package/dist/codex-http.d.ts +0 -3
- package/dist/commands.d.ts +0 -88
- package/dist/config.d.ts +0 -47
- package/dist/core/account-paths.d.ts +0 -25
- package/dist/core/accounts.d.ts +0 -330
- package/dist/core/atomic-write.d.ts +0 -1
- package/dist/core/background-quota-refresh.d.ts +0 -46
- package/dist/core/backoff.d.ts +0 -53
- package/dist/core/cachekeep.d.ts +0 -118
- package/dist/core/oauth.d.ts +0 -138
- package/dist/core/provider.d.ts +0 -70
- package/dist/core/quota-manager.d.ts +0 -192
- package/dist/core/refresh-all-quota.d.ts +0 -86
- package/dist/core/refresh-file-lock.d.ts +0 -12
- package/dist/core/reset-credits.d.ts +0 -116
- package/dist/core/sticky-routing.d.ts +0 -43
- package/dist/dump.d.ts +0 -20
- package/dist/hosted-web-search.d.ts +0 -5
- package/dist/index-87145jkw.js +0 -2
- package/dist/index-8f1200qx.js +0 -6
- package/dist/index-cfjed1st.js +0 -89
- package/dist/index-nrvjr97q.js +0 -3
- package/dist/index-qaggaze1.js +0 -2
- package/dist/index-sdcpzbnk.js +0 -2
- package/dist/logger.d.ts +0 -22
- package/dist/model-costs.d.ts +0 -29
- package/dist/prompt-context.d.ts +0 -19
- package/dist/quota-normalize-dvwtmy7f.js +0 -1
- package/dist/quota-normalize.d.ts +0 -43
- package/dist/raw-ws-bun.d.ts +0 -40
- package/dist/raw-ws-node.d.ts +0 -37
- package/dist/raw-ws-upgrade.d.ts +0 -2
- package/dist/raw-ws.d.ts +0 -3
- package/dist/response-stream-error.d.ts +0 -12
- package/dist/rpc/notifications.d.ts +0 -5
- package/dist/rpc/rpc-server.d.ts +0 -17
- package/dist/token-util-jcj7k7pa.js +0 -1
- package/dist/tui/command-dialogs.d.ts +0 -21
- package/dist/util/error.d.ts +0 -1
- package/dist/util/open-url.d.ts +0 -6
- package/dist/util/proxy-env.d.ts +0 -4
- package/dist/util/record.d.ts +0 -1
- package/dist/util/stable-json.d.ts +0 -1
- package/dist/util/uuid-v7.d.ts +0 -1
- package/dist/version.d.ts +0 -1
- package/dist/ws-pool.d.ts +0 -57
- package/dist/ws.d.ts +0 -71
- package/src/rpc/notifications.ts +0 -54
- package/src/rpc/protocol.ts +0 -33
- package/src/rpc/rpc-server.ts +0 -172
- package/src/tui-compiled/core/refresh-file-lock.ts +0 -381
- package/src/tui-compiled/logger.ts +0 -196
- package/src/tui-compiled/rpc/protocol.ts +0 -33
- package/src/util/error.ts +0 -9
- package/src/util/open-url.ts +0 -30
- package/src/util/proxy-env.ts +0 -42
- package/src/util/record.ts +0 -3
- package/src/util/stable-json.ts +0 -15
- package/src/util/uuid-v7.ts +0 -18
- /package/src/{rpc/rpc-dir.ts → tui-compiled/raw/rpc-rpc-dir.ts} +0 -0
- /package/src/tui-compiled/{tui-preferences.ts → raw/tui-preferences.ts} +0 -0
- /package/src/tui-compiled/{rpc/rpc-dir.ts → runtime/rpc-rpc-dir.ts} +0 -0
- /package/src/{tui-preferences.ts → tui-compiled/runtime/tui-preferences.ts} +0 -0
- /package/src/tui-compiled/{util → shared/util}/error.ts +0 -0
- /package/src/tui-compiled/{util → shared/util}/open-url.ts +0 -0
package/dist/tui.d.ts
CHANGED
|
@@ -1,35 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { a as CommandModalName, r as ApplyRequest } from "./internal-Y7QNVDJT.js";
|
|
2
|
+
import { b as SidebarState, c as QuotaPacing, l as QuotaWindow, r as AccountQuota, x as SpendControlReading } from "./sidebar-state-B98sERYt.js";
|
|
3
|
+
import "./rpc/protocol.js";
|
|
4
|
+
import { TuiPluginModule } from "@opencode-ai/plugin/tui";
|
|
5
|
+
//#region src/tui.d.ts
|
|
5
6
|
export declare function buildApplyRequest(command: CommandModalName, arguments_: string, sessionId?: string): ApplyRequest;
|
|
6
7
|
type Tone = 'ok' | 'warn' | 'err' | 'muted' | 'accent' | 'text';
|
|
7
8
|
export declare function formatResetIn(resetsAt: string | undefined): string;
|
|
8
9
|
export interface QuotaDisplayRow {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
key: 'primary' | 'secondary' | 'spendControl';
|
|
11
|
+
label: string;
|
|
12
|
+
labelWidth: number;
|
|
13
|
+
window: QuotaWindow;
|
|
14
|
+
pacing: QuotaPacing | null;
|
|
13
15
|
}
|
|
14
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Every quota the sidebar will draw, in one list.
|
|
18
|
+
*
|
|
19
|
+
* The label column is shared by all of them, so the width has to be measured
|
|
20
|
+
* over this exact set — measuring a subset is what made stacked accounts
|
|
21
|
+
* disagree about where the bar starts. Named rather than inlined at each call
|
|
22
|
+
* site because the set is the part that can be got wrong, and a call site
|
|
23
|
+
* cannot be tested without the host's rendering runtime.
|
|
24
|
+
*/
|
|
25
|
+
export declare function renderedQuotas(state: {
|
|
26
|
+
main?: {
|
|
27
|
+
quota?: AccountQuota | null;
|
|
28
|
+
} | null;
|
|
29
|
+
fallbacks?: ReadonlyArray<{
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
quota?: AccountQuota | null;
|
|
32
|
+
}>;
|
|
33
|
+
}): ReadonlyArray<AccountQuota | null | undefined>;
|
|
34
|
+
export declare function computeQuotaLabelWidth(quotas: ReadonlyArray<AccountQuota | null | undefined>): number;
|
|
35
|
+
export declare function buildQuotaRowsForDisplay(quota: AccountQuota | null, now: number, pacingEnabled: boolean, labelWidth?: number): QuotaDisplayRow[];
|
|
15
36
|
export declare function isQuotaLoaded(quota: AccountQuota | null): boolean;
|
|
16
37
|
export declare function getQuotaMetadataRows(state: SidebarState): Array<{
|
|
17
|
-
|
|
18
|
-
|
|
38
|
+
label: string;
|
|
39
|
+
value: string;
|
|
19
40
|
}>;
|
|
20
|
-
export declare function
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
export declare function formatSpendControlAmounts(spendControl: SpendControlReading): string;
|
|
42
|
+
export declare function getAccountMetadataRows(resetCredits: number | undefined, spendControl?: SpendControlReading): Array<{
|
|
43
|
+
label: string;
|
|
44
|
+
value: string;
|
|
23
45
|
}>;
|
|
24
46
|
export declare function readStateFromFile(): Promise<SidebarState>;
|
|
25
47
|
export declare function resolveQuotaDialogActiveId(state: SidebarState, sessionId: string | undefined, now?: number): string | undefined;
|
|
26
48
|
export interface RoutingDisplayRow {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
49
|
+
label: string;
|
|
50
|
+
value: string;
|
|
51
|
+
tone: Tone;
|
|
30
52
|
}
|
|
31
53
|
export declare function buildRoutingRowsForDisplay(state: SidebarState, sessionId: string | undefined, now?: number): RoutingDisplayRow[];
|
|
32
54
|
declare const plugin: TuiPluginModule & {
|
|
33
|
-
|
|
55
|
+
id: string;
|
|
34
56
|
};
|
|
35
|
-
|
|
57
|
+
//#endregion
|
|
58
|
+
export { plugin as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-openai-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -10,9 +10,6 @@
|
|
|
10
10
|
},
|
|
11
11
|
"main": "./dist/index.js",
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
|
-
"bin": {
|
|
14
|
-
"openai-auth": "./dist/cli.js"
|
|
15
|
-
},
|
|
16
13
|
"exports": {
|
|
17
14
|
".": {
|
|
18
15
|
"import": "./dist/index.js",
|
|
@@ -36,22 +33,15 @@
|
|
|
36
33
|
},
|
|
37
34
|
"files": [
|
|
38
35
|
"dist",
|
|
39
|
-
"src/tui.
|
|
40
|
-
"src/tui",
|
|
36
|
+
"src/tui/entry.mjs",
|
|
41
37
|
"src/tui-compiled",
|
|
42
|
-
"src/sidebar-state.ts",
|
|
43
|
-
"src/tui-preferences.ts",
|
|
44
|
-
"src/logger.ts",
|
|
45
|
-
"src/core/account-paths.ts",
|
|
46
|
-
"src/core/refresh-file-lock.ts",
|
|
47
|
-
"src/util",
|
|
48
|
-
"src/rpc",
|
|
49
38
|
"README.md",
|
|
50
39
|
"LICENSE"
|
|
51
40
|
],
|
|
52
41
|
"scripts": {
|
|
53
|
-
"build": "rm -rf dist && bun build src/index.ts src/
|
|
42
|
+
"build": "rm -rf dist && bun build src/index.ts src/sidebar-state.ts src/tui-preferences.ts src/rpc/rpc-client.ts src/rpc/port-file.ts src/rpc/protocol.ts src/rpc/rpc-dir.ts --outdir dist --target node --format esm --splitting --external @opencode-ai/plugin --minify && bun run build:types && bun run build:tui",
|
|
54
43
|
"build:tui": "bun scripts/build-tui.ts",
|
|
44
|
+
"build:types": "cd ../core && bun run build && cd ../opencode && tsdown --config tsdown.config.ts",
|
|
55
45
|
"build:dev": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
56
46
|
"test": "bun test src/tests",
|
|
57
47
|
"typecheck": "tsc",
|
|
@@ -61,7 +51,11 @@
|
|
|
61
51
|
"peerDependencies": {
|
|
62
52
|
"@opencode-ai/plugin": "*"
|
|
63
53
|
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@cortexkit/openai-auth-core": "workspace:*"
|
|
56
|
+
},
|
|
64
57
|
"dependencies": {
|
|
58
|
+
"@cortexkit/subc-client": "^0.8.1",
|
|
65
59
|
"@opentui/core": ">=0.5.11",
|
|
66
60
|
"@opentui/solid": ">=0.5.11",
|
|
67
61
|
"ai": "^7.0.99",
|
package/src/tui/entry.mjs
CHANGED
|
@@ -22,12 +22,12 @@ try {
|
|
|
22
22
|
}
|
|
23
23
|
// Older hosts and bare Bun do not provide the virtual registry. Their source
|
|
24
24
|
// loader still applies the Solid transform, so retain the raw TSX fallback.
|
|
25
|
-
mod = await import('../tui.tsx')
|
|
25
|
+
mod = await import('../tui-compiled/raw/tui.tsx')
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
if (!mod) {
|
|
29
29
|
try {
|
|
30
|
-
mod = await import('../tui-compiled/tui.tsx')
|
|
30
|
+
mod = await import('../tui-compiled/runtime/tui.tsx')
|
|
31
31
|
} catch (error) {
|
|
32
32
|
console.error('OpenAI Auth compiled TUI failed to load', error)
|
|
33
33
|
throw error
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import { realpathSync } from 'node:fs'
|
|
2
2
|
import { homedir } from 'node:os'
|
|
3
3
|
import { basename, dirname, join, resolve } from 'node:path'
|
|
4
|
+
import {
|
|
5
|
+
ACCOUNT_FILE_NAME,
|
|
6
|
+
ACCOUNT_STATE_FILE_NAME,
|
|
7
|
+
type AccountPaths,
|
|
8
|
+
deriveStatePath,
|
|
9
|
+
} from '../shared/internal.ts'
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
|
-
* Where
|
|
12
|
+
* Where OpenCode's account store lives.
|
|
7
13
|
*
|
|
8
|
-
* This is the ONLY definition
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* path while writing another — mutual exclusion silently lost on a file holding
|
|
14
|
-
* credentials. Nothing would fail a test, since each copy works on its own.
|
|
14
|
+
* This is the ONLY definition of this host's resolution rules. The store code
|
|
15
|
+
* itself takes both paths as arguments and resolves nothing, so the lock and
|
|
16
|
+
* the write it guards can never disagree about which file they mean — a
|
|
17
|
+
* disagreement would lose mutual exclusion on a file holding credentials while
|
|
18
|
+
* every test still passed, since each side works on its own.
|
|
15
19
|
*
|
|
16
|
-
*
|
|
20
|
+
* The file names come from the core so the two hosts cannot drift apart on what
|
|
21
|
+
* the store is called.
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
|
-
export
|
|
20
|
-
export
|
|
24
|
+
export type { AccountPaths }
|
|
25
|
+
export { ACCOUNT_FILE_NAME, ACCOUNT_STATE_FILE_NAME, deriveStatePath }
|
|
21
26
|
|
|
22
27
|
function getConfigDir() {
|
|
23
28
|
if (process.env.OPENCODE_CONFIG_DIR?.trim()) {
|
|
@@ -36,13 +41,6 @@ export function getAccountStoragePath() {
|
|
|
36
41
|
)
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
/** Derive the state-file path from the config path without reading env vars. */
|
|
40
|
-
export function deriveStatePath(configPath: string): string {
|
|
41
|
-
return basename(configPath) === ACCOUNT_FILE_NAME
|
|
42
|
-
? join(dirname(configPath), ACCOUNT_STATE_FILE_NAME)
|
|
43
|
-
: `${configPath}.state.json`
|
|
44
|
-
}
|
|
45
|
-
|
|
46
44
|
function normalizePathForComparison(path: string, platform: NodeJS.Platform) {
|
|
47
45
|
const resolved = resolve(path)
|
|
48
46
|
return platform === 'win32' || platform === 'darwin'
|
|
@@ -101,3 +99,16 @@ export function getAccountStatePath(configPath = getAccountStoragePath()) {
|
|
|
101
99
|
}
|
|
102
100
|
return deriveStatePath(configPath)
|
|
103
101
|
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Both file paths a store call needs, resolved the way this host resolves them.
|
|
105
|
+
*
|
|
106
|
+
* Every call site that used to pass a single config path and let the store
|
|
107
|
+
* derive the state path now passes this pair instead, so the state-file
|
|
108
|
+
* override keeps being honoured in exactly the places it was before.
|
|
109
|
+
*/
|
|
110
|
+
export function getAccountPaths(
|
|
111
|
+
configPath = getAccountStoragePath(),
|
|
112
|
+
): AccountPaths {
|
|
113
|
+
return { configPath, statePath: getAccountStatePath(configPath) }
|
|
114
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
export type CustodyMode = 'local' | 'claustrum'
|
|
2
|
+
|
|
3
|
+
export type ManifestCustodyState = 'absent' | 'present' | 'unreadable'
|
|
4
|
+
|
|
5
|
+
export type LocalCustodyState =
|
|
6
|
+
| 'real'
|
|
7
|
+
| 'tombstone'
|
|
8
|
+
| 'empty'
|
|
9
|
+
| 'gone'
|
|
10
|
+
| 'slot-absent'
|
|
11
|
+
|
|
12
|
+
export type VaultCustodyState =
|
|
13
|
+
| 'serves'
|
|
14
|
+
| 'cold'
|
|
15
|
+
| 'needs_reauth'
|
|
16
|
+
| 'identity_mismatch'
|
|
17
|
+
| 'no_handle'
|
|
18
|
+
|
|
19
|
+
export const CUSTODY_INERT_REASONS = [
|
|
20
|
+
'needs-login',
|
|
21
|
+
'mode-mismatch',
|
|
22
|
+
'corrupt-under-binding',
|
|
23
|
+
'manifest-unreadable',
|
|
24
|
+
'unbound-under-claustrum',
|
|
25
|
+
'orphan-tombstone',
|
|
26
|
+
'vault-cold',
|
|
27
|
+
'vault-reauth',
|
|
28
|
+
'identity-mismatch',
|
|
29
|
+
'no-handle',
|
|
30
|
+
'takeover-incomplete',
|
|
31
|
+
'new-local-family-under-claustrum',
|
|
32
|
+
'takeover-incomplete/vault-unavailable',
|
|
33
|
+
'takeover-incomplete/slot-absent',
|
|
34
|
+
] as const
|
|
35
|
+
|
|
36
|
+
export type CustodyInertReason = (typeof CUSTODY_INERT_REASONS)[number]
|
|
37
|
+
|
|
38
|
+
export type CustodyVerdict =
|
|
39
|
+
| { kind: 'LOCAL' }
|
|
40
|
+
| {
|
|
41
|
+
kind: 'VAULT'
|
|
42
|
+
canonicalize?: boolean
|
|
43
|
+
installTombstone?: boolean
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
kind: 'INERT'
|
|
47
|
+
reason: CustodyInertReason
|
|
48
|
+
installTombstone?: boolean
|
|
49
|
+
canonicalize?: boolean
|
|
50
|
+
}
|
|
51
|
+
| { kind: 'NEEDS_LOGIN'; reason?: 'corrupt' }
|
|
52
|
+
|
|
53
|
+
export interface EvaluateCustodyStartupInput {
|
|
54
|
+
mode: CustodyMode
|
|
55
|
+
manifest: ManifestCustodyState
|
|
56
|
+
local: LocalCustodyState
|
|
57
|
+
vault: () => VaultCustodyState
|
|
58
|
+
manifestFailureReason?: string
|
|
59
|
+
fingerprintMatch?: boolean
|
|
60
|
+
verifiedInProcessLogin: boolean
|
|
61
|
+
isMain?: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function unreachable(value: never): never {
|
|
65
|
+
return value
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function withCanonicalization<
|
|
69
|
+
T extends Extract<CustodyVerdict, { kind: 'VAULT' | 'INERT' }>,
|
|
70
|
+
>(local: LocalCustodyState, verdict: T): T | (T & { canonicalize: true }) {
|
|
71
|
+
return local === 'empty' ? { ...verdict, canonicalize: true } : verdict
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function evaluateLocal(local: LocalCustodyState): CustodyVerdict {
|
|
75
|
+
switch (local) {
|
|
76
|
+
case 'real':
|
|
77
|
+
return { kind: 'LOCAL' }
|
|
78
|
+
case 'tombstone':
|
|
79
|
+
case 'empty':
|
|
80
|
+
case 'slot-absent':
|
|
81
|
+
return { kind: 'NEEDS_LOGIN' }
|
|
82
|
+
case 'gone':
|
|
83
|
+
return { kind: 'NEEDS_LOGIN', reason: 'corrupt' }
|
|
84
|
+
}
|
|
85
|
+
return unreachable(local)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function evaluateLocalWithBinding(
|
|
89
|
+
local: LocalCustodyState,
|
|
90
|
+
verifiedInProcessLogin: boolean,
|
|
91
|
+
): CustodyVerdict {
|
|
92
|
+
switch (local) {
|
|
93
|
+
case 'real':
|
|
94
|
+
return verifiedInProcessLogin
|
|
95
|
+
? { kind: 'LOCAL' }
|
|
96
|
+
: { kind: 'INERT', reason: 'needs-login' }
|
|
97
|
+
case 'tombstone':
|
|
98
|
+
case 'empty':
|
|
99
|
+
case 'slot-absent':
|
|
100
|
+
return { kind: 'INERT', reason: 'mode-mismatch' }
|
|
101
|
+
case 'gone':
|
|
102
|
+
return { kind: 'INERT', reason: 'corrupt-under-binding' }
|
|
103
|
+
}
|
|
104
|
+
return unreachable(local)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function evaluateUnboundClaustrum(local: LocalCustodyState): CustodyVerdict {
|
|
108
|
+
switch (local) {
|
|
109
|
+
case 'tombstone':
|
|
110
|
+
case 'empty':
|
|
111
|
+
return { kind: 'INERT', reason: 'orphan-tombstone' }
|
|
112
|
+
case 'real':
|
|
113
|
+
case 'gone':
|
|
114
|
+
case 'slot-absent':
|
|
115
|
+
return { kind: 'INERT', reason: 'unbound-under-claustrum' }
|
|
116
|
+
}
|
|
117
|
+
return unreachable(local)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function evaluateTombstone(
|
|
121
|
+
local: Extract<LocalCustodyState, 'tombstone' | 'empty'>,
|
|
122
|
+
vault: VaultCustodyState,
|
|
123
|
+
): CustodyVerdict {
|
|
124
|
+
switch (vault) {
|
|
125
|
+
case 'serves':
|
|
126
|
+
return withCanonicalization(local, { kind: 'VAULT' })
|
|
127
|
+
case 'cold':
|
|
128
|
+
return withCanonicalization(local, {
|
|
129
|
+
kind: 'INERT',
|
|
130
|
+
reason: 'vault-cold',
|
|
131
|
+
})
|
|
132
|
+
case 'needs_reauth':
|
|
133
|
+
return withCanonicalization(local, {
|
|
134
|
+
kind: 'INERT',
|
|
135
|
+
reason: 'vault-reauth',
|
|
136
|
+
})
|
|
137
|
+
case 'identity_mismatch':
|
|
138
|
+
return withCanonicalization(local, {
|
|
139
|
+
kind: 'INERT',
|
|
140
|
+
reason: 'identity-mismatch',
|
|
141
|
+
})
|
|
142
|
+
case 'no_handle':
|
|
143
|
+
return withCanonicalization(local, { kind: 'INERT', reason: 'no-handle' })
|
|
144
|
+
}
|
|
145
|
+
return unreachable(vault)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function evaluateReal(
|
|
149
|
+
vault: VaultCustodyState,
|
|
150
|
+
fingerprintMatch: boolean | undefined,
|
|
151
|
+
): CustodyVerdict {
|
|
152
|
+
switch (vault) {
|
|
153
|
+
case 'serves':
|
|
154
|
+
return fingerprintMatch === false
|
|
155
|
+
? { kind: 'INERT', reason: 'new-local-family-under-claustrum' }
|
|
156
|
+
: { kind: 'INERT', reason: 'takeover-incomplete' }
|
|
157
|
+
case 'cold':
|
|
158
|
+
case 'needs_reauth':
|
|
159
|
+
return { kind: 'INERT', reason: 'takeover-incomplete/vault-unavailable' }
|
|
160
|
+
case 'identity_mismatch':
|
|
161
|
+
return { kind: 'INERT', reason: 'identity-mismatch' }
|
|
162
|
+
case 'no_handle':
|
|
163
|
+
return { kind: 'INERT', reason: 'no-handle' }
|
|
164
|
+
}
|
|
165
|
+
return unreachable(vault)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function evaluateGone(vault: VaultCustodyState): CustodyVerdict {
|
|
169
|
+
switch (vault) {
|
|
170
|
+
case 'serves':
|
|
171
|
+
return { kind: 'VAULT', installTombstone: true }
|
|
172
|
+
case 'cold':
|
|
173
|
+
return { kind: 'INERT', reason: 'vault-cold', installTombstone: true }
|
|
174
|
+
case 'needs_reauth':
|
|
175
|
+
return { kind: 'INERT', reason: 'vault-reauth', installTombstone: true }
|
|
176
|
+
case 'identity_mismatch':
|
|
177
|
+
return { kind: 'INERT', reason: 'identity-mismatch' }
|
|
178
|
+
case 'no_handle':
|
|
179
|
+
return { kind: 'INERT', reason: 'no-handle' }
|
|
180
|
+
}
|
|
181
|
+
return unreachable(vault)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function evaluateSlotAbsent(vault: VaultCustodyState): CustodyVerdict {
|
|
185
|
+
switch (vault) {
|
|
186
|
+
case 'serves':
|
|
187
|
+
case 'cold':
|
|
188
|
+
case 'needs_reauth':
|
|
189
|
+
return { kind: 'INERT', reason: 'takeover-incomplete/slot-absent' }
|
|
190
|
+
case 'identity_mismatch':
|
|
191
|
+
return { kind: 'INERT', reason: 'identity-mismatch' }
|
|
192
|
+
case 'no_handle':
|
|
193
|
+
return { kind: 'INERT', reason: 'no-handle' }
|
|
194
|
+
}
|
|
195
|
+
return unreachable(vault)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function evaluatePresentClaustrum(
|
|
199
|
+
local: LocalCustodyState,
|
|
200
|
+
vault: VaultCustodyState,
|
|
201
|
+
fingerprintMatch: boolean | undefined,
|
|
202
|
+
): CustodyVerdict {
|
|
203
|
+
switch (local) {
|
|
204
|
+
case 'tombstone':
|
|
205
|
+
case 'empty':
|
|
206
|
+
return evaluateTombstone(local, vault)
|
|
207
|
+
case 'real':
|
|
208
|
+
return evaluateReal(vault, fingerprintMatch)
|
|
209
|
+
case 'gone':
|
|
210
|
+
return evaluateGone(vault)
|
|
211
|
+
case 'slot-absent':
|
|
212
|
+
return evaluateSlotAbsent(vault)
|
|
213
|
+
}
|
|
214
|
+
return unreachable(local)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function evaluateCustodyStartup(
|
|
218
|
+
input: EvaluateCustodyStartupInput,
|
|
219
|
+
): CustodyVerdict {
|
|
220
|
+
if (input.manifest === 'unreadable') {
|
|
221
|
+
return { kind: 'INERT', reason: 'manifest-unreadable' }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
switch (input.mode) {
|
|
225
|
+
case 'local':
|
|
226
|
+
return input.manifest === 'present'
|
|
227
|
+
? evaluateLocalWithBinding(input.local, input.verifiedInProcessLogin)
|
|
228
|
+
: evaluateLocal(input.local)
|
|
229
|
+
case 'claustrum':
|
|
230
|
+
if (input.manifest === 'absent') {
|
|
231
|
+
return evaluateUnboundClaustrum(input.local)
|
|
232
|
+
}
|
|
233
|
+
if (input.manifest === 'present') {
|
|
234
|
+
return evaluatePresentClaustrum(
|
|
235
|
+
input.local,
|
|
236
|
+
input.vault(),
|
|
237
|
+
input.fingerprintMatch,
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
return unreachable(input.manifest)
|
|
241
|
+
}
|
|
242
|
+
return unreachable(input.mode)
|
|
243
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode's log destination.
|
|
3
|
+
*
|
|
4
|
+
* The logger itself lives in the shared core so both hosts get the same
|
|
5
|
+
* redaction rules. What stays here is the part that is specific to this host:
|
|
6
|
+
* which file the lines go to, which level floor applies, and when the buffer is
|
|
7
|
+
* drained at process exit. Those are the only three things that read this
|
|
8
|
+
* host's environment variables, and the core reads none.
|
|
9
|
+
*
|
|
10
|
+
* The init runs at module load rather than from one call site because every
|
|
11
|
+
* entry point in this package — the plugin loader, the RPC server, the separate
|
|
12
|
+
* TUI process — reaches the logger through this module, and a core logger no
|
|
13
|
+
* host has initialised silently drops every line. Both values are passed as
|
|
14
|
+
* functions so the destination and level keep resolving on each write, which is
|
|
15
|
+
* what they do today.
|
|
16
|
+
*/
|
|
17
|
+
import { tmpdir } from 'node:os'
|
|
18
|
+
import { join } from 'node:path'
|
|
19
|
+
import {
|
|
20
|
+
flushLogs,
|
|
21
|
+
initLogger,
|
|
22
|
+
type Level,
|
|
23
|
+
} from '../shared/internal.ts'
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
createLogger,
|
|
27
|
+
flushForTest,
|
|
28
|
+
flushLogs,
|
|
29
|
+
type Level,
|
|
30
|
+
redact,
|
|
31
|
+
redactStrings,
|
|
32
|
+
setLogLevel,
|
|
33
|
+
} from '../shared/internal.ts'
|
|
34
|
+
|
|
35
|
+
const LEVELS: readonly Level[] = ['error', 'warn', 'info', 'debug', 'trace']
|
|
36
|
+
|
|
37
|
+
export function getLogFile(): string {
|
|
38
|
+
return (
|
|
39
|
+
process.env.OPENCODE_OPENAI_AUTH_LOG_FILE ??
|
|
40
|
+
join(tmpdir(), 'opencode-openai-auth.log')
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getEnvLogLevel(): Level | undefined {
|
|
45
|
+
const env = process.env.OPENCODE_OPENAI_AUTH_LOG_LEVEL as Level | undefined
|
|
46
|
+
return env && LEVELS.includes(env) ? env : undefined
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
initLogger({ file: getLogFile, level: getEnvLogLevel })
|
|
50
|
+
|
|
51
|
+
// Under `bun test` the buffer is drained explicitly by the tests that assert on
|
|
52
|
+
// it, and registering a process-wide exit handler per test file is noise.
|
|
53
|
+
if (process.env.NODE_ENV !== 'test') process.on('exit', flushLogs)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RPC payload types now live in the shared core, because both hosts build
|
|
3
|
+
* the same payloads. This file stays on disk as a re-export so the existing
|
|
4
|
+
* build entry and every importer that names `rpc/protocol` keep working.
|
|
5
|
+
*/
|
|
6
|
+
export type {
|
|
7
|
+
ApplyRequest,
|
|
8
|
+
ApplyResult,
|
|
9
|
+
CommandModalName,
|
|
10
|
+
OpenDialogPayload,
|
|
11
|
+
RpcNotification,
|
|
12
|
+
} from '../shared/internal.ts'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { discoverPortFile, type PortFileEntry } from './port-file'
|
|
2
|
-
import type { ApplyRequest, ApplyResult, RpcNotification } from './protocol'
|
|
1
|
+
import { discoverPortFile, type PortFileEntry } from './rpc-port-file.ts'
|
|
2
|
+
import type { ApplyRequest, ApplyResult, RpcNotification } from './rpc-protocol.ts'
|
|
3
3
|
|
|
4
4
|
export interface RpcClient {
|
|
5
5
|
pending: (
|