nikou-cli 0.1.5 → 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
@@ -7657,7 +7690,7 @@ var MessageReceiveStrategy = class {
7657
7690
  if (!raw) return "";
7658
7691
  try {
7659
7692
  const parsed = JSON.parse(raw);
7660
- const text = String(parsed?.text || "");
7693
+ const text = String(parsed?.text || "") || extractFeishuPostText(parsed);
7661
7694
  return this.stripMentions(text, message);
7662
7695
  } catch {
7663
7696
  return this.stripMentions(String(raw), message);
@@ -7665,25 +7698,7 @@ var MessageReceiveStrategy = class {
7665
7698
  }
7666
7699
  extractPostText(messageLike) {
7667
7700
  const raw = messageLike?.content || messageLike?.body?.content;
7668
- if (!raw) return "";
7669
- try {
7670
- const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
7671
- const blocks = parsed?.content || [];
7672
- const parts = [];
7673
- blocks.forEach((row) => {
7674
- (row || []).forEach((item) => {
7675
- if ((item?.tag === "text" || item?.tag === "a") && item?.text) {
7676
- parts.push(String(item.text));
7677
- }
7678
- if (item?.tag === "at") {
7679
- parts.push(String(item.user_name || item.name || ""));
7680
- }
7681
- });
7682
- });
7683
- return parts.join("").replace(/\s+/g, " ").trim();
7684
- } catch {
7685
- return "";
7686
- }
7701
+ return extractFeishuPostText(raw);
7687
7702
  }
7688
7703
  stripMentions(text, message) {
7689
7704
  let cleaned = String(text || "");
@@ -10496,6 +10511,7 @@ var LocalBrowserWsServer = class {
10496
10511
  workerId;
10497
10512
  workerType;
10498
10513
  larkClient;
10514
+ getNacosSyncStatus;
10499
10515
  wss = null;
10500
10516
  profile;
10501
10517
  startTime = (/* @__PURE__ */ new Date()).toISOString();
@@ -10505,6 +10521,7 @@ var LocalBrowserWsServer = class {
10505
10521
  this.workerId = options.workerId;
10506
10522
  this.workerType = options.workerType;
10507
10523
  this.larkClient = options.larkClient;
10524
+ this.getNacosSyncStatus = options.getNacosSyncStatus;
10508
10525
  }
10509
10526
  async start() {
10510
10527
  if (this.wss) {
@@ -10685,7 +10702,8 @@ var LocalBrowserWsServer = class {
10685
10702
  cliTools: {
10686
10703
  codex: this.detectCliTool("codex"),
10687
10704
  claude: this.detectCliTool("claude")
10688
- }
10705
+ },
10706
+ nacosSync: this.getNacosSyncStatus?.() ?? null
10689
10707
  }
10690
10708
  });
10691
10709
  }
@@ -13974,6 +13992,263 @@ var OwnerNotifyService = class {
13974
13992
  }
13975
13993
  };
13976
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
+
13977
14252
  // src/hook/worker/worker-command.ts
13978
14253
  var HookWorkerCommand = class {
13979
14254
  logger;
@@ -13982,11 +14257,21 @@ var HookWorkerCommand = class {
13982
14257
  localBrowserWs = null;
13983
14258
  reminderService = null;
13984
14259
  ownerNotifyService = null;
14260
+ nacosSyncCoordinator = null;
13985
14261
  heartbeatTimer = null;
13986
14262
  stateManager = null;
14263
+ stopped = false;
14264
+ stoppedPromise;
14265
+ resolveStopped = () => void 0;
13987
14266
  constructor(logger3, options = {}) {
13988
14267
  this.logger = logger3;
13989
14268
  this.options = options;
14269
+ this.stoppedPromise = new Promise((resolve3) => {
14270
+ this.resolveStopped = resolve3;
14271
+ });
14272
+ }
14273
+ waitUntilStopped() {
14274
+ return this.stoppedPromise;
13990
14275
  }
13991
14276
  async start() {
13992
14277
  const existingPid = findRunningAiHookProcess(process10.pid);
@@ -14009,6 +14294,7 @@ var HookWorkerCommand = class {
14009
14294
  });
14010
14295
  const bindAllowedUserIds = resolveBindAllowedUserIds(config);
14011
14296
  const contextConfig = resolveContextConfig(config);
14297
+ const nacosSyncConfig = resolveNacosSkillSyncConfig(config);
14012
14298
  const opsUserName = resolveOpsUserName();
14013
14299
  const larkClient = new lark3.Client({
14014
14300
  appId: feishuConfig.appId,
@@ -14074,13 +14360,20 @@ var HookWorkerCommand = class {
14074
14360
  config: this.options.runnerConfig || {}
14075
14361
  });
14076
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
+ });
14077
14368
  this.localBrowserWs = new LocalBrowserWsServer({
14078
14369
  logger: this.logger,
14079
14370
  workerId: ownerUserId || `worker-${process10.pid}`,
14080
14371
  workerType: "tooling-cli",
14081
- larkClient
14372
+ larkClient,
14373
+ getNacosSyncStatus: () => this.nacosSyncCoordinator?.getStatus() ?? null
14082
14374
  });
14083
14375
  await this.localBrowserWs.start();
14376
+ this.nacosSyncCoordinator.start();
14084
14377
  const hostProfileService = new HostProfileService({
14085
14378
  logger: this.logger,
14086
14379
  ownerUserId,
@@ -14109,7 +14402,7 @@ var HookWorkerCommand = class {
14109
14402
  const requesterPersonaService = new RequesterPersonaService({
14110
14403
  logger: this.logger,
14111
14404
  larkClient,
14112
- 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")
14113
14406
  });
14114
14407
  const taskExecutor = new TaskExecutor({
14115
14408
  logger: this.logger,
@@ -14166,17 +14459,19 @@ var HookWorkerCommand = class {
14166
14459
  await this.relayClient.startAndWait();
14167
14460
  await reminderService.start();
14168
14461
  await this.ownerNotifyService.notifyOnline();
14169
- const gracefulExit = async (signal) => {
14170
- this.logger.info(`\u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u505C\u6B62\u4ECE\u8282\u70B9...`);
14171
- await this.stop(signal);
14172
- process10.exit(0);
14173
- };
14174
- process10.on("SIGINT", () => {
14175
- void gracefulExit("SIGINT");
14176
- });
14177
- process10.on("SIGTERM", () => {
14178
- void gracefulExit("SIGTERM");
14179
- });
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
+ }
14180
14475
  this.heartbeatTimer = setInterval(() => {
14181
14476
  stateManager.updateHeartbeat();
14182
14477
  }, HEARTBEAT_INTERVAL_MS);
@@ -14186,6 +14481,10 @@ var HookWorkerCommand = class {
14186
14481
  }
14187
14482
  /** 停止从节点 */
14188
14483
  async stop(signal) {
14484
+ if (this.stopped) {
14485
+ return;
14486
+ }
14487
+ this.stopped = true;
14189
14488
  if (this.heartbeatTimer) {
14190
14489
  clearInterval(this.heartbeatTimer);
14191
14490
  this.heartbeatTimer = null;
@@ -14194,6 +14493,10 @@ var HookWorkerCommand = class {
14194
14493
  this.localBrowserWs.stop();
14195
14494
  this.localBrowserWs = null;
14196
14495
  }
14496
+ if (this.nacosSyncCoordinator) {
14497
+ await this.nacosSyncCoordinator.stop();
14498
+ this.nacosSyncCoordinator = null;
14499
+ }
14197
14500
  if (this.reminderService) {
14198
14501
  this.reminderService.stop();
14199
14502
  this.reminderService = null;
@@ -14219,10 +14522,65 @@ var HookWorkerCommand = class {
14219
14522
  slaveConnectionStatus: "offline"
14220
14523
  });
14221
14524
  }
14525
+ this.resolveStopped();
14222
14526
  }
14223
14527
  };
14224
14528
 
14225
- // 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
+ }
14226
14584
  function normalizeClaudeArgs(options) {
14227
14585
  const args = [];
14228
14586
  const pushPair = (flag, value) => {
@@ -14254,6 +14612,8 @@ function normalizeClaudeArgs(options) {
14254
14612
  });
14255
14613
  return args;
14256
14614
  }
14615
+
14616
+ // src/commands/hook/hook-worker.ts
14257
14617
  async function hookWorkerAction(engine, options) {
14258
14618
  const logger3 = createLogger("worker", options.verbose === true);
14259
14619
  const normalizedEngine = (engine || "codex").toLowerCase();
@@ -14263,10 +14623,12 @@ async function hookWorkerAction(engine, options) {
14263
14623
  masterPort: parsePositiveInt(options.masterPort, 0) || void 0,
14264
14624
  sharedSecret: options.sharedSecret,
14265
14625
  connectTimeoutMs: parsePositiveInt(options.connectTimeoutMs, 0) || void 0,
14266
- runnerConfig: normalizedEngine === "claude" ? { claudeArgs: normalizeClaudeArgs(options) } : {}
14626
+ runnerConfig: normalizedEngine === "claude" ? { claudeArgs: normalizeClaudeArgs(options) } : {},
14627
+ manageProcessSignals: true
14267
14628
  });
14268
14629
  try {
14269
14630
  await command.start();
14631
+ await command.waitUntilStopped();
14270
14632
  } catch (error) {
14271
14633
  const msg = error instanceof Error ? error.message : String(error);
14272
14634
  logger3.error(`\u4ECE\u8282\u70B9\u542F\u52A8\u5931\u8D25: ${msg}`);
@@ -14274,23 +14636,98 @@ async function hookWorkerAction(engine, options) {
14274
14636
  }
14275
14637
  }
14276
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
+
14277
14714
  // src/hook/init/hook-init-command.ts
14278
- import fs28 from "fs";
14279
- import os17 from "os";
14280
- import path27 from "path";
14715
+ import fs29 from "fs";
14716
+ import os18 from "os";
14717
+ import path28 from "path";
14281
14718
  import process11 from "process";
14282
14719
  import { createInterface as createInterface3 } from "readline/promises";
14283
14720
  import chalk9 from "chalk";
14284
14721
 
14285
14722
  // src/hook/init/legacy-ai-hook-migrator.ts
14286
- import fs27 from "fs";
14723
+ import fs28 from "fs";
14287
14724
  function loadLegacyMigrationResult(logger3) {
14288
14725
  const sourcePath = LEGACY_STATE_PATH;
14289
- if (!fs27.existsSync(sourcePath)) {
14726
+ if (!fs28.existsSync(sourcePath)) {
14290
14727
  return { state: null, summary: null };
14291
14728
  }
14292
14729
  try {
14293
- const parsed = parseJsonWithLineComments(fs27.readFileSync(sourcePath, "utf-8"), sourcePath);
14730
+ const parsed = parseJsonWithLineComments(fs28.readFileSync(sourcePath, "utf-8"), sourcePath);
14294
14731
  if (!parsed || typeof parsed !== "object") {
14295
14732
  return { state: null, summary: null };
14296
14733
  }
@@ -14318,6 +14755,7 @@ function countObjectKeys(value) {
14318
14755
 
14319
14756
  // src/hook/init/hook-init-normalizer.ts
14320
14757
  var DEFAULT_MASTER_HOST_ENV = "NIKOU_HOOK_MASTER_HOST";
14758
+ var LOCAL_MASTER_HOST = "127.0.0.1";
14321
14759
  function resolveHookSection2(config) {
14322
14760
  return config.hook ?? config.ai_hook ?? config.aiHook ?? {};
14323
14761
  }
@@ -14377,6 +14815,15 @@ function resolveContextConfigFromHook(hook2) {
14377
14815
  function resolveDefaultMasterHost(env = process.env) {
14378
14816
  return String(env[DEFAULT_MASTER_HOST_ENV] || "").trim();
14379
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
+ }
14380
14827
  function normalizeHookInitConfig(config, defaults = {}) {
14381
14828
  const hook2 = resolveHookSection2(config);
14382
14829
  const appId = String(config.feishu?.app_id || config.feishu?.appId || config.feishu?.appid || "").trim();
@@ -14418,7 +14865,8 @@ function normalizeHookInitConfig(config, defaults = {}) {
14418
14865
  // src/hook/init/hook-init-command.ts
14419
14866
  var BOT_OPTIONS = [{ label: "\u59AE\u853B", value: "\u59AE\u853B" }];
14420
14867
  var DEPLOYMENT_OPTIONS = [
14421
- { 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" }
14422
14870
  ];
14423
14871
  var HookInitCommand = class {
14424
14872
  constructor(logger3) {
@@ -14432,14 +14880,22 @@ var HookInitCommand = class {
14432
14880
  const existingAggregateConfig = this.readJson(DEFAULT_CONFIG_PATH);
14433
14881
  const existingMasterConfig = this.readJson(MASTER_CONFIG_PATH);
14434
14882
  const existingWorkerConfig = this.readJson(WORKER_CONFIG_PATH);
14435
- const existingWorkerStatePath = path27.join(STATE_DIR, "worker.json");
14883
+ const existingWorkerStatePath = path28.join(STATE_DIR, "worker.json");
14436
14884
  const existingWorkerState = this.readJson(existingWorkerStatePath);
14437
14885
  const rl = createInterface3({ input: process11.stdin, output: process11.stdout });
14438
14886
  try {
14439
14887
  this.printEnvironmentSummary(source.path, { existingAggregateConfig, existingMasterConfig, existingWorkerConfig }, migration.summary);
14440
14888
  const botName = await this.askChoice(rl, "\u7B2C\u4E00\u6B65\uFF1A\u9009\u62E9\u98DE\u4E66\u673A\u5668\u4EBA", BOT_OPTIONS, 0);
14441
- await this.askChoice(rl, "\u7B2C\u4E8C\u6B65\uFF1A\u9009\u62E9\u90E8\u7F72\u65B9\u5F0F", DEPLOYMENT_OPTIONS, 0);
14442
- 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);
14443
14899
  const normalized = this.normalizeSourceConfig(
14444
14900
  mergeHookInitMasterHost(source.config, masterHost)
14445
14901
  );
@@ -14448,6 +14904,7 @@ var HookInitCommand = class {
14448
14904
  const nextWorkerState = this.buildSeedWorkerState({
14449
14905
  botName,
14450
14906
  configPath: WORKER_CONFIG_PATH,
14907
+ deploymentMode,
14451
14908
  normalized,
14452
14909
  existingWorkerState,
14453
14910
  legacyState: migration.state
@@ -14456,6 +14913,7 @@ var HookInitCommand = class {
14456
14913
  nextMasterConfig,
14457
14914
  nextWorkerConfig,
14458
14915
  nextWorkerState,
14916
+ deploymentMode,
14459
14917
  sourcePath: source.path,
14460
14918
  migrationSummary: migration.summary,
14461
14919
  existingAggregateConfig,
@@ -14480,7 +14938,10 @@ var HookInitCommand = class {
14480
14938
  console.log(chalk9.dim(`\u4E3B\u8282\u70B9\u914D\u7F6E: ${MASTER_CONFIG_PATH}`));
14481
14939
  console.log(chalk9.dim(`\u4ECE\u8282\u70B9\u914D\u7F6E: ${WORKER_CONFIG_PATH}`));
14482
14940
  console.log(chalk9.dim(`\u72B6\u6001\u6587\u4EF6: ${existingWorkerStatePath}`));
14483
- 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
+ `));
14484
14945
  } finally {
14485
14946
  rl.close();
14486
14947
  }
@@ -14493,11 +14954,11 @@ var HookInitCommand = class {
14493
14954
  resolveSourceConfig() {
14494
14955
  const candidates = [LEGACY_CONFIG_PATH, DEFAULT_CONFIG_PATH, MASTER_CONFIG_PATH, WORKER_CONFIG_PATH];
14495
14956
  for (const candidate of candidates) {
14496
- if (!fs28.existsSync(candidate)) {
14957
+ if (!fs29.existsSync(candidate)) {
14497
14958
  continue;
14498
14959
  }
14499
14960
  try {
14500
- const parsed = parseJsonWithLineComments(fs28.readFileSync(candidate, "utf-8"), candidate);
14961
+ const parsed = parseJsonWithLineComments(fs29.readFileSync(candidate, "utf-8"), candidate);
14501
14962
  if (parsed && typeof parsed === "object") {
14502
14963
  return { config: parsed, path: candidate };
14503
14964
  }
@@ -14559,8 +15020,8 @@ var HookInitCommand = class {
14559
15020
  console.log(chalk9.yellow("\u8BF7\u8F93\u5165 y \u6216 n\u3002\n"));
14560
15021
  }
14561
15022
  }
14562
- async askMasterHost(rl, config) {
14563
- const defaultHost = resolveConfiguredMasterHost(config) || resolveDefaultMasterHost();
15023
+ async askMasterHost(rl, config, fallbackHost) {
15024
+ const defaultHost = fallbackHost || resolveConfiguredMasterHost(config) || resolveDefaultMasterHost();
14564
15025
  while (true) {
14565
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: ";
14566
15027
  const answer = (await rl.question(chalk9.dim(label))).trim();
@@ -14650,7 +15111,7 @@ var HookInitCommand = class {
14650
15111
  contextConfig: params.normalized.contextConfig,
14651
15112
  slaveConnectionStatus: "offline",
14652
15113
  status: "stopped",
14653
- lastCommand: "nikou-cli hook worker codex"
15114
+ lastCommand: params.deploymentMode === "local" ? "ai-hook local" : "nikou-cli hook worker codex"
14654
15115
  });
14655
15116
  const mergedState = mergeWorkerState(
14656
15117
  initialState,
@@ -14661,7 +15122,7 @@ var HookInitCommand = class {
14661
15122
  mergedState.initializedAt = now;
14662
15123
  mergedState.initMode = "interactive";
14663
15124
  mergedState.migration = params.legacyState ? {
14664
- source: path27.join(os17.homedir(), "ops", "ai-hook.json"),
15125
+ source: path28.join(os18.homedir(), "ops", "ai-hook.json"),
14665
15126
  migratedAt: now,
14666
15127
  legacyBindingsCount: countObjectKeys2(params.legacyState.bindings),
14667
15128
  legacyChatIndexCount: countObjectKeys2(params.legacyState.chatIndex),
@@ -14682,10 +15143,10 @@ var HookInitCommand = class {
14682
15143
  console.log(chalk9.dim(`- \u4ECE\u8282\u70B9\u914D\u7F6E: ${WORKER_CONFIG_PATH}`));
14683
15144
  console.log(chalk9.dim(`- \u98DE\u4E66 AppId: ${String(params.nextMasterConfig.feishu?.app_id || "")}`));
14684
15145
  console.log(chalk9.dim(`- \u673A\u5668\u4EBA: ${String(masterHook.bot_name || workerHook.bot_name || "")}`));
14685
- 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"}`));
14686
15147
  console.log(chalk9.dim(`- \u4E3B\u8282\u70B9: ${String(masterHook.master_host || "")}:${String(masterHook.master_port || "")}`));
14687
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}`));
14688
- 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")}`));
14689
15150
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E\u7FA4\u7ED1\u5B9A\u6570: ${Object.keys(params.nextWorkerState.bindings || {}).length}`));
14690
15151
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E chatIndex \u6570: ${Object.keys(params.nextWorkerState.chatIndex || {}).length}`));
14691
15152
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E P2P \u4F1A\u8BDD\u6570: ${countObjectKeys2(params.nextWorkerState.p2pSessions)}`));
@@ -14711,28 +15172,28 @@ var HookInitCommand = class {
14711
15172
  });
14712
15173
  }
14713
15174
  backupIfNeeded(targetPath, prefix) {
14714
- if (!fs28.existsSync(targetPath)) {
15175
+ if (!fs29.existsSync(targetPath)) {
14715
15176
  return;
14716
15177
  }
14717
- const backupDir = path27.join(STATE_DIR, "backups");
15178
+ const backupDir = path28.join(STATE_DIR, "backups");
14718
15179
  ensureDirExists(backupDir);
14719
15180
  const fileName = `${prefix}.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`;
14720
- fs28.copyFileSync(targetPath, path27.join(backupDir, fileName));
15181
+ fs29.copyFileSync(targetPath, path28.join(backupDir, fileName));
14721
15182
  }
14722
15183
  readJson(filePath) {
14723
- if (!fs28.existsSync(filePath)) {
15184
+ if (!fs29.existsSync(filePath)) {
14724
15185
  return null;
14725
15186
  }
14726
15187
  try {
14727
- const parsed = parseJsonWithLineComments(fs28.readFileSync(filePath, "utf-8"), filePath);
15188
+ const parsed = parseJsonWithLineComments(fs29.readFileSync(filePath, "utf-8"), filePath);
14728
15189
  return parsed && typeof parsed === "object" ? parsed : null;
14729
15190
  } catch {
14730
15191
  return null;
14731
15192
  }
14732
15193
  }
14733
15194
  writeJson(filePath, value) {
14734
- ensureDirExists(path27.dirname(filePath));
14735
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2));
15195
+ ensureDirExists(path28.dirname(filePath));
15196
+ fs29.writeFileSync(filePath, JSON.stringify(value, null, 2));
14736
15197
  }
14737
15198
  };
14738
15199
  function countObjectKeys2(value) {
@@ -14756,12 +15217,12 @@ async function hookInitAction() {
14756
15217
  }
14757
15218
 
14758
15219
  // src/commands/hook/hook-log.ts
14759
- import fs30 from "fs";
15220
+ import fs31 from "fs";
14760
15221
  import chalk10 from "chalk";
14761
15222
 
14762
15223
  // src/hook/log-searcher.ts
14763
- import fs29 from "fs";
14764
- import path28 from "path";
15224
+ import fs30 from "fs";
15225
+ import path29 from "path";
14765
15226
  function normalizeString(value) {
14766
15227
  if (value === null || value === void 0) {
14767
15228
  return "";
@@ -14799,8 +15260,8 @@ var HookLogSearcher = class {
14799
15260
  logPaths;
14800
15261
  constructor(options = {}) {
14801
15262
  this.logPaths = {
14802
- master: options.masterPath || path28.join(LOGS_DIR, "master", "ai-hook-master.service.log"),
14803
- 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")
14804
15265
  };
14805
15266
  }
14806
15267
  resolveScope(scope) {
@@ -14863,7 +15324,7 @@ var HookLogSearcher = class {
14863
15324
  }
14864
15325
  searchSingleFile(role, query = {}, options = {}) {
14865
15326
  const filePath = this.logPaths[role];
14866
- if (!filePath || !fs29.existsSync(filePath)) {
15327
+ if (!filePath || !fs30.existsSync(filePath)) {
14867
15328
  return {
14868
15329
  role,
14869
15330
  path: filePath,
@@ -14874,7 +15335,7 @@ var HookLogSearcher = class {
14874
15335
  lines: []
14875
15336
  };
14876
15337
  }
14877
- const raw = fs29.readFileSync(filePath, "utf8");
15338
+ const raw = fs30.readFileSync(filePath, "utf8");
14878
15339
  const lines = raw.split(/\r?\n/);
14879
15340
  if (lines.length > 0 && lines[lines.length - 1] === "") {
14880
15341
  lines.pop();
@@ -14992,7 +15453,7 @@ function buildFollowState(file) {
14992
15453
  return {
14993
15454
  role: file.role,
14994
15455
  path: file.path,
14995
- offset: fs30.existsSync(file.path) ? fs30.statSync(file.path).size : 0,
15456
+ offset: fs31.existsSync(file.path) ? fs31.statSync(file.path).size : 0,
14996
15457
  remainder: ""
14997
15458
  };
14998
15459
  }
@@ -15019,10 +15480,10 @@ function flushChunk(state, chunk, queryText) {
15019
15480
  output.forEach((line) => console.log(line));
15020
15481
  }
15021
15482
  function readAppendedChunk(state, queryText) {
15022
- if (!fs30.existsSync(state.path)) {
15483
+ if (!fs31.existsSync(state.path)) {
15023
15484
  return;
15024
15485
  }
15025
- const stat = fs30.statSync(state.path);
15486
+ const stat = fs31.statSync(state.path);
15026
15487
  if (stat.size < state.offset) {
15027
15488
  state.offset = 0;
15028
15489
  state.remainder = "";
@@ -15030,15 +15491,15 @@ function readAppendedChunk(state, queryText) {
15030
15491
  if (stat.size === state.offset) {
15031
15492
  return;
15032
15493
  }
15033
- const fd = fs30.openSync(state.path, "r");
15494
+ const fd = fs31.openSync(state.path, "r");
15034
15495
  try {
15035
15496
  const length = stat.size - state.offset;
15036
15497
  const buffer = Buffer.alloc(length);
15037
- fs30.readSync(fd, buffer, 0, length, state.offset);
15498
+ fs31.readSync(fd, buffer, 0, length, state.offset);
15038
15499
  state.offset = stat.size;
15039
15500
  flushChunk(state, buffer.toString("utf8"), queryText);
15040
15501
  } finally {
15041
- fs30.closeSync(fd);
15502
+ fs31.closeSync(fd);
15042
15503
  }
15043
15504
  }
15044
15505
  async function followLogs(queryText, options = {}) {
@@ -15079,60 +15540,6 @@ async function hookLogAction(query = "", options = {}) {
15079
15540
 
15080
15541
  // src/index.ts
15081
15542
  var DEFAULT_DEST = ".agents/skills";
15082
- var WORKER_BOOLEAN_OPTIONS = /* @__PURE__ */ new Set([
15083
- "--verbose"
15084
- ]);
15085
- var WORKER_VALUE_OPTIONS = /* @__PURE__ */ new Set([
15086
- "--master-host",
15087
- "--master-port",
15088
- "--shared-secret",
15089
- "--connect-timeout-ms",
15090
- "--model",
15091
- "--fallback-model",
15092
- "--permission-mode",
15093
- "--max-budget-usd",
15094
- "--settings",
15095
- "--mcp-config",
15096
- "--add-dir",
15097
- "--system-prompt",
15098
- "--append-system-prompt",
15099
- "--agent",
15100
- "--agents",
15101
- "--tools",
15102
- "--allowedTools",
15103
- "--disallowedTools"
15104
- ]);
15105
- function resolveWorkerPassthroughArgs(engine) {
15106
- const rawArgs = process.argv.slice(2);
15107
- const workerIndex = rawArgs.findIndex((item, index) => item === "worker" && rawArgs[index - 1] === "hook");
15108
- if (workerIndex < 0) {
15109
- return [];
15110
- }
15111
- const tail = rawArgs.slice(workerIndex + 1);
15112
- const args = tail[0] === "worker" ? tail.slice(1) : tail;
15113
- const normalizedEngine = String(engine || "codex").trim();
15114
- const scanArgs = args[0] === normalizedEngine ? args.slice(1) : args;
15115
- const passthroughArgs = [];
15116
- for (let index = 0; index < scanArgs.length; index += 1) {
15117
- const item = scanArgs[index];
15118
- if (item === "--") {
15119
- passthroughArgs.push(...scanArgs.slice(index + 1));
15120
- break;
15121
- }
15122
- const key = item.includes("=") ? item.split("=")[0] : item;
15123
- if (WORKER_BOOLEAN_OPTIONS.has(key)) {
15124
- continue;
15125
- }
15126
- if (WORKER_VALUE_OPTIONS.has(key)) {
15127
- if (!item.includes("=")) {
15128
- index += 1;
15129
- }
15130
- continue;
15131
- }
15132
- passthroughArgs.push(item);
15133
- }
15134
- return passthroughArgs;
15135
- }
15136
15543
  var program = new Command();
15137
15544
  program.name("nikou-cli").description("CLI to install and manage Agent Skills from GitHub repos").version("0.1.0");
15138
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) => {
@@ -15161,16 +15568,23 @@ mcp.command("search").argument("<keyword>", "Search keyword").description("Searc
15161
15568
  await mcpSearchCommand(keyword, options);
15162
15569
  });
15163
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
+ }
15164
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) => {
15165
15575
  await hookMasterAction(options);
15166
15576
  });
15167
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 () => {
15168
15578
  await hookInitAction();
15169
15579
  });
15170
- 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) => {
15171
- 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");
15172
15582
  await hookWorkerAction(engine, { ...options, passthroughArgs });
15173
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
+ });
15174
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) => {
15175
15589
  await hookLogAction(query, options);
15176
15590
  });