@peterwangze/claude-trigger-router 1.0.2 → 1.0.4

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 CHANGED
@@ -30,8 +30,8 @@ ctr setup
30
30
  `ctr setup` 会:
31
31
 
32
32
  - 优先检查当前配置是否可直接复用
33
- - 检测旧版 `~/.ccr/config.yaml` 并优先提供迁移
34
- - 在需要新建时,先询问默认模型 ID,再收集最少必要接入信息
33
+ - 检测旧版 `~/.ccr/config.yaml` 或 `~/.claude-code-router/config.yaml` 并优先提供迁移
34
+ - 在需要新建时,先确认接入方式与 provider 预设,再带出默认模型和默认模型 ID 供确认
35
35
  - 只生成最小可用配置(`Models + Router.default`),高级路由稍后再补
36
36
  - 保存配置后启动服务并进入 Claude Code
37
37
 
@@ -245,7 +245,7 @@ Models:
245
245
 
246
246
  ## `/ui` 可以做什么
247
247
 
248
- 访问 `http://127.0.0.1:3456/ui` 后,可以直接:
248
+ 访问 `http://127.0.0.1:5678/ui` 后,可以直接:
249
249
 
250
250
  - 编辑 `Models` 草稿
251
251
  - 预览 compiled model map
@@ -262,7 +262,7 @@ Models:
262
262
  如果你还在使用旧的 `Providers + provider,model` 配置:
263
263
 
264
264
  - 当前版本仍然兼容旧格式
265
- - `ctr setup` 会优先尝试迁移旧 `ccr` 配置
265
+ - `ctr setup` 会优先尝试迁移旧 `ccr` / `claude-code-router` 配置
266
266
  - 路由字段推荐逐步改成直接引用 `Models[].id`
267
267
 
268
268
  迁移后的核心变化是:
@@ -279,6 +279,8 @@ Models:
279
279
  ```bash
280
280
  ctr setup
281
281
  ctr init
282
+ ctr version
283
+ ctr upgrade
282
284
  ctr start
283
285
  ctr start --daemon
284
286
  ctr stop
@@ -10,7 +10,7 @@
10
10
  # - custom/local: 自定义兼容地址,例如 Ollama / vLLM / one-api
11
11
 
12
12
  HOST: "127.0.0.1"
13
- PORT: 3456
13
+ PORT: 5678
14
14
 
15
15
  LOG: true
16
16
  LOG_LEVEL: "debug"
package/dist/cli.js CHANGED
@@ -32,7 +32,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
32
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
33
 
34
34
  // src/constants.ts
35
- var import_os, import_path, CONFIG_DIR, CONFIG_FILE, CONFIG_FILE_JSON, CONFIG_FILE_YML, HOME_DIR, PID_FILE, GOVERNANCE_TRACE_FILE, GOVERNANCE_TRACE_ARCHIVE_DIR, GOVERNANCE_EXPORT_HISTORY_FILE, GOVERNANCE_SNAPSHOT_DIR, GOVERNANCE_SCHEDULE_FILE, DEFAULT_CONFIG, DEFAULT_TRIGGER_CONFIG, DEFAULT_SMART_ROUTER_CONFIG, DEFAULT_GOVERNANCE_CONFIG;
35
+ var import_os, import_path, CONFIG_DIR, CONFIG_FILE, CONFIG_FILE_JSON, CONFIG_FILE_YML, HOME_DIR, PID_FILE, GOVERNANCE_TRACE_FILE, GOVERNANCE_TRACE_ARCHIVE_DIR, GOVERNANCE_EXPORT_HISTORY_FILE, GOVERNANCE_SNAPSHOT_DIR, GOVERNANCE_SCHEDULE_FILE, DEFAULT_CONFIG2, DEFAULT_TRIGGER_CONFIG, DEFAULT_SMART_ROUTER_CONFIG, DEFAULT_GOVERNANCE_CONFIG;
36
36
  var init_constants = __esm({
37
37
  "src/constants.ts"() {
38
38
  "use strict";
@@ -49,9 +49,9 @@ var init_constants = __esm({
49
49
  GOVERNANCE_EXPORT_HISTORY_FILE = (0, import_path.join)(CONFIG_DIR, "governance-metrics-export-history.json");
50
50
  GOVERNANCE_SNAPSHOT_DIR = (0, import_path.join)(CONFIG_DIR, "governance-metric-snapshots");
51
51
  GOVERNANCE_SCHEDULE_FILE = (0, import_path.join)(CONFIG_DIR, "governance-metric-schedules.json");
52
- DEFAULT_CONFIG = {
52
+ DEFAULT_CONFIG2 = {
53
53
  HOST: "127.0.0.1",
54
- PORT: 3456,
54
+ PORT: 5678,
55
55
  LOG: true,
56
56
  LOG_LEVEL: "debug",
57
57
  API_TIMEOUT_MS: 6e5,
@@ -775,7 +775,7 @@ function validateConfig(config) {
775
775
  function normalizeAndValidateConfig(config = {}) {
776
776
  const normalizedConfig = deepMerge(
777
777
  {
778
- ...DEFAULT_CONFIG,
778
+ ...DEFAULT_CONFIG2,
779
779
  Router: {
780
780
  default: ""
781
781
  },
@@ -1404,6 +1404,7 @@ var CONTEXT_ALIGNMENT_PROMPT, ContextAlignmentService, contextAlignmentService;
1404
1404
  var init_context_alignment = __esm({
1405
1405
  "src/governance/context-alignment.ts"() {
1406
1406
  "use strict";
1407
+ init_constants();
1407
1408
  init_log();
1408
1409
  init_message_ir();
1409
1410
  init_anthropic();
@@ -1432,7 +1433,7 @@ Do not include markdown fences.`;
1432
1433
  buildPrompt(text, previousModel, nextModel) {
1433
1434
  return CONTEXT_ALIGNMENT_PROMPT.replace("{previousModel}", previousModel).replace("{nextModel}", nextModel).replace("{request}", text);
1434
1435
  }
1435
- async summarizeTransition(text, previousModel, nextModel, config, port = 3456, fetchFn, apiKey, timeoutMs) {
1436
+ async summarizeTransition(text, previousModel, nextModel, config, port = DEFAULT_CONFIG2.PORT, fetchFn, apiKey, timeoutMs) {
1436
1437
  if (!config.enabled || !config.summarizer_model || !text.trim()) {
1437
1438
  return null;
1438
1439
  }
@@ -1656,6 +1657,7 @@ var SemanticRouter, semanticRouter;
1656
1657
  var init_semantic_router = __esm({
1657
1658
  "src/governance/semantic-router.ts"() {
1658
1659
  "use strict";
1660
+ init_constants();
1659
1661
  init_log();
1660
1662
  init_message_ir();
1661
1663
  init_anthropic();
@@ -1714,7 +1716,7 @@ Return JSON only:
1714
1716
  analyze(text, config) {
1715
1717
  return this.analyzeEmbedding(text, config);
1716
1718
  }
1717
- async analyzeWithClassifier(text, config, port = 3456, fetchFn, apiKey, timeoutMs) {
1719
+ async analyzeWithClassifier(text, config, port = DEFAULT_CONFIG2.PORT, fetchFn, apiKey, timeoutMs) {
1718
1720
  if (!config?.enabled || config.mode !== "classifier" || !config.prototypes || Object.keys(config.prototypes).length === 0) {
1719
1721
  return this.analyzeEmbedding(text, config);
1720
1722
  }
@@ -1778,6 +1780,7 @@ var SHADOW_VERIFIER_PROMPT, ShadowSupervisor, shadowSupervisor;
1778
1780
  var init_shadow_supervisor = __esm({
1779
1781
  "src/governance/shadow-supervisor.ts"() {
1780
1782
  "use strict";
1783
+ init_constants();
1781
1784
  init_log();
1782
1785
  init_message_ir();
1783
1786
  init_anthropic();
@@ -1852,7 +1855,7 @@ If no issue is found, return:
1852
1855
  }
1853
1856
  });
1854
1857
  }
1855
- async inspectWithVerifier(payload, config, port = 3456, fetchFn, apiKey, timeoutMs) {
1858
+ async inspectWithVerifier(payload, config, port = DEFAULT_CONFIG2.PORT, fetchFn, apiKey, timeoutMs) {
1856
1859
  const text = extractText(payload).trim();
1857
1860
  if (!config.enabled || !config.verifier_model || !text) {
1858
1861
  return this.inspect(payload, config);
@@ -3527,7 +3530,7 @@ function isServiceRunning() {
3527
3530
  function savePid(pid, port) {
3528
3531
  const info = {
3529
3532
  pid,
3530
- port: port ?? 3456,
3533
+ port: port ?? DEFAULT_CONFIG2.PORT,
3531
3534
  startTime: (/* @__PURE__ */ new Date()).toISOString()
3532
3535
  };
3533
3536
  (0, import_fs4.writeFileSync)(PID_FILE, JSON.stringify(info, null, 2), "utf-8");
@@ -3537,7 +3540,7 @@ function readServiceInfo() {
3537
3540
  try {
3538
3541
  const content = (0, import_fs4.readFileSync)(PID_FILE, "utf-8").trim();
3539
3542
  if (/^\d+$/.test(content)) {
3540
- return { pid: parseInt(content, 10), port: 3456, startTime: "" };
3543
+ return { pid: parseInt(content, 10), port: DEFAULT_CONFIG2.PORT, startTime: "" };
3541
3544
  }
3542
3545
  return JSON.parse(content);
3543
3546
  } catch {
@@ -4130,6 +4133,7 @@ var init_intent = __esm({
4130
4133
  "src/trigger/intent.ts"() {
4131
4134
  "use strict";
4132
4135
  import_lru_cache6 = require("lru-cache");
4136
+ init_constants();
4133
4137
  init_log();
4134
4138
  intentCache = new import_lru_cache6.LRUCache({
4135
4139
  max: 500,
@@ -4198,7 +4202,7 @@ Important: Respond ONLY with the JSON, no additional text.`;
4198
4202
  * @param fetchFn fetch 函数(用于发起 API 请求)
4199
4203
  * @returns 意图识别结果
4200
4204
  */
4201
- async detectIntent(text, config, port = 3456, fetchFn, apiKey, timeoutMs) {
4205
+ async detectIntent(text, config, port = DEFAULT_CONFIG2.PORT, fetchFn, apiKey, timeoutMs) {
4202
4206
  if (!config.llm_intent_recognition) {
4203
4207
  return {
4204
4208
  intent: "general",
@@ -4286,6 +4290,7 @@ var init_smart_router = __esm({
4286
4290
  "src/trigger/smart-router.ts"() {
4287
4291
  "use strict";
4288
4292
  import_lru_cache7 = require("lru-cache");
4293
+ init_constants();
4289
4294
  init_log();
4290
4295
  init_message_ir();
4291
4296
  init_anthropic();
@@ -4349,11 +4354,11 @@ Important:
4349
4354
  *
4350
4355
  * @param text 请求文本
4351
4356
  * @param config SmartRouter 配置
4352
- * @param port 本地服务端口(默认 3456
4357
+ * @param port 本地服务端口(默认 5678
4353
4358
  * @param fetchFn 可注入的 fetch 函数(用于测试)
4354
4359
  * @returns 选择结果,失败时返回 null
4355
4360
  */
4356
- async selectModel(text, config, port = 3456, fetchFn, apiKey, timeoutMs) {
4361
+ async selectModel(text, config, port = DEFAULT_CONFIG2.PORT, fetchFn, apiKey, timeoutMs) {
4357
4362
  if (!config.enabled) {
4358
4363
  return null;
4359
4364
  }
@@ -4428,6 +4433,7 @@ var init_selector = __esm({
4428
4433
  init_intent();
4429
4434
  init_smart_router();
4430
4435
  init_log();
4436
+ init_constants();
4431
4437
  init_session_store();
4432
4438
  init_semantic_router();
4433
4439
  init_compile();
@@ -4484,7 +4490,7 @@ var init_selector = __esm({
4484
4490
  * @param config 触发配置
4485
4491
  * @returns 分析结果
4486
4492
  */
4487
- async selectModel(req, config, port = 3456, smartRouterConfig, governanceConfig, apiKey, timeoutMs) {
4493
+ async selectModel(req, config, port = DEFAULT_CONFIG2.PORT, smartRouterConfig, governanceConfig, apiKey, timeoutMs) {
4488
4494
  const startTime = Date.now();
4489
4495
  const appConfig = req.appConfig;
4490
4496
  if (!config.enabled) {
@@ -4683,10 +4689,11 @@ var init_trigger = __esm({
4683
4689
  init_selector();
4684
4690
  init_analyzer();
4685
4691
  init_log();
4692
+ init_constants();
4686
4693
  TriggerRouter = class {
4687
4694
  config = null;
4688
4695
  appConfig = null;
4689
- port = 3456;
4696
+ port = DEFAULT_CONFIG2.PORT;
4690
4697
  smartRouterConfig = void 0;
4691
4698
  governanceConfig = void 0;
4692
4699
  apiKey;
@@ -4699,7 +4706,7 @@ var init_trigger = __esm({
4699
4706
  init(appConfig) {
4700
4707
  this.appConfig = appConfig;
4701
4708
  this.config = appConfig.TriggerRouter || this.getDefaultConfig();
4702
- this.port = appConfig.PORT || 3456;
4709
+ this.port = appConfig.PORT || DEFAULT_CONFIG2.PORT;
4703
4710
  this.smartRouterConfig = appConfig.SmartRouter;
4704
4711
  this.governanceConfig = appConfig.Governance;
4705
4712
  this.apiKey = appConfig.APIKEY;
@@ -5171,7 +5178,7 @@ async function run(options = {}) {
5171
5178
  HOST = "127.0.0.1";
5172
5179
  logWarn("\u26A0\uFE0F API key is not set. HOST is forced to 127.0.0.1.");
5173
5180
  }
5174
- const port = options.port ?? config.PORT ?? 3456;
5181
+ const port = options.port ?? config.PORT ?? DEFAULT_CONFIG.PORT;
5175
5182
  savePid(process.pid, port);
5176
5183
  process.on("SIGINT", () => {
5177
5184
  log("Received SIGINT, cleaning up...");
@@ -5882,7 +5889,7 @@ function decideSetupBranch(input2) {
5882
5889
  ensureNoLegacyAction(legacyConfigAction);
5883
5890
  return { kind: "reuse_current" };
5884
5891
  }
5885
- if (currentConfigAction === "overwrite") {
5892
+ if (currentConfigAction === "overwrite" || currentConfigAction === "fresh") {
5886
5893
  return ensureLegacyFlow(detection, legacyConfigAction);
5887
5894
  }
5888
5895
  return invalidAction();
@@ -6083,50 +6090,58 @@ function readStructuredConfigFile(filePath) {
6083
6090
  }
6084
6091
  return import_js_yaml.default.load(content);
6085
6092
  }
6086
- async function readCurrentConfig() {
6087
- const candidates = [CONFIG_FILE, CONFIG_FILE_YML, CONFIG_FILE_JSON];
6088
- const currentPath = candidates.find((filePath) => (0, import_fs6.existsSync)(filePath));
6089
- if (!currentPath) {
6093
+ async function readLegacyConfig(deps = {}) {
6094
+ const baseHomeDir = deps.homeDir || (0, import_os3.homedir)();
6095
+ const exists = deps.exists || import_fs6.existsSync;
6096
+ const readConfig = deps.readConfig || readStructuredConfigFile;
6097
+ const overridePath = process.env.CTR_SETUP_LEGACY_CONFIG_PATH;
6098
+ const candidatePaths = overridePath ? [overridePath] : [
6099
+ (0, import_path6.join)(baseHomeDir, ".ccr", "config.yaml"),
6100
+ (0, import_path6.join)(baseHomeDir, ".claude-code-router", "config.yaml")
6101
+ ];
6102
+ const legacyPath = candidatePaths.find((filePath) => exists(filePath));
6103
+ if (!legacyPath) {
6090
6104
  return { kind: "missing" };
6091
6105
  }
6092
6106
  try {
6093
6107
  return {
6094
6108
  kind: "found",
6095
- path: currentPath,
6096
- format: currentPath.endsWith(".json") ? "json" : currentPath.endsWith(".yml") ? "yml" : "yaml",
6097
- config: readStructuredConfigFile(currentPath) ?? {}
6109
+ path: legacyPath,
6110
+ config: readConfig(legacyPath)
6098
6111
  };
6099
6112
  } catch (error) {
6100
6113
  return {
6101
- kind: "parse_error",
6102
- path: currentPath,
6103
- format: currentPath.endsWith(".json") ? "json" : currentPath.endsWith(".yml") ? "yml" : "yaml",
6114
+ kind: "read_error",
6115
+ path: legacyPath,
6104
6116
  error: error instanceof Error ? error.message : String(error)
6105
6117
  };
6106
6118
  }
6107
6119
  }
6108
- async function readLegacyConfig() {
6109
- const legacyPath = process.env.CTR_SETUP_LEGACY_CONFIG_PATH || (0, import_path6.join)((0, import_os3.homedir)(), ".ccr", "config.yaml");
6110
- if (!(0, import_fs6.existsSync)(legacyPath)) {
6120
+ async function readCurrentConfig() {
6121
+ const candidates = [CONFIG_FILE, CONFIG_FILE_YML, CONFIG_FILE_JSON];
6122
+ const currentPath = candidates.find((filePath) => (0, import_fs6.existsSync)(filePath));
6123
+ if (!currentPath) {
6111
6124
  return { kind: "missing" };
6112
6125
  }
6113
6126
  try {
6114
6127
  return {
6115
6128
  kind: "found",
6116
- path: legacyPath,
6117
- config: readStructuredConfigFile(legacyPath)
6129
+ path: currentPath,
6130
+ format: currentPath.endsWith(".json") ? "json" : currentPath.endsWith(".yml") ? "yml" : "yaml",
6131
+ config: readStructuredConfigFile(currentPath) ?? {}
6118
6132
  };
6119
6133
  } catch (error) {
6120
6134
  return {
6121
- kind: "read_error",
6122
- path: legacyPath,
6135
+ kind: "parse_error",
6136
+ path: currentPath,
6137
+ format: currentPath.endsWith(".json") ? "json" : currentPath.endsWith(".yml") ? "yml" : "yaml",
6123
6138
  error: error instanceof Error ? error.message : String(error)
6124
6139
  };
6125
6140
  }
6126
6141
  }
6127
6142
  async function probeService() {
6128
- const healthy = await waitForService(DEFAULT_CONFIG.PORT, 500);
6129
- return healthy ? { kind: "self_healthy", port: DEFAULT_CONFIG.PORT } : { kind: "none" };
6143
+ const healthy = await waitForService(DEFAULT_CONFIG2.PORT, 500);
6144
+ return healthy ? { kind: "self_healthy", port: DEFAULT_CONFIG2.PORT } : { kind: "none" };
6130
6145
  }
6131
6146
  async function enterClaudeCode() {
6132
6147
  const cliModule = await Promise.resolve().then(() => (init_cli(), cli_exports));
@@ -6161,7 +6176,10 @@ function mapValidCurrentConfigChoice(choice) {
6161
6176
  if (choice === "overwrite" || choice === "\u68C0\u67E5\u5E76\u8C03\u6574\u5F53\u524D\u914D\u7F6E") {
6162
6177
  return "overwrite";
6163
6178
  }
6164
- if (choice === "cancel" || choice === "\u653E\u5F03\u5F53\u524D\u914D\u7F6E\uFF0C\u91CD\u65B0\u5F00\u59CB") {
6179
+ if (choice === "fresh" || choice === "\u653E\u5F03\u5F53\u524D\u914D\u7F6E\uFF0C\u91CD\u65B0\u5F00\u59CB") {
6180
+ return "fresh";
6181
+ }
6182
+ if (choice === "cancel") {
6165
6183
  return "cancel";
6166
6184
  }
6167
6185
  throw new Error("invalid current config action");
@@ -6276,8 +6294,15 @@ function toDraftFromConfig(config) {
6276
6294
  }
6277
6295
  };
6278
6296
  }
6297
+ function toSuggestedModelId(providerName, model, preset) {
6298
+ const presetDefinition = getProviderPreset(preset);
6299
+ if (presetDefinition?.suggested_id) {
6300
+ return presetDefinition.suggested_id;
6301
+ }
6302
+ const source = model || providerName || "model";
6303
+ return source.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "model";
6304
+ }
6279
6305
  async function buildFreshConfig(io) {
6280
- const modelId = await io.input("\u9ED8\u8BA4\u6A21\u578B ID");
6281
6306
  const connectMode = await io.choose("\u8FD9\u4E2A\u6A21\u578B\u63A5\u5230\u54EA\u91CC\uFF1F", ["\u4F7F\u7528\u5E38\u89C1\u63A5\u5165\u6A21\u677F", "\u624B\u52A8\u586B\u5199\u63A5\u53E3"]);
6282
6307
  let preset = "custom";
6283
6308
  let providerName = "provider";
@@ -6292,7 +6317,9 @@ async function buildFreshConfig(io) {
6292
6317
  apiBaseUrl = await io.input("API Base URL");
6293
6318
  }
6294
6319
  const apiKey = await io.input("API Key");
6295
- const model = await io.input("\u4E0A\u6E38\u6A21\u578B\u540D");
6320
+ const presetDefinition = getProviderPreset(preset);
6321
+ const model = await io.input("\u4E0A\u6E38\u6A21\u578B\u540D", presetDefinition?.default_model ?? "");
6322
+ const modelId = await io.input("\u9ED8\u8BA4\u6A21\u578B ID", toSuggestedModelId(providerName, model, preset));
6296
6323
  const capabilityMode = await io.choose("\u662F\u5426\u914D\u7F6E capability \u63D0\u793A", ["\u4FDD\u6301\u9ED8\u8BA4", "\u914D\u7F6E capability \u63D0\u793A"]);
6297
6324
  const draft = buildMinimalConfig({
6298
6325
  providers: [
@@ -6364,7 +6391,7 @@ function createDefaultDeps(io = createConsoleIO()) {
6364
6391
  executeStart,
6365
6392
  executeReload: executeRestart,
6366
6393
  executeRestart,
6367
- verifyHealth: () => waitForService(DEFAULT_CONFIG.PORT, 5e3),
6394
+ verifyHealth: () => waitForService(DEFAULT_CONFIG2.PORT, 5e3),
6368
6395
  enterClaudeCode,
6369
6396
  io
6370
6397
  };
@@ -6494,6 +6521,14 @@ __export(cli_exports, {
6494
6521
  runClaudeCode: () => runClaudeCode
6495
6522
  });
6496
6523
  module.exports = __toCommonJS(cli_exports);
6524
+ function getPackageInfo() {
6525
+ const content = (0, import_fs7.readFileSync)(PACKAGE_JSON_PATH, "utf-8");
6526
+ const pkg = JSON.parse(content);
6527
+ return {
6528
+ name: pkg.name ?? "@peterwangze/claude-trigger-router",
6529
+ version: pkg.version ?? "unknown"
6530
+ };
6531
+ }
6497
6532
  function getArgs() {
6498
6533
  return process.argv.slice(2);
6499
6534
  }
@@ -6531,7 +6566,7 @@ function getPort() {
6531
6566
  }
6532
6567
  } catch {
6533
6568
  }
6534
- return DEFAULT_CONFIG.PORT;
6569
+ return DEFAULT_CONFIG2.PORT;
6535
6570
  }
6536
6571
  function isDaemonMode() {
6537
6572
  return hasArg("--daemon", "-d");
@@ -6549,18 +6584,22 @@ Claude Trigger Router - \u667A\u80FD\u89E6\u53D1\u8DEF\u7531\u5668
6549
6584
  stop \u505C\u6B62\u540E\u53F0\u670D\u52A1
6550
6585
  restart \u91CD\u542F\u540E\u53F0\u670D\u52A1
6551
6586
  status \u67E5\u770B\u670D\u52A1\u8FD0\u884C\u72B6\u6001\uFF08PID\u3001\u7AEF\u53E3\u3001\u542F\u52A8\u65F6\u95F4\uFF09
6587
+ version \u67E5\u770B\u5F53\u524D\u5B89\u88C5\u7248\u672C\u4E0E\u5305\u4FE1\u606F
6588
+ upgrade \u67E5\u770B\u5347\u7EA7\u5230\u6700\u65B0 npm \u7248\u672C\u7684\u6307\u5F15
6552
6589
  code \u901A\u8FC7\u8DEF\u7531\u5668\u8FD0\u884C Claude Code\uFF08\u9700\u5148\u542F\u52A8\u670D\u52A1\uFF09
6553
6590
  ui \u6253\u5F00\u7BA1\u7406 API \u8BF4\u660E\u9875\uFF08Web UI \u5F00\u53D1\u4E2D\uFF09
6554
6591
  help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
6555
6592
 
6556
6593
  \u9009\u9879\uFF1A
6557
- --port, -p \u6307\u5B9A\u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4\uFF1A3456\uFF09
6594
+ --port, -p \u6307\u5B9A\u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4\uFF1A5678\uFF09
6558
6595
  --daemon, -d \u4EE5\u540E\u53F0\u65B9\u5F0F\u8FD0\u884C\uFF08\u914D\u5408 start/restart \u4F7F\u7528\uFF09
6559
6596
  --force \u5F3A\u5236\u8986\u76D6\u5DF2\u6709\u914D\u7F6E\uFF08\u914D\u5408 init \u4F7F\u7528\uFF09
6560
6597
 
6561
6598
  \u4F7F\u7528\u793A\u4F8B\uFF1A
6562
6599
  ctr setup # \u590D\u7528\u5F53\u524D\u914D\u7F6E / \u8FC1\u79FB\u65E7\u914D\u7F6E / \u65B0\u5EFA\u6700\u5C0F\u914D\u7F6E
6563
6600
  ctr init # \u521D\u59CB\u5316\u6700\u5C0F\u914D\u7F6E\u6A21\u677F
6601
+ ctr version # \u67E5\u770B\u5F53\u524D\u5B89\u88C5\u7248\u672C
6602
+ ctr upgrade # \u67E5\u770B\u5347\u7EA7\u5230\u6700\u65B0\u7248\u672C\u7684\u547D\u4EE4
6564
6603
  ctr start # \u524D\u53F0\u542F\u52A8\uFF08\u63A8\u8350\u9996\u6B21\u4F7F\u7528\uFF0C\u4FBF\u4E8E\u67E5\u770B\u65E5\u5FD7\uFF09
6565
6604
  ctr start --daemon # \u540E\u53F0\u542F\u52A8
6566
6605
  ctr status # \u67E5\u770B\u670D\u52A1\u72B6\u6001
@@ -6577,6 +6616,58 @@ Claude Trigger Router - \u667A\u80FD\u89E6\u53D1\u8DEF\u7531\u5668
6577
6616
  \u66F4\u591A\u4FE1\u606F\uFF1Ahttps://github.com/peterwangze/claude-trigger-router
6578
6617
  `);
6579
6618
  }
6619
+ async function getLatestPackageVersion(timeoutMs = 1500) {
6620
+ try {
6621
+ const response = await fetch(PACKAGE_REGISTRY_LATEST_URL, {
6622
+ signal: AbortSignal.timeout(timeoutMs)
6623
+ });
6624
+ if (!response.ok) {
6625
+ return null;
6626
+ }
6627
+ const payload = await response.json();
6628
+ return typeof payload.version === "string" ? payload.version : null;
6629
+ } catch {
6630
+ return null;
6631
+ }
6632
+ }
6633
+ function isNewerVersion(current, latest) {
6634
+ const currentParts = current.split(".").map((part) => Number.parseInt(part, 10));
6635
+ const latestParts = latest.split(".").map((part) => Number.parseInt(part, 10));
6636
+ const length = Math.max(currentParts.length, latestParts.length);
6637
+ for (let index = 0; index < length; index += 1) {
6638
+ const currentValue = Number.isFinite(currentParts[index]) ? currentParts[index] : 0;
6639
+ const latestValue = Number.isFinite(latestParts[index]) ? latestParts[index] : 0;
6640
+ if (latestValue > currentValue) {
6641
+ return true;
6642
+ }
6643
+ if (latestValue < currentValue) {
6644
+ return false;
6645
+ }
6646
+ }
6647
+ return false;
6648
+ }
6649
+ async function printVersion() {
6650
+ const pkg = getPackageInfo();
6651
+ const latestVersion = await getLatestPackageVersion();
6652
+ console.log(`Package: ${pkg.name}`);
6653
+ console.log(`Version: ${pkg.version}`);
6654
+ console.log(`Latest: ${latestVersion ?? "unavailable"}`);
6655
+ if (latestVersion && isNewerVersion(pkg.version, latestVersion)) {
6656
+ console.log(`Upgrade: npm install -g ${pkg.name}@latest`);
6657
+ }
6658
+ console.log(`NPM: ${PACKAGE_PAGE_URL}`);
6659
+ }
6660
+ function printUpgradeGuidance() {
6661
+ const pkg = getPackageInfo();
6662
+ console.log(`\u5F53\u524D\u5B89\u88C5\u7248\u672C\uFF1A${pkg.version}`);
6663
+ console.log(`\u5305\u540D\uFF1A${pkg.name}`);
6664
+ console.log("\u5347\u7EA7\u5230\u6700\u65B0\u7248\u672C\uFF1A");
6665
+ console.log(` npm install -g ${pkg.name}@latest`);
6666
+ console.log("\u8BF7\u5728\u5F53\u524D ctr \u8FDB\u7A0B\u5916\u6267\u884C\u5347\u7EA7\u547D\u4EE4\uFF0C\u907F\u514D\u81EA\u5347\u7EA7\u65F6\u5360\u7528\u5F53\u524D\u6587\u4EF6\u3002");
6667
+ console.log("\u5982\u679C\u4F60\u6700\u521D\u662F\u901A\u8FC7 GitHub \u6E90\u5B89\u88C5\uFF0C\u8BF7\u7EE7\u7EED\u4F7F\u7528\u539F\u5B89\u88C5\u6765\u6E90\uFF0C\u5F53\u524D\u547D\u4EE4\u4E0D\u4F1A\u81EA\u52A8\u5207\u6362\u6765\u6E90\u3002");
6668
+ console.log("\u5168\u5C40\u5B89\u88C5\u5728\u67D0\u4E9B\u73AF\u5883\u4E0B\u53EF\u80FD\u9700\u8981\u7BA1\u7406\u5458/root \u6743\u9650\u3002");
6669
+ console.log(`NPM: ${PACKAGE_PAGE_URL}`);
6670
+ }
6580
6671
  function initConfig2() {
6581
6672
  const force = hasArg("--force");
6582
6673
  const existingConfig = [CONFIG_FILE, CONFIG_FILE_YML, CONFIG_FILE_JSON].find(import_fs7.existsSync);
@@ -6763,6 +6854,12 @@ async function main() {
6763
6854
  case "status":
6764
6855
  showStatus();
6765
6856
  break;
6857
+ case "version":
6858
+ await printVersion();
6859
+ break;
6860
+ case "upgrade":
6861
+ printUpgradeGuidance();
6862
+ break;
6766
6863
  case "restart":
6767
6864
  restartService();
6768
6865
  break;
@@ -6785,7 +6882,7 @@ async function main() {
6785
6882
  process.exit(command ? 1 : 0);
6786
6883
  }
6787
6884
  }
6788
- var import_child_process2, import_path7, import_openurl, import_fs7;
6885
+ var import_child_process2, import_path7, import_openurl, import_fs7, PACKAGE_JSON_PATH, PACKAGE_PAGE_URL, PACKAGE_REGISTRY_LATEST_URL;
6789
6886
  var init_cli = __esm({
6790
6887
  "src/cli.ts"() {
6791
6888
  import_child_process2 = require("child_process");
@@ -6797,6 +6894,9 @@ var init_cli = __esm({
6797
6894
  init_constants();
6798
6895
  init_service_health();
6799
6896
  init_setup2();
6897
+ PACKAGE_JSON_PATH = (0, import_path7.join)(__dirname, "..", "package.json");
6898
+ PACKAGE_PAGE_URL = "https://www.npmjs.com/package/@peterwangze/claude-trigger-router";
6899
+ PACKAGE_REGISTRY_LATEST_URL = "https://registry.npmjs.org/@peterwangze%2Fclaude-trigger-router/latest";
6800
6900
  if (process.env.CTR_SKIP_MAIN !== "1") {
6801
6901
  main().catch((error) => {
6802
6902
  console.error("Error:", error);