oc-codex-multi-auth 6.1.9 → 6.2.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 +2 -0
- package/assets/icon.svg +8 -0
- package/dist/lib/auth/scopes.d.ts +1 -1
- package/dist/lib/auth/scopes.d.ts.map +1 -1
- package/dist/lib/auth/scopes.js +0 -2
- package/dist/lib/auth/scopes.js.map +1 -1
- package/dist/lib/codex-usage.d.ts +49 -0
- package/dist/lib/codex-usage.d.ts.map +1 -1
- package/dist/lib/codex-usage.js +96 -16
- package/dist/lib/codex-usage.js.map +1 -1
- package/dist/lib/config.d.ts +2 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/schemas.d.ts +2 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +2 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/identity.d.ts.map +1 -1
- package/dist/lib/storage/identity.js +69 -1
- package/dist/lib/storage/identity.js.map +1 -1
- package/dist/lib/tools/codex-limits.d.ts +12 -0
- package/dist/lib/tools/codex-limits.d.ts.map +1 -1
- package/dist/lib/tools/codex-limits.js +43 -2
- package/dist/lib/tools/codex-limits.js.map +1 -1
- package/dist/lib/tui-status.d.ts +4 -1
- package/dist/lib/tui-status.d.ts.map +1 -1
- package/dist/lib/tui-status.js +22 -13
- package/dist/lib/tui-status.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +14 -5
- package/dist/tui.js.map +1 -1
- package/package.json +150 -150
- package/scripts/install-oc-codex-multi-auth-core.js +236 -2
package/package.json
CHANGED
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "oc-codex-multi-auth",
|
|
3
|
-
"version": "6.
|
|
4
|
-
"description": "OpenCode plugin for ChatGPT Plus/Pro OAuth with Codex/GPT-5 routing, multi-account rotation, account switching, health checks, diagnostics, and recovery tools",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/index.d.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"types": "./dist/index.d.ts",
|
|
10
|
-
"import": "./dist/index.js"
|
|
11
|
-
},
|
|
12
|
-
"./tui": {
|
|
13
|
-
"types": "./dist/tui.d.ts",
|
|
14
|
-
"import": "./dist/tui.js"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"type": "module",
|
|
18
|
-
"license": "MIT",
|
|
19
|
-
"author": "ndycode",
|
|
20
|
-
"repository": {
|
|
21
|
-
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/ndycode/oc-codex-multi-auth.git"
|
|
23
|
-
},
|
|
24
|
-
"keywords": [
|
|
25
|
-
"opencode",
|
|
26
|
-
"opencode-plugin",
|
|
27
|
-
"opencode-auth",
|
|
28
|
-
"openai",
|
|
29
|
-
"chatgpt",
|
|
30
|
-
"chatgpt-plus",
|
|
31
|
-
"chatgpt-pro",
|
|
32
|
-
"oauth",
|
|
33
|
-
"oauth2",
|
|
34
|
-
"oauth-login",
|
|
35
|
-
"pkce",
|
|
36
|
-
"codex",
|
|
37
|
-
"codex-auth",
|
|
38
|
-
"codex-oauth",
|
|
39
|
-
"gpt-5",
|
|
40
|
-
"gpt-5-5",
|
|
41
|
-
"multi-account",
|
|
42
|
-
"account-rotation",
|
|
43
|
-
"account-switching",
|
|
44
|
-
"account-health",
|
|
45
|
-
"health-checks",
|
|
46
|
-
"quota-management",
|
|
47
|
-
"diagnostics",
|
|
48
|
-
"recovery-tools",
|
|
49
|
-
"terminal-ui",
|
|
50
|
-
"developer-tools",
|
|
51
|
-
"typescript",
|
|
52
|
-
"nodejs"
|
|
53
|
-
],
|
|
54
|
-
"homepage": "https://github.com/ndycode/oc-codex-multi-auth#readme",
|
|
55
|
-
"bugs": {
|
|
56
|
-
"url": "https://github.com/ndycode/oc-codex-multi-auth/issues"
|
|
57
|
-
},
|
|
58
|
-
"scripts": {
|
|
59
|
-
"build": "node scripts/clean-dist.js && tsc && node scripts/copy-oauth-success.js",
|
|
60
|
-
"typecheck": "tsc --noEmit",
|
|
61
|
-
"lint": "npm run lint:ts && npm run lint:scripts",
|
|
62
|
-
"lint:ts": "eslint . --ext .ts",
|
|
63
|
-
"lint:scripts": "eslint scripts --ext .js",
|
|
64
|
-
"lint:fix": "npm run lint:ts:fix && npm run lint:scripts:fix",
|
|
65
|
-
"lint:ts:fix": "eslint . --ext .ts --fix",
|
|
66
|
-
"lint:scripts:fix": "eslint scripts --ext .js --fix",
|
|
67
|
-
"test": "vitest run",
|
|
68
|
-
"test:watch": "vitest",
|
|
69
|
-
"test:ui": "vitest --ui",
|
|
70
|
-
"test:coverage": "vitest run --coverage",
|
|
71
|
-
"coverage": "vitest run --coverage",
|
|
72
|
-
"audit:prod": "npm audit --omit=dev --audit-level=high",
|
|
73
|
-
"audit:all": "npm audit --audit-level=high",
|
|
74
|
-
"audit:dev:allowlist": "node scripts/audit-dev-allowlist.js",
|
|
75
|
-
"audit:ci": "npm run audit:prod && npm run audit:dev:allowlist",
|
|
76
|
-
"prepublishOnly": "npm run build",
|
|
77
|
-
"prepare": "husky"
|
|
78
|
-
},
|
|
79
|
-
"bin": {
|
|
80
|
-
"oc-codex-multi-auth": "scripts/install-oc-codex-multi-auth.js"
|
|
81
|
-
},
|
|
82
|
-
"files": [
|
|
83
|
-
"dist/",
|
|
84
|
-
"assets/",
|
|
85
|
-
"config/",
|
|
86
|
-
"scripts/",
|
|
87
|
-
"README.md",
|
|
88
|
-
"LICENSE"
|
|
89
|
-
],
|
|
90
|
-
"lint-staged": {
|
|
91
|
-
"*.ts": [
|
|
92
|
-
"eslint --max-warnings=0 --fix --no-warn-ignored"
|
|
93
|
-
],
|
|
94
|
-
"scripts/**/*.js": [
|
|
95
|
-
"eslint --max-warnings=0 --fix --no-warn-ignored"
|
|
96
|
-
]
|
|
97
|
-
},
|
|
98
|
-
"engines": {
|
|
99
|
-
"node": ">=18.0.0"
|
|
100
|
-
},
|
|
101
|
-
"peerDependencies": {
|
|
102
|
-
"typescript": "^5"
|
|
103
|
-
},
|
|
104
|
-
"devDependencies": {
|
|
105
|
-
"@fast-check/vitest": "^0.2.4",
|
|
106
|
-
"@opencode-ai/sdk": "^1.2.10",
|
|
107
|
-
"@types/node": "^25.3.0",
|
|
108
|
-
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
109
|
-
"@typescript-eslint/parser": "^8.56.0",
|
|
110
|
-
"@vitest/coverage-v8": "^4.0.18",
|
|
111
|
-
"@vitest/ui": "^4.0.18",
|
|
112
|
-
"eslint": "^10.0.0",
|
|
113
|
-
"fast-check": "^4.5.3",
|
|
114
|
-
"husky": "^9.1.7",
|
|
115
|
-
"lint-staged": "^16.2.7",
|
|
116
|
-
"typescript": "^5.9.3",
|
|
117
|
-
"typescript-language-server": "^5.1.3",
|
|
118
|
-
"vitest": "^4.0.18"
|
|
119
|
-
},
|
|
120
|
-
"dependencies": {
|
|
121
|
-
"@napi-rs/keyring": "~1.2.0",
|
|
122
|
-
"@openauthjs/openauth": "^0.4.3",
|
|
123
|
-
"@opencode-ai/plugin": "^1.14.22",
|
|
124
|
-
"@opentui/core": "0.2.6",
|
|
125
|
-
"@opentui/solid": "0.2.6",
|
|
126
|
-
"hono": "4.12.18",
|
|
127
|
-
"solid-js": "^1.9.11",
|
|
128
|
-
"web-tree-sitter": "^0.25.10",
|
|
129
|
-
"zod": "^4.3.6"
|
|
130
|
-
},
|
|
131
|
-
"overrides": {
|
|
132
|
-
"flatted": "3.4.2",
|
|
133
|
-
"hono": "4.12.18",
|
|
134
|
-
"rollup": "4.60.0",
|
|
135
|
-
"vite": "^7.3.2",
|
|
136
|
-
"yaml": "^2.8.3",
|
|
137
|
-
"ajv@<6.14.0": "^6.14.0",
|
|
138
|
-
"brace-expansion@>=2.0.0 <=2.0.2": "^2.0.3",
|
|
139
|
-
"minimatch@<9.0.7": "^9.0.7",
|
|
140
|
-
"minimatch@>=10.0.0 <10.2.3": "^10.2.3",
|
|
141
|
-
"picomatch@<2.3.2": "^2.3.2",
|
|
142
|
-
"picomatch@>=4.0.0 <4.0.4": "^4.0.4",
|
|
143
|
-
"@typescript-eslint/typescript-estree": {
|
|
144
|
-
"minimatch": "^9.0.7"
|
|
145
|
-
},
|
|
146
|
-
"uuid": "13.0.1",
|
|
147
|
-
"diff": "8.0.3",
|
|
148
|
-
"file-type": "21.3.1"
|
|
149
|
-
}
|
|
150
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "oc-codex-multi-auth",
|
|
3
|
+
"version": "6.2.0",
|
|
4
|
+
"description": "OpenCode plugin for ChatGPT Plus/Pro OAuth with Codex/GPT-5 routing, multi-account rotation, account switching, health checks, diagnostics, and recovery tools",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./tui": {
|
|
13
|
+
"types": "./dist/tui.d.ts",
|
|
14
|
+
"import": "./dist/tui.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "ndycode",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/ndycode/oc-codex-multi-auth.git"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"opencode",
|
|
26
|
+
"opencode-plugin",
|
|
27
|
+
"opencode-auth",
|
|
28
|
+
"openai",
|
|
29
|
+
"chatgpt",
|
|
30
|
+
"chatgpt-plus",
|
|
31
|
+
"chatgpt-pro",
|
|
32
|
+
"oauth",
|
|
33
|
+
"oauth2",
|
|
34
|
+
"oauth-login",
|
|
35
|
+
"pkce",
|
|
36
|
+
"codex",
|
|
37
|
+
"codex-auth",
|
|
38
|
+
"codex-oauth",
|
|
39
|
+
"gpt-5",
|
|
40
|
+
"gpt-5-5",
|
|
41
|
+
"multi-account",
|
|
42
|
+
"account-rotation",
|
|
43
|
+
"account-switching",
|
|
44
|
+
"account-health",
|
|
45
|
+
"health-checks",
|
|
46
|
+
"quota-management",
|
|
47
|
+
"diagnostics",
|
|
48
|
+
"recovery-tools",
|
|
49
|
+
"terminal-ui",
|
|
50
|
+
"developer-tools",
|
|
51
|
+
"typescript",
|
|
52
|
+
"nodejs"
|
|
53
|
+
],
|
|
54
|
+
"homepage": "https://github.com/ndycode/oc-codex-multi-auth#readme",
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/ndycode/oc-codex-multi-auth/issues"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "node scripts/clean-dist.js && tsc && node scripts/copy-oauth-success.js",
|
|
60
|
+
"typecheck": "tsc --noEmit",
|
|
61
|
+
"lint": "npm run lint:ts && npm run lint:scripts",
|
|
62
|
+
"lint:ts": "eslint . --ext .ts",
|
|
63
|
+
"lint:scripts": "eslint scripts --ext .js",
|
|
64
|
+
"lint:fix": "npm run lint:ts:fix && npm run lint:scripts:fix",
|
|
65
|
+
"lint:ts:fix": "eslint . --ext .ts --fix",
|
|
66
|
+
"lint:scripts:fix": "eslint scripts --ext .js --fix",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:watch": "vitest",
|
|
69
|
+
"test:ui": "vitest --ui",
|
|
70
|
+
"test:coverage": "vitest run --coverage",
|
|
71
|
+
"coverage": "vitest run --coverage",
|
|
72
|
+
"audit:prod": "npm audit --omit=dev --audit-level=high",
|
|
73
|
+
"audit:all": "npm audit --audit-level=high",
|
|
74
|
+
"audit:dev:allowlist": "node scripts/audit-dev-allowlist.js",
|
|
75
|
+
"audit:ci": "npm run audit:prod && npm run audit:dev:allowlist",
|
|
76
|
+
"prepublishOnly": "npm run build",
|
|
77
|
+
"prepare": "husky"
|
|
78
|
+
},
|
|
79
|
+
"bin": {
|
|
80
|
+
"oc-codex-multi-auth": "scripts/install-oc-codex-multi-auth.js"
|
|
81
|
+
},
|
|
82
|
+
"files": [
|
|
83
|
+
"dist/",
|
|
84
|
+
"assets/",
|
|
85
|
+
"config/",
|
|
86
|
+
"scripts/",
|
|
87
|
+
"README.md",
|
|
88
|
+
"LICENSE"
|
|
89
|
+
],
|
|
90
|
+
"lint-staged": {
|
|
91
|
+
"*.ts": [
|
|
92
|
+
"eslint --max-warnings=0 --fix --no-warn-ignored"
|
|
93
|
+
],
|
|
94
|
+
"scripts/**/*.js": [
|
|
95
|
+
"eslint --max-warnings=0 --fix --no-warn-ignored"
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
"engines": {
|
|
99
|
+
"node": ">=18.0.0"
|
|
100
|
+
},
|
|
101
|
+
"peerDependencies": {
|
|
102
|
+
"typescript": "^5"
|
|
103
|
+
},
|
|
104
|
+
"devDependencies": {
|
|
105
|
+
"@fast-check/vitest": "^0.2.4",
|
|
106
|
+
"@opencode-ai/sdk": "^1.2.10",
|
|
107
|
+
"@types/node": "^25.3.0",
|
|
108
|
+
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
109
|
+
"@typescript-eslint/parser": "^8.56.0",
|
|
110
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
111
|
+
"@vitest/ui": "^4.0.18",
|
|
112
|
+
"eslint": "^10.0.0",
|
|
113
|
+
"fast-check": "^4.5.3",
|
|
114
|
+
"husky": "^9.1.7",
|
|
115
|
+
"lint-staged": "^16.2.7",
|
|
116
|
+
"typescript": "^5.9.3",
|
|
117
|
+
"typescript-language-server": "^5.1.3",
|
|
118
|
+
"vitest": "^4.0.18"
|
|
119
|
+
},
|
|
120
|
+
"dependencies": {
|
|
121
|
+
"@napi-rs/keyring": "~1.2.0",
|
|
122
|
+
"@openauthjs/openauth": "^0.4.3",
|
|
123
|
+
"@opencode-ai/plugin": "^1.14.22",
|
|
124
|
+
"@opentui/core": "0.2.6",
|
|
125
|
+
"@opentui/solid": "0.2.6",
|
|
126
|
+
"hono": "4.12.18",
|
|
127
|
+
"solid-js": "^1.9.11",
|
|
128
|
+
"web-tree-sitter": "^0.25.10",
|
|
129
|
+
"zod": "^4.3.6"
|
|
130
|
+
},
|
|
131
|
+
"overrides": {
|
|
132
|
+
"flatted": "3.4.2",
|
|
133
|
+
"hono": "4.12.18",
|
|
134
|
+
"rollup": "4.60.0",
|
|
135
|
+
"vite": "^7.3.2",
|
|
136
|
+
"yaml": "^2.8.3",
|
|
137
|
+
"ajv@<6.14.0": "^6.14.0",
|
|
138
|
+
"brace-expansion@>=2.0.0 <=2.0.2": "^2.0.3",
|
|
139
|
+
"minimatch@<9.0.7": "^9.0.7",
|
|
140
|
+
"minimatch@>=10.0.0 <10.2.3": "^10.2.3",
|
|
141
|
+
"picomatch@<2.3.2": "^2.3.2",
|
|
142
|
+
"picomatch@>=4.0.0 <4.0.4": "^4.0.4",
|
|
143
|
+
"@typescript-eslint/typescript-estree": {
|
|
144
|
+
"minimatch": "^9.0.7"
|
|
145
|
+
},
|
|
146
|
+
"uuid": "13.0.1",
|
|
147
|
+
"diff": "8.0.3",
|
|
148
|
+
"file-type": "21.3.1"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -13,6 +13,43 @@ const STALE_MANAGED_MODEL_KEYS = new Set([
|
|
|
13
13
|
"gpt-5.3-codex",
|
|
14
14
|
"gpt-5.4",
|
|
15
15
|
]);
|
|
16
|
+
const STANDALONE_COMMANDS = new Set(["doctor", "status", "list", "limits", "dashboard", "health", "diag"]);
|
|
17
|
+
const INSTALLER_COMMANDS = new Set(["install"]);
|
|
18
|
+
|
|
19
|
+
function splitCommandArgv(argv) {
|
|
20
|
+
const [first, ...rest] = argv;
|
|
21
|
+
if (!first) return { kind: "install", argv };
|
|
22
|
+
if (INSTALLER_COMMANDS.has(first)) return { kind: "install", argv: rest };
|
|
23
|
+
if (STANDALONE_COMMANDS.has(first)) return { kind: "standalone", command: first, argv: rest };
|
|
24
|
+
if (first.startsWith("-")) return { kind: "install", argv };
|
|
25
|
+
return { kind: "unknown", command: first, argv: rest };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseStandaloneArgs(argv) {
|
|
29
|
+
const options = {
|
|
30
|
+
json: false,
|
|
31
|
+
includeSensitive: false,
|
|
32
|
+
deep: false,
|
|
33
|
+
fix: false,
|
|
34
|
+
tag: undefined,
|
|
35
|
+
configPath: undefined,
|
|
36
|
+
help: false,
|
|
37
|
+
};
|
|
38
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
39
|
+
const arg = argv[index];
|
|
40
|
+
if (arg === "--json") options.json = true;
|
|
41
|
+
else if (arg === "--include-sensitive") options.includeSensitive = true;
|
|
42
|
+
else if (arg === "--deep") options.deep = true;
|
|
43
|
+
else if (arg === "--fix") options.fix = true;
|
|
44
|
+
else if (arg === "--tag") options.tag = argv[++index];
|
|
45
|
+
else if (arg.startsWith("--tag=")) options.tag = arg.slice("--tag=".length);
|
|
46
|
+
else if (arg === "--config-path") options.configPath = argv[++index];
|
|
47
|
+
else if (arg.startsWith("--config-path=")) options.configPath = arg.slice("--config-path=".length);
|
|
48
|
+
else if (arg === "--help" || arg === "-h") options.help = true;
|
|
49
|
+
else throw new Error(`Unknown option for standalone command: ${arg}`);
|
|
50
|
+
}
|
|
51
|
+
return options;
|
|
52
|
+
}
|
|
16
53
|
|
|
17
54
|
function getManagedPackageNames() {
|
|
18
55
|
return [PACKAGE_NAME, ...LEGACY_PACKAGE_NAMES];
|
|
@@ -37,7 +74,17 @@ export function isDirectRunPath(argvPath, modulePath, resolveRealPath = realpath
|
|
|
37
74
|
}
|
|
38
75
|
|
|
39
76
|
function printHelp() {
|
|
40
|
-
console.log(`Usage: ${PACKAGE_NAME} [
|
|
77
|
+
console.log(`Usage: ${PACKAGE_NAME} [command] [options]\n\n` +
|
|
78
|
+
"Commands:\n" +
|
|
79
|
+
" install Install/update OpenCode config (default with no command)\n" +
|
|
80
|
+
" doctor Run local account/config diagnostics\n" +
|
|
81
|
+
" status Show account/config status\n" +
|
|
82
|
+
" list List configured accounts\n" +
|
|
83
|
+
" limits Show stored rate-limit state\n" +
|
|
84
|
+
" dashboard Print dashboard guidance\n" +
|
|
85
|
+
" health Check local token/account health\n" +
|
|
86
|
+
" diag Alias for doctor --deep\n\n" +
|
|
87
|
+
`Installer usage: ${PACKAGE_NAME} [--modern|--full|--legacy] [--dry-run] [--no-cache-clear]\n\n` +
|
|
41
88
|
"Default behavior:\n" +
|
|
42
89
|
" - Installs/updates global config at ~/.config/opencode/opencode.json\n" +
|
|
43
90
|
" - Enables the prompt status bar TUI plugin at ~/.config/opencode/tui.json\n" +
|
|
@@ -173,6 +220,183 @@ function formatJson(obj) {
|
|
|
173
220
|
return `${JSON.stringify(obj, null, 2)}\n`;
|
|
174
221
|
}
|
|
175
222
|
|
|
223
|
+
function getStandaloneStoragePath(options, env = process.env) {
|
|
224
|
+
if (options.configPath) return resolve(options.configPath);
|
|
225
|
+
return join(resolveHomeDirectory(env), ".opencode", "oc-codex-multi-auth-accounts.json");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function readStandaloneStorage(path) {
|
|
229
|
+
try {
|
|
230
|
+
const raw = await readFile(path, "utf-8");
|
|
231
|
+
const parsed = JSON.parse(raw);
|
|
232
|
+
return {
|
|
233
|
+
storage: parsed && typeof parsed === "object" ? normalizeStandaloneStorage(parsed) : null,
|
|
234
|
+
error: null,
|
|
235
|
+
};
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (error?.code === "ENOENT") return { storage: null, error: null };
|
|
238
|
+
return { storage: null, error: formatErrorForLog(error) };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function normalizeStandaloneIdentityPart(value) {
|
|
243
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function sameStandaloneIdentity(left, right) {
|
|
247
|
+
const normalizedLeft = normalizeStandaloneIdentityPart(left);
|
|
248
|
+
const normalizedRight = normalizeStandaloneIdentityPart(right);
|
|
249
|
+
return !!normalizedLeft && !!normalizedRight && normalizedLeft === normalizedRight;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function isStandaloneOrgTokenDuplicate(left, right) {
|
|
253
|
+
const leftOrganizationId = normalizeStandaloneIdentityPart(left?.organizationId);
|
|
254
|
+
const rightOrganizationId = normalizeStandaloneIdentityPart(right?.organizationId);
|
|
255
|
+
if (leftOrganizationId && rightOrganizationId && leftOrganizationId !== rightOrganizationId) return false;
|
|
256
|
+
const leftOrgLike = !!leftOrganizationId || left?.accountIdSource === "org";
|
|
257
|
+
const rightOrgLike = !!rightOrganizationId || right?.accountIdSource === "org";
|
|
258
|
+
const leftTokenLike = !leftOrganizationId && left?.accountIdSource === "token";
|
|
259
|
+
const rightTokenLike = !rightOrganizationId && right?.accountIdSource === "token";
|
|
260
|
+
if (!((leftOrgLike && rightTokenLike) || (rightOrgLike && leftTokenLike))) return false;
|
|
261
|
+
return sameStandaloneIdentity(left?.email, right?.email) ||
|
|
262
|
+
sameStandaloneIdentity(left?.refreshToken, right?.refreshToken);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function mergeStandaloneAccounts(target, source) {
|
|
266
|
+
const targetOrgLike = !!normalizeStandaloneIdentityPart(target?.organizationId) || target?.accountIdSource === "org";
|
|
267
|
+
const sourceOrgLike = !!normalizeStandaloneIdentityPart(source?.organizationId) || source?.accountIdSource === "org";
|
|
268
|
+
if (targetOrgLike || !sourceOrgLike) {
|
|
269
|
+
return {
|
|
270
|
+
...source,
|
|
271
|
+
...target,
|
|
272
|
+
organizationId: target.organizationId ?? source.organizationId,
|
|
273
|
+
accountId: target.accountId ?? source.accountId,
|
|
274
|
+
accountIdSource: target.accountIdSource ?? source.accountIdSource,
|
|
275
|
+
accountLabel: target.accountLabel ?? source.accountLabel,
|
|
276
|
+
email: target.email ?? source.email,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return mergeStandaloneAccounts(source, target);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function normalizeStandaloneStorage(storage) {
|
|
283
|
+
if (!Array.isArray(storage.accounts)) return storage;
|
|
284
|
+
const accounts = [...storage.accounts];
|
|
285
|
+
const removed = new Set();
|
|
286
|
+
for (let i = 0; i < accounts.length; i += 1) {
|
|
287
|
+
if (removed.has(i)) continue;
|
|
288
|
+
for (let j = i + 1; j < accounts.length; j += 1) {
|
|
289
|
+
if (removed.has(j) || !isStandaloneOrgTokenDuplicate(accounts[i], accounts[j])) continue;
|
|
290
|
+
const leftOrgLike = !!normalizeStandaloneIdentityPart(accounts[i]?.organizationId) ||
|
|
291
|
+
accounts[i]?.accountIdSource === "org";
|
|
292
|
+
const targetIndex = leftOrgLike ? i : j;
|
|
293
|
+
const sourceIndex = targetIndex === i ? j : i;
|
|
294
|
+
accounts[targetIndex] = mergeStandaloneAccounts(accounts[targetIndex], accounts[sourceIndex]);
|
|
295
|
+
removed.add(sourceIndex);
|
|
296
|
+
if (sourceIndex === i) break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const normalizedAccounts = accounts.filter((_, index) => !removed.has(index));
|
|
300
|
+
return {
|
|
301
|
+
...storage,
|
|
302
|
+
accounts: normalizedAccounts,
|
|
303
|
+
activeIndex: Math.max(0, Math.min(storage.activeIndex ?? 0, Math.max(0, normalizedAccounts.length - 1))),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function maskValue(value, includeSensitive) {
|
|
308
|
+
if (includeSensitive || typeof value !== "string" || value.length <= 8) return value;
|
|
309
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function summarizeStandaloneAccounts(storage, includeSensitive, tag) {
|
|
313
|
+
const accounts = Array.isArray(storage?.accounts) ? storage.accounts : [];
|
|
314
|
+
const normalizedTag = typeof tag === "string" ? tag.trim().toLowerCase() : "";
|
|
315
|
+
return accounts
|
|
316
|
+
.map((account, index) => ({ account, index }))
|
|
317
|
+
.filter(({ account }) => !normalizedTag ||
|
|
318
|
+
(Array.isArray(account?.accountTags) &&
|
|
319
|
+
account.accountTags.some((entry) => String(entry).toLowerCase() === normalizedTag)))
|
|
320
|
+
.map(({ account, index }) => ({
|
|
321
|
+
index,
|
|
322
|
+
label: account?.accountLabel ?? `Account ${index + 1}`,
|
|
323
|
+
email: maskValue(account?.email, includeSensitive),
|
|
324
|
+
accountId: maskValue(account?.accountId, includeSensitive),
|
|
325
|
+
accountIdSource: account?.accountIdSource,
|
|
326
|
+
enabled: account?.enabled !== false,
|
|
327
|
+
hasRefreshToken: typeof account?.refreshToken === "string" && account.refreshToken.length > 0,
|
|
328
|
+
hasAccessToken: typeof account?.accessToken === "string" && account.accessToken.length > 0,
|
|
329
|
+
expiresAt: account?.expiresAt,
|
|
330
|
+
expired: typeof account?.expiresAt === "number" ? account.expiresAt <= Date.now() : undefined,
|
|
331
|
+
tags: Array.isArray(account?.accountTags) ? account.accountTags : [],
|
|
332
|
+
note: account?.accountNote,
|
|
333
|
+
rateLimitResetTimes: account?.rateLimitResetTimes ?? {},
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function printStandaloneResult(command, payload, json) {
|
|
338
|
+
if (json) {
|
|
339
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
console.log(`oc-codex-multi-auth ${command}`);
|
|
343
|
+
if (payload.message) console.log(payload.message);
|
|
344
|
+
console.log(`Storage: ${payload.storagePath}`);
|
|
345
|
+
console.log(`Accounts: ${payload.totalAccounts}`);
|
|
346
|
+
if (Array.isArray(payload.accounts)) {
|
|
347
|
+
for (const account of payload.accounts) {
|
|
348
|
+
console.log(`- [${account.index}] ${account.label} enabled=${account.enabled} refresh=${account.hasRefreshToken} access=${account.hasAccessToken}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (payload.error) console.log(`Error: ${payload.error}`);
|
|
352
|
+
if (payload.nextAction) console.log(`Next: ${payload.nextAction}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export async function runStandaloneCommand(command, argv = [], options = {}) {
|
|
356
|
+
const parsed = parseStandaloneArgs(argv);
|
|
357
|
+
if (command === "diag") {
|
|
358
|
+
command = "doctor";
|
|
359
|
+
parsed.deep = true;
|
|
360
|
+
}
|
|
361
|
+
if (parsed.help) {
|
|
362
|
+
printHelp();
|
|
363
|
+
return { exitCode: 0, action: "help" };
|
|
364
|
+
}
|
|
365
|
+
const { env = process.env } = options;
|
|
366
|
+
const storagePath = getStandaloneStoragePath(parsed, env);
|
|
367
|
+
const { storage, error } = await readStandaloneStorage(storagePath);
|
|
368
|
+
const accounts = summarizeStandaloneAccounts(storage, parsed.includeSensitive, parsed.tag);
|
|
369
|
+
const totalAccounts = Array.isArray(storage?.accounts) ? storage.accounts.length : 0;
|
|
370
|
+
const payload = {
|
|
371
|
+
command,
|
|
372
|
+
storagePath,
|
|
373
|
+
totalAccounts,
|
|
374
|
+
shownAccounts: accounts.length,
|
|
375
|
+
activeIndex: typeof storage?.activeIndex === "number" ? storage.activeIndex : 0,
|
|
376
|
+
activeIndexByFamily: storage?.activeIndexByFamily ?? {},
|
|
377
|
+
accounts,
|
|
378
|
+
error,
|
|
379
|
+
};
|
|
380
|
+
if (command === "dashboard") {
|
|
381
|
+
payload.message = "Standalone dashboard server is not launched by this safe CLI; use status/list/limits/health or OpenCode codex-dashboard.";
|
|
382
|
+
payload.nextAction = "Run oc-codex-multi-auth status or open OpenCode and call codex-dashboard.";
|
|
383
|
+
} else if (command === "doctor") {
|
|
384
|
+
payload.message = error ? "Storage could not be parsed." : totalAccounts > 0 ? "Local diagnostics completed." : "No accounts configured.";
|
|
385
|
+
payload.deep = parsed.deep;
|
|
386
|
+
payload.fixApplied = parsed.fix ? false : undefined;
|
|
387
|
+
payload.nextAction = totalAccounts > 0 ? "Run oc-codex-multi-auth health --json for scriptable checks." : "Run opencode auth login.";
|
|
388
|
+
} else if (command === "limits") {
|
|
389
|
+
payload.rateLimits = accounts.map((account) => ({ index: account.index, rateLimitResetTimes: account.rateLimitResetTimes }));
|
|
390
|
+
} else if (command === "health") {
|
|
391
|
+
payload.healthyCount = accounts.filter((account) => account.enabled && account.hasRefreshToken).length;
|
|
392
|
+
payload.unhealthyCount = accounts.filter((account) => !account.enabled || !account.hasRefreshToken).length;
|
|
393
|
+
} else if (command === "status") {
|
|
394
|
+
payload.message = totalAccounts > 0 ? "Account storage loaded." : "No accounts configured.";
|
|
395
|
+
}
|
|
396
|
+
printStandaloneResult(command, payload, parsed.json);
|
|
397
|
+
return { exitCode: error ? 1 : 0, action: command, storagePath };
|
|
398
|
+
}
|
|
399
|
+
|
|
176
400
|
// Top-level keys inside `provider.openai` that the installer owns absolutely.
|
|
177
401
|
// These are always sourced from the template (overwritten or removed) so the
|
|
178
402
|
// plugin's required runtime shape is authoritative. Any OTHER key the user has
|
|
@@ -443,7 +667,15 @@ async function clearCache(paths, dryRun, skipCacheClear) {
|
|
|
443
667
|
}
|
|
444
668
|
|
|
445
669
|
export async function runInstaller(argv = process.argv.slice(2), options = {}) {
|
|
446
|
-
const
|
|
670
|
+
const split = splitCommandArgv(argv);
|
|
671
|
+
if (split.kind === "standalone") {
|
|
672
|
+
return runStandaloneCommand(split.command, split.argv, options);
|
|
673
|
+
}
|
|
674
|
+
if (split.kind === "unknown") {
|
|
675
|
+
printHelp();
|
|
676
|
+
throw new Error(`Unknown command: ${split.command}`);
|
|
677
|
+
}
|
|
678
|
+
const parsed = parseCliArgs(split.argv);
|
|
447
679
|
if (parsed.wantsHelp) {
|
|
448
680
|
printHelp();
|
|
449
681
|
return { exitCode: 0, action: "help" };
|
|
@@ -575,6 +807,8 @@ export const __test = {
|
|
|
575
807
|
mergeOpenaiProvider,
|
|
576
808
|
mergeTuiConfig,
|
|
577
809
|
parseCliArgs,
|
|
810
|
+
runStandaloneCommand,
|
|
811
|
+
splitCommandArgv,
|
|
578
812
|
writeFileAtomic,
|
|
579
813
|
renameWithWindowsRetry,
|
|
580
814
|
resolveHomeDirectory,
|