@jsonstudio/rcc 0.89.682 → 0.89.873
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/dist/build-info.js +2 -2
- package/dist/cli.js +164 -116
- package/dist/cli.js.map +1 -1
- package/dist/client/anthropic/anthropic-protocol-client.js +42 -1
- package/dist/client/anthropic/anthropic-protocol-client.js.map +1 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js +4 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
- package/dist/commands/camoufox-backfill.d.ts +2 -0
- package/dist/commands/camoufox-backfill.js +33 -0
- package/dist/commands/camoufox-backfill.js.map +1 -0
- package/dist/commands/camoufox-fp.d.ts +2 -0
- package/dist/commands/camoufox-fp.js +86 -0
- package/dist/commands/camoufox-fp.js.map +1 -0
- package/dist/commands/oauth.d.ts +2 -0
- package/dist/commands/oauth.js +170 -0
- package/dist/commands/oauth.js.map +1 -0
- package/dist/commands/provider-update.js +439 -2
- package/dist/commands/provider-update.js.map +1 -1
- package/dist/commands/quota-status.d.ts +2 -0
- package/dist/commands/quota-status.js +80 -0
- package/dist/commands/quota-status.js.map +1 -0
- package/dist/commands/token-daemon.js +12 -1
- package/dist/commands/token-daemon.js.map +1 -1
- package/dist/config/provider-v2-loader.d.ts +16 -0
- package/dist/config/provider-v2-loader.js +84 -0
- package/dist/config/provider-v2-loader.js.map +1 -0
- package/dist/config/routecodex-config-loader.js +27 -4
- package/dist/config/routecodex-config-loader.js.map +1 -1
- package/dist/config/system-prompts/codex-cli.txt +1 -0
- package/dist/config/virtual-router-builder.d.ts +9 -0
- package/dist/config/virtual-router-builder.js +34 -0
- package/dist/config/virtual-router-builder.js.map +1 -0
- package/dist/config/virtual-router-types.d.ts +25 -0
- package/dist/config/virtual-router-types.js +30 -0
- package/dist/config/virtual-router-types.js.map +1 -0
- package/dist/manager/index.d.ts +10 -0
- package/dist/manager/index.js +27 -0
- package/dist/manager/index.js.map +1 -0
- package/dist/manager/modules/health/index.d.ts +22 -0
- package/dist/manager/modules/health/index.js +82 -0
- package/dist/manager/modules/health/index.js.map +1 -0
- package/dist/manager/modules/quota/index.d.ts +57 -0
- package/dist/manager/modules/quota/index.js +426 -0
- package/dist/manager/modules/quota/index.js.map +1 -0
- package/dist/manager/modules/routing/index.d.ts +17 -0
- package/dist/manager/modules/routing/index.js +61 -0
- package/dist/manager/modules/routing/index.js.map +1 -0
- package/dist/manager/modules/token/index.d.ts +10 -0
- package/dist/manager/modules/token/index.js +58 -0
- package/dist/manager/modules/token/index.js.map +1 -0
- package/dist/manager/storage/base-store.d.ts +6 -0
- package/dist/manager/storage/base-store.js +2 -0
- package/dist/manager/storage/base-store.js.map +1 -0
- package/dist/manager/storage/file-store.d.ts +25 -0
- package/dist/manager/storage/file-store.js +117 -0
- package/dist/manager/storage/file-store.js.map +1 -0
- package/dist/manager/types.d.ts +9 -0
- package/dist/manager/types.js +2 -0
- package/dist/manager/types.js.map +1 -0
- package/dist/message-center/index.d.ts +5 -0
- package/dist/message-center/index.js +6 -0
- package/dist/message-center/index.js.map +1 -0
- package/dist/message-center/message-center.d.ts +93 -0
- package/dist/message-center/message-center.js +189 -0
- package/dist/message-center/message-center.js.map +1 -0
- package/dist/providers/auth/antigravity-userinfo-helper.d.ts +2 -0
- package/dist/providers/auth/antigravity-userinfo-helper.js +102 -0
- package/dist/providers/auth/antigravity-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/iflow-cookie-auth.d.ts +27 -0
- package/dist/providers/auth/iflow-cookie-auth.js +209 -0
- package/dist/providers/auth/iflow-cookie-auth.js.map +1 -0
- package/dist/providers/auth/oauth-lifecycle.js +29 -22
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/auth/token-scanner/index.js +16 -1
- package/dist/providers/auth/token-scanner/index.js.map +1 -1
- package/dist/providers/core/config/camoufox-launcher.d.ts +16 -0
- package/dist/providers/core/config/camoufox-launcher.js +314 -0
- package/dist/providers/core/config/camoufox-launcher.js.map +1 -0
- package/dist/providers/core/config/oauth-flows.d.ts +9 -0
- package/dist/providers/core/config/oauth-flows.js +50 -19
- package/dist/providers/core/config/oauth-flows.js.map +1 -1
- package/dist/providers/core/config/provider-oauth-configs.d.ts +6 -0
- package/dist/providers/core/config/provider-oauth-configs.js +12 -0
- package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
- package/dist/providers/core/config/service-profiles.js +26 -3
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/runtime/antigravity-quota-client.d.ts +10 -0
- package/dist/providers/core/runtime/antigravity-quota-client.js +88 -0
- package/dist/providers/core/runtime/antigravity-quota-client.js.map +1 -0
- package/dist/providers/core/runtime/base-provider.d.ts +2 -1
- package/dist/providers/core/runtime/base-provider.js +93 -34
- package/dist/providers/core/runtime/base-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +42 -10
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.js +24 -0
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.d.ts +0 -3
- package/dist/providers/core/runtime/http-transport-provider.js +32 -136
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/provider-error-classifier.js +18 -10
- package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
- package/dist/providers/core/runtime/rate-limit-manager.d.ts +6 -0
- package/dist/providers/core/runtime/rate-limit-manager.js +23 -0
- package/dist/providers/core/runtime/rate-limit-manager.js.map +1 -1
- package/dist/providers/core/strategies/oauth-auth-code-flow.d.ts +1 -0
- package/dist/providers/core/strategies/oauth-auth-code-flow.js +3 -2
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/strategies/oauth-device-flow.d.ts +1 -0
- package/dist/providers/core/strategies/oauth-device-flow.js +3 -2
- package/dist/providers/core/strategies/oauth-device-flow.js.map +1 -1
- package/dist/providers/core/strategies/oauth-hybrid-flow.d.ts +1 -0
- package/dist/providers/core/strategies/oauth-hybrid-flow.js +3 -2
- package/dist/providers/core/strategies/oauth-hybrid-flow.js.map +1 -1
- package/dist/providers/core/utils/http-client.js +43 -1
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/mock/mock-provider-runtime.js +4 -4
- package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
- package/dist/providers/profile/provider-profile-loader.js +13 -1
- package/dist/providers/profile/provider-profile-loader.js.map +1 -1
- package/dist/providers/profile/provider-profile.d.ts +5 -0
- package/dist/scripts/camoufox/gen-fingerprint-env.py +171 -0
- package/dist/scripts/camoufox/launch-auth.mjs +617 -0
- package/dist/server/runtime/http-server/executor-provider.d.ts +1 -0
- package/dist/server/runtime/http-server/executor-provider.js +26 -0
- package/dist/server/runtime/http-server/executor-provider.js.map +1 -1
- package/dist/server/runtime/http-server/executor-response.d.ts +16 -0
- package/dist/server/runtime/http-server/executor-response.js +164 -0
- package/dist/server/runtime/http-server/executor-response.js.map +1 -0
- package/dist/server/runtime/http-server/index.d.ts +1 -0
- package/dist/server/runtime/http-server/index.js +88 -53
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.js +5 -19
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.d.ts +2 -0
- package/dist/server/runtime/http-server/routes.js +33 -1
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/types.d.ts +1 -0
- package/dist/server/utils/client-connection-state.d.ts +8 -0
- package/dist/server/utils/client-connection-state.js +52 -0
- package/dist/server/utils/client-connection-state.js.map +1 -0
- package/dist/server/utils/request-id-manager.js +21 -3
- package/dist/server/utils/request-id-manager.js.map +1 -1
- package/dist/token-daemon/history-store.d.ts +2 -0
- package/dist/token-daemon/history-store.js +6 -2
- package/dist/token-daemon/history-store.js.map +1 -1
- package/dist/token-daemon/index.js +36 -5
- package/dist/token-daemon/index.js.map +1 -1
- package/dist/token-daemon/leader-lock.d.ts +11 -0
- package/dist/token-daemon/leader-lock.js +79 -0
- package/dist/token-daemon/leader-lock.js.map +1 -0
- package/dist/token-daemon/message-bus-integrator.d.ts +98 -0
- package/dist/token-daemon/message-bus-integrator.js +144 -0
- package/dist/token-daemon/message-bus-integrator.js.map +1 -0
- package/dist/token-daemon/provider-registry.d.ts +22 -0
- package/dist/token-daemon/provider-registry.js +201 -0
- package/dist/token-daemon/provider-registry.js.map +1 -0
- package/dist/token-daemon/token-daemon.d.ts +8 -0
- package/dist/token-daemon/token-daemon.js +196 -11
- package/dist/token-daemon/token-daemon.js.map +1 -1
- package/dist/token-portal/local-token-portal.d.ts +1 -0
- package/dist/token-portal/local-token-portal.js +18 -0
- package/dist/token-portal/local-token-portal.js.map +1 -1
- package/dist/token-portal/render.js +1 -0
- package/dist/token-portal/render.js.map +1 -1
- package/dist/tools/error-log.d.ts +31 -0
- package/dist/tools/error-log.js +117 -0
- package/dist/tools/error-log.js.map +1 -0
- package/dist/tools/stats-request-events.d.ts +2 -0
- package/dist/tools/stats-request-events.js +16 -0
- package/dist/tools/stats-request-events.js.map +1 -0
- package/dist/tools/stats-usage.d.ts +31 -0
- package/dist/tools/stats-usage.js +206 -0
- package/dist/tools/stats-usage.js.map +1 -0
- package/package.json +8 -4
- package/scripts/analyze-codex-error-failures.mjs +109 -0
- package/scripts/camoufox/gen-fingerprint-env.py +171 -0
- package/scripts/camoufox/launch-auth.mjs +617 -0
- package/scripts/classify-codex-samples.mjs +251 -0
- package/scripts/cleanup-codex-error-samples.mjs +88 -0
- package/scripts/compare-codex-rccx.mjs +268 -0
- package/scripts/copy-compat-assets.mjs +18 -0
- package/scripts/install-release.sh +1 -1
- package/scripts/local-replay-openai-response.mjs +1 -2
- package/scripts/pack-mode.mjs +16 -6
- package/scripts/replay-codex-sample.mjs +24 -2
- package/scripts/responses-compare-server.mjs +119 -0
- package/scripts/verify-apply-patch.mjs +28 -17
- package/scripts/verify-codex-error-samples.mjs +99 -0
- package/scripts/verify-e2e-toolcall.mjs +19 -4
- package/scripts/virtual-router-shadow-v2-real.mjs +143 -0
- package/scripts/virtual-router-shadow-v2.mjs +122 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { interactiveRefresh } from '../token-daemon/index.js';
|
|
3
|
+
export function createOauthCommand() {
|
|
4
|
+
const cmd = new Command('oauth');
|
|
5
|
+
cmd
|
|
6
|
+
.description('Force OAuth re-authentication for a specific token (opens browser / Camoufox when enabled)')
|
|
7
|
+
.argument('<selector>', 'Token selector: file basename, full path, or provider id (e.g. "iflow-oauth-1-186.json" or "iflow")')
|
|
8
|
+
.action(async (selector) => {
|
|
9
|
+
await interactiveRefresh(selector);
|
|
10
|
+
});
|
|
11
|
+
cmd
|
|
12
|
+
.command('iflow-auto')
|
|
13
|
+
.description('Trigger iFlow OAuth re-auth using Camoufox automation (auto portal + account selection)')
|
|
14
|
+
.argument('<selector>', 'Token selector: file basename, full path, or provider id (e.g. "iflow-oauth-1-186.json" or "iflow")')
|
|
15
|
+
.option('--dev', 'Run Camoufox automation in headed debug mode (default headless)')
|
|
16
|
+
.action(async (selector, options) => {
|
|
17
|
+
const prevBrowser = process.env.ROUTECODEX_OAUTH_BROWSER;
|
|
18
|
+
const prevAutoMode = process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE;
|
|
19
|
+
const prevDevMode = process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
20
|
+
process.env.ROUTECODEX_OAUTH_BROWSER = 'camoufox';
|
|
21
|
+
process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE = 'iflow';
|
|
22
|
+
process.env.ROUTECODEX_OAUTH_AUTO_CONFIRM = '1';
|
|
23
|
+
if (options?.dev) {
|
|
24
|
+
process.env.ROUTECODEX_CAMOUFOX_DEV_MODE = '1';
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
delete process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
await interactiveRefresh(selector);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
if (prevBrowser === undefined) {
|
|
34
|
+
delete process.env.ROUTECODEX_OAUTH_BROWSER;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
process.env.ROUTECODEX_OAUTH_BROWSER = prevBrowser;
|
|
38
|
+
}
|
|
39
|
+
if (prevAutoMode === undefined) {
|
|
40
|
+
delete process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE = prevAutoMode;
|
|
44
|
+
}
|
|
45
|
+
if (prevDevMode === undefined) {
|
|
46
|
+
delete process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
process.env.ROUTECODEX_CAMOUFOX_DEV_MODE = prevDevMode;
|
|
50
|
+
}
|
|
51
|
+
delete process.env.ROUTECODEX_OAUTH_AUTO_CONFIRM;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
cmd
|
|
55
|
+
.command('gemini-auto')
|
|
56
|
+
.description('Trigger Gemini OAuth re-auth using Camoufox automation (auto account selection + confirmation)')
|
|
57
|
+
.argument('<selector>', 'Token selector: file basename, full path, or provider id (e.g. "gemini-oauth-1-foo.json" or "gemini-cli")')
|
|
58
|
+
.option('--dev', 'Run Camoufox automation in headed debug mode (default headless)')
|
|
59
|
+
.option('--account-text <text>', 'Preferred Gemini account display text to auto-select')
|
|
60
|
+
.action(async (selector, options) => {
|
|
61
|
+
const prevBrowser = process.env.ROUTECODEX_OAUTH_BROWSER;
|
|
62
|
+
const prevAutoMode = process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE;
|
|
63
|
+
const prevDevMode = process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
64
|
+
const prevAccountText = process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT;
|
|
65
|
+
process.env.ROUTECODEX_OAUTH_BROWSER = 'camoufox';
|
|
66
|
+
process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE = 'gemini';
|
|
67
|
+
process.env.ROUTECODEX_OAUTH_AUTO_CONFIRM = '1';
|
|
68
|
+
if (options?.dev) {
|
|
69
|
+
process.env.ROUTECODEX_CAMOUFOX_DEV_MODE = '1';
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
delete process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
73
|
+
}
|
|
74
|
+
if (options?.accountText) {
|
|
75
|
+
process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT = options.accountText;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
delete process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
await interactiveRefresh(selector);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
if (prevBrowser === undefined) {
|
|
85
|
+
delete process.env.ROUTECODEX_OAUTH_BROWSER;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
process.env.ROUTECODEX_OAUTH_BROWSER = prevBrowser;
|
|
89
|
+
}
|
|
90
|
+
if (prevAutoMode === undefined) {
|
|
91
|
+
delete process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE = prevAutoMode;
|
|
95
|
+
}
|
|
96
|
+
if (prevDevMode === undefined) {
|
|
97
|
+
delete process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
process.env.ROUTECODEX_CAMOUFOX_DEV_MODE = prevDevMode;
|
|
101
|
+
}
|
|
102
|
+
if (prevAccountText === undefined) {
|
|
103
|
+
delete process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT = prevAccountText;
|
|
107
|
+
}
|
|
108
|
+
delete process.env.ROUTECODEX_OAUTH_AUTO_CONFIRM;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
cmd
|
|
112
|
+
.command('antigravity-auto')
|
|
113
|
+
.description('Trigger Antigravity OAuth re-auth using Camoufox automation (auto account selection + confirmation)')
|
|
114
|
+
.argument('<selector>', 'Token selector: file basename, full path, or provider id (e.g. "antigravity-oauth-1-foo.json" or "antigravity")')
|
|
115
|
+
.option('--dev', 'Run Camoufox automation in headed debug mode (default headless)')
|
|
116
|
+
.option('--account-text <text>', 'Preferred Antigravity account display text/email to auto-select')
|
|
117
|
+
.action(async (selector, options) => {
|
|
118
|
+
const prevBrowser = process.env.ROUTECODEX_OAUTH_BROWSER;
|
|
119
|
+
const prevAutoMode = process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE;
|
|
120
|
+
const prevDevMode = process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
121
|
+
const prevAccountText = process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT;
|
|
122
|
+
process.env.ROUTECODEX_OAUTH_BROWSER = 'camoufox';
|
|
123
|
+
process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE = 'antigravity';
|
|
124
|
+
process.env.ROUTECODEX_OAUTH_AUTO_CONFIRM = '1';
|
|
125
|
+
if (options?.dev) {
|
|
126
|
+
process.env.ROUTECODEX_CAMOUFOX_DEV_MODE = '1';
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
delete process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
130
|
+
}
|
|
131
|
+
if (options?.accountText) {
|
|
132
|
+
process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT = options.accountText;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
delete process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
await interactiveRefresh(selector);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
if (prevBrowser === undefined) {
|
|
142
|
+
delete process.env.ROUTECODEX_OAUTH_BROWSER;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
process.env.ROUTECODEX_OAUTH_BROWSER = prevBrowser;
|
|
146
|
+
}
|
|
147
|
+
if (prevAutoMode === undefined) {
|
|
148
|
+
delete process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
process.env.ROUTECODEX_CAMOUFOX_AUTO_MODE = prevAutoMode;
|
|
152
|
+
}
|
|
153
|
+
if (prevDevMode === undefined) {
|
|
154
|
+
delete process.env.ROUTECODEX_CAMOUFOX_DEV_MODE;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
process.env.ROUTECODEX_CAMOUFOX_DEV_MODE = prevDevMode;
|
|
158
|
+
}
|
|
159
|
+
if (prevAccountText === undefined) {
|
|
160
|
+
delete process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
process.env.ROUTECODEX_CAMOUFOX_ACCOUNT_TEXT = prevAccountText;
|
|
164
|
+
}
|
|
165
|
+
delete process.env.ROUTECODEX_OAUTH_AUTO_CONFIRM;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return cmd;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/commands/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,205 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import readline from 'node:readline';
|
|
3
6
|
import { updateProviderModels } from '../tools/provider-update/index.js';
|
|
7
|
+
import { API_ENDPOINTS } from '../constants/index.js';
|
|
8
|
+
import { loadProviderConfigsV2 } from '../config/provider-v2-loader.js';
|
|
9
|
+
function resolveProviderRoot(customRoot) {
|
|
10
|
+
const trimmed = typeof customRoot === 'string' ? customRoot.trim() : '';
|
|
11
|
+
if (trimmed) {
|
|
12
|
+
return path.resolve(trimmed);
|
|
13
|
+
}
|
|
14
|
+
return path.join(os.homedir(), '.routecodex', 'provider');
|
|
15
|
+
}
|
|
16
|
+
async function ensureDir(p) {
|
|
17
|
+
await fs.mkdir(p, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
async function fileExists(p) {
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(p);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function ask(question, defaultValue) {
|
|
29
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
30
|
+
const prompt = defaultValue && defaultValue.trim().length
|
|
31
|
+
? `${question} [${defaultValue}]: `
|
|
32
|
+
: `${question}: `;
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
rl.question(prompt, (answer) => {
|
|
35
|
+
rl.close();
|
|
36
|
+
const raw = String(answer || '').trim();
|
|
37
|
+
resolve(raw || (defaultValue ?? ''));
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function askYesNo(question, defaultYes = true) {
|
|
42
|
+
const suffix = defaultYes ? ' [Y/n]: ' : ' [y/N]: ';
|
|
43
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
rl.question(`${question}${suffix}`, (answer) => {
|
|
46
|
+
rl.close();
|
|
47
|
+
const raw = String(answer || '').trim().toLowerCase();
|
|
48
|
+
if (!raw) {
|
|
49
|
+
resolve(defaultYes);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
resolve(raw === 'y' || raw === 'yes');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const PROVIDER_TEMPLATES = [
|
|
57
|
+
{
|
|
58
|
+
id: 'glm',
|
|
59
|
+
label: 'GLM (Zhipu coding)',
|
|
60
|
+
providerTypeHint: 'glm',
|
|
61
|
+
defaultBaseUrl: 'https://open.bigmodel.cn/api/coding/paas/v4',
|
|
62
|
+
defaultModel: 'glm-4.7',
|
|
63
|
+
defaultCompat: 'chat:glm',
|
|
64
|
+
defaultAuthType: 'apikey'
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'qwen',
|
|
68
|
+
label: 'Qwen Code (portal.qwen.ai)',
|
|
69
|
+
providerTypeHint: 'qwen',
|
|
70
|
+
defaultBaseUrl: 'https://portal.qwen.ai/v1',
|
|
71
|
+
defaultModel: 'qwen3-coder-plus',
|
|
72
|
+
defaultCompat: 'chat:qwen',
|
|
73
|
+
defaultAuthType: 'qwen-oauth'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'iflow',
|
|
77
|
+
label: 'iFlow aggregator',
|
|
78
|
+
providerTypeHint: 'iflow',
|
|
79
|
+
defaultBaseUrl: 'https://apis.iflow.cn/v1',
|
|
80
|
+
defaultModel: 'kimi-k2',
|
|
81
|
+
defaultCompat: 'chat:iflow',
|
|
82
|
+
defaultAuthType: 'iflow-oauth'
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'kimi',
|
|
86
|
+
label: 'Kimi Coding',
|
|
87
|
+
providerTypeHint: 'openai',
|
|
88
|
+
defaultBaseUrl: 'https://api.kimi.com/coding/v1',
|
|
89
|
+
defaultModel: 'kimi-for-coding',
|
|
90
|
+
defaultAuthType: 'apikey'
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'modelscope',
|
|
94
|
+
label: 'ModelScope (OpenAI-compatible)',
|
|
95
|
+
providerTypeHint: 'openai',
|
|
96
|
+
defaultBaseUrl: 'https://api-inference.modelscope.cn/v1',
|
|
97
|
+
defaultModel: 'Qwen/Qwen3-Coder-480B-A35B-Instruct',
|
|
98
|
+
defaultAuthType: 'apikey'
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'gemini-cli',
|
|
102
|
+
label: 'Gemini CLI (Cloud Code Assist)',
|
|
103
|
+
providerTypeHint: 'gemini-cli',
|
|
104
|
+
defaultBaseUrl: 'https://cloudcode-pa.googleapis.com',
|
|
105
|
+
defaultModel: 'gemini-2.5-pro',
|
|
106
|
+
defaultAuthType: 'gemini-cli-oauth'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'antigravity',
|
|
110
|
+
label: 'Antigravity (Gemini CLI dev)',
|
|
111
|
+
providerTypeHint: 'antigravity',
|
|
112
|
+
defaultBaseUrl: 'https://cloudcode-pa.googleapis.com',
|
|
113
|
+
defaultModel: 'gemini-2.5-pro',
|
|
114
|
+
defaultAuthType: 'gemini-cli-oauth'
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'gemini',
|
|
118
|
+
label: 'Gemini HTTP API',
|
|
119
|
+
providerTypeHint: 'gemini',
|
|
120
|
+
defaultBaseUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
|
121
|
+
defaultModel: 'models/gemini-2.0-flash',
|
|
122
|
+
defaultAuthType: 'apikey'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: 'openai',
|
|
126
|
+
label: 'OpenAI Chat (api.openai.com)',
|
|
127
|
+
providerTypeHint: 'openai',
|
|
128
|
+
defaultBaseUrl: API_ENDPOINTS.OPENAI,
|
|
129
|
+
defaultModel: 'gpt-4.1',
|
|
130
|
+
defaultAuthType: 'apikey'
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'anthropic',
|
|
134
|
+
label: 'Anthropic Messages (api.anthropic.com)',
|
|
135
|
+
providerTypeHint: 'anthropic',
|
|
136
|
+
defaultBaseUrl: API_ENDPOINTS.ANTHROPIC,
|
|
137
|
+
defaultModel: 'claude-3.5-sonnet',
|
|
138
|
+
defaultAuthType: 'apikey'
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'responses',
|
|
142
|
+
label: 'Responses-style aggregator (ChatGPT/Codex compatible)',
|
|
143
|
+
providerTypeHint: 'responses',
|
|
144
|
+
defaultAuthType: 'apikey'
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'custom',
|
|
148
|
+
label: 'Custom provider (manual configuration)',
|
|
149
|
+
providerTypeHint: 'openai'
|
|
150
|
+
}
|
|
151
|
+
];
|
|
152
|
+
function pickTemplate(id) {
|
|
153
|
+
const normalized = (id || '').trim().toLowerCase();
|
|
154
|
+
if (!normalized) {
|
|
155
|
+
return PROVIDER_TEMPLATES[0];
|
|
156
|
+
}
|
|
157
|
+
const found = PROVIDER_TEMPLATES.find((t) => t.id === normalized);
|
|
158
|
+
return found ?? PROVIDER_TEMPLATES[PROVIDER_TEMPLATES.length - 1];
|
|
159
|
+
}
|
|
160
|
+
function buildProviderFromTemplate(providerId, tpl, baseUrl, authType, apiKeyOrPlaceholder, tokenFile, primaryModelId) {
|
|
161
|
+
const auth = { type: authType };
|
|
162
|
+
if (authType.toLowerCase().includes('apikey')) {
|
|
163
|
+
if (apiKeyOrPlaceholder.trim()) {
|
|
164
|
+
auth.apiKey = apiKeyOrPlaceholder.trim();
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
auth.apiKey = 'YOUR_API_KEY_HERE';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (authType.toLowerCase().includes('oauth')) {
|
|
171
|
+
if (tokenFile.trim()) {
|
|
172
|
+
auth.tokenFile = tokenFile.trim();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const models = {};
|
|
176
|
+
if (primaryModelId.trim()) {
|
|
177
|
+
models[primaryModelId.trim()] = {
|
|
178
|
+
supportsStreaming: true
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const provider = {
|
|
182
|
+
id: providerId,
|
|
183
|
+
enabled: true,
|
|
184
|
+
type: tpl.providerTypeHint,
|
|
185
|
+
baseURL: baseUrl,
|
|
186
|
+
auth,
|
|
187
|
+
models
|
|
188
|
+
};
|
|
189
|
+
if (tpl.defaultCompat) {
|
|
190
|
+
provider.compatibilityProfile = tpl.defaultCompat;
|
|
191
|
+
}
|
|
192
|
+
if (tpl.id === 'responses') {
|
|
193
|
+
provider.responses = {
|
|
194
|
+
process: 'chat',
|
|
195
|
+
streaming: 'always'
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return provider;
|
|
199
|
+
}
|
|
4
200
|
export function createProviderUpdateCommand() {
|
|
5
201
|
const cmd = new Command('provider');
|
|
202
|
+
// provider update (existing behavior)
|
|
6
203
|
const update = new Command('update')
|
|
7
204
|
.description('Update a provider\'s model list and generate a minimal single-provider config')
|
|
8
205
|
.requiredOption('-c, --config <file>', 'Provider input config JSON (contains providerId/type/baseUrl/auth)')
|
|
@@ -17,7 +214,9 @@ export function createProviderUpdateCommand() {
|
|
|
17
214
|
.option('--probe-keys', 'Probe apiKey list and set auth.apiKey to first working key', false)
|
|
18
215
|
.option('--verbose', 'Verbose logs', false)
|
|
19
216
|
.action(async (opts) => {
|
|
20
|
-
const splitCsv = (s) => (typeof s === 'string' && s.trim())
|
|
217
|
+
const splitCsv = (s) => (typeof s === 'string' && s.trim())
|
|
218
|
+
? s.split(',').map((x) => x.trim()).filter(Boolean)
|
|
219
|
+
: [];
|
|
21
220
|
const args = {
|
|
22
221
|
providerId: opts.provider,
|
|
23
222
|
configPath: path.resolve(opts.config),
|
|
@@ -47,7 +246,245 @@ export function createProviderUpdateCommand() {
|
|
|
47
246
|
process.exit(1);
|
|
48
247
|
}
|
|
49
248
|
});
|
|
249
|
+
// provider list
|
|
250
|
+
const list = new Command('list')
|
|
251
|
+
.description('List provider v2 configs under ~/.routecodex/provider')
|
|
252
|
+
.option('--root <dir>', 'Override provider root directory')
|
|
253
|
+
.option('--json', 'Output raw JSON', false)
|
|
254
|
+
.action(async (opts) => {
|
|
255
|
+
const root = resolveProviderRoot(opts.root);
|
|
256
|
+
await ensureDir(root);
|
|
257
|
+
const configs = await loadProviderConfigsV2(root);
|
|
258
|
+
const entries = Object.entries(configs);
|
|
259
|
+
if (opts.json) {
|
|
260
|
+
const payload = entries.map(([id, cfg]) => ({
|
|
261
|
+
providerId: id,
|
|
262
|
+
version: cfg.version,
|
|
263
|
+
provider: cfg.provider
|
|
264
|
+
}));
|
|
265
|
+
console.log(JSON.stringify({ root, providers: payload }, null, 2));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (!entries.length) {
|
|
269
|
+
console.log(`No provider v2 configs found under ${root}`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
console.log(`Provider v2 configs under ${root}:`);
|
|
273
|
+
for (const [id, cfg] of entries) {
|
|
274
|
+
const node = cfg.provider;
|
|
275
|
+
const type = typeof node.type === 'string' ? node.type : '-';
|
|
276
|
+
const providerType = typeof node.providerType === 'string'
|
|
277
|
+
? String(node.providerType)
|
|
278
|
+
: '-';
|
|
279
|
+
const baseUrl = typeof node.baseUrl === 'string'
|
|
280
|
+
? String(node.baseUrl)
|
|
281
|
+
: typeof node.baseURL === 'string'
|
|
282
|
+
? String(node.baseURL)
|
|
283
|
+
: '-';
|
|
284
|
+
const modelsNode = node.models;
|
|
285
|
+
const modelCount = modelsNode && typeof modelsNode === 'object' && !Array.isArray(modelsNode)
|
|
286
|
+
? Object.keys(modelsNode).length
|
|
287
|
+
: 0;
|
|
288
|
+
console.log(`- ${id}: type=${type}, providerType=${providerType}, baseUrl=${baseUrl}, models=${modelCount}`);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
// provider add
|
|
292
|
+
const add = new Command('add')
|
|
293
|
+
.description('Interactively create a new provider v2 config')
|
|
294
|
+
.option('-i, --id <id>', 'Provider id (e.g. glm, qwen, iflow, gemini-cli, antigravity, modelscope, kimi, openai)')
|
|
295
|
+
.option('--root <dir>', 'Override provider root directory')
|
|
296
|
+
.action(async (opts) => {
|
|
297
|
+
let providerId = (opts.id || '').trim();
|
|
298
|
+
if (!providerId) {
|
|
299
|
+
providerId = await ask('Provider id (e.g. glm, qwen, iflow, kimi, modelscope, gemini-cli, antigravity, openai)', 'glm');
|
|
300
|
+
}
|
|
301
|
+
if (!providerId.trim()) {
|
|
302
|
+
console.error('Provider id is required');
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
const root = resolveProviderRoot(opts.root);
|
|
306
|
+
await ensureDir(root);
|
|
307
|
+
const dir = path.join(root, providerId);
|
|
308
|
+
await ensureDir(dir);
|
|
309
|
+
const v2Path = path.join(dir, 'config.v2.json');
|
|
310
|
+
if (await fileExists(v2Path)) {
|
|
311
|
+
const shouldOverwrite = await askYesNo(`config.v2.json already exists for "${providerId}". Overwrite?`, false);
|
|
312
|
+
if (!shouldOverwrite) {
|
|
313
|
+
console.log('Aborted');
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
console.log('Available provider templates:');
|
|
318
|
+
for (const tpl of PROVIDER_TEMPLATES) {
|
|
319
|
+
console.log(`- ${tpl.id}: ${tpl.label}`);
|
|
320
|
+
}
|
|
321
|
+
const templateIdRaw = await ask('Template id', 'glm');
|
|
322
|
+
const tpl = pickTemplate(templateIdRaw);
|
|
323
|
+
const baseUrlDefault = tpl.defaultBaseUrl ?? '';
|
|
324
|
+
const baseUrl = await ask('Base URL', baseUrlDefault);
|
|
325
|
+
if (!baseUrl.trim()) {
|
|
326
|
+
console.error('Base URL is required');
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
const authTypeDefault = tpl.defaultAuthType ?? 'apikey';
|
|
330
|
+
const authType = await ask('Auth type (e.g. apikey, oauth, qwen-oauth, iflow-oauth, gemini-cli-oauth)', authTypeDefault);
|
|
331
|
+
let apiKeyPlaceholder = '';
|
|
332
|
+
let tokenFile = '';
|
|
333
|
+
if (authType.toLowerCase().includes('apikey')) {
|
|
334
|
+
apiKeyPlaceholder = await ask('API key (or placeholder, e.g. ${PROVIDER_API_KEY:-})', 'YOUR_API_KEY_HERE');
|
|
335
|
+
}
|
|
336
|
+
else if (authType.toLowerCase().includes('oauth')) {
|
|
337
|
+
tokenFile = await ask('Token file path or alias (leave empty to use default)', '');
|
|
338
|
+
}
|
|
339
|
+
const modelDefault = tpl.defaultModel ?? '';
|
|
340
|
+
const primaryModelId = await ask('Primary model id (at least one)', modelDefault);
|
|
341
|
+
if (!primaryModelId.trim()) {
|
|
342
|
+
console.error('Primary model id is required');
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
const provider = buildProviderFromTemplate(providerId, tpl, baseUrl, authType, apiKeyPlaceholder, tokenFile, primaryModelId);
|
|
346
|
+
const payload = {
|
|
347
|
+
version: '2.0.0',
|
|
348
|
+
providerId,
|
|
349
|
+
provider
|
|
350
|
+
};
|
|
351
|
+
console.log('\nPlanned config.v2.json content:\n');
|
|
352
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
353
|
+
const confirm = await askYesNo('Write this provider config?', true);
|
|
354
|
+
if (!confirm) {
|
|
355
|
+
console.log('Aborted');
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
await fs.writeFile(v2Path, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
359
|
+
console.log(`Provider "${providerId}" written to ${v2Path}`);
|
|
360
|
+
});
|
|
361
|
+
// provider change
|
|
362
|
+
const change = new Command('change')
|
|
363
|
+
.description('Interactively modify an existing provider v2 config')
|
|
364
|
+
.argument('<id>', 'Provider id to modify')
|
|
365
|
+
.option('--root <dir>', 'Override provider root directory')
|
|
366
|
+
.action(async (id, opts) => {
|
|
367
|
+
const providerId = (id || '').trim();
|
|
368
|
+
if (!providerId) {
|
|
369
|
+
console.error('Provider id is required');
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
const root = resolveProviderRoot(opts.root);
|
|
373
|
+
const dir = path.join(root, providerId);
|
|
374
|
+
const v2Path = path.join(dir, 'config.v2.json');
|
|
375
|
+
if (!(await fileExists(v2Path))) {
|
|
376
|
+
console.error(`No config.v2.json found for provider "${providerId}" under ${dir}`);
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
const raw = await fs.readFile(v2Path, 'utf8');
|
|
380
|
+
let parsed;
|
|
381
|
+
try {
|
|
382
|
+
parsed = JSON.parse(raw);
|
|
383
|
+
}
|
|
384
|
+
catch (e) {
|
|
385
|
+
console.error('Failed to parse existing config.v2.json:', e?.message ?? String(e));
|
|
386
|
+
process.exit(1);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const node = (parsed.provider ?? {});
|
|
390
|
+
const currentBaseUrl = typeof node.baseUrl === 'string'
|
|
391
|
+
? String(node.baseUrl)
|
|
392
|
+
: typeof node.baseURL === 'string'
|
|
393
|
+
? String(node.baseURL)
|
|
394
|
+
: '';
|
|
395
|
+
const baseUrl = await ask('Base URL', currentBaseUrl);
|
|
396
|
+
if (baseUrl.trim()) {
|
|
397
|
+
node.baseURL = baseUrl.trim();
|
|
398
|
+
}
|
|
399
|
+
const authNode = (node.auth ?? {});
|
|
400
|
+
const currentAuthType = typeof authNode.type === 'string'
|
|
401
|
+
? authNode.type
|
|
402
|
+
: 'apikey';
|
|
403
|
+
const authType = await ask('Auth type (e.g. apikey, oauth, qwen-oauth, iflow-oauth, gemini-cli-oauth)', currentAuthType);
|
|
404
|
+
authNode.type = authType;
|
|
405
|
+
let apiKeyPlaceholder = typeof authNode.apiKey === 'string'
|
|
406
|
+
? String(authNode.apiKey)
|
|
407
|
+
: 'YOUR_API_KEY_HERE';
|
|
408
|
+
let tokenFile = typeof authNode.tokenFile === 'string'
|
|
409
|
+
? String(authNode.tokenFile)
|
|
410
|
+
: '';
|
|
411
|
+
if (authType.toLowerCase().includes('apikey')) {
|
|
412
|
+
apiKeyPlaceholder = await ask('API key (or placeholder, e.g. ${PROVIDER_API_KEY:-})', apiKeyPlaceholder);
|
|
413
|
+
authNode.apiKey = apiKeyPlaceholder;
|
|
414
|
+
delete authNode.tokenFile;
|
|
415
|
+
}
|
|
416
|
+
else if (authType.toLowerCase().includes('oauth')) {
|
|
417
|
+
tokenFile = await ask('Token file path or alias (leave empty to use default)', tokenFile);
|
|
418
|
+
if (tokenFile.trim()) {
|
|
419
|
+
authNode.tokenFile = tokenFile.trim();
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
delete authNode.tokenFile;
|
|
423
|
+
}
|
|
424
|
+
delete authNode.apiKey;
|
|
425
|
+
}
|
|
426
|
+
node.auth = authNode;
|
|
427
|
+
const modelsNode = (node.models ?? {});
|
|
428
|
+
const existingModelIds = Object.keys(modelsNode);
|
|
429
|
+
const currentPrimary = existingModelIds[0] ?? '';
|
|
430
|
+
const primaryModelId = await ask('Primary model id', currentPrimary);
|
|
431
|
+
if (primaryModelId.trim()) {
|
|
432
|
+
if (!modelsNode[primaryModelId.trim()]) {
|
|
433
|
+
modelsNode[primaryModelId.trim()] = { supportsStreaming: true };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
node.models = modelsNode;
|
|
437
|
+
parsed.provider = node;
|
|
438
|
+
console.log('\nUpdated config.v2.json content:\n');
|
|
439
|
+
console.log(JSON.stringify(parsed, null, 2));
|
|
440
|
+
const confirm = await askYesNo('Save changes to this provider config?', true);
|
|
441
|
+
if (!confirm) {
|
|
442
|
+
console.log('Aborted');
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
await fs.writeFile(v2Path, `${JSON.stringify(parsed, null, 2)}\n`, 'utf8');
|
|
446
|
+
console.log(`Provider "${providerId}" updated at ${v2Path}`);
|
|
447
|
+
});
|
|
448
|
+
// provider delete
|
|
449
|
+
const del = new Command('delete')
|
|
450
|
+
.description('Delete provider v2 config (config.v2.json only by default)')
|
|
451
|
+
.argument('<id>', 'Provider id to delete')
|
|
452
|
+
.option('--root <dir>', 'Override provider root directory')
|
|
453
|
+
.option('--purge', 'Remove entire provider directory (including runtime state)', false)
|
|
454
|
+
.action(async (id, opts) => {
|
|
455
|
+
const providerId = (id || '').trim();
|
|
456
|
+
if (!providerId) {
|
|
457
|
+
console.error('Provider id is required');
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
const root = resolveProviderRoot(opts.root);
|
|
461
|
+
const dir = path.join(root, providerId);
|
|
462
|
+
const v2Path = path.join(dir, 'config.v2.json');
|
|
463
|
+
const targetDescription = opts.purge ? `directory ${dir}` : `file ${v2Path}`;
|
|
464
|
+
const confirmed = await askYesNo(`Are you sure you want to delete provider "${providerId}" (${targetDescription})?`, false);
|
|
465
|
+
if (!confirmed) {
|
|
466
|
+
console.log('Aborted');
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
if (opts.purge) {
|
|
471
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
await fs.unlink(v2Path);
|
|
475
|
+
}
|
|
476
|
+
console.log(`Provider "${providerId}" deleted (${targetDescription})`);
|
|
477
|
+
}
|
|
478
|
+
catch (e) {
|
|
479
|
+
console.error('Failed to delete provider:', e?.message ?? String(e));
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
50
483
|
cmd.addCommand(update);
|
|
484
|
+
cmd.addCommand(list);
|
|
485
|
+
cmd.addCommand(add);
|
|
486
|
+
cmd.addCommand(change);
|
|
487
|
+
cmd.addCommand(del);
|
|
51
488
|
return cmd;
|
|
52
489
|
}
|
|
53
490
|
//# sourceMappingURL=provider-update.js.map
|