@cortexkit/opencode-openai-auth 0.11.0 → 1.0.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 +15 -26
- package/dist/index-766sfrh5.js +1 -0
- package/dist/index-8kj7sa1p.js +12 -0
- package/dist/index-8rqfbhfw.js +221 -0
- package/dist/index-b3q3j39x.js +2 -0
- package/dist/index-bmd7x5aq.js +2 -0
- package/dist/index-eqh53k8v.js +2 -0
- package/dist/{index-sdt2d2ra.js → index-hy9030jh.js} +1 -1
- package/dist/index-jyr8y8k4.js +7 -0
- package/dist/index-rv08jmd4.js +2 -0
- package/dist/index.d.ts +578 -145
- package/dist/index.js +1 -110
- package/dist/pool-lifecycle-DbDmgabd.d.ts +2430 -0
- package/dist/port-file-Do3lQIEk.d.ts +29 -0
- package/dist/protocol-BOBdkqqm.d.ts +198 -0
- package/dist/rpc/port-file.d.ts +4 -17
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +2 -2
- package/dist/rpc/protocol.js +1 -0
- package/dist/rpc/rpc-client.d.ts +5 -4
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.d.ts +2 -0
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/sidebar-state-D6cUKgkm.d.ts +385 -0
- package/dist/sidebar-state.d.ts +2 -2
- package/dist/sidebar-state.js +1 -1
- package/dist/token-util-wdza526f.js +1 -0
- package/dist/{token-9823y0pc.js → token-ypk61xj0.js} +1 -1
- package/dist/tui-preferences.js +5 -2
- package/dist/tui.d.ts +9 -4
- package/dist/v2/server.d.ts +54 -0
- package/dist/v2/server.js +4 -0
- package/package.json +21 -9
- package/src/tui/entry.mjs +26 -25
- package/src/tui-compiled/raw/logger.js +24 -0
- package/src/tui-compiled/raw/rpc/protocol.js +3 -0
- package/src/tui-compiled/raw/rpc/rpc-client.js +7 -0
- package/src/tui-compiled/raw/rpc/rpc-dir.js +27 -0
- package/src/tui-compiled/raw/selector.js +21 -0
- package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +138 -0
- package/src/tui-compiled/raw/shared/04a6357627de5ed0-errors.js +38 -0
- package/src/tui-compiled/raw/shared/053cc17b5419261e-codec.js +12 -0
- package/src/tui-compiled/raw/shared/0766d78c6f481f18-watcher.js +157 -0
- package/src/tui-compiled/raw/shared/08eef45626a94992-reserved.js +13 -0
- package/src/tui-compiled/raw/shared/39c3cb05738fcf6d-projection.js +134 -0
- package/src/tui-compiled/raw/shared/3c2d4169d00f907e-tui-support.js +5 -0
- package/src/tui-compiled/raw/shared/3e7d45a77c6ad4d5-index.js +4 -0
- package/src/tui-compiled/raw/shared/415792e85d80819e-capture-sink.js +13 -0
- package/src/tui-compiled/raw/shared/42ae93ab554fcec2-open-url.js +28 -0
- package/src/tui-compiled/raw/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
- package/src/tui-compiled/raw/shared/458b934e92490331-mutate.js +500 -0
- package/src/tui-compiled/raw/shared/45bf5343bbbf9add-admission.js +142 -0
- package/src/tui-compiled/raw/shared/46f3dff1266160e8-client.js +2 -0
- package/src/tui-compiled/raw/shared/4825e03209d6ae3e-with-lock.js +48 -0
- package/src/tui-compiled/raw/shared/4f36d333d081d040-merge.js +162 -0
- package/src/tui-compiled/raw/shared/54eba94fd64c8c3c-index.js +7 -0
- package/src/tui-compiled/raw/shared/7211cfec06a298bc-atomic-write.js +80 -0
- package/src/tui-compiled/raw/shared/754d6017f8e419cf-builtins.js +534 -0
- package/src/tui-compiled/raw/shared/75bd8c3bd63b315f-identity.js +79 -0
- package/src/tui-compiled/raw/shared/7a91bb630e6f4804-index.js +1 -0
- package/src/tui-compiled/raw/shared/826342a4b6a8b89b-model.js +16 -0
- package/src/tui-compiled/raw/shared/83068c0dbe8a6f13-publication.js +304 -0
- package/src/tui-compiled/raw/shared/836fc615de6afbd9-settings.js +120 -0
- package/src/tui-compiled/raw/shared/83bc3ba9d0fe2761-runtime.js +68 -0
- package/src/tui-compiled/raw/shared/8a18abe4fa2977c8-menu.js +300 -0
- package/src/tui-compiled/raw/shared/901e149c5a0dbbff-sticky.js +339 -0
- package/src/tui-compiled/raw/shared/9020c407ce64f672-ordered.js +58 -0
- package/src/tui-compiled/raw/shared/905bffc0220f43d0-map.js +105 -0
- package/src/tui-compiled/raw/shared/94820e324f2b4c09-seam.js +301 -0
- package/src/tui-compiled/raw/shared/97484d9bb98c705b-provider-state.js +226 -0
- package/src/tui-compiled/raw/shared/9968e3cade6969a1-index.js +2 -0
- package/src/tui-compiled/raw/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
- package/src/tui-compiled/raw/shared/9d3fc9e37456504e-refresh.js +225 -0
- package/src/tui-compiled/raw/shared/9deae3c4da4fd63b-hooks.js +34 -0
- package/src/tui-compiled/raw/shared/a09b6a533c11858f-tui-preferences.js +77 -0
- package/src/tui-compiled/raw/shared/a62a27a204266acd-attribution.js +60 -0
- package/src/tui-compiled/raw/shared/a910277ac43a8626-pull.js +143 -0
- package/src/tui-compiled/raw/shared/b85c5301fddcb98a-paths.js +5 -0
- package/src/tui-compiled/raw/shared/c14988621dc60a75-port-file.js +217 -0
- package/src/tui-compiled/raw/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
- package/src/tui-compiled/raw/shared/c6b176708c446d6d-pins.js +34 -0
- package/src/tui-compiled/raw/shared/c7d252bedee01600-pi.js +206 -0
- package/src/tui-compiled/raw/shared/c852a15cfe489d12-pool.js +120 -0
- package/src/tui-compiled/raw/shared/cc9e0702a752cea7-index.js +3 -0
- package/src/tui-compiled/raw/shared/cd94316a888b0a8e-schema.js +754 -0
- package/src/tui-compiled/raw/shared/d319517e3ea6ad70-redact.js +83 -0
- package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +5 -0
- package/src/tui-compiled/raw/shared/d669d72c40e9e116-logger.js +19 -0
- package/src/tui-compiled/raw/shared/d7dcba96fea1685c-engine.js +246 -0
- package/src/tui-compiled/raw/shared/dbef697d1df1cd84-index.js +5 -0
- package/src/tui-compiled/raw/shared/e4acc4cb713f300c-protocol.js +3 -0
- package/src/tui-compiled/raw/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
- package/src/tui-compiled/raw/shared/ec971142548df90e-index.js +6 -0
- package/src/tui-compiled/raw/shared/f37fcefc7aedbf25-error.js +11 -0
- package/src/tui-compiled/raw/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
- package/src/tui-compiled/raw/shared/fd45e82f2f9c0a0d-torn.js +294 -0
- package/src/tui-compiled/raw/shared/fe02abeadc535f77-rows.js +1258 -0
- package/src/tui-compiled/raw/sidebar-state.js +960 -0
- package/src/tui-compiled/raw/tui/command-dialogs.tsx +424 -0
- package/src/tui-compiled/raw/tui-preferences.js +135 -0
- package/src/tui-compiled/raw/tui.tsx +135 -143
- package/src/tui-compiled/runtime/logger.js +24 -0
- package/src/tui-compiled/runtime/rpc/protocol.js +3 -0
- package/src/tui-compiled/runtime/rpc/rpc-client.js +7 -0
- package/src/tui-compiled/runtime/rpc/rpc-dir.js +27 -0
- package/src/tui-compiled/runtime/selector.js +21 -0
- package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +138 -0
- package/src/tui-compiled/runtime/shared/04a6357627de5ed0-errors.js +38 -0
- package/src/tui-compiled/runtime/shared/053cc17b5419261e-codec.js +12 -0
- package/src/tui-compiled/runtime/shared/0766d78c6f481f18-watcher.js +157 -0
- package/src/tui-compiled/runtime/shared/08eef45626a94992-reserved.js +13 -0
- package/src/tui-compiled/runtime/shared/39c3cb05738fcf6d-projection.js +134 -0
- package/src/tui-compiled/runtime/shared/3c2d4169d00f907e-tui-support.js +5 -0
- package/src/tui-compiled/runtime/shared/3e7d45a77c6ad4d5-index.js +4 -0
- package/src/tui-compiled/runtime/shared/415792e85d80819e-capture-sink.js +13 -0
- package/src/tui-compiled/runtime/shared/42ae93ab554fcec2-open-url.js +28 -0
- package/src/tui-compiled/runtime/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
- package/src/tui-compiled/runtime/shared/458b934e92490331-mutate.js +500 -0
- package/src/tui-compiled/runtime/shared/45bf5343bbbf9add-admission.js +142 -0
- package/src/tui-compiled/runtime/shared/46f3dff1266160e8-client.js +2 -0
- package/src/tui-compiled/runtime/shared/4825e03209d6ae3e-with-lock.js +48 -0
- package/src/tui-compiled/runtime/shared/4f36d333d081d040-merge.js +162 -0
- package/src/tui-compiled/runtime/shared/54eba94fd64c8c3c-index.js +7 -0
- package/src/tui-compiled/runtime/shared/7211cfec06a298bc-atomic-write.js +80 -0
- package/src/tui-compiled/runtime/shared/754d6017f8e419cf-builtins.js +534 -0
- package/src/tui-compiled/runtime/shared/75bd8c3bd63b315f-identity.js +79 -0
- package/src/tui-compiled/runtime/shared/7a91bb630e6f4804-index.js +1 -0
- package/src/tui-compiled/runtime/shared/826342a4b6a8b89b-model.js +16 -0
- package/src/tui-compiled/runtime/shared/83068c0dbe8a6f13-publication.js +304 -0
- package/src/tui-compiled/runtime/shared/836fc615de6afbd9-settings.js +120 -0
- package/src/tui-compiled/runtime/shared/83bc3ba9d0fe2761-runtime.js +68 -0
- package/src/tui-compiled/runtime/shared/8a18abe4fa2977c8-menu.js +300 -0
- package/src/tui-compiled/runtime/shared/901e149c5a0dbbff-sticky.js +339 -0
- package/src/tui-compiled/runtime/shared/9020c407ce64f672-ordered.js +58 -0
- package/src/tui-compiled/runtime/shared/905bffc0220f43d0-map.js +105 -0
- package/src/tui-compiled/runtime/shared/94820e324f2b4c09-seam.js +301 -0
- package/src/tui-compiled/runtime/shared/97484d9bb98c705b-provider-state.js +226 -0
- package/src/tui-compiled/runtime/shared/9968e3cade6969a1-index.js +2 -0
- package/src/tui-compiled/runtime/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
- package/src/tui-compiled/runtime/shared/9d3fc9e37456504e-refresh.js +225 -0
- package/src/tui-compiled/runtime/shared/9deae3c4da4fd63b-hooks.js +34 -0
- package/src/tui-compiled/runtime/shared/a09b6a533c11858f-tui-preferences.js +77 -0
- package/src/tui-compiled/runtime/shared/a62a27a204266acd-attribution.js +60 -0
- package/src/tui-compiled/runtime/shared/a910277ac43a8626-pull.js +143 -0
- package/src/tui-compiled/runtime/shared/b85c5301fddcb98a-paths.js +5 -0
- package/src/tui-compiled/runtime/shared/c14988621dc60a75-port-file.js +217 -0
- package/src/tui-compiled/runtime/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
- package/src/tui-compiled/runtime/shared/c6b176708c446d6d-pins.js +34 -0
- package/src/tui-compiled/runtime/shared/c7d252bedee01600-pi.js +206 -0
- package/src/tui-compiled/runtime/shared/c852a15cfe489d12-pool.js +120 -0
- package/src/tui-compiled/runtime/shared/cc9e0702a752cea7-index.js +3 -0
- package/src/tui-compiled/runtime/shared/cd94316a888b0a8e-schema.js +754 -0
- package/src/tui-compiled/runtime/shared/d319517e3ea6ad70-redact.js +83 -0
- package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +5 -0
- package/src/tui-compiled/runtime/shared/d669d72c40e9e116-logger.js +19 -0
- package/src/tui-compiled/runtime/shared/d7dcba96fea1685c-engine.js +246 -0
- package/src/tui-compiled/runtime/shared/dbef697d1df1cd84-index.js +5 -0
- package/src/tui-compiled/runtime/shared/e4acc4cb713f300c-protocol.js +3 -0
- package/src/tui-compiled/runtime/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
- package/src/tui-compiled/runtime/shared/ec971142548df90e-index.js +6 -0
- package/src/tui-compiled/runtime/shared/f37fcefc7aedbf25-error.js +11 -0
- package/src/tui-compiled/runtime/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
- package/src/tui-compiled/runtime/shared/fd45e82f2f9c0a0d-torn.js +294 -0
- package/src/tui-compiled/runtime/shared/fe02abeadc535f77-rows.js +1258 -0
- package/src/tui-compiled/runtime/sidebar-state.js +960 -0
- package/src/tui-compiled/runtime/tui/command-dialogs.js +378 -0
- package/src/tui-compiled/runtime/tui-preferences.js +135 -0
- package/src/tui-compiled/runtime/{tui.tsx → tui.js} +288 -316
- package/dist/index-4effjmr6.js +0 -2
- package/dist/index-9ehhb051.js +0 -2
- package/dist/index-g3hsdjh4.js +0 -2
- package/dist/index-hh52v8sb.js +0 -2
- package/dist/index-jwn4r3sw.js +0 -1
- package/dist/index-sq36b120.js +0 -211
- package/dist/internal-BTndPmW2.d.ts +0 -914
- package/dist/quota-normalize-08hpp51v.js +0 -1
- package/dist/sidebar-state-CoKYKcLn.d.ts +0 -220
- package/dist/token-util-ypgz7rge.js +0 -1
- package/src/tui-compiled/raw/core-account-paths.ts +0 -114
- package/src/tui-compiled/raw/core-custody-state.ts +0 -243
- package/src/tui-compiled/raw/logger.ts +0 -53
- package/src/tui-compiled/raw/rpc-port-file.ts +0 -177
- package/src/tui-compiled/raw/rpc-protocol.ts +0 -12
- package/src/tui-compiled/raw/rpc-rpc-client.ts +0 -82
- package/src/tui-compiled/raw/rpc-rpc-dir.ts +0 -46
- package/src/tui-compiled/raw/sidebar-state.ts +0 -1659
- package/src/tui-compiled/raw/tui-command-dialogs.tsx +0 -1096
- package/src/tui-compiled/raw/tui-preferences.ts +0 -328
- package/src/tui-compiled/runtime/core-account-paths.ts +0 -114
- package/src/tui-compiled/runtime/core-custody-state.ts +0 -243
- package/src/tui-compiled/runtime/logger.ts +0 -53
- package/src/tui-compiled/runtime/rpc-port-file.ts +0 -177
- package/src/tui-compiled/runtime/rpc-protocol.ts +0 -12
- package/src/tui-compiled/runtime/rpc-rpc-client.ts +0 -82
- package/src/tui-compiled/runtime/rpc-rpc-dir.ts +0 -46
- package/src/tui-compiled/runtime/sidebar-state.ts +0 -1659
- package/src/tui-compiled/runtime/tui-command-dialogs.tsx +0 -855
- package/src/tui-compiled/runtime/tui-preferences.ts +0 -328
- package/src/tui-compiled/shared/internal.ts +0 -6
- package/src/tui-compiled/shared/logger.ts +0 -264
- package/src/tui-compiled/shared/paths.ts +0 -33
- package/src/tui-compiled/shared/refresh-file-lock.ts +0 -384
- package/src/tui-compiled/shared/util/error.ts +0 -9
- package/src/tui-compiled/shared/util/open-url.ts +0 -30
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
import { watch } from 'node:fs'
|
|
2
|
-
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'
|
|
3
|
-
import { homedir } from 'node:os'
|
|
4
|
-
import { basename, dirname, join } from 'node:path'
|
|
5
|
-
import { applyEdits, modify, type ParseError, parse } from 'jsonc-parser'
|
|
6
|
-
|
|
7
|
-
export const TUI_PREFS_FILE_ENV = 'OPENCODE_TUI_PREFERENCES_FILE'
|
|
8
|
-
const FILE_NAME = 'tui-preferences.jsonc'
|
|
9
|
-
|
|
10
|
-
// Shared preferences file for opencode TUI plugins. One top-level key per
|
|
11
|
-
// plugin (short name, e.g. "openai-auth"). The file is optional: every
|
|
12
|
-
// reader must fall back to defaults when it is missing or malformed.
|
|
13
|
-
export function getTuiPreferencesFile(): string {
|
|
14
|
-
const override = process.env[TUI_PREFS_FILE_ENV]
|
|
15
|
-
if (override) return override
|
|
16
|
-
const configDir =
|
|
17
|
-
process.env.OPENCODE_CONFIG_DIR ||
|
|
18
|
-
join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'opencode')
|
|
19
|
-
return join(configDir, FILE_NAME)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
23
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Tolerant read: missing file, parse errors, or a non-object root all resolve
|
|
27
|
-
// to {} so the sidebar never crashes on user-edited content. jsonc-parser's
|
|
28
|
-
// fault-tolerant parse can still hand back a partial object for an
|
|
29
|
-
// unterminated/bracketed file or trailing garbage, so we collect errors and
|
|
30
|
-
// treat any reported fault as malformed.
|
|
31
|
-
export async function readTuiPreferencesFile(): Promise<
|
|
32
|
-
Record<string, unknown>
|
|
33
|
-
> {
|
|
34
|
-
try {
|
|
35
|
-
const raw = await readFile(getTuiPreferencesFile(), 'utf8')
|
|
36
|
-
const errors: ParseError[] = []
|
|
37
|
-
const root: unknown = parse(raw, errors, { allowTrailingComma: true })
|
|
38
|
-
if (errors.length > 0) return {}
|
|
39
|
-
return isRecord(root) ? root : {}
|
|
40
|
-
} catch {
|
|
41
|
-
return {}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const PLUGIN_KEY = 'openai-auth'
|
|
46
|
-
export const DEFAULT_SLOT_ORDER = 160
|
|
47
|
-
|
|
48
|
-
export interface OpenaiAuthTuiPrefs {
|
|
49
|
-
forceToTop: boolean
|
|
50
|
-
order: number
|
|
51
|
-
startCollapsed: boolean
|
|
52
|
-
rememberCollapsed: boolean
|
|
53
|
-
// null = never persisted; seed the UI from startCollapsed instead.
|
|
54
|
-
collapsed: boolean | null
|
|
55
|
-
pollMs: number
|
|
56
|
-
refreshDebounceMs: number
|
|
57
|
-
header: {
|
|
58
|
-
label: string
|
|
59
|
-
showVersion: boolean
|
|
60
|
-
}
|
|
61
|
-
sections: {
|
|
62
|
-
quota: boolean
|
|
63
|
-
fallbackAccounts: boolean
|
|
64
|
-
routing: boolean
|
|
65
|
-
health: boolean
|
|
66
|
-
pacing: boolean
|
|
67
|
-
}
|
|
68
|
-
appearance: {
|
|
69
|
-
barWidth: number
|
|
70
|
-
barFilledChar: string
|
|
71
|
-
barEmptyChar: string
|
|
72
|
-
warnThreshold: number
|
|
73
|
-
errorThreshold: number
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export type AppearancePrefs = OpenaiAuthTuiPrefs['appearance']
|
|
78
|
-
|
|
79
|
-
export const DEFAULT_PREFS: OpenaiAuthTuiPrefs = {
|
|
80
|
-
forceToTop: false,
|
|
81
|
-
order: DEFAULT_SLOT_ORDER,
|
|
82
|
-
startCollapsed: false,
|
|
83
|
-
rememberCollapsed: true,
|
|
84
|
-
collapsed: null,
|
|
85
|
-
pollMs: 1500,
|
|
86
|
-
refreshDebounceMs: 200,
|
|
87
|
-
header: { label: 'OPENAI', showVersion: true },
|
|
88
|
-
sections: {
|
|
89
|
-
quota: true,
|
|
90
|
-
fallbackAccounts: true,
|
|
91
|
-
routing: true,
|
|
92
|
-
health: true,
|
|
93
|
-
pacing: true,
|
|
94
|
-
},
|
|
95
|
-
appearance: {
|
|
96
|
-
barWidth: 10,
|
|
97
|
-
barFilledChar: '\u2588',
|
|
98
|
-
barEmptyChar: '\u2591',
|
|
99
|
-
warnThreshold: 50,
|
|
100
|
-
errorThreshold: 80,
|
|
101
|
-
},
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function bool(value: unknown, fallback: boolean): boolean {
|
|
105
|
-
return typeof value === 'boolean' ? value : fallback
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function int(
|
|
109
|
-
value: unknown,
|
|
110
|
-
fallback: number,
|
|
111
|
-
min: number,
|
|
112
|
-
max: number,
|
|
113
|
-
): number {
|
|
114
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) return fallback
|
|
115
|
-
return Math.min(Math.max(Math.round(value), min), max)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function label(value: unknown, fallback: string, maxLength: number): string {
|
|
119
|
-
if (typeof value !== 'string' || value.length === 0) return fallback
|
|
120
|
-
return value.slice(0, maxLength)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function char(value: unknown, fallback: string): string {
|
|
124
|
-
if (typeof value !== 'string') return fallback
|
|
125
|
-
const first = [...value][0]
|
|
126
|
-
return first ?? fallback
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Per-key validation: every value is independently clamped/defaulted so one
|
|
130
|
-
// bad entry never poisons the rest. Never throws.
|
|
131
|
-
export function resolveOpenaiAuthPrefs(
|
|
132
|
-
root: Record<string, unknown>,
|
|
133
|
-
): OpenaiAuthTuiPrefs {
|
|
134
|
-
const entry = root[PLUGIN_KEY]
|
|
135
|
-
if (!isRecord(entry)) return structuredClone(DEFAULT_PREFS)
|
|
136
|
-
|
|
137
|
-
const d = DEFAULT_PREFS
|
|
138
|
-
const header = isRecord(entry.header) ? entry.header : {}
|
|
139
|
-
const sections = isRecord(entry.sections) ? entry.sections : {}
|
|
140
|
-
const appearance = isRecord(entry.appearance) ? entry.appearance : {}
|
|
141
|
-
|
|
142
|
-
const warnThreshold = int(
|
|
143
|
-
appearance.warnThreshold,
|
|
144
|
-
d.appearance.warnThreshold,
|
|
145
|
-
0,
|
|
146
|
-
99,
|
|
147
|
-
)
|
|
148
|
-
const errorThreshold = Math.max(
|
|
149
|
-
int(appearance.errorThreshold, d.appearance.errorThreshold, 0, 100),
|
|
150
|
-
Math.min(warnThreshold + 1, 100),
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
forceToTop: bool(entry.forceToTop, d.forceToTop),
|
|
155
|
-
order: int(entry.order, d.order, -10000, 10000),
|
|
156
|
-
startCollapsed: bool(entry.startCollapsed, d.startCollapsed),
|
|
157
|
-
rememberCollapsed: bool(entry.rememberCollapsed, d.rememberCollapsed),
|
|
158
|
-
collapsed: typeof entry.collapsed === 'boolean' ? entry.collapsed : null,
|
|
159
|
-
pollMs: int(entry.pollMs, d.pollMs, 500, 30000),
|
|
160
|
-
refreshDebounceMs: int(
|
|
161
|
-
entry.refreshDebounceMs,
|
|
162
|
-
d.refreshDebounceMs,
|
|
163
|
-
50,
|
|
164
|
-
5000,
|
|
165
|
-
),
|
|
166
|
-
header: {
|
|
167
|
-
label: label(header.label, d.header.label, 20),
|
|
168
|
-
showVersion: bool(header.showVersion, d.header.showVersion),
|
|
169
|
-
},
|
|
170
|
-
sections: {
|
|
171
|
-
quota: bool(sections.quota, d.sections.quota),
|
|
172
|
-
fallbackAccounts: bool(
|
|
173
|
-
sections.fallbackAccounts,
|
|
174
|
-
d.sections.fallbackAccounts,
|
|
175
|
-
),
|
|
176
|
-
routing: bool(sections.routing, d.sections.routing),
|
|
177
|
-
health: bool(sections.health, d.sections.health),
|
|
178
|
-
pacing: bool(sections.pacing, d.sections.pacing),
|
|
179
|
-
},
|
|
180
|
-
appearance: {
|
|
181
|
-
barWidth: int(appearance.barWidth, d.appearance.barWidth, 4, 40),
|
|
182
|
-
barFilledChar: char(appearance.barFilledChar, d.appearance.barFilledChar),
|
|
183
|
-
barEmptyChar: char(appearance.barEmptyChar, d.appearance.barEmptyChar),
|
|
184
|
-
warnThreshold,
|
|
185
|
-
errorThreshold,
|
|
186
|
-
},
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const FORCE_TOP_BASE = -100000
|
|
191
|
-
|
|
192
|
-
// Shared forceToTop convention: forced plugins sort below FORCE_TOP_BASE,
|
|
193
|
-
// ordered among themselves by their top-level key's position in the file, so
|
|
194
|
-
// users reprioritize by reordering keys. The user-facing `order` knob clamps
|
|
195
|
-
// to -10000..10000, strictly above the forced band, so a manual order can
|
|
196
|
-
// never beat forceToTop. Host slots render ascending by order; opencode's
|
|
197
|
-
// built-ins occupy 100-500.
|
|
198
|
-
//
|
|
199
|
-
// Key-naming requirement: plugin keys must be non-integer-like short names
|
|
200
|
-
// (e.g. "openai-auth"). JavaScript object key iteration order hoists
|
|
201
|
-
// integer-like keys ("0", "1", "42") ahead of any string keys, which would
|
|
202
|
-
// skew the indexOf-based ordering of forced plugins. The shared
|
|
203
|
-
// `tui-preferences.jsonc` convention requires non-numeric names, so this
|
|
204
|
-
// implementation does not paper over the JS quirk.
|
|
205
|
-
export function computeEffectiveOrder(
|
|
206
|
-
root: Record<string, unknown>,
|
|
207
|
-
pluginKey: string,
|
|
208
|
-
defaultOrder: number,
|
|
209
|
-
): number {
|
|
210
|
-
const entry = root[pluginKey]
|
|
211
|
-
if (!isRecord(entry)) return defaultOrder
|
|
212
|
-
if (entry.forceToTop === true) {
|
|
213
|
-
return FORCE_TOP_BASE + Object.keys(root).indexOf(pluginKey)
|
|
214
|
-
}
|
|
215
|
-
return int(entry.order, defaultOrder, -10000, 10000)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const TEMPLATE = `// Shared preferences for opencode TUI plugins.
|
|
219
|
-
// One top-level key per plugin (short name). See each plugin's README for
|
|
220
|
-
// its supported settings. This file is safe to hand-edit; plugins update
|
|
221
|
-
// individual keys in place and preserve comments.
|
|
222
|
-
{}
|
|
223
|
-
`
|
|
224
|
-
|
|
225
|
-
type JsonValue = string | number | boolean | null
|
|
226
|
-
|
|
227
|
-
async function writePreference(
|
|
228
|
-
pluginKey: string,
|
|
229
|
-
path: string[],
|
|
230
|
-
value: JsonValue,
|
|
231
|
-
): Promise<void> {
|
|
232
|
-
const file = getTuiPreferencesFile()
|
|
233
|
-
await mkdir(dirname(file), { recursive: true })
|
|
234
|
-
let text: string
|
|
235
|
-
try {
|
|
236
|
-
text = await readFile(file, 'utf8')
|
|
237
|
-
} catch {
|
|
238
|
-
text = ''
|
|
239
|
-
}
|
|
240
|
-
if (text.trim() === '') text = TEMPLATE
|
|
241
|
-
const edits = modify(text, [pluginKey, ...path], value, {
|
|
242
|
-
formattingOptions: { insertSpaces: true, tabSize: 2 },
|
|
243
|
-
})
|
|
244
|
-
const next = applyEdits(text, edits)
|
|
245
|
-
const tmp = `${file}.${process.pid}.tmp`
|
|
246
|
-
await writeFile(tmp, next, 'utf8')
|
|
247
|
-
await rename(tmp, file)
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
let writeChain: Promise<void> = Promise.resolve()
|
|
251
|
-
|
|
252
|
-
// Writes are serialized on a promise chain: each update re-reads the file,
|
|
253
|
-
// applies a minimal comment-preserving edit to one property, and replaces the
|
|
254
|
-
// file atomically (temp + rename in the same directory). Best-effort by
|
|
255
|
-
// design — preferences are never worth crashing the TUI over.
|
|
256
|
-
export function queueTuiPreferenceUpdate(
|
|
257
|
-
pluginKey: string,
|
|
258
|
-
path: string[],
|
|
259
|
-
value: JsonValue,
|
|
260
|
-
): Promise<void> {
|
|
261
|
-
writeChain = writeChain
|
|
262
|
-
.then(() => writePreference(pluginKey, path, value))
|
|
263
|
-
.catch(() => {})
|
|
264
|
-
return writeChain
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const WATCH_DEBOUNCE_MS = 150
|
|
268
|
-
|
|
269
|
-
// Watches the directory rather than the file: editors and our own atomic
|
|
270
|
-
// writes replace the file via rename, which kills file-level watchers.
|
|
271
|
-
//
|
|
272
|
-
// Filtering is two-stage:
|
|
273
|
-
// 1. Filename pre-filter: only debounce events for the preferences file
|
|
274
|
-
// name, or our atomic-write temp file. This is a cheap first pass that
|
|
275
|
-
// drops the common case (unrelated sibling files).
|
|
276
|
-
// 2. Content check inside the debounce: after the timer fires, re-read
|
|
277
|
-
// the file and compare it against the last seen content. Only fire
|
|
278
|
-
// `onChange` when the content actually changed. This is the authority.
|
|
279
|
-
// Some platforms (notably macOS FSEvents and a few inotify backends)
|
|
280
|
-
// can misattribute a rename of an unrelated sibling file to the
|
|
281
|
-
// real preferences filename in addition to emitting the sibling's
|
|
282
|
-
// own event, so a name-only filter still produces a stray callback.
|
|
283
|
-
// A content comparison is robust against that, against coalesced
|
|
284
|
-
// events, and against mtime granularity.
|
|
285
|
-
//
|
|
286
|
-
// Returns a disposer; never throws.
|
|
287
|
-
export function watchTuiPreferences(onChange: () => void): () => void {
|
|
288
|
-
const file = getTuiPreferencesFile()
|
|
289
|
-
const name = basename(file)
|
|
290
|
-
let timer: ReturnType<typeof setTimeout> | null = null
|
|
291
|
-
let lastSeen: string | null = null
|
|
292
|
-
// Seed asynchronously; a real change that fires before the seed resolves
|
|
293
|
-
// still wins because the debounce re-reads the file fresh and compares
|
|
294
|
-
// against `lastSeen` (which will be `null` → does not match → fires).
|
|
295
|
-
void readFile(file, 'utf8')
|
|
296
|
-
.then((text) => {
|
|
297
|
-
if (lastSeen === null) lastSeen = text
|
|
298
|
-
})
|
|
299
|
-
.catch(() => {})
|
|
300
|
-
try {
|
|
301
|
-
const watcher = watch(dirname(file), (_event, filename) => {
|
|
302
|
-
// Exact match against the preferences file name, plus the temp file
|
|
303
|
-
// we use for atomic writes (carrying our pid + `.tmp`).
|
|
304
|
-
const isOurs =
|
|
305
|
-
filename === name ||
|
|
306
|
-
(filename?.startsWith(`${name}.`) && filename.endsWith('.tmp'))
|
|
307
|
-
if (filename != null && !isOurs) return
|
|
308
|
-
if (timer) clearTimeout(timer)
|
|
309
|
-
timer = setTimeout(() => {
|
|
310
|
-
timer = null
|
|
311
|
-
void readFile(file, 'utf8')
|
|
312
|
-
.catch(() => null)
|
|
313
|
-
.then((text) => {
|
|
314
|
-
if (text === null) return
|
|
315
|
-
if (text === lastSeen) return
|
|
316
|
-
lastSeen = text
|
|
317
|
-
onChange()
|
|
318
|
-
})
|
|
319
|
-
}, WATCH_DEBOUNCE_MS)
|
|
320
|
-
})
|
|
321
|
-
return () => {
|
|
322
|
-
if (timer) clearTimeout(timer)
|
|
323
|
-
watcher.close()
|
|
324
|
-
}
|
|
325
|
-
} catch {
|
|
326
|
-
return () => {}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
appendFileSync,
|
|
3
|
-
chmodSync,
|
|
4
|
-
existsSync,
|
|
5
|
-
renameSync,
|
|
6
|
-
statSync,
|
|
7
|
-
} from 'node:fs'
|
|
8
|
-
|
|
9
|
-
export type Level = 'error' | 'warn' | 'info' | 'debug' | 'trace'
|
|
10
|
-
const ORDER: Record<Level, number> = {
|
|
11
|
-
error: 0,
|
|
12
|
-
warn: 1,
|
|
13
|
-
info: 2,
|
|
14
|
-
debug: 3,
|
|
15
|
-
trace: 4,
|
|
16
|
-
}
|
|
17
|
-
const MAX_BYTES = 5 * 1024 * 1024
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Where lines are written, and the level floor, as the host supplied them.
|
|
21
|
-
*
|
|
22
|
-
* Both start unset. A host decides where its log lives — that decision reads
|
|
23
|
-
* host environment variables and host directories, neither of which belongs in
|
|
24
|
-
* shared code — and calls `initLogger` before it runs any command. Until then
|
|
25
|
-
* every `log.*` call is a silent no-op: throwing would turn the first command a
|
|
26
|
-
* host forgot to wire into a crash, and buffering would hold credential-bearing
|
|
27
|
-
* lines for an init that may never arrive.
|
|
28
|
-
*/
|
|
29
|
-
let logFileSource: string | (() => string) | undefined
|
|
30
|
-
let initLevelSource: Level | (() => Level | undefined) | undefined
|
|
31
|
-
let runtimeLevel: Level | undefined
|
|
32
|
-
|
|
33
|
-
export interface InitLoggerOptions {
|
|
34
|
-
/**
|
|
35
|
-
* Path of the file lines are appended to, or a function returning it.
|
|
36
|
-
*
|
|
37
|
-
* A function is what a host passes when its destination can move while the
|
|
38
|
-
* process runs — an operator or a test changing the variable that names the
|
|
39
|
-
* log file expects the next line to land in the new file, and a value
|
|
40
|
-
* captured once at init would keep writing to the old one.
|
|
41
|
-
*/
|
|
42
|
-
file: string | (() => string)
|
|
43
|
-
/** Level floor applied when no `setLogLevel` call has overridden it. */
|
|
44
|
-
level?: Level | (() => Level | undefined)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Point the logger at a host's file and level. Idempotent: calling it again
|
|
49
|
-
* replaces both. A runtime level installed by `setLogLevel` is deliberately
|
|
50
|
-
* left alone, because it is the operator's explicit choice and outranks the
|
|
51
|
-
* floor a host computed at start-up.
|
|
52
|
-
*/
|
|
53
|
-
export function initLogger(options: InitLoggerOptions): void {
|
|
54
|
-
logFileSource = options.file
|
|
55
|
-
initLevelSource = options.level
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function setLogLevel(l: Level | undefined) {
|
|
59
|
-
if (l === undefined || l in ORDER) runtimeLevel = l
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function logFilePath(): string | undefined {
|
|
63
|
-
if (logFileSource === undefined) return undefined
|
|
64
|
-
const resolved =
|
|
65
|
-
typeof logFileSource === 'function' ? logFileSource() : logFileSource
|
|
66
|
-
return resolved || undefined
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function configuredLevel(): Level {
|
|
70
|
-
if (runtimeLevel) return runtimeLevel
|
|
71
|
-
const floor =
|
|
72
|
-
typeof initLevelSource === 'function' ? initLevelSource() : initLevelSource
|
|
73
|
-
if (floor && floor in ORDER) return floor
|
|
74
|
-
return 'info'
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const SECRET_KEY_EXACT =
|
|
78
|
-
/^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i
|
|
79
|
-
function isSecretKey(key: string): boolean {
|
|
80
|
-
if (SECRET_KEY_EXACT.test(key)) return true
|
|
81
|
-
const k = key.toLowerCase().replace(/[-_]/g, '')
|
|
82
|
-
// Only the unambiguous ChatGPT stable id (chatgpt-account-id / chatgptAccountId)
|
|
83
|
-
// is sensitive. A bare `accountId` field is overloaded — most diagnostic logs put
|
|
84
|
-
// the INTERNAL account id/key ('main' or a fallback id) there, which is safe and
|
|
85
|
-
// needed for debugging — so it is intentionally NOT redacted by name. The genuine
|
|
86
|
-
// ChatGPT id is kept out of logs at the source instead.
|
|
87
|
-
if (k === 'chatgptaccountid') return true
|
|
88
|
-
// Operator identity carried on a served vault credential. These arrived with
|
|
89
|
-
// the client widening that added `accountId`/`email`/`orgName`/`projectId`;
|
|
90
|
-
// they are personal data with no diagnostic value, so they never reach a log
|
|
91
|
-
// file. The structural guard is not logging a served credential wholesale —
|
|
92
|
-
// this list only closes the fields that exist today.
|
|
93
|
-
if (k === 'email' || k === 'orgname' || k === 'organizationname') return true
|
|
94
|
-
if (k.includes('apikey')) return true
|
|
95
|
-
if (k.endsWith('secret') || k.endsWith('password')) return true
|
|
96
|
-
if (k.endsWith('token') && !k.endsWith('tokens')) return true
|
|
97
|
-
return false
|
|
98
|
-
}
|
|
99
|
-
const TOKEN_VALUE =
|
|
100
|
-
/\b(Bearer\s+[\w.-]+|sk-[\w-]+|eyJ[\w.-]+)\b|ckh_[A-Za-z0-9_-]{20,}/g
|
|
101
|
-
export function redact(value: unknown): unknown {
|
|
102
|
-
return redactInner(value, new WeakSet<object>())
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Scrub credential-shaped strings without redacting by key name.
|
|
107
|
-
*
|
|
108
|
-
* For regions where a key names a piece of data rather than holding a secret —
|
|
109
|
-
* a JSON Schema property called `api_key` describes an argument, it does not
|
|
110
|
-
* carry one — replacing the node by name destroys the structure while removing
|
|
111
|
-
* nothing sensitive. A credential pasted into a description is still caught,
|
|
112
|
-
* because that is a string.
|
|
113
|
-
*/
|
|
114
|
-
export function redactStrings(value: unknown): unknown {
|
|
115
|
-
return redactStringsInner(value, new WeakSet<object>())
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function redactStringsInner(value: unknown, seen: WeakSet<object>): unknown {
|
|
119
|
-
if (typeof value === 'string')
|
|
120
|
-
return value.replace(TOKEN_VALUE, '***REDACTED***')
|
|
121
|
-
if (Array.isArray(value)) {
|
|
122
|
-
if (seen.has(value)) return '[Circular]'
|
|
123
|
-
seen.add(value)
|
|
124
|
-
const arr = value.map((v) => redactStringsInner(v, seen))
|
|
125
|
-
seen.delete(value)
|
|
126
|
-
return arr
|
|
127
|
-
}
|
|
128
|
-
if (value && typeof value === 'object') {
|
|
129
|
-
if (seen.has(value)) return '[Circular]'
|
|
130
|
-
seen.add(value)
|
|
131
|
-
const out: Record<string, unknown> = {}
|
|
132
|
-
for (const [k, v] of Object.entries(value))
|
|
133
|
-
out[k] = redactStringsInner(v, seen)
|
|
134
|
-
seen.delete(value)
|
|
135
|
-
return out
|
|
136
|
-
}
|
|
137
|
-
return value
|
|
138
|
-
}
|
|
139
|
-
function redactInner(value: unknown, seen: WeakSet<object>): unknown {
|
|
140
|
-
if (typeof value === 'string')
|
|
141
|
-
return value.replace(TOKEN_VALUE, '***REDACTED***')
|
|
142
|
-
if (Array.isArray(value)) {
|
|
143
|
-
if (seen.has(value)) return '[Circular]'
|
|
144
|
-
seen.add(value)
|
|
145
|
-
const arr = value.map((v) => redactInner(v, seen))
|
|
146
|
-
seen.delete(value)
|
|
147
|
-
return arr
|
|
148
|
-
}
|
|
149
|
-
if (value && typeof value === 'object') {
|
|
150
|
-
if (seen.has(value)) return '[Circular]'
|
|
151
|
-
seen.add(value)
|
|
152
|
-
const out: Record<string, unknown> = {}
|
|
153
|
-
for (const [k, v] of Object.entries(value))
|
|
154
|
-
out[k] = isSecretKey(k) ? '***REDACTED***' : redactInner(v, seen)
|
|
155
|
-
seen.delete(value)
|
|
156
|
-
return out
|
|
157
|
-
}
|
|
158
|
-
return value
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let buffer: string[] = []
|
|
162
|
-
let timer: ReturnType<typeof setTimeout> | undefined
|
|
163
|
-
const ROTATE_KEEP = 3
|
|
164
|
-
function chmodPrivate(path: string) {
|
|
165
|
-
try {
|
|
166
|
-
chmodSync(path, 0o600)
|
|
167
|
-
} catch {
|
|
168
|
-
/* never throw */
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
function rotateIfNeeded(f: string) {
|
|
172
|
-
try {
|
|
173
|
-
if (!(existsSync(f) && statSync(f).size > MAX_BYTES)) return
|
|
174
|
-
for (let i = ROTATE_KEEP - 1; i >= 1; i--) {
|
|
175
|
-
if (existsSync(`${f}.${i}`)) {
|
|
176
|
-
const rotated = `${f}.${i + 1}`
|
|
177
|
-
renameSync(`${f}.${i}`, rotated)
|
|
178
|
-
chmodPrivate(rotated)
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
const rotated = `${f}.1`
|
|
182
|
-
renameSync(f, rotated)
|
|
183
|
-
chmodPrivate(rotated)
|
|
184
|
-
} catch {
|
|
185
|
-
/* never throw */
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Write whatever is buffered. Safe to call synchronously from a process-exit
|
|
191
|
-
* handler, which is how each host drains the buffer on shutdown.
|
|
192
|
-
*/
|
|
193
|
-
export function flushLogs() {
|
|
194
|
-
if (!buffer.length) return
|
|
195
|
-
const file = logFilePath()
|
|
196
|
-
const text = buffer.join('')
|
|
197
|
-
buffer = []
|
|
198
|
-
if (!file) return
|
|
199
|
-
try {
|
|
200
|
-
rotateIfNeeded(file)
|
|
201
|
-
if (existsSync(file)) chmodPrivate(file)
|
|
202
|
-
appendFileSync(file, text, { encoding: 'utf8', mode: 0o600 })
|
|
203
|
-
} catch {
|
|
204
|
-
/* never throw */
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
function schedule() {
|
|
208
|
-
if (!timer)
|
|
209
|
-
timer = setTimeout(() => {
|
|
210
|
-
timer = undefined
|
|
211
|
-
flushLogs()
|
|
212
|
-
}, 500)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function safeSerialize(data: unknown): string {
|
|
216
|
-
try {
|
|
217
|
-
return ` ${JSON.stringify(redact(data))}`
|
|
218
|
-
} catch {
|
|
219
|
-
return ' [unserializable]'
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function emit(channel: string, level: Level, message: string, data?: unknown) {
|
|
224
|
-
// No destination means no host has initialised the logger. Drop the line
|
|
225
|
-
// rather than buffering it: see the note on logFileSource above.
|
|
226
|
-
if (logFileSource === undefined) return
|
|
227
|
-
if (ORDER[level] > ORDER[configuredLevel()]) return
|
|
228
|
-
const line =
|
|
229
|
-
`[${new Date().toISOString()}] ${level.toUpperCase()} [${channel}] ${redactStrings(message)}` +
|
|
230
|
-
(data === undefined ? '' : safeSerialize(data)) +
|
|
231
|
-
'\n'
|
|
232
|
-
buffer.push(line)
|
|
233
|
-
if (buffer.length >= 50) flushLogs()
|
|
234
|
-
else schedule()
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export function createLogger(channel: string) {
|
|
238
|
-
return {
|
|
239
|
-
error: (m: string, d?: unknown) => emit(channel, 'error', m, d),
|
|
240
|
-
warn: (m: string, d?: unknown) => emit(channel, 'warn', m, d),
|
|
241
|
-
info: (m: string, d?: unknown) => emit(channel, 'info', m, d),
|
|
242
|
-
debug: (m: string, d?: unknown) => emit(channel, 'debug', m, d),
|
|
243
|
-
trace: (m: string, d?: unknown) => emit(channel, 'trace', m, d),
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
export async function flushForTest() {
|
|
247
|
-
flushLogs()
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Return the logger to its uninitialised state. Only a test needs this: a
|
|
252
|
-
* single process runs every test file, so a file path left over from one test
|
|
253
|
-
* would keep a later "logger was never initialised" case writing lines.
|
|
254
|
-
*/
|
|
255
|
-
export function resetLoggerForTest() {
|
|
256
|
-
buffer = []
|
|
257
|
-
if (timer) {
|
|
258
|
-
clearTimeout(timer)
|
|
259
|
-
timer = undefined
|
|
260
|
-
}
|
|
261
|
-
logFileSource = undefined
|
|
262
|
-
initLevelSource = undefined
|
|
263
|
-
runtimeLevel = undefined
|
|
264
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure path values shared by every host.
|
|
3
|
-
*
|
|
4
|
-
* Nothing here reads an environment variable or knows where a host keeps its
|
|
5
|
-
* configuration. A host resolves its own two file paths and hands them to the
|
|
6
|
-
* store as an `AccountPaths`; the only thing that must not drift between hosts
|
|
7
|
-
* is the file name itself, which is why the constants live here rather than in
|
|
8
|
-
* each host's own resolver.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { basename, dirname, join } from 'node:path'
|
|
12
|
-
|
|
13
|
-
export const ACCOUNT_FILE_NAME = 'openai-auth.json'
|
|
14
|
-
export const ACCOUNT_STATE_FILE_NAME = 'openai-auth-state.json'
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The config file and the runtime-state file a store operation reads and writes.
|
|
18
|
-
*
|
|
19
|
-
* Both are required. A store function never derives one from the other, because
|
|
20
|
-
* a host may point its state file somewhere the derivation would not reach and
|
|
21
|
-
* a silently derived path would lock one file while writing another.
|
|
22
|
-
*/
|
|
23
|
-
export interface AccountPaths {
|
|
24
|
-
configPath: string
|
|
25
|
-
statePath: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Derive the state-file path from the config path without reading env vars. */
|
|
29
|
-
export function deriveStatePath(configPath: string): string {
|
|
30
|
-
return basename(configPath) === ACCOUNT_FILE_NAME
|
|
31
|
-
? join(dirname(configPath), ACCOUNT_STATE_FILE_NAME)
|
|
32
|
-
: `${configPath}.state.json`
|
|
33
|
-
}
|