@h1d3rone/claude-proxy 0.1.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 +338 -0
- package/config_example.toml +11 -0
- package/package.json +38 -0
- package/src/cli.js +458 -0
- package/src/config.js +494 -0
- package/src/proxy/converters.js +533 -0
- package/src/proxy/server.js +271 -0
- package/src/services/client-config-manager.js +302 -0
- package/src/services/host-manager.js +369 -0
- package/src/utils.js +224 -0
package/src/cli.js
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs/promises");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const toml = require("@iarna/toml");
|
|
6
|
+
const { Command } = require("commander");
|
|
7
|
+
const {
|
|
8
|
+
clearConfigSection,
|
|
9
|
+
getDefaultConfigDisplayPath,
|
|
10
|
+
getDefaultConfigPath,
|
|
11
|
+
loadConfig,
|
|
12
|
+
promptForConfig,
|
|
13
|
+
promptForConfigSection,
|
|
14
|
+
readConfigDocument
|
|
15
|
+
} = require("./config");
|
|
16
|
+
const {
|
|
17
|
+
applyClaudeManagedHostConfig,
|
|
18
|
+
applyManagedHostConfig,
|
|
19
|
+
applyOpenAIManagedHostConfig,
|
|
20
|
+
cleanClaudeManagedHostConfig,
|
|
21
|
+
cleanManagedHostConfig,
|
|
22
|
+
cleanOpenAIManagedHostConfig
|
|
23
|
+
} = require("./services/client-config-manager");
|
|
24
|
+
const { ensureProxy, stopProxy } = require("./services/host-manager");
|
|
25
|
+
const { startServer } = require("./proxy/server");
|
|
26
|
+
const { pathExists, readJson } = require("./utils");
|
|
27
|
+
|
|
28
|
+
const PROJECT_ROOT = path.resolve(__dirname, "..");
|
|
29
|
+
|
|
30
|
+
function formatErrorMessage(error) {
|
|
31
|
+
return error && error.message ? error.message : String(error);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getConfigOptionDescription() {
|
|
35
|
+
return `Path to config.toml (default: ${getDefaultConfigDisplayPath()})`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveConfigPath(options, command) {
|
|
39
|
+
return command?.optsWithGlobals?.().config || options?.config || getDefaultConfigPath();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createStepLogger(config) {
|
|
43
|
+
return (event) => {
|
|
44
|
+
const suffix = event.error ? ` (${formatErrorMessage(event.error)})` : "";
|
|
45
|
+
console.log(`[local] ${event.label}: ${event.status}${suffix}`);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function applyLocalConfig(config) {
|
|
50
|
+
const logStep = createStepLogger(config);
|
|
51
|
+
await applyManagedHostConfig(config, config, { onProgress: logStep });
|
|
52
|
+
console.log("Configured local environment");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function applyClaudeConfig(config) {
|
|
56
|
+
const logStep = createStepLogger(config);
|
|
57
|
+
await applyClaudeManagedHostConfig(config, config, { onProgress: logStep });
|
|
58
|
+
console.log("Configured Claude environment");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function applyOpenAIConfig(config) {
|
|
62
|
+
const logStep = createStepLogger(config);
|
|
63
|
+
await applyOpenAIManagedHostConfig(config, config, { onProgress: logStep });
|
|
64
|
+
console.log("Configured OpenAI environment");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function clearConfigSectionsWithLog(config, sections, prefix) {
|
|
68
|
+
const logStep = createStepLogger(config);
|
|
69
|
+
|
|
70
|
+
for (const section of sections) {
|
|
71
|
+
const label = `Clear ${prefix || section} config fields (${config.__configPath})`;
|
|
72
|
+
logStep({ label, status: "started" });
|
|
73
|
+
const changed = await clearConfigSection(config.__configPath, section);
|
|
74
|
+
logStep({ label, status: changed ? "completed" : "skipped" });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function cleanClaudeConfig(config) {
|
|
79
|
+
const logStep = createStepLogger(config);
|
|
80
|
+
await cleanClaudeManagedHostConfig(config, config, { onProgress: logStep });
|
|
81
|
+
await clearConfigSectionsWithLog(config, ["claude"], "Claude");
|
|
82
|
+
console.log("Restored Claude configuration");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function cleanOpenAIConfig(config) {
|
|
86
|
+
const logStep = createStepLogger(config);
|
|
87
|
+
await cleanOpenAIManagedHostConfig(config, config, { onProgress: logStep });
|
|
88
|
+
await clearConfigSectionsWithLog(config, ["openai"], "OpenAI");
|
|
89
|
+
console.log("Restored OpenAI configuration");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function cleanAllConfig(config) {
|
|
93
|
+
const logStep = createStepLogger(config);
|
|
94
|
+
await cleanManagedHostConfig(config, config, { onProgress: logStep });
|
|
95
|
+
await clearConfigSectionsWithLog(config, ["claude", "openai"]);
|
|
96
|
+
console.log("Restored local configuration");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function formatValue(value) {
|
|
100
|
+
if (value == null || value === "") {
|
|
101
|
+
return "(not set)";
|
|
102
|
+
}
|
|
103
|
+
return String(value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function flattenHooks(groups) {
|
|
107
|
+
return Array.isArray(groups) ? groups.flatMap((group) => group?.hooks || []) : [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function hasManagedHook(settings, actionToken) {
|
|
111
|
+
const groups =
|
|
112
|
+
actionToken === "ensure-proxy"
|
|
113
|
+
? settings?.hooks?.SessionStart
|
|
114
|
+
: settings?.hooks?.SessionEnd;
|
|
115
|
+
return flattenHooks(groups).some((hook) =>
|
|
116
|
+
typeof hook?.command === "string" && hook.command.includes(`internal ${actionToken}`)
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function getCodexProviderName(document, preferredProvider) {
|
|
121
|
+
const providers =
|
|
122
|
+
document.model_providers && typeof document.model_providers === "object"
|
|
123
|
+
? document.model_providers
|
|
124
|
+
: null;
|
|
125
|
+
|
|
126
|
+
if (providers && preferredProvider && providers[preferredProvider]) {
|
|
127
|
+
return preferredProvider;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (providers && document.model_provider && providers[document.model_provider]) {
|
|
131
|
+
return document.model_provider;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!providers) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return Object.keys(providers)[0] || null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function readToml(targetPath, fallback = {}) {
|
|
142
|
+
try {
|
|
143
|
+
const raw = await fs.readFile(targetPath, "utf8");
|
|
144
|
+
return toml.parse(raw);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error.code === "ENOENT") {
|
|
147
|
+
return fallback;
|
|
148
|
+
}
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function formatSection(title, lines) {
|
|
154
|
+
return [title, ...lines.map((line) => ` ${line}`)].join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function buildConfigSummary(configPath) {
|
|
158
|
+
const resolvedConfigPath = path.resolve(configPath || getDefaultConfigPath());
|
|
159
|
+
const configExists = await pathExists(resolvedConfigPath);
|
|
160
|
+
const { document } = await readConfigDocument(resolvedConfigPath, {
|
|
161
|
+
allowMissing: true
|
|
162
|
+
});
|
|
163
|
+
const config = await loadConfig(resolvedConfigPath, {
|
|
164
|
+
allowIncomplete: true,
|
|
165
|
+
allowMissing: true,
|
|
166
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const claudeExists = await pathExists(config.settings_path);
|
|
170
|
+
const claudeSettings = await readJson(config.settings_path, {});
|
|
171
|
+
|
|
172
|
+
const codexConfigExists = await pathExists(config.codex_config_path);
|
|
173
|
+
const codexConfig = await readToml(config.codex_config_path, {});
|
|
174
|
+
const codexProvider = getCodexProviderName(codexConfig, config.codex_provider || null);
|
|
175
|
+
const codexBaseUrl = codexProvider
|
|
176
|
+
? codexConfig.model_providers?.[codexProvider]?.base_url
|
|
177
|
+
: codexConfig.base_url;
|
|
178
|
+
|
|
179
|
+
const codexAuthExists = await pathExists(config.codex_auth_path);
|
|
180
|
+
const codexAuth = await readJson(config.codex_auth_path, {});
|
|
181
|
+
|
|
182
|
+
return [
|
|
183
|
+
formatSection("Config File", [
|
|
184
|
+
`path: ${resolvedConfigPath}`,
|
|
185
|
+
`exists: ${configExists ? "yes" : "no"}`,
|
|
186
|
+
`server_host: ${formatValue(document.server_host)}`,
|
|
187
|
+
`server_port: ${formatValue(document.server_port)}`,
|
|
188
|
+
`base_url: ${formatValue(document.base_url)}`,
|
|
189
|
+
`api_key: ${formatValue(document.api_key)}`,
|
|
190
|
+
`big_model: ${formatValue(document.big_model)}`,
|
|
191
|
+
`middle_model: ${formatValue(document.middle_model)}`,
|
|
192
|
+
`small_model: ${formatValue(document.small_model)}`,
|
|
193
|
+
`default_claude_model: ${formatValue(document.default_claude_model)}`,
|
|
194
|
+
`home_dir: ${formatValue(document.home_dir)}`,
|
|
195
|
+
`claude_dir: ${formatValue(document.claude_dir)}`,
|
|
196
|
+
`codex_dir: ${formatValue(document.codex_dir)}`,
|
|
197
|
+
`codex_provider: ${formatValue(document.codex_provider)}`
|
|
198
|
+
]),
|
|
199
|
+
formatSection("Claude", [
|
|
200
|
+
`path: ${config.settings_path}`,
|
|
201
|
+
`exists: ${claudeExists ? "yes" : "no"}`,
|
|
202
|
+
`ANTHROPIC_BASE_URL: ${formatValue(claudeSettings?.env?.ANTHROPIC_BASE_URL)}`,
|
|
203
|
+
`ANTHROPIC_API_KEY: ${formatValue(claudeSettings?.env?.ANTHROPIC_API_KEY)}`,
|
|
204
|
+
`ANTHROPIC_MODEL: ${formatValue(claudeSettings?.env?.ANTHROPIC_MODEL)}`,
|
|
205
|
+
`ANTHROPIC_REASONING_MODEL: ${formatValue(claudeSettings?.env?.ANTHROPIC_REASONING_MODEL)}`,
|
|
206
|
+
`model: ${formatValue(claudeSettings?.model)}`,
|
|
207
|
+
`ensure-proxy hook: ${hasManagedHook(claudeSettings, "ensure-proxy") ? "installed" : "missing"}`,
|
|
208
|
+
`stop-proxy hook: ${hasManagedHook(claudeSettings, "stop-proxy") ? "installed" : "missing"}`
|
|
209
|
+
]),
|
|
210
|
+
formatSection("Codex Config", [
|
|
211
|
+
`path: ${config.codex_config_path}`,
|
|
212
|
+
`exists: ${codexConfigExists ? "yes" : "no"}`,
|
|
213
|
+
`provider: ${formatValue(codexProvider)}`,
|
|
214
|
+
`base_url: ${formatValue(codexBaseUrl)}`
|
|
215
|
+
]),
|
|
216
|
+
formatSection("Codex Auth", [
|
|
217
|
+
`path: ${config.codex_auth_path}`,
|
|
218
|
+
`exists: ${codexAuthExists ? "yes" : "no"}`,
|
|
219
|
+
`OPENAI_API_KEY: ${formatValue(codexAuth?.OPENAI_API_KEY)}`
|
|
220
|
+
])
|
|
221
|
+
].join("\n\n");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function main() {
|
|
225
|
+
const program = new Command();
|
|
226
|
+
program.name("claude-proxy");
|
|
227
|
+
program.addHelpCommand("help [command]", "Show help for the main command or a subcommand");
|
|
228
|
+
program.addHelpText(
|
|
229
|
+
"after",
|
|
230
|
+
[
|
|
231
|
+
"",
|
|
232
|
+
"Config:",
|
|
233
|
+
` Default config path: ${getDefaultConfigDisplayPath()}`,
|
|
234
|
+
"",
|
|
235
|
+
"Common usage:",
|
|
236
|
+
" claude-proxy config",
|
|
237
|
+
" Interactively configure the local proxy and apply local Claude/Codex settings",
|
|
238
|
+
" claude-proxy config claude",
|
|
239
|
+
" Interactively configure Claude settings only",
|
|
240
|
+
" claude-proxy config openai",
|
|
241
|
+
" Interactively configure OpenAI/Codex settings only",
|
|
242
|
+
" claude-proxy config get",
|
|
243
|
+
" Show the current local proxy, Claude, and Codex configuration summary",
|
|
244
|
+
" claude-proxy clean",
|
|
245
|
+
" Restore local Claude/Codex settings and clear config.toml owned fields",
|
|
246
|
+
" claude-proxy clean claude",
|
|
247
|
+
" Restore Claude settings from backups",
|
|
248
|
+
" claude-proxy clean openai",
|
|
249
|
+
" Restore OpenAI/Codex settings from backups",
|
|
250
|
+
" claude-proxy start",
|
|
251
|
+
" Start the local proxy server only",
|
|
252
|
+
" claude-proxy stop",
|
|
253
|
+
" Stop the managed local proxy server",
|
|
254
|
+
"",
|
|
255
|
+
"More help:",
|
|
256
|
+
" claude-proxy help",
|
|
257
|
+
" claude-proxy help config",
|
|
258
|
+
" claude-proxy help start",
|
|
259
|
+
" claude-proxy help stop",
|
|
260
|
+
" claude-proxy help clean",
|
|
261
|
+
""
|
|
262
|
+
].join("\n")
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
program
|
|
266
|
+
.command("start")
|
|
267
|
+
.description("Start the local proxy server only")
|
|
268
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
269
|
+
.addHelpText(
|
|
270
|
+
"after",
|
|
271
|
+
[
|
|
272
|
+
"",
|
|
273
|
+
"Help:",
|
|
274
|
+
" claude-proxy help start",
|
|
275
|
+
""
|
|
276
|
+
].join("\n")
|
|
277
|
+
)
|
|
278
|
+
.action(async (options, command) => {
|
|
279
|
+
const configPath = resolveConfigPath(options, command);
|
|
280
|
+
const config = await loadConfig(configPath, {
|
|
281
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
282
|
+
});
|
|
283
|
+
await startServer(config, config);
|
|
284
|
+
console.log(`Proxy listening on ${config.server_host}:${config.server_port}`);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
program
|
|
288
|
+
.command("stop")
|
|
289
|
+
.description("Stop the managed local proxy server")
|
|
290
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
291
|
+
.addHelpText(
|
|
292
|
+
"after",
|
|
293
|
+
[
|
|
294
|
+
"",
|
|
295
|
+
"Help:",
|
|
296
|
+
" claude-proxy help stop",
|
|
297
|
+
""
|
|
298
|
+
].join("\n")
|
|
299
|
+
)
|
|
300
|
+
.action(async (options, command) => {
|
|
301
|
+
const configPath = resolveConfigPath(options, command);
|
|
302
|
+
const config = await loadConfig(configPath, {
|
|
303
|
+
allowIncomplete: true,
|
|
304
|
+
allowMissing: true,
|
|
305
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
306
|
+
});
|
|
307
|
+
await stopProxy(config, config, { force: true });
|
|
308
|
+
console.log("Stopped local proxy");
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const configCommand = new Command("config")
|
|
312
|
+
.description("Manage the local proxy configuration")
|
|
313
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
314
|
+
.addHelpText(
|
|
315
|
+
"after",
|
|
316
|
+
[
|
|
317
|
+
"",
|
|
318
|
+
"Subcommands:",
|
|
319
|
+
" claude-proxy config",
|
|
320
|
+
" claude-proxy config claude",
|
|
321
|
+
" claude-proxy config openai",
|
|
322
|
+
" claude-proxy config get",
|
|
323
|
+
""
|
|
324
|
+
].join("\n")
|
|
325
|
+
)
|
|
326
|
+
.action(async (options, command) => {
|
|
327
|
+
const configPath = resolveConfigPath(options, command);
|
|
328
|
+
const config = await promptForConfig(configPath, {
|
|
329
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
330
|
+
});
|
|
331
|
+
await applyLocalConfig(config);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
configCommand
|
|
335
|
+
.command("claude")
|
|
336
|
+
.description("Interactively configure Claude settings only")
|
|
337
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
338
|
+
.action(async (options, command) => {
|
|
339
|
+
const configPath = resolveConfigPath(options, command);
|
|
340
|
+
const config = await promptForConfigSection(configPath, "claude", {
|
|
341
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
342
|
+
});
|
|
343
|
+
await applyClaudeConfig(config);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
configCommand
|
|
347
|
+
.command("openai")
|
|
348
|
+
.description("Interactively configure OpenAI/Codex settings only")
|
|
349
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
350
|
+
.action(async (options, command) => {
|
|
351
|
+
const configPath = resolveConfigPath(options, command);
|
|
352
|
+
const config = await promptForConfigSection(configPath, "openai", {
|
|
353
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
354
|
+
});
|
|
355
|
+
await applyOpenAIConfig(config);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
configCommand
|
|
359
|
+
.command("get")
|
|
360
|
+
.description("Show the current local proxy, Claude, and Codex configuration summary")
|
|
361
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
362
|
+
.action(async (options, command) => {
|
|
363
|
+
const configPath = resolveConfigPath(options, command);
|
|
364
|
+
console.log(await buildConfigSummary(configPath));
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
program.addCommand(configCommand);
|
|
368
|
+
|
|
369
|
+
const cleanCommand = new Command("clean")
|
|
370
|
+
.description("Restore local Claude or OpenAI settings from backups")
|
|
371
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
372
|
+
.addHelpText(
|
|
373
|
+
"after",
|
|
374
|
+
[
|
|
375
|
+
"",
|
|
376
|
+
"Subcommands:",
|
|
377
|
+
" claude-proxy clean",
|
|
378
|
+
" claude-proxy clean claude",
|
|
379
|
+
" claude-proxy clean openai",
|
|
380
|
+
"",
|
|
381
|
+
"Behavior:",
|
|
382
|
+
" Does not stop the running proxy server",
|
|
383
|
+
""
|
|
384
|
+
].join("\n")
|
|
385
|
+
)
|
|
386
|
+
.action(async (options, command) => {
|
|
387
|
+
const configPath = resolveConfigPath(options, command);
|
|
388
|
+
const config = await loadConfig(configPath, {
|
|
389
|
+
allowIncomplete: true,
|
|
390
|
+
allowMissing: true,
|
|
391
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
392
|
+
});
|
|
393
|
+
await cleanAllConfig(config);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
cleanCommand
|
|
397
|
+
.command("claude")
|
|
398
|
+
.description("Restore Claude settings from backups")
|
|
399
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
400
|
+
.action(async (options, command) => {
|
|
401
|
+
const configPath = resolveConfigPath(options, command);
|
|
402
|
+
const config = await loadConfig(configPath, {
|
|
403
|
+
allowIncomplete: true,
|
|
404
|
+
allowMissing: true,
|
|
405
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
406
|
+
});
|
|
407
|
+
await cleanClaudeConfig(config);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
cleanCommand
|
|
411
|
+
.command("openai")
|
|
412
|
+
.description("Restore OpenAI/Codex settings from backups")
|
|
413
|
+
.option("--config <path>", getConfigOptionDescription())
|
|
414
|
+
.action(async (options, command) => {
|
|
415
|
+
const configPath = resolveConfigPath(options, command);
|
|
416
|
+
const config = await loadConfig(configPath, {
|
|
417
|
+
allowIncomplete: true,
|
|
418
|
+
allowMissing: true,
|
|
419
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
420
|
+
});
|
|
421
|
+
await cleanOpenAIConfig(config);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
program.addCommand(cleanCommand);
|
|
425
|
+
|
|
426
|
+
const internalCommand = new Command("internal")
|
|
427
|
+
.addCommand(
|
|
428
|
+
new Command("ensure-proxy")
|
|
429
|
+
.requiredOption("--config <path>")
|
|
430
|
+
.action(async (options) => {
|
|
431
|
+
const config = await loadConfig(options.config, {
|
|
432
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
433
|
+
});
|
|
434
|
+
await ensureProxy(config, config, options.config);
|
|
435
|
+
})
|
|
436
|
+
)
|
|
437
|
+
.addCommand(
|
|
438
|
+
new Command("stop-proxy")
|
|
439
|
+
.requiredOption("--config <path>")
|
|
440
|
+
.action(async (options) => {
|
|
441
|
+
const config = await loadConfig(options.config, {
|
|
442
|
+
allowIncomplete: true,
|
|
443
|
+
allowMissing: true,
|
|
444
|
+
runtimeProjectRoot: PROJECT_ROOT
|
|
445
|
+
});
|
|
446
|
+
await stopProxy(config, config);
|
|
447
|
+
})
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
program.addCommand(internalCommand, { hidden: true });
|
|
451
|
+
|
|
452
|
+
await program.parseAsync(process.argv);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
main().catch((error) => {
|
|
456
|
+
console.error(error.message || error);
|
|
457
|
+
process.exit(1);
|
|
458
|
+
});
|