nikou-cli 0.1.4 → 0.1.6

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  normalizeCliArgs
4
- } from "./chunk-F3EPGM5N.js";
4
+ } from "./chunk-OQQEIFQV.js";
5
5
 
6
6
  // src/index.ts
7
7
  import { Command } from "commander";
@@ -463,17 +463,17 @@ var TOOL_CONFIG = {
463
463
  gemini: { filePath: join6(homedir(), ".gemini", "settings.json"), format: "json", jsonPath: ["mcpServers"] },
464
464
  opencode: { filePath: join6(homedir(), ".config", "opencode", "opencode.json"), format: "json", jsonPath: ["mcp"] }
465
465
  };
466
- function readJsonFile(path29) {
467
- if (!existsSync5(path29)) return {};
466
+ function readJsonFile(path30) {
467
+ if (!existsSync5(path30)) return {};
468
468
  try {
469
- return JSON.parse(readFileSync4(path29, "utf-8"));
469
+ return JSON.parse(readFileSync4(path30, "utf-8"));
470
470
  } catch {
471
471
  return {};
472
472
  }
473
473
  }
474
- function writeJsonFile(path29, data) {
475
- mkdirSync4(dirname2(path29), { recursive: true });
476
- writeFileSync3(path29, JSON.stringify(data, null, 2) + "\n", "utf-8");
474
+ function writeJsonFile(path30, data) {
475
+ mkdirSync4(dirname2(path30), { recursive: true });
476
+ writeFileSync3(path30, JSON.stringify(data, null, 2) + "\n", "utf-8");
477
477
  }
478
478
  function getNestedObj(root, keys) {
479
479
  let cur = root;
@@ -500,10 +500,10 @@ function extractServerFromContent(content, jsonPath) {
500
500
  return null;
501
501
  }
502
502
  }
503
- function readTomlFile(path29) {
504
- if (!existsSync5(path29)) return "";
503
+ function readTomlFile(path30) {
504
+ if (!existsSync5(path30)) return "";
505
505
  try {
506
- return readFileSync4(path29, "utf-8");
506
+ return readFileSync4(path30, "utf-8");
507
507
  } catch {
508
508
  return "";
509
509
  }
@@ -549,9 +549,9 @@ function tomlListServers(toml) {
549
549
  }
550
550
  return results;
551
551
  }
552
- function backupFile(path29) {
553
- if (existsSync5(path29)) {
554
- copyFileSync(path29, path29 + ".bak");
552
+ function backupFile(path30) {
553
+ if (existsSync5(path30)) {
554
+ copyFileSync(path30, path30 + ".bak");
555
555
  }
556
556
  }
557
557
  function writeMcpToTool(toolId, serverName, variantContent, force) {
@@ -1538,6 +1538,19 @@ function resolveContextConfig(config) {
1538
1538
  )
1539
1539
  };
1540
1540
  }
1541
+ function resolveNacosSkillSyncConfig(config) {
1542
+ const raw = config.nacos_skill_sync ?? config.nacosSkillSync ?? {};
1543
+ return {
1544
+ enabled: parseBoolean(raw.enabled, true),
1545
+ profile: String(raw.profile || "nikou-block").trim(),
1546
+ label: String(raw.label || "latest").trim(),
1547
+ interval: String(raw.interval || "30s").trim(),
1548
+ plan_poll_interval_ms: parsePositiveInt(raw.plan_poll_interval_ms ?? raw.planPollIntervalMs, 6e4),
1549
+ planPollIntervalMs: parsePositiveInt(raw.plan_poll_interval_ms ?? raw.planPollIntervalMs, 6e4),
1550
+ auto_upload: parseBoolean(raw.auto_upload ?? raw.autoUpload, false),
1551
+ autoUpload: parseBoolean(raw.auto_upload ?? raw.autoUpload, false)
1552
+ };
1553
+ }
1541
1554
  function resolveMasterConfig(config, overrides = {}) {
1542
1555
  const hook2 = resolveHookSection(config);
1543
1556
  const feishuConfig = extractFeishuConfig(config);
@@ -2582,7 +2595,7 @@ var CardStreamUpdater = class {
2582
2595
  lastTokenUsage = "";
2583
2596
  sequence = 1;
2584
2597
  flushTimer = null;
2585
- flushing = false;
2598
+ flushPromise = null;
2586
2599
  disposed = false;
2587
2600
  constructor(larkClient, cardId, taskId, logger3, intervalMs = CARD_STREAM_INTERVAL_MS) {
2588
2601
  this.larkClient = larkClient;
@@ -2670,8 +2683,19 @@ ${incoming}`, CARD_STREAM_STDOUT_MAX_LENGTH);
2670
2683
  }, this.intervalMs);
2671
2684
  }
2672
2685
  async flush() {
2673
- if (this.disposed || this.flushing) return;
2674
- this.flushing = true;
2686
+ if (this.disposed) return;
2687
+ if (this.flushPromise) {
2688
+ await this.flushPromise;
2689
+ return;
2690
+ }
2691
+ this.flushPromise = this.flushNow();
2692
+ try {
2693
+ await this.flushPromise;
2694
+ } finally {
2695
+ this.flushPromise = null;
2696
+ }
2697
+ }
2698
+ async flushNow() {
2675
2699
  try {
2676
2700
  const tokenKey = JSON.stringify(this.tokenUsage);
2677
2701
  const needsFullUpdate = this.lastSession !== this.sessionBuffer || this.lastPath !== this.pathBuffer || this.lastHostName !== this.hostName || this.lastStopResult !== this.stopResultBuffer || this.lastDisableStop !== this.stopDisabled || this.lastTokenUsage !== tokenKey;
@@ -2703,8 +2727,6 @@ ${incoming}`, CARD_STREAM_STDOUT_MAX_LENGTH);
2703
2727
  }
2704
2728
  } catch (error) {
2705
2729
  this.logger.warn(`\u5361\u7247\u5237\u65B0\u5931\u8D25: ${formatCardUpdateError(error)}`);
2706
- } finally {
2707
- this.flushing = false;
2708
2730
  }
2709
2731
  }
2710
2732
  // ─── 飞书 API 调用 ──────────────────────────────
@@ -2814,19 +2836,64 @@ ${incoming}`, CARD_STREAM_STDOUT_MAX_LENGTH);
2814
2836
  clearTimeout(this.flushTimer);
2815
2837
  this.flushTimer = null;
2816
2838
  }
2817
- this.flushing = false;
2839
+ if (this.flushPromise) {
2840
+ await this.flushPromise;
2841
+ }
2818
2842
  await this.flush();
2819
2843
  this.disposed = true;
2820
2844
  }
2821
2845
  };
2822
2846
 
2847
+ // src/hook/message-content-parser.ts
2848
+ function parseContent(content) {
2849
+ if (content && typeof content === "object" && !Array.isArray(content)) {
2850
+ return content;
2851
+ }
2852
+ if (typeof content !== "string") {
2853
+ return {};
2854
+ }
2855
+ try {
2856
+ const parsed = JSON.parse(content);
2857
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
2858
+ } catch {
2859
+ return {};
2860
+ }
2861
+ }
2862
+ function extractFeishuPostText(content) {
2863
+ const parsed = parseContent(content);
2864
+ const title = String(parsed.title || "").trim();
2865
+ const rows = Array.isArray(parsed.content) ? parsed.content : Array.isArray(parsed.content_v2) ? parsed.content_v2 : [];
2866
+ const parts = [];
2867
+ if (title) {
2868
+ parts.push(title);
2869
+ }
2870
+ rows.forEach((row) => {
2871
+ if (!Array.isArray(row)) {
2872
+ return;
2873
+ }
2874
+ row.forEach((item) => {
2875
+ const block = item;
2876
+ const tag = String(block.tag || "").trim();
2877
+ if ((tag === "text" || tag === "a") && block.text) {
2878
+ parts.push(String(block.text));
2879
+ return;
2880
+ }
2881
+ if (tag === "at") {
2882
+ const name = String(block.user_name || block.name || "").replace(/^@/, "").trim();
2883
+ parts.push(name ? `@${name}` : "@");
2884
+ }
2885
+ });
2886
+ });
2887
+ return parts.join("").replace(/\s+/g, " ").trim();
2888
+ }
2889
+
2823
2890
  // src/hook/master/message-parser.ts
2824
2891
  function extractMessageText(message) {
2825
2892
  const content = message?.content || message?.body?.content;
2826
2893
  if (!content) return "";
2827
2894
  try {
2828
2895
  const parsed = typeof content === "string" ? JSON.parse(content) : content;
2829
- const text = String(parsed?.text || "") || extractPostText(parsed);
2896
+ const text = String(parsed?.text || "") || extractFeishuPostText(parsed);
2830
2897
  return stripMentions(text, message);
2831
2898
  } catch {
2832
2899
  return stripMentions(String(content || ""), message);
@@ -2858,47 +2925,6 @@ function isMentionBot(message, botName) {
2858
2925
  return name === botName || name === DEFAULT_BOT_NAME;
2859
2926
  });
2860
2927
  }
2861
- function extractPostText(content) {
2862
- const parsed = parseContent(content);
2863
- const title = String(parsed?.title || "").trim();
2864
- const rows = Array.isArray(parsed?.content) ? parsed.content : [];
2865
- const parts = [];
2866
- if (title) {
2867
- parts.push(title);
2868
- }
2869
- rows.forEach((row) => {
2870
- if (!Array.isArray(row)) {
2871
- return;
2872
- }
2873
- row.forEach((item) => {
2874
- const block = item;
2875
- const tag = String(block?.tag || "").trim();
2876
- if ((tag === "text" || tag === "a") && block?.text) {
2877
- parts.push(String(block.text));
2878
- return;
2879
- }
2880
- if (tag === "at") {
2881
- const name = String(block.user_name || block.name || "").replace(/^@/, "").trim();
2882
- parts.push(name ? `@${name}` : "@");
2883
- }
2884
- });
2885
- });
2886
- return parts.join("").replace(/\s+/g, " ").trim();
2887
- }
2888
- function parseContent(content) {
2889
- if (content && typeof content === "object" && !Array.isArray(content)) {
2890
- return content;
2891
- }
2892
- if (typeof content !== "string") {
2893
- return {};
2894
- }
2895
- try {
2896
- const parsed = JSON.parse(content);
2897
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
2898
- } catch {
2899
- return {};
2900
- }
2901
- }
2902
2928
  function stripMentions(text, message) {
2903
2929
  let cleaned = String(text || "");
2904
2930
  const mentions = Array.isArray(message?.mentions) ? message.mentions : [];
@@ -5275,13 +5301,20 @@ var HookMasterCommand = class {
5275
5301
  onCardAction: (data) => this.handleCardActionTrigger(data)
5276
5302
  });
5277
5303
  this.startBindWhitelistWatcher();
5278
- this.setupExitHandlers();
5304
+ if (this.options.manageProcessSignals !== false) {
5305
+ this.setupExitHandlers();
5306
+ }
5279
5307
  this.heartbeatTimer = setInterval(() => {
5280
5308
  this.stateManager?.updateHeartbeat();
5281
5309
  }, HEARTBEAT_INTERVAL_MS);
5282
5310
  this.logger.info(
5283
5311
  `\u4E3B\u8282\u70B9\u542F\u52A8\u5B8C\u6210 host=${masterConfig.masterHost} port=${masterConfig.masterPort}`
5284
5312
  );
5313
+ return {
5314
+ masterHost: masterConfig.masterHost,
5315
+ masterPort: masterConfig.masterPort,
5316
+ sharedSecret: masterConfig.sharedSecret
5317
+ };
5285
5318
  }
5286
5319
  async handleFeishuMessage(data) {
5287
5320
  const message = resolveEventMessage(data);
@@ -5772,34 +5805,34 @@ var HookMasterCommand = class {
5772
5805
  });
5773
5806
  }
5774
5807
  setupExitHandlers() {
5775
- const cleanup2 = async (signal) => {
5776
- this.logger.info(`\u6536\u5230\u4FE1\u53F7 ${signal}\uFF0C\u6B63\u5728\u5173\u95ED...`);
5777
- if (this.heartbeatTimer) {
5778
- clearInterval(this.heartbeatTimer);
5779
- this.heartbeatTimer = null;
5780
- }
5781
- if (this.bindWhitelistWatchTimer) {
5782
- clearInterval(this.bindWhitelistWatchTimer);
5783
- this.bindWhitelistWatchTimer = null;
5784
- }
5785
- this.groupRouteDispatcher?.clear();
5786
- this.wsServer?.stop();
5787
- this.stateManager?.update({
5788
- status: "stopped",
5789
- stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
5790
- lastSignal: signal
5791
- });
5792
- await this.usageRecorder.close();
5793
- };
5794
5808
  process4.on("SIGINT", async () => {
5795
- await cleanup2("SIGINT");
5809
+ await this.stop("SIGINT");
5796
5810
  process4.exit(0);
5797
5811
  });
5798
5812
  process4.on("SIGTERM", async () => {
5799
- await cleanup2("SIGTERM");
5813
+ await this.stop("SIGTERM");
5800
5814
  process4.exit(0);
5801
5815
  });
5802
5816
  }
5817
+ async stop(signal = "manual") {
5818
+ this.logger.info(`\u6536\u5230\u4FE1\u53F7 ${signal}\uFF0C\u6B63\u5728\u5173\u95ED...`);
5819
+ if (this.heartbeatTimer) {
5820
+ clearInterval(this.heartbeatTimer);
5821
+ this.heartbeatTimer = null;
5822
+ }
5823
+ if (this.bindWhitelistWatchTimer) {
5824
+ clearInterval(this.bindWhitelistWatchTimer);
5825
+ this.bindWhitelistWatchTimer = null;
5826
+ }
5827
+ this.groupRouteDispatcher?.clear();
5828
+ this.wsServer?.stop();
5829
+ this.stateManager?.update({
5830
+ status: "stopped",
5831
+ stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
5832
+ lastSignal: signal
5833
+ });
5834
+ await this.usageRecorder.close();
5835
+ }
5803
5836
  };
5804
5837
 
5805
5838
  // src/hook/logger.ts
@@ -5920,8 +5953,8 @@ async function hookMasterAction(options) {
5920
5953
 
5921
5954
  // src/hook/worker/worker-command.ts
5922
5955
  import process10 from "process";
5923
- import os16 from "os";
5924
- import path26 from "path";
5956
+ import os17 from "os";
5957
+ import path27 from "path";
5925
5958
  import * as lark3 from "@larksuiteoapi/node-sdk";
5926
5959
 
5927
5960
  // src/hook/worker/runners/codex-runner.ts
@@ -6115,13 +6148,6 @@ import fs11 from "fs";
6115
6148
  import path10 from "path";
6116
6149
  import process6 from "process";
6117
6150
  import { spawnSync as spawnSync3 } from "child_process";
6118
- function quoteWindowsCmdArg(value) {
6119
- const text = String(value ?? "");
6120
- if (!text) {
6121
- return '""';
6122
- }
6123
- return `"${text.replace(/(["^&|<>%])/g, "^$1")}"`;
6124
- }
6125
6151
  function isExecutableFile(filePath) {
6126
6152
  if (!filePath || !fs11.existsSync(filePath)) {
6127
6153
  return false;
@@ -6220,22 +6246,22 @@ function resolveCliExecution(commandName, args, env = process6.env) {
6220
6246
  prependPathDir(env, path10.dirname(commandPath));
6221
6247
  }
6222
6248
  if (process6.platform === "win32") {
6223
- const commandLine = [
6224
- quoteWindowsCmdArg(commandPath),
6225
- ...normalizedArgs.map((item) => quoteWindowsCmdArg(item))
6226
- ].join(" ");
6227
6249
  return {
6228
6250
  command: "cmd.exe",
6229
- args: ["/d", "/s", "/c", `"${commandLine}"`],
6251
+ args: ["/d", "/c", "call", commandPath, ...normalizedArgs],
6230
6252
  shell: false,
6231
- displayCommand
6253
+ displayCommand,
6254
+ resolvedPath: commandPath,
6255
+ fallback: "cmd"
6232
6256
  };
6233
6257
  }
6234
6258
  return {
6235
6259
  command: commandPath,
6236
6260
  args: normalizedArgs,
6237
6261
  shell: false,
6238
- displayCommand
6262
+ displayCommand,
6263
+ resolvedPath: commandPath,
6264
+ fallback: commandPath === commandName ? "direct-unresolved" : "direct"
6239
6265
  };
6240
6266
  }
6241
6267
 
@@ -6327,14 +6353,19 @@ var CodexRunner = class {
6327
6353
  resolveCodexExecution(args, env = process7.env) {
6328
6354
  return resolveCliExecution("codex", args, env);
6329
6355
  }
6330
- buildFailureMessage(stderr, stdout, code, dirPath, displayCommand) {
6356
+ buildFailureMessage(stderr, stdout, code, dirPath, execConfig) {
6331
6357
  const reason = stderr.trim() || stdout.trim() || `codex \u9000\u51FA\u7801 ${code}`;
6332
6358
  return [
6333
6359
  reason,
6334
6360
  `cwd=${dirPath || "-"}`,
6335
- `command=${displayCommand || "codex"}`
6361
+ `command=${execConfig.displayCommand || "codex"}`,
6362
+ `resolved=${execConfig.resolvedPath || "-"}`,
6363
+ `fallback=${execConfig.fallback || "-"}`
6336
6364
  ].join("\n");
6337
6365
  }
6366
+ buildExecutionLog(prefix, execConfig) {
6367
+ return `${prefix}: ${execConfig.displayCommand} (resolved=${execConfig.resolvedPath || "-"}, fallback=${execConfig.fallback || "direct"})`;
6368
+ }
6338
6369
  writePromptToChildStdin(child, prompt, traceMessage = "") {
6339
6370
  if (!this.shouldUseStdinPrompt(prompt) || !child?.stdin) {
6340
6371
  return;
@@ -6433,7 +6464,7 @@ var CodexRunner = class {
6433
6464
  this.logger.info(`Codex \u51C6\u5907\u6267\u884C: dir=${dirPath}, thread_key=${threadKey || "-"}, session_id=${resolvedSessionId || "-"}, resume=${isResume}`);
6434
6465
  const childEnv = this.buildChildEnv(resolveTraceId(threadKey, resolvedSessionId, dirPath));
6435
6466
  const execConfig = this.resolveCodexExecution(args, childEnv);
6436
- this.logger.info(formatTraceLog(resolveTraceId(threadKey, resolvedSessionId, dirPath), `Codex \u6267\u884C\u547D\u4EE4: ${execConfig.displayCommand}`));
6467
+ this.logger.info(formatTraceLog(resolveTraceId(threadKey, resolvedSessionId, dirPath), this.buildExecutionLog("Codex \u6267\u884C\u547D\u4EE4", execConfig)));
6437
6468
  const child = spawn(execConfig.command, execConfig.args, {
6438
6469
  cwd: dirPath,
6439
6470
  shell: execConfig.shell,
@@ -6479,7 +6510,7 @@ command=${execConfig.displayCommand}`));
6479
6510
  this.stateManager.markCodexRun(dirPath);
6480
6511
  }
6481
6512
  if (code !== 0) {
6482
- reject(new Error(this.buildFailureMessage(stderr, stdout, code, dirPath, execConfig.displayCommand)));
6513
+ reject(new Error(this.buildFailureMessage(stderr, stdout, code, dirPath, execConfig)));
6483
6514
  return;
6484
6515
  }
6485
6516
  resolve3({
@@ -6530,7 +6561,7 @@ command=${execConfig.displayCommand}`));
6530
6561
  let notifiedSessionId = false;
6531
6562
  const childEnv = this.buildChildEnv(logTraceId);
6532
6563
  const execConfig = this.resolveCodexExecution(args, childEnv);
6533
- this.logger.info(formatTraceLog(logTraceId, `Codex \u6267\u884C\u547D\u4EE4(\u6D41\u5F0F): ${execConfig.displayCommand}`));
6564
+ this.logger.info(formatTraceLog(logTraceId, this.buildExecutionLog("Codex \u6267\u884C\u547D\u4EE4(\u6D41\u5F0F)", execConfig)));
6534
6565
  const child = spawn(execConfig.command, execConfig.args, {
6535
6566
  cwd: dirPath,
6536
6567
  shell: execConfig.shell,
@@ -6660,7 +6691,7 @@ command=${execConfig.displayCommand}`));
6660
6691
  this.stateManager.markCodexRun(dirPath);
6661
6692
  }
6662
6693
  if (code !== 0) {
6663
- reject(new Error(this.buildFailureMessage(stderr, stdout, code, dirPath, execConfig.displayCommand)));
6694
+ reject(new Error(this.buildFailureMessage(stderr, stdout, code, dirPath, execConfig)));
6664
6695
  return;
6665
6696
  }
6666
6697
  resolve3({
@@ -7659,7 +7690,7 @@ var MessageReceiveStrategy = class {
7659
7690
  if (!raw) return "";
7660
7691
  try {
7661
7692
  const parsed = JSON.parse(raw);
7662
- const text = String(parsed?.text || "");
7693
+ const text = String(parsed?.text || "") || extractFeishuPostText(parsed);
7663
7694
  return this.stripMentions(text, message);
7664
7695
  } catch {
7665
7696
  return this.stripMentions(String(raw), message);
@@ -7667,25 +7698,7 @@ var MessageReceiveStrategy = class {
7667
7698
  }
7668
7699
  extractPostText(messageLike) {
7669
7700
  const raw = messageLike?.content || messageLike?.body?.content;
7670
- if (!raw) return "";
7671
- try {
7672
- const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
7673
- const blocks = parsed?.content || [];
7674
- const parts = [];
7675
- blocks.forEach((row) => {
7676
- (row || []).forEach((item) => {
7677
- if ((item?.tag === "text" || item?.tag === "a") && item?.text) {
7678
- parts.push(String(item.text));
7679
- }
7680
- if (item?.tag === "at") {
7681
- parts.push(String(item.user_name || item.name || ""));
7682
- }
7683
- });
7684
- });
7685
- return parts.join("").replace(/\s+/g, " ").trim();
7686
- } catch {
7687
- return "";
7688
- }
7701
+ return extractFeishuPostText(raw);
7689
7702
  }
7690
7703
  stripMentions(text, message) {
7691
7704
  let cleaned = String(text || "");
@@ -10498,6 +10511,7 @@ var LocalBrowserWsServer = class {
10498
10511
  workerId;
10499
10512
  workerType;
10500
10513
  larkClient;
10514
+ getNacosSyncStatus;
10501
10515
  wss = null;
10502
10516
  profile;
10503
10517
  startTime = (/* @__PURE__ */ new Date()).toISOString();
@@ -10507,6 +10521,7 @@ var LocalBrowserWsServer = class {
10507
10521
  this.workerId = options.workerId;
10508
10522
  this.workerType = options.workerType;
10509
10523
  this.larkClient = options.larkClient;
10524
+ this.getNacosSyncStatus = options.getNacosSyncStatus;
10510
10525
  }
10511
10526
  async start() {
10512
10527
  if (this.wss) {
@@ -10687,7 +10702,8 @@ var LocalBrowserWsServer = class {
10687
10702
  cliTools: {
10688
10703
  codex: this.detectCliTool("codex"),
10689
10704
  claude: this.detectCliTool("claude")
10690
- }
10705
+ },
10706
+ nacosSync: this.getNacosSyncStatus?.() ?? null
10691
10707
  }
10692
10708
  });
10693
10709
  }
@@ -13976,6 +13992,263 @@ var OwnerNotifyService = class {
13976
13992
  }
13977
13993
  };
13978
13994
 
13995
+ // src/hook/worker/nacos-subscription-sync.ts
13996
+ import fs27 from "fs";
13997
+ import os16 from "os";
13998
+ import path26 from "path";
13999
+ import { execFile, spawn as spawn4 } from "child_process";
14000
+ import { createRequire } from "module";
14001
+ import { promisify } from "util";
14002
+ var execFileAsync = promisify(execFile);
14003
+ var NacosSubscriptionSyncCoordinator = class {
14004
+ logger;
14005
+ workerId;
14006
+ config;
14007
+ timer = null;
14008
+ daemon = null;
14009
+ running = false;
14010
+ stopped = false;
14011
+ status;
14012
+ statePath = path26.join(os16.homedir(), ".nikou-block", "worker", "nacos-subscriptions.json");
14013
+ constructor(options) {
14014
+ this.logger = options.logger;
14015
+ this.workerId = options.workerId;
14016
+ this.config = options.config;
14017
+ this.status = {
14018
+ enabled: this.config.enabled,
14019
+ configured: false,
14020
+ profile: this.config.profile,
14021
+ label: this.config.label,
14022
+ daemonRunning: false,
14023
+ totalSkills: 0,
14024
+ synced: 0,
14025
+ pending: 0,
14026
+ conflicts: 0,
14027
+ failed: 0
14028
+ };
14029
+ }
14030
+ start() {
14031
+ if (!this.config.enabled || this.timer) return;
14032
+ void this.syncOnce();
14033
+ this.timer = setInterval(() => {
14034
+ void this.syncOnce();
14035
+ }, this.config.plan_poll_interval_ms);
14036
+ }
14037
+ async stop() {
14038
+ this.stopped = true;
14039
+ if (this.timer) {
14040
+ clearInterval(this.timer);
14041
+ this.timer = null;
14042
+ }
14043
+ if (this.daemon) {
14044
+ this.daemon.kill("SIGTERM");
14045
+ this.daemon = null;
14046
+ this.status.daemonRunning = false;
14047
+ }
14048
+ }
14049
+ getStatus() {
14050
+ return { ...this.status };
14051
+ }
14052
+ async syncOnce() {
14053
+ if (this.running || this.stopped) return;
14054
+ this.running = true;
14055
+ try {
14056
+ const plan = await this.fetchPlan();
14057
+ this.status.enabled = plan.nacos.enabled;
14058
+ this.status.host = plan.nacos.host;
14059
+ this.status.profile = plan.nacos.profile || this.config.profile;
14060
+ this.status.label = plan.nacos.label || this.config.label;
14061
+ this.status.lastPlanAt = (/* @__PURE__ */ new Date()).toISOString();
14062
+ if (!plan.nacos.enabled) {
14063
+ this.status.configured = false;
14064
+ this.status.totalSkills = 0;
14065
+ this.status.pending = 0;
14066
+ return;
14067
+ }
14068
+ await this.configureProfile(plan);
14069
+ await this.ensureMode(plan);
14070
+ this.ensureDaemon(plan);
14071
+ const results = [];
14072
+ for (const skill of plan.skills) {
14073
+ const result = await this.syncSkill(plan, skill);
14074
+ results.push(result);
14075
+ }
14076
+ await this.report(results);
14077
+ this.persistManagedState(plan.skills);
14078
+ this.applySummary(results);
14079
+ this.status.lastSyncAt = (/* @__PURE__ */ new Date()).toISOString();
14080
+ this.status.lastError = void 0;
14081
+ } catch (error) {
14082
+ const message = error instanceof Error ? error.message : String(error);
14083
+ this.status.lastError = message;
14084
+ this.logger.warn(`Nacos Skill \u8BA2\u9605\u540C\u6B65\u5931\u8D25: ${message}`);
14085
+ } finally {
14086
+ this.running = false;
14087
+ }
14088
+ }
14089
+ async fetchPlan() {
14090
+ const runtime = resolveCliAuthRuntimeConfig();
14091
+ if (!runtime.cliAuthSecret) {
14092
+ throw new Error("\u672A\u914D\u7F6E CLI_AUTH_SECRET\uFF0C\u65E0\u6CD5\u62C9\u53D6 Nacos \u540C\u6B65\u8BA1\u5212");
14093
+ }
14094
+ const response = await fetch(`${runtime.apiUrl}/worker-skills/nacos-sync/plan`, {
14095
+ method: "POST",
14096
+ headers: { "Content-Type": "application/json" },
14097
+ body: JSON.stringify({ secret: runtime.cliAuthSecret, workerId: this.workerId })
14098
+ });
14099
+ if (!response.ok) {
14100
+ const text = await response.text().catch(() => "");
14101
+ throw new Error(`\u62C9\u53D6 Nacos \u540C\u6B65\u8BA1\u5212\u5931\u8D25: ${response.status} ${text}`);
14102
+ }
14103
+ return await response.json();
14104
+ }
14105
+ async report(results) {
14106
+ if (!results.length) return;
14107
+ const runtime = resolveCliAuthRuntimeConfig();
14108
+ const response = await fetch(`${runtime.apiUrl}/worker-skills/nacos-sync/report`, {
14109
+ method: "POST",
14110
+ headers: { "Content-Type": "application/json" },
14111
+ body: JSON.stringify({ secret: runtime.cliAuthSecret, workerId: this.workerId, results })
14112
+ });
14113
+ if (!response.ok) {
14114
+ const text = await response.text().catch(() => "");
14115
+ throw new Error(`\u4E0A\u62A5 Nacos \u540C\u6B65\u72B6\u6001\u5931\u8D25: ${response.status} ${text}`);
14116
+ }
14117
+ }
14118
+ async configureProfile(plan) {
14119
+ const nacos = plan.nacos;
14120
+ const profile = nacos.profile || this.config.profile;
14121
+ const args = [
14122
+ "profile",
14123
+ "set",
14124
+ profile,
14125
+ `host=${nacos.serverHost || this.parseHost(nacos.host).host}`,
14126
+ `port=${nacos.port || this.parseHost(nacos.host).port}`,
14127
+ `scheme=${nacos.scheme || this.parseHost(nacos.host).scheme}`,
14128
+ `namespace=${nacos.namespace || "public"}`,
14129
+ `auth-type=${nacos.authType || "none"}`
14130
+ ];
14131
+ if (nacos.username) args.push(`username=${nacos.username}`);
14132
+ if (nacos.password) args.push(`password=${nacos.password}`);
14133
+ await this.run(args);
14134
+ this.status.configured = true;
14135
+ }
14136
+ async ensureMode(plan) {
14137
+ await this.run(["skill-sync", "mode", "nacos", "--profile", plan.nacos.profile || this.config.profile, "--switch-profile"]);
14138
+ }
14139
+ ensureDaemon(plan) {
14140
+ if (this.daemon && !this.daemon.killed) return;
14141
+ const args = [
14142
+ "skill-sync",
14143
+ "start",
14144
+ "--foreground",
14145
+ "--non-interactive",
14146
+ "--profile",
14147
+ plan.nacos.profile || this.config.profile,
14148
+ "--label",
14149
+ plan.nacos.label || this.config.label,
14150
+ "--interval",
14151
+ this.config.interval,
14152
+ "--switch-profile"
14153
+ ];
14154
+ if (!this.config.auto_upload) {
14155
+ args.push("--no-auto-upload");
14156
+ }
14157
+ const bin = this.resolveNacosCliBin();
14158
+ this.daemon = spawn4(bin, args, { stdio: ["ignore", "pipe", "pipe"] });
14159
+ this.status.daemonRunning = true;
14160
+ this.daemon.stdout?.on("data", (chunk) => this.logger.debug(`nacos-cli: ${String(chunk).trim()}`));
14161
+ this.daemon.stderr?.on("data", (chunk) => this.logger.warn(`nacos-cli: ${String(chunk).trim()}`));
14162
+ this.daemon.once("exit", (code, signal) => {
14163
+ this.status.daemonRunning = false;
14164
+ this.daemon = null;
14165
+ if (!this.stopped) {
14166
+ this.status.lastError = `Nacos daemon \u5DF2\u9000\u51FA: code=${code ?? "-"}, signal=${signal ?? "-"}`;
14167
+ }
14168
+ });
14169
+ }
14170
+ async syncSkill(plan, skill) {
14171
+ try {
14172
+ await this.run([
14173
+ "skill-sync",
14174
+ skill.status === "pending_remove" ? "remove" : "add",
14175
+ skill.nacosSkillName,
14176
+ "--profile",
14177
+ plan.nacos.profile || this.config.profile,
14178
+ "--switch-profile",
14179
+ ...skill.status === "pending_remove" ? [] : ["--non-interactive"]
14180
+ ]);
14181
+ return {
14182
+ resourceId: skill.resourceId,
14183
+ nacosSkillName: skill.nacosSkillName,
14184
+ desiredVersionKey: skill.desiredVersionKey,
14185
+ status: skill.status === "pending_remove" ? "removed" : "synced",
14186
+ message: "Nacos Skill Sync \u6267\u884C\u6210\u529F"
14187
+ };
14188
+ } catch (error) {
14189
+ const message = error instanceof Error ? error.message : String(error);
14190
+ const status = /conflict|冲突/i.test(message) ? "conflict" : "failed";
14191
+ return {
14192
+ resourceId: skill.resourceId,
14193
+ nacosSkillName: skill.nacosSkillName,
14194
+ desiredVersionKey: skill.desiredVersionKey,
14195
+ status,
14196
+ message
14197
+ };
14198
+ }
14199
+ }
14200
+ async run(args) {
14201
+ const bin = this.resolveNacosCliBin();
14202
+ const result = await execFileAsync(bin, args, { timeout: 6e4, maxBuffer: 1024 * 1024 * 2 });
14203
+ return `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
14204
+ }
14205
+ resolveNacosCliBin() {
14206
+ try {
14207
+ const require2 = createRequire(import.meta.url);
14208
+ const pkgPath = require2.resolve("@nacos-group/cli/package.json");
14209
+ return path26.join(path26.dirname(pkgPath), "bin", "cli.js");
14210
+ } catch {
14211
+ return "nacos-cli";
14212
+ }
14213
+ }
14214
+ applySummary(results) {
14215
+ this.status.totalSkills = results.length;
14216
+ this.status.synced = results.filter((item) => item.status === "synced" || item.status === "removed").length;
14217
+ this.status.pending = results.filter((item) => item.status.startsWith("pending")).length;
14218
+ this.status.conflicts = results.filter((item) => item.status === "conflict").length;
14219
+ this.status.failed = results.filter((item) => item.status === "failed").length;
14220
+ }
14221
+ persistManagedState(skills) {
14222
+ try {
14223
+ fs27.mkdirSync(path26.dirname(this.statePath), { recursive: true });
14224
+ fs27.writeFileSync(this.statePath, JSON.stringify({
14225
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
14226
+ skills: skills.map((item) => ({
14227
+ resourceId: item.resourceId,
14228
+ nacosSkillName: item.nacosSkillName,
14229
+ desiredLabel: item.desiredLabel,
14230
+ desiredVersionKey: item.desiredVersionKey,
14231
+ status: item.status
14232
+ }))
14233
+ }, null, 2));
14234
+ } catch (error) {
14235
+ this.logger.warn(`\u5199\u5165 Nacos \u8BA2\u9605\u72B6\u6001\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
14236
+ }
14237
+ }
14238
+ parseHost(raw) {
14239
+ try {
14240
+ const url = new URL(raw.includes("://") ? raw : `http://${raw}`);
14241
+ return {
14242
+ scheme: url.protocol.replace(":", "") || "http",
14243
+ host: url.hostname,
14244
+ port: url.port || (url.protocol === "https:" ? "443" : "8848")
14245
+ };
14246
+ } catch {
14247
+ return { scheme: "http", host: raw.replace(/^https?:\/\//, ""), port: "8848" };
14248
+ }
14249
+ }
14250
+ };
14251
+
13979
14252
  // src/hook/worker/worker-command.ts
13980
14253
  var HookWorkerCommand = class {
13981
14254
  logger;
@@ -13984,11 +14257,21 @@ var HookWorkerCommand = class {
13984
14257
  localBrowserWs = null;
13985
14258
  reminderService = null;
13986
14259
  ownerNotifyService = null;
14260
+ nacosSyncCoordinator = null;
13987
14261
  heartbeatTimer = null;
13988
14262
  stateManager = null;
14263
+ stopped = false;
14264
+ stoppedPromise;
14265
+ resolveStopped = () => void 0;
13989
14266
  constructor(logger3, options = {}) {
13990
14267
  this.logger = logger3;
13991
14268
  this.options = options;
14269
+ this.stoppedPromise = new Promise((resolve3) => {
14270
+ this.resolveStopped = resolve3;
14271
+ });
14272
+ }
14273
+ waitUntilStopped() {
14274
+ return this.stoppedPromise;
13992
14275
  }
13993
14276
  async start() {
13994
14277
  const existingPid = findRunningAiHookProcess(process10.pid);
@@ -14011,6 +14294,7 @@ var HookWorkerCommand = class {
14011
14294
  });
14012
14295
  const bindAllowedUserIds = resolveBindAllowedUserIds(config);
14013
14296
  const contextConfig = resolveContextConfig(config);
14297
+ const nacosSyncConfig = resolveNacosSkillSyncConfig(config);
14014
14298
  const opsUserName = resolveOpsUserName();
14015
14299
  const larkClient = new lark3.Client({
14016
14300
  appId: feishuConfig.appId,
@@ -14076,13 +14360,20 @@ var HookWorkerCommand = class {
14076
14360
  config: this.options.runnerConfig || {}
14077
14361
  });
14078
14362
  this.logger.info(`\u5F15\u64CE\u5DF2\u521B\u5EFA: engine=${engine}, \u53EF\u7528\u5F15\u64CE=[${registry.list().join(", ")}]`);
14363
+ this.nacosSyncCoordinator = new NacosSubscriptionSyncCoordinator({
14364
+ logger: this.logger,
14365
+ workerId: ownerUserId || `worker-${process10.pid}`,
14366
+ config: nacosSyncConfig
14367
+ });
14079
14368
  this.localBrowserWs = new LocalBrowserWsServer({
14080
14369
  logger: this.logger,
14081
14370
  workerId: ownerUserId || `worker-${process10.pid}`,
14082
14371
  workerType: "tooling-cli",
14083
- larkClient
14372
+ larkClient,
14373
+ getNacosSyncStatus: () => this.nacosSyncCoordinator?.getStatus() ?? null
14084
14374
  });
14085
14375
  await this.localBrowserWs.start();
14376
+ this.nacosSyncCoordinator.start();
14086
14377
  const hostProfileService = new HostProfileService({
14087
14378
  logger: this.logger,
14088
14379
  ownerUserId,
@@ -14111,7 +14402,7 @@ var HookWorkerCommand = class {
14111
14402
  const requesterPersonaService = new RequesterPersonaService({
14112
14403
  logger: this.logger,
14113
14404
  larkClient,
14114
- rootDir: process10.env.HOOK_REQUESTER_PERSONA_DIR || path26.join(os16.homedir(), "ops", "ai-hook", "persona")
14405
+ rootDir: process10.env.HOOK_REQUESTER_PERSONA_DIR || path27.join(os17.homedir(), "ops", "ai-hook", "persona")
14115
14406
  });
14116
14407
  const taskExecutor = new TaskExecutor({
14117
14408
  logger: this.logger,
@@ -14168,17 +14459,19 @@ var HookWorkerCommand = class {
14168
14459
  await this.relayClient.startAndWait();
14169
14460
  await reminderService.start();
14170
14461
  await this.ownerNotifyService.notifyOnline();
14171
- const gracefulExit = async (signal) => {
14172
- this.logger.info(`\u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u505C\u6B62\u4ECE\u8282\u70B9...`);
14173
- await this.stop(signal);
14174
- process10.exit(0);
14175
- };
14176
- process10.on("SIGINT", () => {
14177
- void gracefulExit("SIGINT");
14178
- });
14179
- process10.on("SIGTERM", () => {
14180
- void gracefulExit("SIGTERM");
14181
- });
14462
+ if (this.options.manageProcessSignals !== false) {
14463
+ const gracefulExit = async (signal) => {
14464
+ this.logger.info(`\u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u505C\u6B62\u4ECE\u8282\u70B9...`);
14465
+ await this.stop(signal);
14466
+ process10.exit(0);
14467
+ };
14468
+ process10.on("SIGINT", () => {
14469
+ void gracefulExit("SIGINT");
14470
+ });
14471
+ process10.on("SIGTERM", () => {
14472
+ void gracefulExit("SIGTERM");
14473
+ });
14474
+ }
14182
14475
  this.heartbeatTimer = setInterval(() => {
14183
14476
  stateManager.updateHeartbeat();
14184
14477
  }, HEARTBEAT_INTERVAL_MS);
@@ -14188,6 +14481,10 @@ var HookWorkerCommand = class {
14188
14481
  }
14189
14482
  /** 停止从节点 */
14190
14483
  async stop(signal) {
14484
+ if (this.stopped) {
14485
+ return;
14486
+ }
14487
+ this.stopped = true;
14191
14488
  if (this.heartbeatTimer) {
14192
14489
  clearInterval(this.heartbeatTimer);
14193
14490
  this.heartbeatTimer = null;
@@ -14196,6 +14493,10 @@ var HookWorkerCommand = class {
14196
14493
  this.localBrowserWs.stop();
14197
14494
  this.localBrowserWs = null;
14198
14495
  }
14496
+ if (this.nacosSyncCoordinator) {
14497
+ await this.nacosSyncCoordinator.stop();
14498
+ this.nacosSyncCoordinator = null;
14499
+ }
14199
14500
  if (this.reminderService) {
14200
14501
  this.reminderService.stop();
14201
14502
  this.reminderService = null;
@@ -14221,10 +14522,65 @@ var HookWorkerCommand = class {
14221
14522
  slaveConnectionStatus: "offline"
14222
14523
  });
14223
14524
  }
14525
+ this.resolveStopped();
14224
14526
  }
14225
14527
  };
14226
14528
 
14227
- // src/commands/hook/hook-worker.ts
14529
+ // src/commands/hook/worker-options.ts
14530
+ var WORKER_BOOLEAN_OPTIONS = /* @__PURE__ */ new Set([
14531
+ "--verbose"
14532
+ ]);
14533
+ var WORKER_VALUE_OPTIONS = /* @__PURE__ */ new Set([
14534
+ "--master-host",
14535
+ "--master-port",
14536
+ "--shared-secret",
14537
+ "--claim-timeout-ms",
14538
+ "--connect-timeout-ms",
14539
+ "--model",
14540
+ "--fallback-model",
14541
+ "--permission-mode",
14542
+ "--max-budget-usd",
14543
+ "--settings",
14544
+ "--mcp-config",
14545
+ "--add-dir",
14546
+ "--system-prompt",
14547
+ "--append-system-prompt",
14548
+ "--agent",
14549
+ "--agents",
14550
+ "--tools",
14551
+ "--allowedTools",
14552
+ "--disallowedTools"
14553
+ ]);
14554
+ function resolveWorkerPassthroughArgs(engine, commandName = "worker", rawArgs = process.argv.slice(2)) {
14555
+ const commandIndex = rawArgs.findIndex((item, index) => item === commandName && rawArgs[index - 1] === "hook");
14556
+ if (commandIndex < 0) {
14557
+ return [];
14558
+ }
14559
+ const tail = rawArgs.slice(commandIndex + 1);
14560
+ const args = tail[0] === commandName ? tail.slice(1) : tail;
14561
+ const normalizedEngine = String(engine || "codex").trim();
14562
+ const scanArgs = args[0] === normalizedEngine ? args.slice(1) : args;
14563
+ const passthroughArgs = [];
14564
+ for (let index = 0; index < scanArgs.length; index += 1) {
14565
+ const item = scanArgs[index];
14566
+ if (item === "--") {
14567
+ passthroughArgs.push(...scanArgs.slice(index + 1));
14568
+ break;
14569
+ }
14570
+ const key = item.includes("=") ? item.split("=")[0] : item;
14571
+ if (WORKER_BOOLEAN_OPTIONS.has(key)) {
14572
+ continue;
14573
+ }
14574
+ if (WORKER_VALUE_OPTIONS.has(key)) {
14575
+ if (!item.includes("=")) {
14576
+ index += 1;
14577
+ }
14578
+ continue;
14579
+ }
14580
+ passthroughArgs.push(item);
14581
+ }
14582
+ return passthroughArgs;
14583
+ }
14228
14584
  function normalizeClaudeArgs(options) {
14229
14585
  const args = [];
14230
14586
  const pushPair = (flag, value) => {
@@ -14256,6 +14612,8 @@ function normalizeClaudeArgs(options) {
14256
14612
  });
14257
14613
  return args;
14258
14614
  }
14615
+
14616
+ // src/commands/hook/hook-worker.ts
14259
14617
  async function hookWorkerAction(engine, options) {
14260
14618
  const logger3 = createLogger("worker", options.verbose === true);
14261
14619
  const normalizedEngine = (engine || "codex").toLowerCase();
@@ -14265,10 +14623,12 @@ async function hookWorkerAction(engine, options) {
14265
14623
  masterPort: parsePositiveInt(options.masterPort, 0) || void 0,
14266
14624
  sharedSecret: options.sharedSecret,
14267
14625
  connectTimeoutMs: parsePositiveInt(options.connectTimeoutMs, 0) || void 0,
14268
- runnerConfig: normalizedEngine === "claude" ? { claudeArgs: normalizeClaudeArgs(options) } : {}
14626
+ runnerConfig: normalizedEngine === "claude" ? { claudeArgs: normalizeClaudeArgs(options) } : {},
14627
+ manageProcessSignals: true
14269
14628
  });
14270
14629
  try {
14271
14630
  await command.start();
14631
+ await command.waitUntilStopped();
14272
14632
  } catch (error) {
14273
14633
  const msg = error instanceof Error ? error.message : String(error);
14274
14634
  logger3.error(`\u4ECE\u8282\u70B9\u542F\u52A8\u5931\u8D25: ${msg}`);
@@ -14276,23 +14636,98 @@ async function hookWorkerAction(engine, options) {
14276
14636
  }
14277
14637
  }
14278
14638
 
14639
+ // src/commands/hook/hook-local.ts
14640
+ function resolveLocalWorkerMasterHost(masterHost) {
14641
+ const host = String(masterHost || "").trim();
14642
+ if (host === "0.0.0.0" || host === "::" || host === "[::]") {
14643
+ return "127.0.0.1";
14644
+ }
14645
+ return host;
14646
+ }
14647
+ function resolveLocalWorkerOverrides(runtime) {
14648
+ return {
14649
+ masterHost: resolveLocalWorkerMasterHost(runtime.masterHost),
14650
+ masterPort: runtime.masterPort,
14651
+ sharedSecret: runtime.sharedSecret
14652
+ };
14653
+ }
14654
+ async function hookLocalAction(engine, options) {
14655
+ const localLogger = createLogger("local", options.verbose === true);
14656
+ const masterLogger = createLogger("master");
14657
+ const workerLogger = createLogger("worker", options.verbose === true);
14658
+ const normalizedEngine = (engine || "codex").toLowerCase();
14659
+ const master = new HookMasterCommand({
14660
+ logger: masterLogger,
14661
+ masterHost: options.masterHost,
14662
+ masterPort: parsePositiveInt(options.masterPort, 0) || void 0,
14663
+ sharedSecret: options.sharedSecret,
14664
+ claimTimeoutMs: parsePositiveInt(options.claimTimeoutMs, 0) || void 0,
14665
+ manageProcessSignals: false
14666
+ });
14667
+ let worker = null;
14668
+ let stopping = false;
14669
+ const stopAll = async (signal) => {
14670
+ if (stopping) {
14671
+ return;
14672
+ }
14673
+ stopping = true;
14674
+ localLogger.info(`\u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u505C\u6B62\u672C\u673A\u4E00\u4F53\u8282\u70B9...`);
14675
+ if (worker) {
14676
+ await worker.stop(signal);
14677
+ worker = null;
14678
+ }
14679
+ await master.stop(signal);
14680
+ };
14681
+ process.on("SIGINT", async () => {
14682
+ await stopAll("SIGINT");
14683
+ process.exit(0);
14684
+ });
14685
+ process.on("SIGTERM", async () => {
14686
+ await stopAll("SIGTERM");
14687
+ process.exit(0);
14688
+ });
14689
+ try {
14690
+ const runtime = await master.start();
14691
+ const workerOverrides = resolveLocalWorkerOverrides(runtime);
14692
+ worker = new HookWorkerCommand(workerLogger, {
14693
+ engine: normalizedEngine,
14694
+ masterHost: workerOverrides.masterHost,
14695
+ masterPort: workerOverrides.masterPort,
14696
+ sharedSecret: workerOverrides.sharedSecret,
14697
+ connectTimeoutMs: parsePositiveInt(options.connectTimeoutMs, 0) || void 0,
14698
+ runnerConfig: normalizedEngine === "claude" ? { claudeArgs: normalizeClaudeArgs(options) } : {},
14699
+ manageProcessSignals: false
14700
+ });
14701
+ await worker.start();
14702
+ localLogger.info(
14703
+ `\u672C\u673A\u4E00\u4F53\u8282\u70B9\u542F\u52A8\u5B8C\u6210 | engine=${normalizedEngine} | master=${workerOverrides.masterHost}:${workerOverrides.masterPort}`
14704
+ );
14705
+ await worker.waitUntilStopped();
14706
+ } catch (error) {
14707
+ const msg = error instanceof Error ? error.message : String(error);
14708
+ localLogger.error(`\u672C\u673A\u4E00\u4F53\u8282\u70B9\u542F\u52A8\u5931\u8D25: ${msg}`);
14709
+ await stopAll("startup_error");
14710
+ process.exit(1);
14711
+ }
14712
+ }
14713
+
14279
14714
  // src/hook/init/hook-init-command.ts
14280
- import fs28 from "fs";
14281
- import os17 from "os";
14282
- import path27 from "path";
14715
+ import fs29 from "fs";
14716
+ import os18 from "os";
14717
+ import path28 from "path";
14283
14718
  import process11 from "process";
14284
14719
  import { createInterface as createInterface3 } from "readline/promises";
14285
14720
  import chalk9 from "chalk";
14286
14721
 
14287
14722
  // src/hook/init/legacy-ai-hook-migrator.ts
14288
- import fs27 from "fs";
14723
+ import fs28 from "fs";
14289
14724
  function loadLegacyMigrationResult(logger3) {
14290
14725
  const sourcePath = LEGACY_STATE_PATH;
14291
- if (!fs27.existsSync(sourcePath)) {
14726
+ if (!fs28.existsSync(sourcePath)) {
14292
14727
  return { state: null, summary: null };
14293
14728
  }
14294
14729
  try {
14295
- const parsed = parseJsonWithLineComments(fs27.readFileSync(sourcePath, "utf-8"), sourcePath);
14730
+ const parsed = parseJsonWithLineComments(fs28.readFileSync(sourcePath, "utf-8"), sourcePath);
14296
14731
  if (!parsed || typeof parsed !== "object") {
14297
14732
  return { state: null, summary: null };
14298
14733
  }
@@ -14320,6 +14755,7 @@ function countObjectKeys(value) {
14320
14755
 
14321
14756
  // src/hook/init/hook-init-normalizer.ts
14322
14757
  var DEFAULT_MASTER_HOST_ENV = "NIKOU_HOOK_MASTER_HOST";
14758
+ var LOCAL_MASTER_HOST = "127.0.0.1";
14323
14759
  function resolveHookSection2(config) {
14324
14760
  return config.hook ?? config.ai_hook ?? config.aiHook ?? {};
14325
14761
  }
@@ -14379,6 +14815,15 @@ function resolveContextConfigFromHook(hook2) {
14379
14815
  function resolveDefaultMasterHost(env = process.env) {
14380
14816
  return String(env[DEFAULT_MASTER_HOST_ENV] || "").trim();
14381
14817
  }
14818
+ function resolveHookInitDeploymentDefaults(config, env = process.env) {
14819
+ const configuredHost = resolveConfiguredMasterHost(config);
14820
+ const envHost = resolveDefaultMasterHost(env);
14821
+ const masterHost = configuredHost || envHost || LOCAL_MASTER_HOST;
14822
+ return {
14823
+ deploymentMode: isLocalHost(masterHost) ? "local" : "slave",
14824
+ masterHost
14825
+ };
14826
+ }
14382
14827
  function normalizeHookInitConfig(config, defaults = {}) {
14383
14828
  const hook2 = resolveHookSection2(config);
14384
14829
  const appId = String(config.feishu?.app_id || config.feishu?.appId || config.feishu?.appid || "").trim();
@@ -14420,7 +14865,8 @@ function normalizeHookInitConfig(config, defaults = {}) {
14420
14865
  // src/hook/init/hook-init-command.ts
14421
14866
  var BOT_OPTIONS = [{ label: "\u59AE\u853B", value: "\u59AE\u853B" }];
14422
14867
  var DEPLOYMENT_OPTIONS = [
14423
- { label: "\u4ECE\u8282\u70B9", value: "slave", description: "\u4E3B\u8282\u70B9\u521D\u59CB\u5316\u672C\u671F\u4E0D\u5F00\u653E\uFF0C\u4E3B\u914D\u7F6E\u4F7F\u7528\u56FA\u5B9A\u503C" }
14868
+ { label: "\u672C\u673A\u4E00\u4F53", value: "local", description: "\u540C\u4E00\u53F0\u673A\u5668\u542F\u52A8\u4E3B\u8282\u70B9\u548C\u672C\u5730\u4ECE\u8282\u70B9" },
14869
+ { label: "\u4ECE\u8282\u70B9", value: "slave", description: "\u8FDE\u63A5\u5DF2\u6709\u8FDC\u7AEF\u4E3B\u8282\u70B9" }
14424
14870
  ];
14425
14871
  var HookInitCommand = class {
14426
14872
  constructor(logger3) {
@@ -14434,14 +14880,22 @@ var HookInitCommand = class {
14434
14880
  const existingAggregateConfig = this.readJson(DEFAULT_CONFIG_PATH);
14435
14881
  const existingMasterConfig = this.readJson(MASTER_CONFIG_PATH);
14436
14882
  const existingWorkerConfig = this.readJson(WORKER_CONFIG_PATH);
14437
- const existingWorkerStatePath = path27.join(STATE_DIR, "worker.json");
14883
+ const existingWorkerStatePath = path28.join(STATE_DIR, "worker.json");
14438
14884
  const existingWorkerState = this.readJson(existingWorkerStatePath);
14439
14885
  const rl = createInterface3({ input: process11.stdin, output: process11.stdout });
14440
14886
  try {
14441
14887
  this.printEnvironmentSummary(source.path, { existingAggregateConfig, existingMasterConfig, existingWorkerConfig }, migration.summary);
14442
14888
  const botName = await this.askChoice(rl, "\u7B2C\u4E00\u6B65\uFF1A\u9009\u62E9\u98DE\u4E66\u673A\u5668\u4EBA", BOT_OPTIONS, 0);
14443
- await this.askChoice(rl, "\u7B2C\u4E8C\u6B65\uFF1A\u9009\u62E9\u90E8\u7F72\u65B9\u5F0F", DEPLOYMENT_OPTIONS, 0);
14444
- const masterHost = await this.askMasterHost(rl, source.config);
14889
+ const deploymentDefaults = resolveHookInitDeploymentDefaults(source.config);
14890
+ const deploymentDefaultIndex = DEPLOYMENT_OPTIONS.findIndex((item) => item.value === deploymentDefaults.deploymentMode);
14891
+ const deploymentMode = await this.askChoice(
14892
+ rl,
14893
+ "\u7B2C\u4E8C\u6B65\uFF1A\u9009\u62E9\u90E8\u7F72\u65B9\u5F0F",
14894
+ DEPLOYMENT_OPTIONS,
14895
+ deploymentDefaultIndex >= 0 ? deploymentDefaultIndex : 0
14896
+ );
14897
+ const defaultMasterHost = deploymentMode === "local" ? LOCAL_MASTER_HOST : deploymentDefaults.masterHost;
14898
+ const masterHost = await this.askMasterHost(rl, source.config, defaultMasterHost);
14445
14899
  const normalized = this.normalizeSourceConfig(
14446
14900
  mergeHookInitMasterHost(source.config, masterHost)
14447
14901
  );
@@ -14450,6 +14904,7 @@ var HookInitCommand = class {
14450
14904
  const nextWorkerState = this.buildSeedWorkerState({
14451
14905
  botName,
14452
14906
  configPath: WORKER_CONFIG_PATH,
14907
+ deploymentMode,
14453
14908
  normalized,
14454
14909
  existingWorkerState,
14455
14910
  legacyState: migration.state
@@ -14458,6 +14913,7 @@ var HookInitCommand = class {
14458
14913
  nextMasterConfig,
14459
14914
  nextWorkerConfig,
14460
14915
  nextWorkerState,
14916
+ deploymentMode,
14461
14917
  sourcePath: source.path,
14462
14918
  migrationSummary: migration.summary,
14463
14919
  existingAggregateConfig,
@@ -14482,7 +14938,10 @@ var HookInitCommand = class {
14482
14938
  console.log(chalk9.dim(`\u4E3B\u8282\u70B9\u914D\u7F6E: ${MASTER_CONFIG_PATH}`));
14483
14939
  console.log(chalk9.dim(`\u4ECE\u8282\u70B9\u914D\u7F6E: ${WORKER_CONFIG_PATH}`));
14484
14940
  console.log(chalk9.dim(`\u72B6\u6001\u6587\u4EF6: ${existingWorkerStatePath}`));
14485
- console.log(chalk9.dim("\n\u4E0B\u4E00\u6B65: nikou-cli hook worker codex\n"));
14941
+ const nextCommand = deploymentMode === "local" ? "ai-hook local" : "nikou-cli hook worker codex";
14942
+ console.log(chalk9.dim(`
14943
+ \u4E0B\u4E00\u6B65: ${nextCommand}
14944
+ `));
14486
14945
  } finally {
14487
14946
  rl.close();
14488
14947
  }
@@ -14495,11 +14954,11 @@ var HookInitCommand = class {
14495
14954
  resolveSourceConfig() {
14496
14955
  const candidates = [LEGACY_CONFIG_PATH, DEFAULT_CONFIG_PATH, MASTER_CONFIG_PATH, WORKER_CONFIG_PATH];
14497
14956
  for (const candidate of candidates) {
14498
- if (!fs28.existsSync(candidate)) {
14957
+ if (!fs29.existsSync(candidate)) {
14499
14958
  continue;
14500
14959
  }
14501
14960
  try {
14502
- const parsed = parseJsonWithLineComments(fs28.readFileSync(candidate, "utf-8"), candidate);
14961
+ const parsed = parseJsonWithLineComments(fs29.readFileSync(candidate, "utf-8"), candidate);
14503
14962
  if (parsed && typeof parsed === "object") {
14504
14963
  return { config: parsed, path: candidate };
14505
14964
  }
@@ -14561,8 +15020,8 @@ var HookInitCommand = class {
14561
15020
  console.log(chalk9.yellow("\u8BF7\u8F93\u5165 y \u6216 n\u3002\n"));
14562
15021
  }
14563
15022
  }
14564
- async askMasterHost(rl, config) {
14565
- const defaultHost = resolveConfiguredMasterHost(config) || resolveDefaultMasterHost();
15023
+ async askMasterHost(rl, config, fallbackHost) {
15024
+ const defaultHost = fallbackHost || resolveConfiguredMasterHost(config) || resolveDefaultMasterHost();
14566
15025
  while (true) {
14567
15026
  const label = defaultHost ? `\u7B2C\u4E09\u6B65\uFF1A\u4E3B\u8282\u70B9\u5730\u5740 [\u9ED8\u8BA4 ${defaultHost}]: ` : "\u7B2C\u4E09\u6B65\uFF1A\u4E3B\u8282\u70B9\u5730\u5740: ";
14568
15027
  const answer = (await rl.question(chalk9.dim(label))).trim();
@@ -14652,7 +15111,7 @@ var HookInitCommand = class {
14652
15111
  contextConfig: params.normalized.contextConfig,
14653
15112
  slaveConnectionStatus: "offline",
14654
15113
  status: "stopped",
14655
- lastCommand: "nikou-cli hook worker codex"
15114
+ lastCommand: params.deploymentMode === "local" ? "ai-hook local" : "nikou-cli hook worker codex"
14656
15115
  });
14657
15116
  const mergedState = mergeWorkerState(
14658
15117
  initialState,
@@ -14663,7 +15122,7 @@ var HookInitCommand = class {
14663
15122
  mergedState.initializedAt = now;
14664
15123
  mergedState.initMode = "interactive";
14665
15124
  mergedState.migration = params.legacyState ? {
14666
- source: path27.join(os17.homedir(), "ops", "ai-hook.json"),
15125
+ source: path28.join(os18.homedir(), "ops", "ai-hook.json"),
14667
15126
  migratedAt: now,
14668
15127
  legacyBindingsCount: countObjectKeys2(params.legacyState.bindings),
14669
15128
  legacyChatIndexCount: countObjectKeys2(params.legacyState.chatIndex),
@@ -14684,10 +15143,10 @@ var HookInitCommand = class {
14684
15143
  console.log(chalk9.dim(`- \u4ECE\u8282\u70B9\u914D\u7F6E: ${WORKER_CONFIG_PATH}`));
14685
15144
  console.log(chalk9.dim(`- \u98DE\u4E66 AppId: ${String(params.nextMasterConfig.feishu?.app_id || "")}`));
14686
15145
  console.log(chalk9.dim(`- \u673A\u5668\u4EBA: ${String(masterHook.bot_name || workerHook.bot_name || "")}`));
14687
- console.log(chalk9.dim(`- \u90E8\u7F72\u65B9\u5F0F: \u4ECE\u8282\u70B9`));
15146
+ console.log(chalk9.dim(`- \u90E8\u7F72\u65B9\u5F0F: ${params.deploymentMode === "local" ? "\u672C\u673A\u4E00\u4F53" : "\u4ECE\u8282\u70B9"}`));
14688
15147
  console.log(chalk9.dim(`- \u4E3B\u8282\u70B9: ${String(masterHook.master_host || "")}:${String(masterHook.master_port || "")}`));
14689
15148
  console.log(chalk9.dim(`- \u4E3B\u8282\u70B9\u767D\u540D\u5355\u6570\u91CF: ${Array.isArray(masterHook.bind_allowed_user_ids) ? masterHook.bind_allowed_user_ids.length : 0}`));
14690
- console.log(chalk9.dim(`- \u79CD\u5B50\u72B6\u6001: ${path27.join(STATE_DIR, "worker.json")}`));
15149
+ console.log(chalk9.dim(`- \u79CD\u5B50\u72B6\u6001: ${path28.join(STATE_DIR, "worker.json")}`));
14691
15150
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E\u7FA4\u7ED1\u5B9A\u6570: ${Object.keys(params.nextWorkerState.bindings || {}).length}`));
14692
15151
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E chatIndex \u6570: ${Object.keys(params.nextWorkerState.chatIndex || {}).length}`));
14693
15152
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E P2P \u4F1A\u8BDD\u6570: ${countObjectKeys2(params.nextWorkerState.p2pSessions)}`));
@@ -14713,28 +15172,28 @@ var HookInitCommand = class {
14713
15172
  });
14714
15173
  }
14715
15174
  backupIfNeeded(targetPath, prefix) {
14716
- if (!fs28.existsSync(targetPath)) {
15175
+ if (!fs29.existsSync(targetPath)) {
14717
15176
  return;
14718
15177
  }
14719
- const backupDir = path27.join(STATE_DIR, "backups");
15178
+ const backupDir = path28.join(STATE_DIR, "backups");
14720
15179
  ensureDirExists(backupDir);
14721
15180
  const fileName = `${prefix}.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`;
14722
- fs28.copyFileSync(targetPath, path27.join(backupDir, fileName));
15181
+ fs29.copyFileSync(targetPath, path28.join(backupDir, fileName));
14723
15182
  }
14724
15183
  readJson(filePath) {
14725
- if (!fs28.existsSync(filePath)) {
15184
+ if (!fs29.existsSync(filePath)) {
14726
15185
  return null;
14727
15186
  }
14728
15187
  try {
14729
- const parsed = parseJsonWithLineComments(fs28.readFileSync(filePath, "utf-8"), filePath);
15188
+ const parsed = parseJsonWithLineComments(fs29.readFileSync(filePath, "utf-8"), filePath);
14730
15189
  return parsed && typeof parsed === "object" ? parsed : null;
14731
15190
  } catch {
14732
15191
  return null;
14733
15192
  }
14734
15193
  }
14735
15194
  writeJson(filePath, value) {
14736
- ensureDirExists(path27.dirname(filePath));
14737
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2));
15195
+ ensureDirExists(path28.dirname(filePath));
15196
+ fs29.writeFileSync(filePath, JSON.stringify(value, null, 2));
14738
15197
  }
14739
15198
  };
14740
15199
  function countObjectKeys2(value) {
@@ -14758,12 +15217,12 @@ async function hookInitAction() {
14758
15217
  }
14759
15218
 
14760
15219
  // src/commands/hook/hook-log.ts
14761
- import fs30 from "fs";
15220
+ import fs31 from "fs";
14762
15221
  import chalk10 from "chalk";
14763
15222
 
14764
15223
  // src/hook/log-searcher.ts
14765
- import fs29 from "fs";
14766
- import path28 from "path";
15224
+ import fs30 from "fs";
15225
+ import path29 from "path";
14767
15226
  function normalizeString(value) {
14768
15227
  if (value === null || value === void 0) {
14769
15228
  return "";
@@ -14801,8 +15260,8 @@ var HookLogSearcher = class {
14801
15260
  logPaths;
14802
15261
  constructor(options = {}) {
14803
15262
  this.logPaths = {
14804
- master: options.masterPath || path28.join(LOGS_DIR, "master", "ai-hook-master.service.log"),
14805
- worker: options.workerPath || path28.join(LOGS_DIR, "worker", "ai-hook-worker.service.log")
15263
+ master: options.masterPath || path29.join(LOGS_DIR, "master", "ai-hook-master.service.log"),
15264
+ worker: options.workerPath || path29.join(LOGS_DIR, "worker", "ai-hook-worker.service.log")
14806
15265
  };
14807
15266
  }
14808
15267
  resolveScope(scope) {
@@ -14865,7 +15324,7 @@ var HookLogSearcher = class {
14865
15324
  }
14866
15325
  searchSingleFile(role, query = {}, options = {}) {
14867
15326
  const filePath = this.logPaths[role];
14868
- if (!filePath || !fs29.existsSync(filePath)) {
15327
+ if (!filePath || !fs30.existsSync(filePath)) {
14869
15328
  return {
14870
15329
  role,
14871
15330
  path: filePath,
@@ -14876,7 +15335,7 @@ var HookLogSearcher = class {
14876
15335
  lines: []
14877
15336
  };
14878
15337
  }
14879
- const raw = fs29.readFileSync(filePath, "utf8");
15338
+ const raw = fs30.readFileSync(filePath, "utf8");
14880
15339
  const lines = raw.split(/\r?\n/);
14881
15340
  if (lines.length > 0 && lines[lines.length - 1] === "") {
14882
15341
  lines.pop();
@@ -14994,7 +15453,7 @@ function buildFollowState(file) {
14994
15453
  return {
14995
15454
  role: file.role,
14996
15455
  path: file.path,
14997
- offset: fs30.existsSync(file.path) ? fs30.statSync(file.path).size : 0,
15456
+ offset: fs31.existsSync(file.path) ? fs31.statSync(file.path).size : 0,
14998
15457
  remainder: ""
14999
15458
  };
15000
15459
  }
@@ -15021,10 +15480,10 @@ function flushChunk(state, chunk, queryText) {
15021
15480
  output.forEach((line) => console.log(line));
15022
15481
  }
15023
15482
  function readAppendedChunk(state, queryText) {
15024
- if (!fs30.existsSync(state.path)) {
15483
+ if (!fs31.existsSync(state.path)) {
15025
15484
  return;
15026
15485
  }
15027
- const stat = fs30.statSync(state.path);
15486
+ const stat = fs31.statSync(state.path);
15028
15487
  if (stat.size < state.offset) {
15029
15488
  state.offset = 0;
15030
15489
  state.remainder = "";
@@ -15032,15 +15491,15 @@ function readAppendedChunk(state, queryText) {
15032
15491
  if (stat.size === state.offset) {
15033
15492
  return;
15034
15493
  }
15035
- const fd = fs30.openSync(state.path, "r");
15494
+ const fd = fs31.openSync(state.path, "r");
15036
15495
  try {
15037
15496
  const length = stat.size - state.offset;
15038
15497
  const buffer = Buffer.alloc(length);
15039
- fs30.readSync(fd, buffer, 0, length, state.offset);
15498
+ fs31.readSync(fd, buffer, 0, length, state.offset);
15040
15499
  state.offset = stat.size;
15041
15500
  flushChunk(state, buffer.toString("utf8"), queryText);
15042
15501
  } finally {
15043
- fs30.closeSync(fd);
15502
+ fs31.closeSync(fd);
15044
15503
  }
15045
15504
  }
15046
15505
  async function followLogs(queryText, options = {}) {
@@ -15081,60 +15540,6 @@ async function hookLogAction(query = "", options = {}) {
15081
15540
 
15082
15541
  // src/index.ts
15083
15542
  var DEFAULT_DEST = ".agents/skills";
15084
- var WORKER_BOOLEAN_OPTIONS = /* @__PURE__ */ new Set([
15085
- "--verbose"
15086
- ]);
15087
- var WORKER_VALUE_OPTIONS = /* @__PURE__ */ new Set([
15088
- "--master-host",
15089
- "--master-port",
15090
- "--shared-secret",
15091
- "--connect-timeout-ms",
15092
- "--model",
15093
- "--fallback-model",
15094
- "--permission-mode",
15095
- "--max-budget-usd",
15096
- "--settings",
15097
- "--mcp-config",
15098
- "--add-dir",
15099
- "--system-prompt",
15100
- "--append-system-prompt",
15101
- "--agent",
15102
- "--agents",
15103
- "--tools",
15104
- "--allowedTools",
15105
- "--disallowedTools"
15106
- ]);
15107
- function resolveWorkerPassthroughArgs(engine) {
15108
- const rawArgs = process.argv.slice(2);
15109
- const workerIndex = rawArgs.findIndex((item, index) => item === "worker" && rawArgs[index - 1] === "hook");
15110
- if (workerIndex < 0) {
15111
- return [];
15112
- }
15113
- const tail = rawArgs.slice(workerIndex + 1);
15114
- const args = tail[0] === "worker" ? tail.slice(1) : tail;
15115
- const normalizedEngine = String(engine || "codex").trim();
15116
- const scanArgs = args[0] === normalizedEngine ? args.slice(1) : args;
15117
- const passthroughArgs = [];
15118
- for (let index = 0; index < scanArgs.length; index += 1) {
15119
- const item = scanArgs[index];
15120
- if (item === "--") {
15121
- passthroughArgs.push(...scanArgs.slice(index + 1));
15122
- break;
15123
- }
15124
- const key = item.includes("=") ? item.split("=")[0] : item;
15125
- if (WORKER_BOOLEAN_OPTIONS.has(key)) {
15126
- continue;
15127
- }
15128
- if (WORKER_VALUE_OPTIONS.has(key)) {
15129
- if (!item.includes("=")) {
15130
- index += 1;
15131
- }
15132
- continue;
15133
- }
15134
- passthroughArgs.push(item);
15135
- }
15136
- return passthroughArgs;
15137
- }
15138
15543
  var program = new Command();
15139
15544
  program.name("nikou-cli").description("CLI to install and manage Agent Skills from GitHub repos").version("0.1.0");
15140
15545
  program.command("add").argument("<source>", 'GitHub repo in "owner/repo" format (e.g., vercel-labs/agent-skills)').description("Install skill(s) from a GitHub repository").option("-p, --path <path>", "Specific subfolder path in the repo").option("-d, --dest <dir>", "Destination directory", DEFAULT_DEST).option("-b, --branch <branch>", "Git branch", "main").option("-f, --force", "Overwrite existing skills", false).action(async (source, options) => {
@@ -15163,16 +15568,23 @@ mcp.command("search").argument("<keyword>", "Search keyword").description("Searc
15163
15568
  await mcpSearchCommand(keyword, options);
15164
15569
  });
15165
15570
  var hook = program.command("hook").description("\u4E3B\u4ECE\u8282\u70B9\u4EFB\u52A1\u5206\u53D1\u4E0E\u6267\u884C\uFF08\u98DE\u4E66\u96C6\u6210\uFF09");
15571
+ function addWorkerRuntimeOptions(command) {
15572
+ return command.allowUnknownOption(true).allowExcessArguments(true).option("--verbose", "\u5F00\u542F\u8C03\u8BD5\u65E5\u5FD7", false).option("--connect-timeout-ms <ms>", "\u4ECE\u8282\u70B9\u8FDE\u63A5\u4E3B\u8282\u70B9\u8D85\u65F6\u6BEB\u79D2\u6570").option("--model <model>", "Claude \u6A21\u578B").option("--fallback-model <model>", "Claude fallback \u6A21\u578B").option("--permission-mode <mode>", "Claude \u6743\u9650\u6A21\u5F0F").option("--max-budget-usd <usd>", "Claude \u6700\u5927\u9884\u7B97").option("--settings <path>", "Claude settings \u6587\u4EF6").option("--mcp-config <path>", "Claude MCP \u914D\u7F6E").option("--add-dir <path>", "Claude \u989D\u5916\u53EF\u8BBF\u95EE\u76EE\u5F55").option("--system-prompt <prompt>", "Claude system prompt").option("--append-system-prompt <prompt>", "Claude \u8FFD\u52A0 system prompt").option("--agent <agent>", "Claude agent").option("--agents <agents>", "Claude agents").option("--tools <tools>", "Claude tools").option("--allowedTools <tools>", "Claude allowedTools").option("--disallowedTools <tools>", "Claude disallowedTools");
15573
+ }
15166
15574
  hook.command("master").description("\u542F\u52A8\u4E3B\u8282\u70B9\uFF1A\u63A5\u6536\u98DE\u4E66\u6D88\u606F\u5E76\u5206\u53D1\u4EFB\u52A1\u7ED9\u4ECE\u8282\u70B9").option("--master-host <host>", "\u4E3B\u8282\u70B9\u76D1\u542C\u5730\u5740").option("--master-port <port>", "\u4E3B\u8282\u70B9\u7AEF\u53E3\uFF08\u9ED8\u8BA4 19732\uFF09").option("--shared-secret <secret>", "\u4E3B\u4ECE\u901A\u4FE1\u9274\u6743\u5BC6\u94A5").option("--claim-timeout-ms <ms>", "\u4EFB\u52A1\u8BA4\u9886\u8D85\u65F6\u6BEB\u79D2\u6570").action(async (options) => {
15167
15575
  await hookMasterAction(options);
15168
15576
  });
15169
15577
  hook.command("init").description("\u4EA4\u4E92\u5F0F\u521D\u59CB\u5316 Hook \u4ECE\u8282\u70B9\u914D\u7F6E\u4E0E\u5386\u53F2\u72B6\u6001\u8FC1\u79FB").action(async () => {
15170
15578
  await hookInitAction();
15171
15579
  });
15172
- hook.command("worker").argument("[engine]", "codex|gemini|claude", "codex").description("\u542F\u52A8\u4ECE\u8282\u70B9\uFF1A\u8FDE\u63A5\u4E3B\u8282\u70B9\u540E\u6267\u884C\u5DE5\u5177\u4EFB\u52A1").allowUnknownOption(true).allowExcessArguments(true).option("--verbose", "\u5F00\u542F\u8C03\u8BD5\u65E5\u5FD7", false).option("--master-host <host>", "\u4E3B\u8282\u70B9\u5730\u5740\uFF08IP \u6216\u57DF\u540D\uFF09").option("--master-port <port>", "\u4E3B\u8282\u70B9\u7AEF\u53E3").option("--shared-secret <secret>", "\u4E3B\u4ECE\u901A\u4FE1\u9274\u6743\u5BC6\u94A5").option("--connect-timeout-ms <ms>", "\u4ECE\u8282\u70B9\u8FDE\u63A5\u4E3B\u8282\u70B9\u8D85\u65F6\u6BEB\u79D2\u6570").option("--model <model>", "Claude \u6A21\u578B").option("--fallback-model <model>", "Claude fallback \u6A21\u578B").option("--permission-mode <mode>", "Claude \u6743\u9650\u6A21\u5F0F").option("--max-budget-usd <usd>", "Claude \u6700\u5927\u9884\u7B97").option("--settings <path>", "Claude settings \u6587\u4EF6").option("--mcp-config <path>", "Claude MCP \u914D\u7F6E").option("--add-dir <path>", "Claude \u989D\u5916\u53EF\u8BBF\u95EE\u76EE\u5F55").option("--system-prompt <prompt>", "Claude system prompt").option("--append-system-prompt <prompt>", "Claude \u8FFD\u52A0 system prompt").option("--agent <agent>", "Claude agent").option("--agents <agents>", "Claude agents").option("--tools <tools>", "Claude tools").option("--allowedTools <tools>", "Claude allowedTools").option("--disallowedTools <tools>", "Claude disallowedTools").action(async (engine, options) => {
15173
- const passthroughArgs = resolveWorkerPassthroughArgs(engine);
15580
+ addWorkerRuntimeOptions(hook.command("worker").argument("[engine]", "codex|gemini|claude", "codex").description("\u542F\u52A8\u4ECE\u8282\u70B9\uFF1A\u8FDE\u63A5\u4E3B\u8282\u70B9\u540E\u6267\u884C\u5DE5\u5177\u4EFB\u52A1").option("--master-host <host>", "\u4E3B\u8282\u70B9\u5730\u5740\uFF08IP \u6216\u57DF\u540D\uFF09").option("--master-port <port>", "\u4E3B\u8282\u70B9\u7AEF\u53E3").option("--shared-secret <secret>", "\u4E3B\u4ECE\u901A\u4FE1\u9274\u6743\u5BC6\u94A5")).action(async (engine, options) => {
15581
+ const passthroughArgs = resolveWorkerPassthroughArgs(engine, "worker");
15174
15582
  await hookWorkerAction(engine, { ...options, passthroughArgs });
15175
15583
  });
15584
+ addWorkerRuntimeOptions(hook.command("local").argument("[engine]", "codex|gemini|claude", "codex").description("\u542F\u52A8\u672C\u673A\u4E00\u4F53\u8282\u70B9\uFF1A\u540C\u4E00\u8FDB\u7A0B\u5185\u542F\u52A8\u4E3B\u8282\u70B9\u548C\u672C\u5730\u4ECE\u8282\u70B9").option("--master-host <host>", "\u4E3B\u8282\u70B9\u76D1\u542C\u5730\u5740").option("--master-port <port>", "\u4E3B\u8282\u70B9\u7AEF\u53E3\uFF08\u9ED8\u8BA4 19732\uFF09").option("--shared-secret <secret>", "\u4E3B\u4ECE\u901A\u4FE1\u9274\u6743\u5BC6\u94A5").option("--claim-timeout-ms <ms>", "\u4EFB\u52A1\u8BA4\u9886\u8D85\u65F6\u6BEB\u79D2\u6570")).action(async (engine, options) => {
15585
+ const passthroughArgs = resolveWorkerPassthroughArgs(engine, "local");
15586
+ await hookLocalAction(engine, { ...options, passthroughArgs });
15587
+ });
15176
15588
  hook.command("log").argument("[query]", "\u53EF\u9009\uFF0CtraceId / messageId / \u5173\u952E\u5B57").description("\u67E5\u770B\u672C\u673A agent-block hook master/worker \u65E5\u5FD7").option("--worker", "\u53EA\u770B worker \u65E5\u5FD7", false).option("--slave", "\u53EA\u770B worker \u65E5\u5FD7\uFF08\u517C\u5BB9\u65E7 ai-hook \u4E60\u60EF\uFF09", false).option("--master", "\u53EA\u770B master \u65E5\u5FD7", false).option("-n, --lines <lines>", "\u9ED8\u8BA4\u8FD4\u56DE\u6700\u8FD1\u591A\u5C11\u884C\uFF0C\u9ED8\u8BA4 200").option("-f, --follow", "\u6301\u7EED\u8DDF\u968F\u65E5\u5FD7\u8F93\u51FA\uFF0C\u7B49\u4EF7 tail -f", false).option("-c, --context <lines>", "\u5E26 query \u65F6\u8FD4\u56DE\u524D\u540E\u6587\uFF0C\u9ED8\u8BA4 20").action(async (query, options) => {
15177
15589
  await hookLogAction(query, options);
15178
15590
  });