coding-tool-x 3.9.4 → 3.9.5-261012
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/CHANGELOG.md +2 -0
- package/package.json +1 -1
- package/src/commands/daemon.js +12 -4
- package/src/config/model-metadata.json +153 -10
- package/src/index.js +98 -14
- package/src/platforms/drivers/claude/openai-gateway.js +1 -1
- package/src/platforms/drivers/claude/proxy-implementation.js +13 -8
- package/src/platforms/drivers/codex/proxy-implementation.js +16 -10
- package/src/platforms/drivers/dsh/api-operations.js +46 -43
- package/src/platforms/drivers/dsh/proxy-implementation.js +32 -17
- package/src/platforms/drivers/gemini/proxy-implementation.js +16 -10
- package/src/platforms/drivers/omp/gateway.js +35 -11
- package/src/platforms/drivers/opencode/proxy-implementation.js +36 -29
- package/src/server/api/mcp.js +2 -2
- package/src/server/index.js +3 -0
- package/src/server/services/mcp-service.js +2 -19
- package/src/server/services/memory-reclaimer.js +88 -0
- package/src/server/services/server-shutdown.js +28 -3
- package/src/shared/driver-factories/mcp.js +2 -8
- package/src/shared/mcp-import.js +107 -0
- package/src/shared/proxy-transport.js +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
- **依赖安全升级** - adm-zip 升至 0.6.0(GHSA-xcpc-8h2w-3j85)、js-yaml 升至 4.3.1(GHSA-h67p-54hq-rp68 / GHSA-52cp-r559-cp3m / GHSA-5p4m-2wfm-xmqj)、pm2 升至 7.0.3(GHSA-x5gf-qvw8-r2rm),overrides 同步更新 basic-ftp / qs / systeminformation / js-yaml;`npm audit` 报告归零
|
|
24
24
|
|
|
25
25
|
### Fixed
|
|
26
|
+
- **代理连接与内存回收** - 修复各 CLI 请求残留、客户端断开后的上游取消和渠道释放;后台退出时排空代理请求,延长上游空闲超时;OMP 重试取消失败响应,过滤连接保活头,并增加内存压力回收。
|
|
27
|
+
- **CLI 状态和错误子命令** - 修复 `ctx status` 的代理 Driver 状态解析;错误子命令返回非零退出码,不再进入控制面。
|
|
26
28
|
- **Claude / Gemini 原生 wire 兼容** - OpenCode Claude / Gemini 代理、模型探测和测速统一使用 Anthropic Messages 与 Google Generative AI(含 Gemini CLI)原生请求格式、认证头与目标 URL;OMP managed provider 写入 `anthropic-messages` / `google-generative-ai` 标识,避免代理、adapter 与探测各自维护不一致的请求格式
|
|
27
29
|
- **OMP Codex Responses 路径兼容** - managed gateway 将 OMP 原生 `openai-codex-responses` 的 `/codex/responses` 请求映射到 OpenAI-compatible 渠道的 `/v1/responses`,避免 Codex-only 服务返回 HTTP 404
|
|
28
30
|
- **OMP 渠道失败不再冻结** - OMP 动态切换开启后,失败渠道不再进入冻结状态。OMP 网关按请求即时回退其他渠道,冻结会导致短暂故障的渠道在恢复前被排除在后续分配之外
|
package/package.json
CHANGED
package/src/commands/daemon.js
CHANGED
|
@@ -299,7 +299,13 @@ async function getProxyStatusEntries({ registry = getPlatformRegistry(), runtime
|
|
|
299
299
|
entries.push({ platform, state: { status: 'unsupported', ...context }, port: getPlatformPort(platform, config) });
|
|
300
300
|
continue;
|
|
301
301
|
}
|
|
302
|
-
const
|
|
302
|
+
const result = await Promise.resolve(driver.status());
|
|
303
|
+
// Proxy drivers use the shared Driver contract and wrap successful
|
|
304
|
+
// service status payloads in { status: 'ok', data }. Keep compatibility
|
|
305
|
+
// with legacy drivers that return the service payload directly.
|
|
306
|
+
const state = result && result.status === 'ok'
|
|
307
|
+
? (result.data && typeof result.data === 'object' ? result.data : {})
|
|
308
|
+
: result;
|
|
303
309
|
if (state && typeof state.status === 'string' && state.status !== 'ok') {
|
|
304
310
|
entries.push({ platform, state: { ...state, ...context }, port: getPlatformPort(platform, config) });
|
|
305
311
|
} else {
|
|
@@ -508,8 +514,10 @@ function buildStartOptions(port, enableHost, enableHttps) {
|
|
|
508
514
|
interpreter: 'node',
|
|
509
515
|
windowsHide: true,
|
|
510
516
|
autorestart: true,
|
|
511
|
-
|
|
517
|
+
// Let active OMP streams drain before PM2 forcibly terminates the process.
|
|
518
|
+
kill_timeout: 125000,
|
|
512
519
|
max_memory_restart: '500M',
|
|
520
|
+
node_args: '--expose-gc',
|
|
513
521
|
env,
|
|
514
522
|
output: path.join(PATHS.logs, STARTUP_LOG_FILE),
|
|
515
523
|
error: path.join(PATHS.logs, STARTUP_LOG_FILE),
|
|
@@ -761,9 +769,9 @@ async function handleStatus() {
|
|
|
761
769
|
console.log(chalk.gray(` ${label}${chalk.gray('[N/A] 不支持')}`));
|
|
762
770
|
continue;
|
|
763
771
|
}
|
|
764
|
-
if (state.status
|
|
772
|
+
if (state.status && state.status !== 'ok') {
|
|
765
773
|
console.log(chalk.red(` ${label}${chalk.red('[ERROR] 查询失败')}`) +
|
|
766
|
-
chalk.gray(` (${state.error || 'unknown error'})`));
|
|
774
|
+
chalk.gray(` (${state.error || state.status || 'unknown error'})`));
|
|
767
775
|
continue;
|
|
768
776
|
}
|
|
769
777
|
console.log(chalk.gray(` ${label}`) + (state.running
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "models.dev",
|
|
3
3
|
"sourceUrl": "https://models.dev/api.json",
|
|
4
|
-
"lastUpdated": "2026-09
|
|
4
|
+
"lastUpdated": "2026-10-09",
|
|
5
5
|
"defaultModels": {
|
|
6
6
|
"claude": [
|
|
7
7
|
"claude-opus-4-7",
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"claude-opus-4-5",
|
|
19
19
|
"claude-haiku-4-5",
|
|
20
20
|
"claude-sonnet-4-5",
|
|
21
|
-
"claude-opus-5-5"
|
|
21
|
+
"claude-opus-5-5",
|
|
22
|
+
"claude-sonnet-5-5",
|
|
23
|
+
"claude-haiku-5-5"
|
|
22
24
|
],
|
|
23
25
|
"codex": [
|
|
24
26
|
"gpt-5.5",
|
|
@@ -58,7 +60,8 @@
|
|
|
58
60
|
"gpt-6-luna",
|
|
59
61
|
"gpt-6-sol",
|
|
60
62
|
"gpt-daybreak-blue-latest",
|
|
61
|
-
"gpt-daybreak-red-latest"
|
|
63
|
+
"gpt-daybreak-red-latest",
|
|
64
|
+
"gpt-6.1-sol"
|
|
62
65
|
],
|
|
63
66
|
"gemini": [
|
|
64
67
|
"gemini-3.1-pro-preview",
|
|
@@ -95,6 +98,7 @@
|
|
|
95
98
|
"claude-opus-4.7": "claude-opus-4-7",
|
|
96
99
|
"claude-sonnet-4-5": "claude-sonnet-4-5-20250929",
|
|
97
100
|
"claude-sonnet-4-6": "claude-sonnet-4-6",
|
|
101
|
+
"codestral-2508": "mistral/codestral-2508",
|
|
98
102
|
"codestral-latest": "mistral/codestral-latest",
|
|
99
103
|
"deepseek-flash": "deepseek/deepseek-flash",
|
|
100
104
|
"deepseek-v4-flash": "deepseek/deepseek-v4-flash",
|
|
@@ -142,6 +146,7 @@
|
|
|
142
146
|
"kimi-k3": "moonshotai/kimi-k3",
|
|
143
147
|
"mistral-large-2411": "mistral/mistral-large-2411",
|
|
144
148
|
"mistral-large-2512": "mistral/mistral-large-2512",
|
|
149
|
+
"mistral-large-4": "mistral/mistral-large-4",
|
|
145
150
|
"mistral-large-latest": "mistral/mistral-large-latest",
|
|
146
151
|
"mistral-medium-2505": "mistral/mistral-medium-2505",
|
|
147
152
|
"mistral-medium-2508": "mistral/mistral-medium-2508",
|
|
@@ -1506,6 +1511,33 @@
|
|
|
1506
1511
|
"omp"
|
|
1507
1512
|
]
|
|
1508
1513
|
},
|
|
1514
|
+
"claude-haiku-5-5": {
|
|
1515
|
+
"id": "claude-haiku-5-5",
|
|
1516
|
+
"input": [
|
|
1517
|
+
"text",
|
|
1518
|
+
"image"
|
|
1519
|
+
],
|
|
1520
|
+
"limit": {
|
|
1521
|
+
"context": 1000000,
|
|
1522
|
+
"output": 128000
|
|
1523
|
+
},
|
|
1524
|
+
"name": "Claude Haiku 5.5",
|
|
1525
|
+
"pricing": {
|
|
1526
|
+
"cacheCreation": 0.125,
|
|
1527
|
+
"cacheRead": 0.01,
|
|
1528
|
+
"input": 0.1,
|
|
1529
|
+
"output": 0.5
|
|
1530
|
+
},
|
|
1531
|
+
"provider": "anthropic",
|
|
1532
|
+
"reasoning": true,
|
|
1533
|
+
"sourceId": "anthropic/claude-haiku-5-5",
|
|
1534
|
+
"supportsTools": true,
|
|
1535
|
+
"toolTypes": [
|
|
1536
|
+
"claude",
|
|
1537
|
+
"opencode",
|
|
1538
|
+
"omp"
|
|
1539
|
+
]
|
|
1540
|
+
},
|
|
1509
1541
|
"claude-opus-4-5": {
|
|
1510
1542
|
"id": "claude-opus-4-5",
|
|
1511
1543
|
"input": [
|
|
@@ -1702,7 +1734,7 @@
|
|
|
1702
1734
|
"image"
|
|
1703
1735
|
],
|
|
1704
1736
|
"limit": {
|
|
1705
|
-
"context":
|
|
1737
|
+
"context": 200000,
|
|
1706
1738
|
"output": 64000
|
|
1707
1739
|
},
|
|
1708
1740
|
"name": "Claude Sonnet 4.5 (latest)",
|
|
@@ -1729,7 +1761,7 @@
|
|
|
1729
1761
|
"image"
|
|
1730
1762
|
],
|
|
1731
1763
|
"limit": {
|
|
1732
|
-
"context":
|
|
1764
|
+
"context": 200000,
|
|
1733
1765
|
"output": 64000
|
|
1734
1766
|
},
|
|
1735
1767
|
"name": "Claude Sonnet 4.5",
|
|
@@ -1803,6 +1835,33 @@
|
|
|
1803
1835
|
"omp"
|
|
1804
1836
|
]
|
|
1805
1837
|
},
|
|
1838
|
+
"claude-sonnet-5-5": {
|
|
1839
|
+
"id": "claude-sonnet-5-5",
|
|
1840
|
+
"input": [
|
|
1841
|
+
"text",
|
|
1842
|
+
"image"
|
|
1843
|
+
],
|
|
1844
|
+
"limit": {
|
|
1845
|
+
"context": 1000000,
|
|
1846
|
+
"output": 128000
|
|
1847
|
+
},
|
|
1848
|
+
"name": "Claude Sonnet 5.5",
|
|
1849
|
+
"pricing": {
|
|
1850
|
+
"cacheCreation": 2.5,
|
|
1851
|
+
"cacheRead": 0.1,
|
|
1852
|
+
"input": 2,
|
|
1853
|
+
"output": 10
|
|
1854
|
+
},
|
|
1855
|
+
"provider": "anthropic",
|
|
1856
|
+
"reasoning": true,
|
|
1857
|
+
"sourceId": "anthropic/claude-sonnet-5-5",
|
|
1858
|
+
"supportsTools": true,
|
|
1859
|
+
"toolTypes": [
|
|
1860
|
+
"claude",
|
|
1861
|
+
"opencode",
|
|
1862
|
+
"omp"
|
|
1863
|
+
]
|
|
1864
|
+
},
|
|
1806
1865
|
"deepseek/deepseek-flash": {
|
|
1807
1866
|
"id": "deepseek/deepseek-flash",
|
|
1808
1867
|
"input": [
|
|
@@ -1889,9 +1948,9 @@
|
|
|
1889
1948
|
},
|
|
1890
1949
|
"name": "DeepSeek V4 Pro",
|
|
1891
1950
|
"pricing": {
|
|
1892
|
-
"cacheRead": 0.
|
|
1893
|
-
"input": 0.
|
|
1894
|
-
"output":
|
|
1951
|
+
"cacheRead": 0.022,
|
|
1952
|
+
"input": 0.66,
|
|
1953
|
+
"output": 1.98
|
|
1895
1954
|
},
|
|
1896
1955
|
"provider": "deepseek",
|
|
1897
1956
|
"reasoning": true,
|
|
@@ -3181,6 +3240,33 @@
|
|
|
3181
3240
|
"omp"
|
|
3182
3241
|
]
|
|
3183
3242
|
},
|
|
3243
|
+
"gpt-6.1-sol": {
|
|
3244
|
+
"id": "gpt-6.1-sol",
|
|
3245
|
+
"input": [
|
|
3246
|
+
"text",
|
|
3247
|
+
"image"
|
|
3248
|
+
],
|
|
3249
|
+
"limit": {
|
|
3250
|
+
"context": 1050000,
|
|
3251
|
+
"output": 128000
|
|
3252
|
+
},
|
|
3253
|
+
"name": "GPT-6.1 Sol",
|
|
3254
|
+
"pricing": {
|
|
3255
|
+
"cacheCreation": 2.5,
|
|
3256
|
+
"cacheRead": 0.1,
|
|
3257
|
+
"input": 2,
|
|
3258
|
+
"output": 10
|
|
3259
|
+
},
|
|
3260
|
+
"provider": "openai",
|
|
3261
|
+
"reasoning": true,
|
|
3262
|
+
"sourceId": "openai/gpt-6.1-sol",
|
|
3263
|
+
"supportsTools": true,
|
|
3264
|
+
"toolTypes": [
|
|
3265
|
+
"codex",
|
|
3266
|
+
"opencode",
|
|
3267
|
+
"omp"
|
|
3268
|
+
]
|
|
3269
|
+
},
|
|
3184
3270
|
"gpt-daybreak-blue-latest": {
|
|
3185
3271
|
"id": "gpt-daybreak-blue-latest",
|
|
3186
3272
|
"input": [
|
|
@@ -3261,6 +3347,30 @@
|
|
|
3261
3347
|
"omp"
|
|
3262
3348
|
]
|
|
3263
3349
|
},
|
|
3350
|
+
"mistral/codestral-2508": {
|
|
3351
|
+
"id": "mistral/codestral-2508",
|
|
3352
|
+
"input": [
|
|
3353
|
+
"text"
|
|
3354
|
+
],
|
|
3355
|
+
"limit": {
|
|
3356
|
+
"context": 256000,
|
|
3357
|
+
"output": 8192
|
|
3358
|
+
},
|
|
3359
|
+
"name": "Codestral 25.08",
|
|
3360
|
+
"pricing": {
|
|
3361
|
+
"cacheRead": 0.03,
|
|
3362
|
+
"input": 0.3,
|
|
3363
|
+
"output": 0.9
|
|
3364
|
+
},
|
|
3365
|
+
"provider": "mistral",
|
|
3366
|
+
"reasoning": false,
|
|
3367
|
+
"sourceId": "mistral/codestral-2508",
|
|
3368
|
+
"supportsTools": true,
|
|
3369
|
+
"toolTypes": [
|
|
3370
|
+
"opencode",
|
|
3371
|
+
"omp"
|
|
3372
|
+
]
|
|
3373
|
+
},
|
|
3264
3374
|
"mistral/codestral-latest": {
|
|
3265
3375
|
"id": "mistral/codestral-latest",
|
|
3266
3376
|
"input": [
|
|
@@ -3272,6 +3382,7 @@
|
|
|
3272
3382
|
},
|
|
3273
3383
|
"name": "Codestral (latest)",
|
|
3274
3384
|
"pricing": {
|
|
3385
|
+
"cacheRead": 0.03,
|
|
3275
3386
|
"input": 0.3,
|
|
3276
3387
|
"output": 0.9
|
|
3277
3388
|
},
|
|
@@ -3319,6 +3430,7 @@
|
|
|
3319
3430
|
},
|
|
3320
3431
|
"name": "Mistral Large 3",
|
|
3321
3432
|
"pricing": {
|
|
3433
|
+
"cacheRead": 0.05,
|
|
3322
3434
|
"input": 0.5,
|
|
3323
3435
|
"output": 1.5
|
|
3324
3436
|
},
|
|
@@ -3331,6 +3443,31 @@
|
|
|
3331
3443
|
"omp"
|
|
3332
3444
|
]
|
|
3333
3445
|
},
|
|
3446
|
+
"mistral/mistral-large-4": {
|
|
3447
|
+
"id": "mistral/mistral-large-4",
|
|
3448
|
+
"input": [
|
|
3449
|
+
"text",
|
|
3450
|
+
"image"
|
|
3451
|
+
],
|
|
3452
|
+
"limit": {
|
|
3453
|
+
"context": 1048576,
|
|
3454
|
+
"output": 262144
|
|
3455
|
+
},
|
|
3456
|
+
"name": "Mistral Large 4",
|
|
3457
|
+
"pricing": {
|
|
3458
|
+
"cacheRead": 0.07,
|
|
3459
|
+
"input": 0.68,
|
|
3460
|
+
"output": 2.09
|
|
3461
|
+
},
|
|
3462
|
+
"provider": "mistral",
|
|
3463
|
+
"reasoning": true,
|
|
3464
|
+
"sourceId": "mistral/mistral-large-4",
|
|
3465
|
+
"supportsTools": true,
|
|
3466
|
+
"toolTypes": [
|
|
3467
|
+
"opencode",
|
|
3468
|
+
"omp"
|
|
3469
|
+
]
|
|
3470
|
+
},
|
|
3334
3471
|
"mistral/mistral-large-latest": {
|
|
3335
3472
|
"id": "mistral/mistral-large-latest",
|
|
3336
3473
|
"input": [
|
|
@@ -3343,6 +3480,7 @@
|
|
|
3343
3480
|
},
|
|
3344
3481
|
"name": "Mistral Large (latest)",
|
|
3345
3482
|
"pricing": {
|
|
3483
|
+
"cacheRead": 0.05,
|
|
3346
3484
|
"input": 0.5,
|
|
3347
3485
|
"output": 1.5
|
|
3348
3486
|
},
|
|
@@ -3415,6 +3553,7 @@
|
|
|
3415
3553
|
},
|
|
3416
3554
|
"name": "Mistral Medium 3.5",
|
|
3417
3555
|
"pricing": {
|
|
3556
|
+
"cacheRead": 0.15,
|
|
3418
3557
|
"input": 1.5,
|
|
3419
3558
|
"output": 7.5
|
|
3420
3559
|
},
|
|
@@ -3439,6 +3578,7 @@
|
|
|
3439
3578
|
},
|
|
3440
3579
|
"name": "Mistral Medium (latest)",
|
|
3441
3580
|
"pricing": {
|
|
3581
|
+
"cacheRead": 0.15,
|
|
3442
3582
|
"input": 1.5,
|
|
3443
3583
|
"output": 7.5
|
|
3444
3584
|
},
|
|
@@ -3505,11 +3645,12 @@
|
|
|
3505
3645
|
"image"
|
|
3506
3646
|
],
|
|
3507
3647
|
"limit": {
|
|
3508
|
-
"context":
|
|
3648
|
+
"context": 262144,
|
|
3509
3649
|
"output": 256000
|
|
3510
3650
|
},
|
|
3511
3651
|
"name": "Mistral Small 4",
|
|
3512
3652
|
"pricing": {
|
|
3653
|
+
"cacheRead": 0.015,
|
|
3513
3654
|
"input": 0.15,
|
|
3514
3655
|
"output": 0.6
|
|
3515
3656
|
},
|
|
@@ -3529,11 +3670,12 @@
|
|
|
3529
3670
|
"image"
|
|
3530
3671
|
],
|
|
3531
3672
|
"limit": {
|
|
3532
|
-
"context":
|
|
3673
|
+
"context": 262144,
|
|
3533
3674
|
"output": 256000
|
|
3534
3675
|
},
|
|
3535
3676
|
"name": "Mistral Small (latest)",
|
|
3536
3677
|
"pricing": {
|
|
3678
|
+
"cacheRead": 0.015,
|
|
3537
3679
|
"input": 0.15,
|
|
3538
3680
|
"output": 0.6
|
|
3539
3681
|
},
|
|
@@ -3778,6 +3920,7 @@
|
|
|
3778
3920
|
},
|
|
3779
3921
|
"name": "Kimi K3",
|
|
3780
3922
|
"pricing": {
|
|
3923
|
+
"cacheCreation": 3,
|
|
3781
3924
|
"cacheRead": 0.3,
|
|
3782
3925
|
"input": 3,
|
|
3783
3926
|
"output": 15
|
package/src/index.js
CHANGED
|
@@ -164,6 +164,11 @@ async function dispatchPlatformCommand(platform, args = [], dependencies = {}) {
|
|
|
164
164
|
const channel = platform.key;
|
|
165
165
|
const options = dependencies.registry ? { registry: dependencies.registry } : {};
|
|
166
166
|
|
|
167
|
+
if (args.length > 1) {
|
|
168
|
+
reportInvalidCommand('参数', args[1], `ctx ${platform.command || platform.key} ${action}`);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
167
172
|
switch (action) {
|
|
168
173
|
case 'start':
|
|
169
174
|
await proxyStart(channel, options);
|
|
@@ -178,11 +183,23 @@ async function dispatchPlatformCommand(platform, args = [], dependencies = {}) {
|
|
|
178
183
|
await proxyStatus(channel, options);
|
|
179
184
|
return;
|
|
180
185
|
default:
|
|
181
|
-
|
|
182
|
-
console.log(chalk.gray('支持的操作: start, stop, restart, status\n'));
|
|
186
|
+
reportInvalidCommand('操作', action, '支持的操作: start, stop, restart, status');
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
189
|
|
|
190
|
+
function reportInvalidCommand(kind, value, usage = '') {
|
|
191
|
+
console.error(chalk.red(`\n[ERROR] 未知${kind}: ${value}\n`));
|
|
192
|
+
if (usage) console.error(chalk.gray(`${usage}\n`));
|
|
193
|
+
process.exitCode = 1;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function validateCommandOptions(command, args, allowedOptions) {
|
|
197
|
+
const invalidOption = args.find(argument => !allowedOptions.has(argument));
|
|
198
|
+
if (!invalidOption) return true;
|
|
199
|
+
reportInvalidCommand('参数', invalidOption, `用法: ${command}`);
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
186
203
|
let processShutdownPromise = null;
|
|
187
204
|
|
|
188
205
|
async function stopOwnedOmpGatewayBeforeExit() {
|
|
@@ -193,7 +210,7 @@ async function stopOwnedOmpGatewayBeforeExit() {
|
|
|
193
210
|
} = require('./platforms/drivers/omp/proxy-implementation');
|
|
194
211
|
if (getOmpProxyStatus().running) {
|
|
195
212
|
const preserveManagedMode = process.argv.includes('--daemon');
|
|
196
|
-
await stopOmpProxyServer({ forceAfterMs:
|
|
213
|
+
await stopOmpProxyServer({ forceAfterMs: 120000, preserveManagedMode });
|
|
197
214
|
}
|
|
198
215
|
} catch (error) {
|
|
199
216
|
console.error(chalk.yellow(`[WARN] OMP 动态网关退出清理失败: ${error.message}`));
|
|
@@ -203,12 +220,21 @@ async function stopOwnedOmpGatewayBeforeExit() {
|
|
|
203
220
|
function shutdownProcess(code = 0, error = null) {
|
|
204
221
|
if (processShutdownPromise) return processShutdownPromise;
|
|
205
222
|
processShutdownPromise = (async () => {
|
|
223
|
+
try {
|
|
224
|
+
require('./server/services/memory-reclaimer').stopMemoryReclaimer();
|
|
225
|
+
} catch (memoryError) {
|
|
226
|
+
console.error(chalk.yellow(`[WARN] 内存回收器退出清理失败: ${memoryError.message}`));
|
|
227
|
+
}
|
|
206
228
|
try {
|
|
207
229
|
require('./server/services/native-log-observer').shutdownNativeCliLogObserver();
|
|
208
230
|
} catch (observerError) {
|
|
209
231
|
console.error(chalk.yellow(`[WARN] CLI 原生会话日志退出清理失败: ${observerError.message}`));
|
|
210
232
|
}
|
|
211
|
-
await
|
|
233
|
+
await Promise.all([
|
|
234
|
+
stopOwnedOmpGatewayBeforeExit(),
|
|
235
|
+
require('./server/services/server-shutdown').drainProxyServers({ forceAfterMs: 120000 })
|
|
236
|
+
.catch(drainError => console.error(`[WARN] Proxy drain failed: ${drainError.message}`))
|
|
237
|
+
]);
|
|
212
238
|
eventBus.emitSync('cli:shutdown', {});
|
|
213
239
|
PluginManager.shutdownPlugins();
|
|
214
240
|
try {
|
|
@@ -282,6 +308,11 @@ async function main({ menuDependencies } = {}) {
|
|
|
282
308
|
if (args[0] === 'daemon') {
|
|
283
309
|
const subCommand = args[1] || 'status';
|
|
284
310
|
|
|
311
|
+
if (['start', 'stop', 'restart', 'status'].includes(subCommand) && args.length > 2) {
|
|
312
|
+
reportInvalidCommand('参数', args[2], `ctx daemon ${subCommand}`);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
285
316
|
if (subCommand === 'start') {
|
|
286
317
|
await handleStart();
|
|
287
318
|
return finishCli(0);
|
|
@@ -314,25 +345,37 @@ async function main({ menuDependencies } = {}) {
|
|
|
314
345
|
return;
|
|
315
346
|
}
|
|
316
347
|
|
|
317
|
-
|
|
318
|
-
console.log(chalk.gray('支持的命令: start, stop, restart, status, logs\n'));
|
|
348
|
+
reportInvalidCommand('daemon 子命令', subCommand, '支持的命令: start, stop, restart, status, logs');
|
|
319
349
|
return;
|
|
320
350
|
}
|
|
321
351
|
|
|
322
352
|
// reset 命令 - 恢复默认配置
|
|
323
353
|
if (args[0] === 'reset') {
|
|
354
|
+
if (args.length > 1) {
|
|
355
|
+
reportInvalidCommand('参数', args[1], '用法: ctx reset');
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
324
358
|
await resetConfig();
|
|
325
359
|
return;
|
|
326
360
|
}
|
|
327
361
|
|
|
328
362
|
// security 命令 - 安全设置
|
|
329
363
|
if (args[0] === 'security') {
|
|
330
|
-
const { showSecurityHelp, handleSecurityReset } = require('./commands/security');
|
|
331
364
|
const subCommand = args[1] || 'help';
|
|
332
365
|
|
|
366
|
+
if (!['reset', 'disable', 'off', 'help'].includes(subCommand)) {
|
|
367
|
+
reportInvalidCommand('security 子命令', subCommand, '支持的命令: reset, disable, off, help');
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const { showSecurityHelp, handleSecurityReset } = require('./commands/security');
|
|
371
|
+
|
|
333
372
|
if (subCommand === 'reset' || subCommand === 'disable' || subCommand === 'off') {
|
|
373
|
+
if (args.length > 2) {
|
|
374
|
+
reportInvalidCommand('参数', args[2], `ctx security ${subCommand}`);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
334
377
|
await handleSecurityReset();
|
|
335
|
-
} else {
|
|
378
|
+
} else if (subCommand === 'help') {
|
|
336
379
|
showSecurityHelp();
|
|
337
380
|
}
|
|
338
381
|
return;
|
|
@@ -340,31 +383,52 @@ async function main({ menuDependencies } = {}) {
|
|
|
340
383
|
|
|
341
384
|
// start 命令 - 启动服务(后台)
|
|
342
385
|
if (args[0] === 'start') {
|
|
386
|
+
if (!validateCommandOptions('ctx start [--host|--hosts] [--https]', args.slice(1), new Set(['--host', '--hosts', '--https']))) return;
|
|
343
387
|
await handleStart();
|
|
344
388
|
return finishCli(0);
|
|
345
389
|
}
|
|
346
390
|
|
|
347
391
|
// stop 命令 - 停止服务
|
|
348
392
|
if (args[0] === 'stop') {
|
|
393
|
+
if (args.length > 1) {
|
|
394
|
+
reportInvalidCommand('参数', args[1], '用法: ctx stop');
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
349
397
|
await handleStop();
|
|
350
398
|
return finishCli(0);
|
|
351
399
|
}
|
|
352
400
|
|
|
353
401
|
// restart 命令 - 重启服务
|
|
354
402
|
if (args[0] === 'restart') {
|
|
403
|
+
if (!validateCommandOptions('ctx restart [--host|--hosts] [--https]', args.slice(1), new Set(['--host', '--hosts', '--https']))) return;
|
|
355
404
|
await handleRestart();
|
|
356
405
|
return finishCli(0);
|
|
357
406
|
}
|
|
358
407
|
|
|
359
408
|
// status 命令 - 查看服务状态
|
|
360
409
|
if (args[0] === 'status') {
|
|
410
|
+
if (args.length > 1) {
|
|
411
|
+
reportInvalidCommand('参数', args[1], '用法: ctx status');
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
361
414
|
await handleStatus();
|
|
362
415
|
return finishCli(0);
|
|
363
416
|
}
|
|
364
417
|
|
|
365
418
|
// ui 命令 - Web UI 管理
|
|
366
419
|
if (args[0] === 'ui') {
|
|
367
|
-
const
|
|
420
|
+
const commandArgs = args.slice(1);
|
|
421
|
+
const subCommand = commandArgs[0] && !commandArgs[0].startsWith('-') ? commandArgs[0] : null;
|
|
422
|
+
const optionArgs = subCommand ? commandArgs.slice(1) : commandArgs;
|
|
423
|
+
const allowedOptions = new Set(['--https', '--host', '--hosts', '--daemon']);
|
|
424
|
+
|
|
425
|
+
if (subCommand && !['start', 'stop', 'restart'].includes(subCommand)) {
|
|
426
|
+
reportInvalidCommand('ui 子命令', subCommand, '支持的命令: start, stop, restart');
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (!validateCommandOptions(`ctx ui${subCommand ? ` ${subCommand}` : ''}`, optionArgs,
|
|
430
|
+
subCommand === 'stop' ? new Set() : allowedOptions)) return;
|
|
431
|
+
|
|
368
432
|
if (subCommand === 'start') {
|
|
369
433
|
await handleStart(); // UI start 实际上就是启动整个服务
|
|
370
434
|
return finishCli(0);
|
|
@@ -440,12 +504,17 @@ async function main({ menuDependencies } = {}) {
|
|
|
440
504
|
|
|
441
505
|
// doctor 命令 - 系统诊断
|
|
442
506
|
if (args[0] === 'doctor') {
|
|
507
|
+
if (args.length > 1) {
|
|
508
|
+
reportInvalidCommand('参数', args[1], '用法: ctx doctor');
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
443
511
|
await handleDoctor();
|
|
444
512
|
return;
|
|
445
513
|
}
|
|
446
514
|
|
|
447
515
|
// update 命令 - 检查并更新到最新版本
|
|
448
516
|
if (args[0] === 'update') {
|
|
517
|
+
if (!validateCommandOptions('ctx update [--check]', args.slice(1), new Set(['--check']))) return;
|
|
449
518
|
const checkOnly = args.includes('--check');
|
|
450
519
|
await handleUpdate({ checkOnly });
|
|
451
520
|
return;
|
|
@@ -453,6 +522,10 @@ async function main({ menuDependencies } = {}) {
|
|
|
453
522
|
|
|
454
523
|
// port 命令 - 配置端口
|
|
455
524
|
if (args[0] === 'port') {
|
|
525
|
+
if (args.length > 1) {
|
|
526
|
+
reportInvalidCommand('参数', args[1], '用法: ctx port');
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
456
529
|
const { handlePortConfig } = require('./commands/port-config');
|
|
457
530
|
await handlePortConfig();
|
|
458
531
|
return;
|
|
@@ -460,9 +533,18 @@ async function main({ menuDependencies } = {}) {
|
|
|
460
533
|
|
|
461
534
|
// 代理命令
|
|
462
535
|
if (args[0] === 'proxy') {
|
|
463
|
-
const { handleProxyStart, handleProxyStop, handleProxyStatus } = require('./commands/proxy');
|
|
464
536
|
const subCommand = args[1] || 'start';
|
|
465
537
|
|
|
538
|
+
if (!['start', 'stop', 'status'].includes(subCommand)) {
|
|
539
|
+
reportInvalidCommand('proxy 子命令', subCommand, '支持的命令: start, stop, status');
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
if (args.length > 2) {
|
|
543
|
+
reportInvalidCommand('参数', args[2], `ctx proxy ${subCommand}`);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const { handleProxyStart, handleProxyStop, handleProxyStatus } = require('./commands/proxy');
|
|
547
|
+
|
|
466
548
|
switch (subCommand) {
|
|
467
549
|
case 'start':
|
|
468
550
|
await handleProxyStart();
|
|
@@ -476,10 +558,6 @@ async function main({ menuDependencies } = {}) {
|
|
|
476
558
|
handleProxyStatus();
|
|
477
559
|
return;
|
|
478
560
|
|
|
479
|
-
default:
|
|
480
|
-
// 默认执行 start
|
|
481
|
-
await handleProxyStart();
|
|
482
|
-
return;
|
|
483
561
|
}
|
|
484
562
|
}
|
|
485
563
|
|
|
@@ -508,6 +586,12 @@ async function main({ menuDependencies } = {}) {
|
|
|
508
586
|
return;
|
|
509
587
|
}
|
|
510
588
|
|
|
589
|
+
if (args.length > 0) {
|
|
590
|
+
reportInvalidCommand('命令', args[0], '运行 ctx --help 查看可用命令');
|
|
591
|
+
PluginManager.shutdownPlugins();
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
511
595
|
while (true) {
|
|
512
596
|
// 显示主菜单
|
|
513
597
|
const { showMainMenu } = require('./ui/menu');
|
|
@@ -7,7 +7,7 @@ const { createDecodedStream } = require('../../../server/services/response-decod
|
|
|
7
7
|
const { parseNonStreamingUsage } = require('../../../shared/response-usage-parser');
|
|
8
8
|
const { convertClaudeToOpenCodePayload } = require('../opencode/gateway-converter');
|
|
9
9
|
|
|
10
|
-
const REQUEST_TIMEOUT_MS =
|
|
10
|
+
const { PROXY_IDLE_TIMEOUT_MS: REQUEST_TIMEOUT_MS } = require('../../../server/services/server-shutdown');
|
|
11
11
|
|
|
12
12
|
function getRequestPathname(urlPath = '') {
|
|
13
13
|
try {
|