nikou-cli 0.1.5 → 0.1.7

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) {
@@ -912,7 +912,7 @@ function pad3(str, width) {
912
912
 
913
913
  // src/hook/master/master-command.ts
914
914
  import * as lark2 from "@larksuiteoapi/node-sdk";
915
- import fs8 from "fs";
915
+ import fs7 from "fs";
916
916
  import path8 from "path";
917
917
  import process4 from "process";
918
918
 
@@ -975,7 +975,7 @@ var CARD_STREAM_STDOUT_MAX_LENGTH = 12e3;
975
975
  var BIND_WHITELIST_REFRESH_MS = 5e3;
976
976
 
977
977
  // src/hook/utils.ts
978
- import fs2 from "fs";
978
+ import fs from "fs";
979
979
  import os2 from "os";
980
980
  import path2 from "path";
981
981
  import process2 from "process";
@@ -1029,9 +1029,9 @@ function resolveAiHookGuestPath() {
1029
1029
  }
1030
1030
  function resolveOpsUserName() {
1031
1031
  const propsPath = path2.join(os2.homedir(), "ops", "ops_global.properties");
1032
- if (!fs2.existsSync(propsPath)) return "\u672A\u77E5";
1032
+ if (!fs.existsSync(propsPath)) return "\u672A\u77E5";
1033
1033
  try {
1034
- const lines = fs2.readFileSync(propsPath, "utf-8").split(/\r?\n/);
1034
+ const lines = fs.readFileSync(propsPath, "utf-8").split(/\r?\n/);
1035
1035
  for (const line of lines) {
1036
1036
  const trimmed = line.trim();
1037
1037
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -1056,8 +1056,8 @@ function resolveCardPathDisplay(dirPath) {
1056
1056
  return `${segments[segments.length - 2]}\\${segments[segments.length - 1]}`;
1057
1057
  }
1058
1058
  function ensureDirExists(dirPath) {
1059
- if (!fs2.existsSync(dirPath)) {
1060
- fs2.mkdirSync(dirPath, { recursive: true });
1059
+ if (!fs.existsSync(dirPath)) {
1060
+ fs.mkdirSync(dirPath, { recursive: true });
1061
1061
  }
1062
1062
  }
1063
1063
  function normalizeHost(value) {
@@ -1369,7 +1369,7 @@ function formatCompactTokenCount(value) {
1369
1369
  }
1370
1370
 
1371
1371
  // src/hook/config-loader.ts
1372
- import fs3 from "fs";
1372
+ import fs2 from "fs";
1373
1373
 
1374
1374
  // src/hook/json-config.ts
1375
1375
  function stripJsonLineComments(content) {
@@ -1413,7 +1413,7 @@ function parseJsonWithLineComments(content, filePath = "JSON \u914D\u7F6E") {
1413
1413
  // src/hook/config-loader.ts
1414
1414
  function resolveConfigPath(role = "worker") {
1415
1415
  const configPath = role === "master" ? MASTER_CONFIG_PATH : WORKER_CONFIG_PATH;
1416
- return fs3.existsSync(configPath) ? configPath : null;
1416
+ return fs2.existsSync(configPath) ? configPath : null;
1417
1417
  }
1418
1418
  function loadRoleConfig(role) {
1419
1419
  const configPath = resolveConfigPath(role);
@@ -1427,7 +1427,7 @@ function loadRoleConfig(role) {
1427
1427
  ` + sampleBody + "}"
1428
1428
  );
1429
1429
  }
1430
- const raw = fs3.readFileSync(configPath, "utf-8");
1430
+ const raw = fs2.readFileSync(configPath, "utf-8");
1431
1431
  const config = parseJsonWithLineComments(raw, configPath);
1432
1432
  return { config, configPath };
1433
1433
  }
@@ -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);
@@ -1584,7 +1597,7 @@ function resolveAuthConfig(config) {
1584
1597
  }
1585
1598
 
1586
1599
  // src/hook/state-manager.ts
1587
- import fs4 from "fs";
1600
+ import fs3 from "fs";
1588
1601
  import path3 from "path";
1589
1602
  import process3 from "process";
1590
1603
  var StateManager = class {
@@ -1596,13 +1609,13 @@ var StateManager = class {
1596
1609
  this.logger = logger3;
1597
1610
  }
1598
1611
  read() {
1599
- if (!fs4.existsSync(this.statePath)) {
1612
+ if (!fs3.existsSync(this.statePath)) {
1600
1613
  this.state = null;
1601
1614
  return null;
1602
1615
  }
1603
1616
  for (let attempt = 0; attempt < 2; attempt++) {
1604
1617
  try {
1605
- const raw = fs4.readFileSync(this.statePath, "utf-8");
1618
+ const raw = fs3.readFileSync(this.statePath, "utf-8");
1606
1619
  this.state = JSON.parse(raw);
1607
1620
  return this.state;
1608
1621
  } catch (error) {
@@ -1621,13 +1634,13 @@ var StateManager = class {
1621
1634
  const tempPath = `${this.statePath}.tmp.${process3.pid}`;
1622
1635
  try {
1623
1636
  ensureDirExists(path3.dirname(this.statePath));
1624
- fs4.writeFileSync(tempPath, JSON.stringify(nextState, null, 4));
1625
- fs4.renameSync(tempPath, this.statePath);
1637
+ fs3.writeFileSync(tempPath, JSON.stringify(nextState, null, 4));
1638
+ fs3.renameSync(tempPath, this.statePath);
1626
1639
  } catch (error) {
1627
1640
  const msg = error instanceof Error ? error.message : String(error);
1628
1641
  this.logger.warn(`\u5199\u5165\u72B6\u6001\u6587\u4EF6\u5931\u8D25: ${msg}`);
1629
1642
  try {
1630
- if (fs4.existsSync(tempPath)) fs4.unlinkSync(tempPath);
1643
+ if (fs3.existsSync(tempPath)) fs3.unlinkSync(tempPath);
1631
1644
  } catch (cleanupError) {
1632
1645
  const cmsg = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
1633
1646
  this.logger.warn(`\u6E05\u7406\u4E34\u65F6\u72B6\u6001\u6587\u4EF6\u5931\u8D25: ${cmsg}`);
@@ -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 : [];
@@ -3883,7 +3909,7 @@ var DirectWorkerRegistry = class {
3883
3909
  };
3884
3910
 
3885
3911
  // src/hook/master/routing/chat-binding-registry.ts
3886
- import fs5 from "fs";
3912
+ import fs4 from "fs";
3887
3913
  import path4 from "path";
3888
3914
  function normalize2(value) {
3889
3915
  return String(value || "").trim();
@@ -3944,12 +3970,12 @@ var ChatBindingRegistry = class {
3944
3970
  }
3945
3971
  load() {
3946
3972
  this.bindings.clear();
3947
- if (!fs5.existsSync(this.filePath)) {
3973
+ if (!fs4.existsSync(this.filePath)) {
3948
3974
  this.save();
3949
3975
  return;
3950
3976
  }
3951
3977
  try {
3952
- const parsed = JSON.parse(fs5.readFileSync(this.filePath, "utf-8") || "{}");
3978
+ const parsed = JSON.parse(fs4.readFileSync(this.filePath, "utf-8") || "{}");
3953
3979
  for (const [chatId, value] of Object.entries(parsed.bindings || {})) {
3954
3980
  const normalizedChatId = normalize2(chatId);
3955
3981
  if (!normalizedChatId || !value || typeof value !== "object") {
@@ -3977,7 +4003,7 @@ var ChatBindingRegistry = class {
3977
4003
  }
3978
4004
  }
3979
4005
  save() {
3980
- fs5.mkdirSync(path4.dirname(this.filePath), { recursive: true });
4006
+ fs4.mkdirSync(path4.dirname(this.filePath), { recursive: true });
3981
4007
  const bindings = {};
3982
4008
  for (const [chatId, chatMap] of this.bindings.entries()) {
3983
4009
  const serialized = {};
@@ -3986,7 +4012,7 @@ var ChatBindingRegistry = class {
3986
4012
  }
3987
4013
  bindings[chatId] = serialized;
3988
4014
  }
3989
- fs5.writeFileSync(
4015
+ fs4.writeFileSync(
3990
4016
  this.filePath,
3991
4017
  `${JSON.stringify({ version: 2, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), bindings }, null, 2)}
3992
4018
  `,
@@ -4110,7 +4136,7 @@ var ChatBindingRegistry = class {
4110
4136
  };
4111
4137
 
4112
4138
  // src/hook/master/routing/worker-diagnostics-registry.ts
4113
- import fs6 from "fs";
4139
+ import fs5 from "fs";
4114
4140
  import path5 from "path";
4115
4141
  function normalize3(value) {
4116
4142
  return String(value || "").trim();
@@ -4135,12 +4161,12 @@ var WorkerDiagnosticsRegistry = class {
4135
4161
  }
4136
4162
  load() {
4137
4163
  this.workers.clear();
4138
- if (!fs6.existsSync(this.filePath)) {
4164
+ if (!fs5.existsSync(this.filePath)) {
4139
4165
  this.save();
4140
4166
  return;
4141
4167
  }
4142
4168
  try {
4143
- const parsed = JSON.parse(fs6.readFileSync(this.filePath, "utf-8") || "{}");
4169
+ const parsed = JSON.parse(fs5.readFileSync(this.filePath, "utf-8") || "{}");
4144
4170
  for (const [workerId, diagnostics] of Object.entries(safeObject(parsed.workers))) {
4145
4171
  const normalizedWorkerId = normalize3(diagnostics.workerId || workerId);
4146
4172
  const registryKey = buildRegistryKey({
@@ -4172,12 +4198,12 @@ var WorkerDiagnosticsRegistry = class {
4172
4198
  }
4173
4199
  }
4174
4200
  save() {
4175
- fs6.mkdirSync(path5.dirname(this.filePath), { recursive: true });
4201
+ fs5.mkdirSync(path5.dirname(this.filePath), { recursive: true });
4176
4202
  const workers = {};
4177
4203
  for (const [key, diagnostics] of this.workers.entries()) {
4178
4204
  workers[key] = diagnostics;
4179
4205
  }
4180
- fs6.writeFileSync(
4206
+ fs5.writeFileSync(
4181
4207
  this.filePath,
4182
4208
  `${JSON.stringify({ version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), workers }, null, 2)}
4183
4209
  `,
@@ -4226,7 +4252,7 @@ var WorkerDiagnosticsRegistry = class {
4226
4252
  };
4227
4253
 
4228
4254
  // src/hook/master/master-event-utils.ts
4229
- import fs7 from "fs";
4255
+ import fs6 from "fs";
4230
4256
  import os3 from "os";
4231
4257
  import path6 from "path";
4232
4258
  var VOPS_USER_CACHE_PATH = path6.join(os3.homedir(), "ops", "vops_user.json");
@@ -4313,11 +4339,11 @@ function isTaskOperatorMatched(task, operatorUserId, operatorOpenId) {
4313
4339
  function resolveSenderNameFromCache(senderUserId, senderOpenId = "") {
4314
4340
  const targetUserId = String(senderUserId || "").trim();
4315
4341
  const targetOpenId = String(senderOpenId || "").trim();
4316
- if (!targetUserId && !targetOpenId || !fs7.existsSync(VOPS_USER_CACHE_PATH)) {
4342
+ if (!targetUserId && !targetOpenId || !fs6.existsSync(VOPS_USER_CACHE_PATH)) {
4317
4343
  return "";
4318
4344
  }
4319
4345
  try {
4320
- const parsed = JSON.parse(fs7.readFileSync(VOPS_USER_CACHE_PATH, "utf-8"));
4346
+ const parsed = JSON.parse(fs6.readFileSync(VOPS_USER_CACHE_PATH, "utf-8"));
4321
4347
  const entries = Array.isArray(parsed) ? parsed : [];
4322
4348
  const matched = entries.find((item) => {
4323
4349
  if (!item || typeof item !== "object" || item.__cacheType) {
@@ -5205,7 +5231,7 @@ var HookMasterCommand = class {
5205
5231
  if (masterConfig.botName) {
5206
5232
  this.botName = masterConfig.botName;
5207
5233
  }
5208
- const configStat = fs8.statSync(configPath);
5234
+ const configStat = fs7.statSync(configPath);
5209
5235
  this.bindWhitelistConfigMtimeMs = Number(configStat.mtimeMs || 0);
5210
5236
  this.chatBindingRegistry.load();
5211
5237
  this.workerDiagnosticsRegistry.load();
@@ -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);
@@ -5720,8 +5753,8 @@ var HookMasterCommand = class {
5720
5753
  }
5721
5754
  refreshBindWhitelistIfNeeded() {
5722
5755
  try {
5723
- if (!this.configPath || !fs8.existsSync(this.configPath)) return;
5724
- const stat = fs8.statSync(this.configPath);
5756
+ if (!this.configPath || !fs7.existsSync(this.configPath)) return;
5757
+ const stat = fs7.statSync(this.configPath);
5725
5758
  const mtimeMs = Number(stat.mtimeMs || 0);
5726
5759
  if (!mtimeMs || mtimeMs <= this.bindWhitelistConfigMtimeMs) return;
5727
5760
  const { config } = loadMasterHstMcpConfig();
@@ -5772,42 +5805,42 @@ 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
5806
- import fs9 from "fs";
5839
+ import fs8 from "fs";
5807
5840
  import path9 from "path";
5808
5841
  function ensureLogsDir() {
5809
- if (!fs9.existsSync(LOGS_DIR)) {
5810
- fs9.mkdirSync(LOGS_DIR, { recursive: true });
5842
+ if (!fs8.existsSync(LOGS_DIR)) {
5843
+ fs8.mkdirSync(LOGS_DIR, { recursive: true });
5811
5844
  }
5812
5845
  }
5813
5846
  function todaySuffix() {
@@ -5818,8 +5851,8 @@ function timestamp() {
5818
5851
  }
5819
5852
  function appendLine(filePath, line) {
5820
5853
  try {
5821
- fs9.mkdirSync(path9.dirname(filePath), { recursive: true });
5822
- fs9.appendFileSync(filePath, line + "\n", "utf-8");
5854
+ fs8.mkdirSync(path9.dirname(filePath), { recursive: true });
5855
+ fs8.appendFileSync(filePath, line + "\n", "utf-8");
5823
5856
  } catch {
5824
5857
  }
5825
5858
  }
@@ -5844,22 +5877,22 @@ function resolveServiceLogName(role) {
5844
5877
  }
5845
5878
  function ensureCurrentLogLink(currentPath, dailyPath) {
5846
5879
  try {
5847
- fs9.mkdirSync(path9.dirname(currentPath), { recursive: true });
5848
- fs9.mkdirSync(path9.dirname(dailyPath), { recursive: true });
5849
- fs9.writeFileSync(dailyPath, "", { flag: "a" });
5850
- if (fs9.existsSync(currentPath)) {
5851
- const stat = fs9.lstatSync(currentPath);
5880
+ fs8.mkdirSync(path9.dirname(currentPath), { recursive: true });
5881
+ fs8.mkdirSync(path9.dirname(dailyPath), { recursive: true });
5882
+ fs8.writeFileSync(dailyPath, "", { flag: "a" });
5883
+ if (fs8.existsSync(currentPath)) {
5884
+ const stat = fs8.lstatSync(currentPath);
5852
5885
  if (!stat.isSymbolicLink()) {
5853
5886
  const legacyDir = path9.join(path9.dirname(currentPath), "logs", "legacy");
5854
- fs9.mkdirSync(legacyDir, { recursive: true });
5887
+ fs8.mkdirSync(legacyDir, { recursive: true });
5855
5888
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5856
- fs9.renameSync(currentPath, path9.join(legacyDir, `${path9.basename(currentPath)}.${stamp}`));
5889
+ fs8.renameSync(currentPath, path9.join(legacyDir, `${path9.basename(currentPath)}.${stamp}`));
5857
5890
  }
5858
5891
  }
5859
- fs9.rmSync(currentPath, { force: true });
5860
- fs9.symlinkSync(dailyPath, currentPath);
5892
+ fs8.rmSync(currentPath, { force: true });
5893
+ fs8.symlinkSync(dailyPath, currentPath);
5861
5894
  } catch {
5862
- fs9.writeFileSync(currentPath, "", { flag: "a" });
5895
+ fs8.writeFileSync(currentPath, "", { flag: "a" });
5863
5896
  }
5864
5897
  }
5865
5898
  function createLogger(name, verbose = false) {
@@ -5920,16 +5953,19 @@ 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
 
5960
+ // src/hook/worker/message-strategy.ts
5961
+ import fs14 from "fs";
5962
+
5927
5963
  // src/hook/worker/runners/codex-runner.ts
5928
5964
  import process7 from "process";
5929
5965
  import { spawn, spawnSync as spawnSync4 } from "child_process";
5930
5966
 
5931
5967
  // src/hook/runtime-env.ts
5932
- import fs10 from "fs";
5968
+ import fs9 from "fs";
5933
5969
  import process5 from "process";
5934
5970
  import { spawnSync as spawnSync2 } from "child_process";
5935
5971
  var HST_MCP_DISABLED_MODULES_ENV_KEY = "HST_MCP_DISABLED_MODULES";
@@ -5974,11 +6010,11 @@ function parseEnvFileContent(content = "") {
5974
6010
  return env;
5975
6011
  }
5976
6012
  function loadRuntimeEnvFile(filePath = RUNTIME_ENV_PATH) {
5977
- if (!filePath || !fs10.existsSync(filePath)) {
6013
+ if (!filePath || !fs9.existsSync(filePath)) {
5978
6014
  return {};
5979
6015
  }
5980
6016
  try {
5981
- return parseEnvFileContent(fs10.readFileSync(filePath, "utf-8"));
6017
+ return parseEnvFileContent(fs9.readFileSync(filePath, "utf-8"));
5982
6018
  } catch {
5983
6019
  return {};
5984
6020
  }
@@ -5990,7 +6026,7 @@ function resolveUnixShell(baseEnv = process5.env) {
5990
6026
  "/bin/bash",
5991
6027
  "/bin/sh"
5992
6028
  ].filter(Boolean);
5993
- return candidates.find((item) => fs10.existsSync(item)) || "/bin/sh";
6029
+ return candidates.find((item) => fs9.existsSync(item)) || "/bin/sh";
5994
6030
  }
5995
6031
  function parseNullSeparatedEnv(output = "") {
5996
6032
  const env = {};
@@ -6111,23 +6147,23 @@ function buildHookRuntimeEnv(baseEnv = process5.env, {
6111
6147
  }
6112
6148
 
6113
6149
  // src/hook/worker/runners/command-resolver.ts
6114
- import fs11 from "fs";
6150
+ import fs10 from "fs";
6115
6151
  import path10 from "path";
6116
6152
  import process6 from "process";
6117
6153
  import { spawnSync as spawnSync3 } from "child_process";
6118
6154
  function isExecutableFile(filePath) {
6119
- if (!filePath || !fs11.existsSync(filePath)) {
6155
+ if (!filePath || !fs10.existsSync(filePath)) {
6120
6156
  return false;
6121
6157
  }
6122
6158
  try {
6123
- const stat = fs11.statSync(filePath);
6159
+ const stat = fs10.statSync(filePath);
6124
6160
  if (!stat.isFile()) {
6125
6161
  return false;
6126
6162
  }
6127
6163
  if (process6.platform === "win32") {
6128
6164
  return true;
6129
6165
  }
6130
- fs11.accessSync(filePath, fs11.constants.X_OK);
6166
+ fs10.accessSync(filePath, fs10.constants.X_OK);
6131
6167
  return true;
6132
6168
  } catch {
6133
6169
  return false;
@@ -6672,7 +6708,7 @@ command=${execConfig.displayCommand}`));
6672
6708
  };
6673
6709
 
6674
6710
  // src/hook/context/identity-resolver.ts
6675
- import fs12 from "fs";
6711
+ import fs11 from "fs";
6676
6712
  import os5 from "os";
6677
6713
  import path11 from "path";
6678
6714
 
@@ -6877,12 +6913,12 @@ var IdentityResolver = class {
6877
6913
  if (Array.isArray(this.cache)) {
6878
6914
  return this.cache;
6879
6915
  }
6880
- if (!fs12.existsSync(this.cachePath)) {
6916
+ if (!fs11.existsSync(this.cachePath)) {
6881
6917
  this.cache = [];
6882
6918
  return this.cache;
6883
6919
  }
6884
6920
  try {
6885
- const parsed = JSON.parse(fs12.readFileSync(this.cachePath, "utf-8"));
6921
+ const parsed = JSON.parse(fs11.readFileSync(this.cachePath, "utf-8"));
6886
6922
  const input = Array.isArray(parsed) ? parsed : [];
6887
6923
  this.cache = this.dedupeCacheEntries(input);
6888
6924
  if (this.cache.length !== input.length) {
@@ -6899,7 +6935,7 @@ var IdentityResolver = class {
6899
6935
  saveCache() {
6900
6936
  try {
6901
6937
  this.cache = this.dedupeCacheEntries(this.cache || []);
6902
- fs12.writeFileSync(this.cachePath, JSON.stringify(this.cache, null, 2));
6938
+ fs11.writeFileSync(this.cachePath, JSON.stringify(this.cache, null, 2));
6903
6939
  } catch (error) {
6904
6940
  const message = error instanceof Error ? error.message : String(error);
6905
6941
  this.logger.warn(`\u5199\u5165 vops_user.json \u5931\u8D25: ${message}`);
@@ -7232,7 +7268,7 @@ var IdentityResolver = class {
7232
7268
  };
7233
7269
 
7234
7270
  // src/hook/prompt/nikou-prompt-store.ts
7235
- import fs13 from "fs";
7271
+ import fs12 from "fs";
7236
7272
  import os6 from "os";
7237
7273
  import path12 from "path";
7238
7274
 
@@ -7287,7 +7323,7 @@ function normalizeText2(value) {
7287
7323
  return String(value || "").trim();
7288
7324
  }
7289
7325
  function ensureDir(dirPath) {
7290
- fs13.mkdirSync(dirPath, { recursive: true });
7326
+ fs12.mkdirSync(dirPath, { recursive: true });
7291
7327
  }
7292
7328
  function parseFrontmatter(content) {
7293
7329
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
@@ -7334,18 +7370,18 @@ ${body}
7334
7370
  ` : "\n"}`;
7335
7371
  }
7336
7372
  function readTextFile(filePath) {
7337
- if (!fs13.existsSync(filePath)) {
7373
+ if (!fs12.existsSync(filePath)) {
7338
7374
  return "";
7339
7375
  }
7340
7376
  try {
7341
- return fs13.readFileSync(filePath, "utf-8").trim();
7377
+ return fs12.readFileSync(filePath, "utf-8").trim();
7342
7378
  } catch {
7343
7379
  return "";
7344
7380
  }
7345
7381
  }
7346
7382
  function writeTextFile(filePath, content) {
7347
7383
  ensureDir(path12.dirname(filePath));
7348
- fs13.writeFileSync(filePath, content, "utf-8");
7384
+ fs12.writeFileSync(filePath, content, "utf-8");
7349
7385
  }
7350
7386
  function buildPromptDefinitions() {
7351
7387
  const roleDefinitions = Object.entries(REQUESTER_PERSONA_RULES.roles).map(([roleKey, roleConfig]) => ({
@@ -7406,7 +7442,7 @@ var NikouPromptStore = class {
7406
7442
  return definition.defaultContent.trim();
7407
7443
  }
7408
7444
  readPromptFile(definition) {
7409
- if (!fs13.existsSync(definition.path)) {
7445
+ if (!fs12.existsSync(definition.path)) {
7410
7446
  const content = this.resolveBootstrapContent(definition);
7411
7447
  const file = {
7412
7448
  title: definition.title,
@@ -7417,7 +7453,7 @@ var NikouPromptStore = class {
7417
7453
  this.syncLegacyFiles(definition, file);
7418
7454
  return file;
7419
7455
  }
7420
- return parseFrontmatter(fs13.readFileSync(definition.path, "utf-8"));
7456
+ return parseFrontmatter(fs12.readFileSync(definition.path, "utf-8"));
7421
7457
  }
7422
7458
  syncLegacyFiles(definition, file) {
7423
7459
  if (!definition.legacyPaths.length) {
@@ -7430,7 +7466,7 @@ var NikouPromptStore = class {
7430
7466
  }
7431
7467
  }
7432
7468
  toSummary(definition, file) {
7433
- const stat = fs13.existsSync(definition.path) ? fs13.statSync(definition.path) : null;
7469
+ const stat = fs12.existsSync(definition.path) ? fs12.statSync(definition.path) : null;
7434
7470
  return {
7435
7471
  id: definition.id,
7436
7472
  kind: definition.kind,
@@ -7511,7 +7547,7 @@ var NikouPromptStore = class {
7511
7547
  };
7512
7548
 
7513
7549
  // src/hook/worker/task-route-binding.ts
7514
- import fs14 from "fs";
7550
+ import fs13 from "fs";
7515
7551
  import path13 from "path";
7516
7552
  function resolveTaskRouteDir(routeBinding, fallbackDir) {
7517
7553
  if (routeBinding) {
@@ -7520,7 +7556,7 @@ function resolveTaskRouteDir(routeBinding, fallbackDir) {
7520
7556
  return { dirPath: "", reason: "invalid_route_dir" };
7521
7557
  }
7522
7558
  try {
7523
- if (!fs14.statSync(dirPath2).isDirectory()) {
7559
+ if (!fs13.statSync(dirPath2).isDirectory()) {
7524
7560
  return { dirPath: "", reason: "invalid_route_dir" };
7525
7561
  }
7526
7562
  } catch {
@@ -7657,7 +7693,7 @@ var MessageReceiveStrategy = class {
7657
7693
  if (!raw) return "";
7658
7694
  try {
7659
7695
  const parsed = JSON.parse(raw);
7660
- const text = String(parsed?.text || "");
7696
+ const text = String(parsed?.text || "") || extractFeishuPostText(parsed);
7661
7697
  return this.stripMentions(text, message);
7662
7698
  } catch {
7663
7699
  return this.stripMentions(String(raw), message);
@@ -7665,25 +7701,7 @@ var MessageReceiveStrategy = class {
7665
7701
  }
7666
7702
  extractPostText(messageLike) {
7667
7703
  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
- }
7704
+ return extractFeishuPostText(raw);
7687
7705
  }
7688
7706
  stripMentions(text, message) {
7689
7707
  let cleaned = String(text || "");
@@ -8308,7 +8326,7 @@ ${question}` : question;
8308
8326
  if (!bindTarget) {
8309
8327
  return { ...baseTask, canHandle: true, type: "quick_result", quickResult: "\u8BF7\u6307\u5B9A\u7ED1\u5B9A\u8DEF\u5F84\uFF0C\u683C\u5F0F: \u7ED1\u5B9A:/path/to/project" };
8310
8328
  }
8311
- if (!fs.existsSync(bindTarget)) {
8329
+ if (!fs14.existsSync(bindTarget)) {
8312
8330
  return { ...baseTask, canHandle: true, type: "quick_result", quickResult: `\u8DEF\u5F84\u4E0D\u5B58\u5728: ${bindTarget}` };
8313
8331
  }
8314
8332
  this.stateManager.rebindChatToDir(message.chatId, bindTarget);
@@ -9744,7 +9762,15 @@ var SlaveRelayClient = class {
9744
9762
  this.resolvePendingClaim(packet.task_id, false);
9745
9763
  break;
9746
9764
  case "task_offer":
9747
- this.handleTaskOffer(packet);
9765
+ void this.handleTaskOffer(packet).catch((error) => {
9766
+ const taskId = String(packet.task_id || "").trim();
9767
+ const traceId = resolveTraceId(taskId);
9768
+ const message = error instanceof Error ? error.message : String(error);
9769
+ this.logger.error(formatTraceLog(
9770
+ traceId,
9771
+ `\u5904\u7406\u4EFB\u52A1 offer \u5931\u8D25: task_id=${taskId || "-"}, error=${message}`
9772
+ ));
9773
+ });
9748
9774
  break;
9749
9775
  case "task_cancel": {
9750
9776
  const cancelResult = this.taskExecutor.cancelTask(packet.task_id);
@@ -10496,6 +10522,7 @@ var LocalBrowserWsServer = class {
10496
10522
  workerId;
10497
10523
  workerType;
10498
10524
  larkClient;
10525
+ getNacosSyncStatus;
10499
10526
  wss = null;
10500
10527
  profile;
10501
10528
  startTime = (/* @__PURE__ */ new Date()).toISOString();
@@ -10505,6 +10532,7 @@ var LocalBrowserWsServer = class {
10505
10532
  this.workerId = options.workerId;
10506
10533
  this.workerType = options.workerType;
10507
10534
  this.larkClient = options.larkClient;
10535
+ this.getNacosSyncStatus = options.getNacosSyncStatus;
10508
10536
  }
10509
10537
  async start() {
10510
10538
  if (this.wss) {
@@ -10685,7 +10713,8 @@ var LocalBrowserWsServer = class {
10685
10713
  cliTools: {
10686
10714
  codex: this.detectCliTool("codex"),
10687
10715
  claude: this.detectCliTool("claude")
10688
- }
10716
+ },
10717
+ nacosSync: this.getNacosSyncStatus?.() ?? null
10689
10718
  }
10690
10719
  });
10691
10720
  }
@@ -13974,6 +14003,263 @@ var OwnerNotifyService = class {
13974
14003
  }
13975
14004
  };
13976
14005
 
14006
+ // src/hook/worker/nacos-subscription-sync.ts
14007
+ import fs27 from "fs";
14008
+ import os16 from "os";
14009
+ import path26 from "path";
14010
+ import { execFile, spawn as spawn4 } from "child_process";
14011
+ import { createRequire } from "module";
14012
+ import { promisify } from "util";
14013
+ var execFileAsync = promisify(execFile);
14014
+ var NacosSubscriptionSyncCoordinator = class {
14015
+ logger;
14016
+ workerId;
14017
+ config;
14018
+ timer = null;
14019
+ daemon = null;
14020
+ running = false;
14021
+ stopped = false;
14022
+ status;
14023
+ statePath = path26.join(os16.homedir(), ".nikou-block", "worker", "nacos-subscriptions.json");
14024
+ constructor(options) {
14025
+ this.logger = options.logger;
14026
+ this.workerId = options.workerId;
14027
+ this.config = options.config;
14028
+ this.status = {
14029
+ enabled: this.config.enabled,
14030
+ configured: false,
14031
+ profile: this.config.profile,
14032
+ label: this.config.label,
14033
+ daemonRunning: false,
14034
+ totalSkills: 0,
14035
+ synced: 0,
14036
+ pending: 0,
14037
+ conflicts: 0,
14038
+ failed: 0
14039
+ };
14040
+ }
14041
+ start() {
14042
+ if (!this.config.enabled || this.timer) return;
14043
+ void this.syncOnce();
14044
+ this.timer = setInterval(() => {
14045
+ void this.syncOnce();
14046
+ }, this.config.plan_poll_interval_ms);
14047
+ }
14048
+ async stop() {
14049
+ this.stopped = true;
14050
+ if (this.timer) {
14051
+ clearInterval(this.timer);
14052
+ this.timer = null;
14053
+ }
14054
+ if (this.daemon) {
14055
+ this.daemon.kill("SIGTERM");
14056
+ this.daemon = null;
14057
+ this.status.daemonRunning = false;
14058
+ }
14059
+ }
14060
+ getStatus() {
14061
+ return { ...this.status };
14062
+ }
14063
+ async syncOnce() {
14064
+ if (this.running || this.stopped) return;
14065
+ this.running = true;
14066
+ try {
14067
+ const plan = await this.fetchPlan();
14068
+ this.status.enabled = plan.nacos.enabled;
14069
+ this.status.host = plan.nacos.host;
14070
+ this.status.profile = plan.nacos.profile || this.config.profile;
14071
+ this.status.label = plan.nacos.label || this.config.label;
14072
+ this.status.lastPlanAt = (/* @__PURE__ */ new Date()).toISOString();
14073
+ if (!plan.nacos.enabled) {
14074
+ this.status.configured = false;
14075
+ this.status.totalSkills = 0;
14076
+ this.status.pending = 0;
14077
+ return;
14078
+ }
14079
+ await this.configureProfile(plan);
14080
+ await this.ensureMode(plan);
14081
+ this.ensureDaemon(plan);
14082
+ const results = [];
14083
+ for (const skill of plan.skills) {
14084
+ const result = await this.syncSkill(plan, skill);
14085
+ results.push(result);
14086
+ }
14087
+ await this.report(results);
14088
+ this.persistManagedState(plan.skills);
14089
+ this.applySummary(results);
14090
+ this.status.lastSyncAt = (/* @__PURE__ */ new Date()).toISOString();
14091
+ this.status.lastError = void 0;
14092
+ } catch (error) {
14093
+ const message = error instanceof Error ? error.message : String(error);
14094
+ this.status.lastError = message;
14095
+ this.logger.warn(`Nacos Skill \u8BA2\u9605\u540C\u6B65\u5931\u8D25: ${message}`);
14096
+ } finally {
14097
+ this.running = false;
14098
+ }
14099
+ }
14100
+ async fetchPlan() {
14101
+ const runtime = resolveCliAuthRuntimeConfig();
14102
+ if (!runtime.cliAuthSecret) {
14103
+ throw new Error("\u672A\u914D\u7F6E CLI_AUTH_SECRET\uFF0C\u65E0\u6CD5\u62C9\u53D6 Nacos \u540C\u6B65\u8BA1\u5212");
14104
+ }
14105
+ const response = await fetch(`${runtime.apiUrl}/worker-skills/nacos-sync/plan`, {
14106
+ method: "POST",
14107
+ headers: { "Content-Type": "application/json" },
14108
+ body: JSON.stringify({ secret: runtime.cliAuthSecret, workerId: this.workerId })
14109
+ });
14110
+ if (!response.ok) {
14111
+ const text = await response.text().catch(() => "");
14112
+ throw new Error(`\u62C9\u53D6 Nacos \u540C\u6B65\u8BA1\u5212\u5931\u8D25: ${response.status} ${text}`);
14113
+ }
14114
+ return await response.json();
14115
+ }
14116
+ async report(results) {
14117
+ if (!results.length) return;
14118
+ const runtime = resolveCliAuthRuntimeConfig();
14119
+ const response = await fetch(`${runtime.apiUrl}/worker-skills/nacos-sync/report`, {
14120
+ method: "POST",
14121
+ headers: { "Content-Type": "application/json" },
14122
+ body: JSON.stringify({ secret: runtime.cliAuthSecret, workerId: this.workerId, results })
14123
+ });
14124
+ if (!response.ok) {
14125
+ const text = await response.text().catch(() => "");
14126
+ throw new Error(`\u4E0A\u62A5 Nacos \u540C\u6B65\u72B6\u6001\u5931\u8D25: ${response.status} ${text}`);
14127
+ }
14128
+ }
14129
+ async configureProfile(plan) {
14130
+ const nacos = plan.nacos;
14131
+ const profile = nacos.profile || this.config.profile;
14132
+ const args = [
14133
+ "profile",
14134
+ "set",
14135
+ profile,
14136
+ `host=${nacos.serverHost || this.parseHost(nacos.host).host}`,
14137
+ `port=${nacos.port || this.parseHost(nacos.host).port}`,
14138
+ `scheme=${nacos.scheme || this.parseHost(nacos.host).scheme}`,
14139
+ `namespace=${nacos.namespace || "public"}`,
14140
+ `auth-type=${nacos.authType || "none"}`
14141
+ ];
14142
+ if (nacos.username) args.push(`username=${nacos.username}`);
14143
+ if (nacos.password) args.push(`password=${nacos.password}`);
14144
+ await this.run(args);
14145
+ this.status.configured = true;
14146
+ }
14147
+ async ensureMode(plan) {
14148
+ await this.run(["skill-sync", "mode", "nacos", "--profile", plan.nacos.profile || this.config.profile, "--switch-profile"]);
14149
+ }
14150
+ ensureDaemon(plan) {
14151
+ if (this.daemon && !this.daemon.killed) return;
14152
+ const args = [
14153
+ "skill-sync",
14154
+ "start",
14155
+ "--foreground",
14156
+ "--non-interactive",
14157
+ "--profile",
14158
+ plan.nacos.profile || this.config.profile,
14159
+ "--label",
14160
+ plan.nacos.label || this.config.label,
14161
+ "--interval",
14162
+ this.config.interval,
14163
+ "--switch-profile"
14164
+ ];
14165
+ if (!this.config.auto_upload) {
14166
+ args.push("--no-auto-upload");
14167
+ }
14168
+ const bin = this.resolveNacosCliBin();
14169
+ this.daemon = spawn4(bin, args, { stdio: ["ignore", "pipe", "pipe"] });
14170
+ this.status.daemonRunning = true;
14171
+ this.daemon.stdout?.on("data", (chunk) => this.logger.debug(`nacos-cli: ${String(chunk).trim()}`));
14172
+ this.daemon.stderr?.on("data", (chunk) => this.logger.warn(`nacos-cli: ${String(chunk).trim()}`));
14173
+ this.daemon.once("exit", (code, signal) => {
14174
+ this.status.daemonRunning = false;
14175
+ this.daemon = null;
14176
+ if (!this.stopped) {
14177
+ this.status.lastError = `Nacos daemon \u5DF2\u9000\u51FA: code=${code ?? "-"}, signal=${signal ?? "-"}`;
14178
+ }
14179
+ });
14180
+ }
14181
+ async syncSkill(plan, skill) {
14182
+ try {
14183
+ await this.run([
14184
+ "skill-sync",
14185
+ skill.status === "pending_remove" ? "remove" : "add",
14186
+ skill.nacosSkillName,
14187
+ "--profile",
14188
+ plan.nacos.profile || this.config.profile,
14189
+ "--switch-profile",
14190
+ ...skill.status === "pending_remove" ? [] : ["--non-interactive"]
14191
+ ]);
14192
+ return {
14193
+ resourceId: skill.resourceId,
14194
+ nacosSkillName: skill.nacosSkillName,
14195
+ desiredVersionKey: skill.desiredVersionKey,
14196
+ status: skill.status === "pending_remove" ? "removed" : "synced",
14197
+ message: "Nacos Skill Sync \u6267\u884C\u6210\u529F"
14198
+ };
14199
+ } catch (error) {
14200
+ const message = error instanceof Error ? error.message : String(error);
14201
+ const status = /conflict|冲突/i.test(message) ? "conflict" : "failed";
14202
+ return {
14203
+ resourceId: skill.resourceId,
14204
+ nacosSkillName: skill.nacosSkillName,
14205
+ desiredVersionKey: skill.desiredVersionKey,
14206
+ status,
14207
+ message
14208
+ };
14209
+ }
14210
+ }
14211
+ async run(args) {
14212
+ const bin = this.resolveNacosCliBin();
14213
+ const result = await execFileAsync(bin, args, { timeout: 6e4, maxBuffer: 1024 * 1024 * 2 });
14214
+ return `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
14215
+ }
14216
+ resolveNacosCliBin() {
14217
+ try {
14218
+ const require2 = createRequire(import.meta.url);
14219
+ const pkgPath = require2.resolve("@nacos-group/cli/package.json");
14220
+ return path26.join(path26.dirname(pkgPath), "bin", "cli.js");
14221
+ } catch {
14222
+ return "nacos-cli";
14223
+ }
14224
+ }
14225
+ applySummary(results) {
14226
+ this.status.totalSkills = results.length;
14227
+ this.status.synced = results.filter((item) => item.status === "synced" || item.status === "removed").length;
14228
+ this.status.pending = results.filter((item) => item.status.startsWith("pending")).length;
14229
+ this.status.conflicts = results.filter((item) => item.status === "conflict").length;
14230
+ this.status.failed = results.filter((item) => item.status === "failed").length;
14231
+ }
14232
+ persistManagedState(skills) {
14233
+ try {
14234
+ fs27.mkdirSync(path26.dirname(this.statePath), { recursive: true });
14235
+ fs27.writeFileSync(this.statePath, JSON.stringify({
14236
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
14237
+ skills: skills.map((item) => ({
14238
+ resourceId: item.resourceId,
14239
+ nacosSkillName: item.nacosSkillName,
14240
+ desiredLabel: item.desiredLabel,
14241
+ desiredVersionKey: item.desiredVersionKey,
14242
+ status: item.status
14243
+ }))
14244
+ }, null, 2));
14245
+ } catch (error) {
14246
+ this.logger.warn(`\u5199\u5165 Nacos \u8BA2\u9605\u72B6\u6001\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
14247
+ }
14248
+ }
14249
+ parseHost(raw) {
14250
+ try {
14251
+ const url = new URL(raw.includes("://") ? raw : `http://${raw}`);
14252
+ return {
14253
+ scheme: url.protocol.replace(":", "") || "http",
14254
+ host: url.hostname,
14255
+ port: url.port || (url.protocol === "https:" ? "443" : "8848")
14256
+ };
14257
+ } catch {
14258
+ return { scheme: "http", host: raw.replace(/^https?:\/\//, ""), port: "8848" };
14259
+ }
14260
+ }
14261
+ };
14262
+
13977
14263
  // src/hook/worker/worker-command.ts
13978
14264
  var HookWorkerCommand = class {
13979
14265
  logger;
@@ -13982,11 +14268,21 @@ var HookWorkerCommand = class {
13982
14268
  localBrowserWs = null;
13983
14269
  reminderService = null;
13984
14270
  ownerNotifyService = null;
14271
+ nacosSyncCoordinator = null;
13985
14272
  heartbeatTimer = null;
13986
14273
  stateManager = null;
14274
+ stopped = false;
14275
+ stoppedPromise;
14276
+ resolveStopped = () => void 0;
13987
14277
  constructor(logger3, options = {}) {
13988
14278
  this.logger = logger3;
13989
14279
  this.options = options;
14280
+ this.stoppedPromise = new Promise((resolve3) => {
14281
+ this.resolveStopped = resolve3;
14282
+ });
14283
+ }
14284
+ waitUntilStopped() {
14285
+ return this.stoppedPromise;
13990
14286
  }
13991
14287
  async start() {
13992
14288
  const existingPid = findRunningAiHookProcess(process10.pid);
@@ -14009,6 +14305,7 @@ var HookWorkerCommand = class {
14009
14305
  });
14010
14306
  const bindAllowedUserIds = resolveBindAllowedUserIds(config);
14011
14307
  const contextConfig = resolveContextConfig(config);
14308
+ const nacosSyncConfig = resolveNacosSkillSyncConfig(config);
14012
14309
  const opsUserName = resolveOpsUserName();
14013
14310
  const larkClient = new lark3.Client({
14014
14311
  appId: feishuConfig.appId,
@@ -14074,13 +14371,20 @@ var HookWorkerCommand = class {
14074
14371
  config: this.options.runnerConfig || {}
14075
14372
  });
14076
14373
  this.logger.info(`\u5F15\u64CE\u5DF2\u521B\u5EFA: engine=${engine}, \u53EF\u7528\u5F15\u64CE=[${registry.list().join(", ")}]`);
14374
+ this.nacosSyncCoordinator = new NacosSubscriptionSyncCoordinator({
14375
+ logger: this.logger,
14376
+ workerId: ownerUserId || `worker-${process10.pid}`,
14377
+ config: nacosSyncConfig
14378
+ });
14077
14379
  this.localBrowserWs = new LocalBrowserWsServer({
14078
14380
  logger: this.logger,
14079
14381
  workerId: ownerUserId || `worker-${process10.pid}`,
14080
14382
  workerType: "tooling-cli",
14081
- larkClient
14383
+ larkClient,
14384
+ getNacosSyncStatus: () => this.nacosSyncCoordinator?.getStatus() ?? null
14082
14385
  });
14083
14386
  await this.localBrowserWs.start();
14387
+ this.nacosSyncCoordinator.start();
14084
14388
  const hostProfileService = new HostProfileService({
14085
14389
  logger: this.logger,
14086
14390
  ownerUserId,
@@ -14109,7 +14413,7 @@ var HookWorkerCommand = class {
14109
14413
  const requesterPersonaService = new RequesterPersonaService({
14110
14414
  logger: this.logger,
14111
14415
  larkClient,
14112
- rootDir: process10.env.HOOK_REQUESTER_PERSONA_DIR || path26.join(os16.homedir(), "ops", "ai-hook", "persona")
14416
+ rootDir: process10.env.HOOK_REQUESTER_PERSONA_DIR || path27.join(os17.homedir(), "ops", "ai-hook", "persona")
14113
14417
  });
14114
14418
  const taskExecutor = new TaskExecutor({
14115
14419
  logger: this.logger,
@@ -14166,17 +14470,19 @@ var HookWorkerCommand = class {
14166
14470
  await this.relayClient.startAndWait();
14167
14471
  await reminderService.start();
14168
14472
  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
- });
14473
+ if (this.options.manageProcessSignals !== false) {
14474
+ const gracefulExit = async (signal) => {
14475
+ this.logger.info(`\u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u505C\u6B62\u4ECE\u8282\u70B9...`);
14476
+ await this.stop(signal);
14477
+ process10.exit(0);
14478
+ };
14479
+ process10.on("SIGINT", () => {
14480
+ void gracefulExit("SIGINT");
14481
+ });
14482
+ process10.on("SIGTERM", () => {
14483
+ void gracefulExit("SIGTERM");
14484
+ });
14485
+ }
14180
14486
  this.heartbeatTimer = setInterval(() => {
14181
14487
  stateManager.updateHeartbeat();
14182
14488
  }, HEARTBEAT_INTERVAL_MS);
@@ -14186,6 +14492,10 @@ var HookWorkerCommand = class {
14186
14492
  }
14187
14493
  /** 停止从节点 */
14188
14494
  async stop(signal) {
14495
+ if (this.stopped) {
14496
+ return;
14497
+ }
14498
+ this.stopped = true;
14189
14499
  if (this.heartbeatTimer) {
14190
14500
  clearInterval(this.heartbeatTimer);
14191
14501
  this.heartbeatTimer = null;
@@ -14194,6 +14504,10 @@ var HookWorkerCommand = class {
14194
14504
  this.localBrowserWs.stop();
14195
14505
  this.localBrowserWs = null;
14196
14506
  }
14507
+ if (this.nacosSyncCoordinator) {
14508
+ await this.nacosSyncCoordinator.stop();
14509
+ this.nacosSyncCoordinator = null;
14510
+ }
14197
14511
  if (this.reminderService) {
14198
14512
  this.reminderService.stop();
14199
14513
  this.reminderService = null;
@@ -14219,10 +14533,65 @@ var HookWorkerCommand = class {
14219
14533
  slaveConnectionStatus: "offline"
14220
14534
  });
14221
14535
  }
14536
+ this.resolveStopped();
14222
14537
  }
14223
14538
  };
14224
14539
 
14225
- // src/commands/hook/hook-worker.ts
14540
+ // src/commands/hook/worker-options.ts
14541
+ var WORKER_BOOLEAN_OPTIONS = /* @__PURE__ */ new Set([
14542
+ "--verbose"
14543
+ ]);
14544
+ var WORKER_VALUE_OPTIONS = /* @__PURE__ */ new Set([
14545
+ "--master-host",
14546
+ "--master-port",
14547
+ "--shared-secret",
14548
+ "--claim-timeout-ms",
14549
+ "--connect-timeout-ms",
14550
+ "--model",
14551
+ "--fallback-model",
14552
+ "--permission-mode",
14553
+ "--max-budget-usd",
14554
+ "--settings",
14555
+ "--mcp-config",
14556
+ "--add-dir",
14557
+ "--system-prompt",
14558
+ "--append-system-prompt",
14559
+ "--agent",
14560
+ "--agents",
14561
+ "--tools",
14562
+ "--allowedTools",
14563
+ "--disallowedTools"
14564
+ ]);
14565
+ function resolveWorkerPassthroughArgs(engine, commandName = "worker", rawArgs = process.argv.slice(2)) {
14566
+ const commandIndex = rawArgs.findIndex((item, index) => item === commandName && rawArgs[index - 1] === "hook");
14567
+ if (commandIndex < 0) {
14568
+ return [];
14569
+ }
14570
+ const tail = rawArgs.slice(commandIndex + 1);
14571
+ const args = tail[0] === commandName ? tail.slice(1) : tail;
14572
+ const normalizedEngine = String(engine || "codex").trim();
14573
+ const scanArgs = args[0] === normalizedEngine ? args.slice(1) : args;
14574
+ const passthroughArgs = [];
14575
+ for (let index = 0; index < scanArgs.length; index += 1) {
14576
+ const item = scanArgs[index];
14577
+ if (item === "--") {
14578
+ passthroughArgs.push(...scanArgs.slice(index + 1));
14579
+ break;
14580
+ }
14581
+ const key = item.includes("=") ? item.split("=")[0] : item;
14582
+ if (WORKER_BOOLEAN_OPTIONS.has(key)) {
14583
+ continue;
14584
+ }
14585
+ if (WORKER_VALUE_OPTIONS.has(key)) {
14586
+ if (!item.includes("=")) {
14587
+ index += 1;
14588
+ }
14589
+ continue;
14590
+ }
14591
+ passthroughArgs.push(item);
14592
+ }
14593
+ return passthroughArgs;
14594
+ }
14226
14595
  function normalizeClaudeArgs(options) {
14227
14596
  const args = [];
14228
14597
  const pushPair = (flag, value) => {
@@ -14254,6 +14623,8 @@ function normalizeClaudeArgs(options) {
14254
14623
  });
14255
14624
  return args;
14256
14625
  }
14626
+
14627
+ // src/commands/hook/hook-worker.ts
14257
14628
  async function hookWorkerAction(engine, options) {
14258
14629
  const logger3 = createLogger("worker", options.verbose === true);
14259
14630
  const normalizedEngine = (engine || "codex").toLowerCase();
@@ -14263,10 +14634,12 @@ async function hookWorkerAction(engine, options) {
14263
14634
  masterPort: parsePositiveInt(options.masterPort, 0) || void 0,
14264
14635
  sharedSecret: options.sharedSecret,
14265
14636
  connectTimeoutMs: parsePositiveInt(options.connectTimeoutMs, 0) || void 0,
14266
- runnerConfig: normalizedEngine === "claude" ? { claudeArgs: normalizeClaudeArgs(options) } : {}
14637
+ runnerConfig: normalizedEngine === "claude" ? { claudeArgs: normalizeClaudeArgs(options) } : {},
14638
+ manageProcessSignals: true
14267
14639
  });
14268
14640
  try {
14269
14641
  await command.start();
14642
+ await command.waitUntilStopped();
14270
14643
  } catch (error) {
14271
14644
  const msg = error instanceof Error ? error.message : String(error);
14272
14645
  logger3.error(`\u4ECE\u8282\u70B9\u542F\u52A8\u5931\u8D25: ${msg}`);
@@ -14274,23 +14647,98 @@ async function hookWorkerAction(engine, options) {
14274
14647
  }
14275
14648
  }
14276
14649
 
14650
+ // src/commands/hook/hook-local.ts
14651
+ function resolveLocalWorkerMasterHost(masterHost) {
14652
+ const host = String(masterHost || "").trim();
14653
+ if (host === "0.0.0.0" || host === "::" || host === "[::]") {
14654
+ return "127.0.0.1";
14655
+ }
14656
+ return host;
14657
+ }
14658
+ function resolveLocalWorkerOverrides(runtime) {
14659
+ return {
14660
+ masterHost: resolveLocalWorkerMasterHost(runtime.masterHost),
14661
+ masterPort: runtime.masterPort,
14662
+ sharedSecret: runtime.sharedSecret
14663
+ };
14664
+ }
14665
+ async function hookLocalAction(engine, options) {
14666
+ const localLogger = createLogger("local", options.verbose === true);
14667
+ const masterLogger = createLogger("master");
14668
+ const workerLogger = createLogger("worker", options.verbose === true);
14669
+ const normalizedEngine = (engine || "codex").toLowerCase();
14670
+ const master = new HookMasterCommand({
14671
+ logger: masterLogger,
14672
+ masterHost: options.masterHost,
14673
+ masterPort: parsePositiveInt(options.masterPort, 0) || void 0,
14674
+ sharedSecret: options.sharedSecret,
14675
+ claimTimeoutMs: parsePositiveInt(options.claimTimeoutMs, 0) || void 0,
14676
+ manageProcessSignals: false
14677
+ });
14678
+ let worker = null;
14679
+ let stopping = false;
14680
+ const stopAll = async (signal) => {
14681
+ if (stopping) {
14682
+ return;
14683
+ }
14684
+ stopping = true;
14685
+ localLogger.info(`\u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u505C\u6B62\u672C\u673A\u4E00\u4F53\u8282\u70B9...`);
14686
+ if (worker) {
14687
+ await worker.stop(signal);
14688
+ worker = null;
14689
+ }
14690
+ await master.stop(signal);
14691
+ };
14692
+ process.on("SIGINT", async () => {
14693
+ await stopAll("SIGINT");
14694
+ process.exit(0);
14695
+ });
14696
+ process.on("SIGTERM", async () => {
14697
+ await stopAll("SIGTERM");
14698
+ process.exit(0);
14699
+ });
14700
+ try {
14701
+ const runtime = await master.start();
14702
+ const workerOverrides = resolveLocalWorkerOverrides(runtime);
14703
+ worker = new HookWorkerCommand(workerLogger, {
14704
+ engine: normalizedEngine,
14705
+ masterHost: workerOverrides.masterHost,
14706
+ masterPort: workerOverrides.masterPort,
14707
+ sharedSecret: workerOverrides.sharedSecret,
14708
+ connectTimeoutMs: parsePositiveInt(options.connectTimeoutMs, 0) || void 0,
14709
+ runnerConfig: normalizedEngine === "claude" ? { claudeArgs: normalizeClaudeArgs(options) } : {},
14710
+ manageProcessSignals: false
14711
+ });
14712
+ await worker.start();
14713
+ localLogger.info(
14714
+ `\u672C\u673A\u4E00\u4F53\u8282\u70B9\u542F\u52A8\u5B8C\u6210 | engine=${normalizedEngine} | master=${workerOverrides.masterHost}:${workerOverrides.masterPort}`
14715
+ );
14716
+ await worker.waitUntilStopped();
14717
+ } catch (error) {
14718
+ const msg = error instanceof Error ? error.message : String(error);
14719
+ localLogger.error(`\u672C\u673A\u4E00\u4F53\u8282\u70B9\u542F\u52A8\u5931\u8D25: ${msg}`);
14720
+ await stopAll("startup_error");
14721
+ process.exit(1);
14722
+ }
14723
+ }
14724
+
14277
14725
  // src/hook/init/hook-init-command.ts
14278
- import fs28 from "fs";
14279
- import os17 from "os";
14280
- import path27 from "path";
14726
+ import fs29 from "fs";
14727
+ import os18 from "os";
14728
+ import path28 from "path";
14281
14729
  import process11 from "process";
14282
14730
  import { createInterface as createInterface3 } from "readline/promises";
14283
14731
  import chalk9 from "chalk";
14284
14732
 
14285
14733
  // src/hook/init/legacy-ai-hook-migrator.ts
14286
- import fs27 from "fs";
14734
+ import fs28 from "fs";
14287
14735
  function loadLegacyMigrationResult(logger3) {
14288
14736
  const sourcePath = LEGACY_STATE_PATH;
14289
- if (!fs27.existsSync(sourcePath)) {
14737
+ if (!fs28.existsSync(sourcePath)) {
14290
14738
  return { state: null, summary: null };
14291
14739
  }
14292
14740
  try {
14293
- const parsed = parseJsonWithLineComments(fs27.readFileSync(sourcePath, "utf-8"), sourcePath);
14741
+ const parsed = parseJsonWithLineComments(fs28.readFileSync(sourcePath, "utf-8"), sourcePath);
14294
14742
  if (!parsed || typeof parsed !== "object") {
14295
14743
  return { state: null, summary: null };
14296
14744
  }
@@ -14318,6 +14766,7 @@ function countObjectKeys(value) {
14318
14766
 
14319
14767
  // src/hook/init/hook-init-normalizer.ts
14320
14768
  var DEFAULT_MASTER_HOST_ENV = "NIKOU_HOOK_MASTER_HOST";
14769
+ var LOCAL_MASTER_HOST = "127.0.0.1";
14321
14770
  function resolveHookSection2(config) {
14322
14771
  return config.hook ?? config.ai_hook ?? config.aiHook ?? {};
14323
14772
  }
@@ -14377,6 +14826,15 @@ function resolveContextConfigFromHook(hook2) {
14377
14826
  function resolveDefaultMasterHost(env = process.env) {
14378
14827
  return String(env[DEFAULT_MASTER_HOST_ENV] || "").trim();
14379
14828
  }
14829
+ function resolveHookInitDeploymentDefaults(config, env = process.env) {
14830
+ const configuredHost = resolveConfiguredMasterHost(config);
14831
+ const envHost = resolveDefaultMasterHost(env);
14832
+ const masterHost = configuredHost || envHost || LOCAL_MASTER_HOST;
14833
+ return {
14834
+ deploymentMode: isLocalHost(masterHost) ? "local" : "slave",
14835
+ masterHost
14836
+ };
14837
+ }
14380
14838
  function normalizeHookInitConfig(config, defaults = {}) {
14381
14839
  const hook2 = resolveHookSection2(config);
14382
14840
  const appId = String(config.feishu?.app_id || config.feishu?.appId || config.feishu?.appid || "").trim();
@@ -14392,7 +14850,9 @@ function normalizeHookInitConfig(config, defaults = {}) {
14392
14850
  if (!appSecret) missing.push("feishu.app_secret");
14393
14851
  if (!masterHost) missing.push(`hook.master_host \u6216 ${DEFAULT_MASTER_HOST_ENV}`);
14394
14852
  if (!sharedSecret) missing.push("hook.shared_secret");
14395
- if (bindAllowedUserIds.length === 0) missing.push("hook.bind_allowed_user_ids");
14853
+ if (defaults.deploymentMode !== "slave" && bindAllowedUserIds.length === 0) {
14854
+ missing.push("hook.bind_allowed_user_ids");
14855
+ }
14396
14856
  if (missing.length > 0) {
14397
14857
  throw new Error(`\u521D\u59CB\u5316\u6240\u9700\u914D\u7F6E\u7F3A\u5931: ${missing.join(", ")}`);
14398
14858
  }
@@ -14418,7 +14878,8 @@ function normalizeHookInitConfig(config, defaults = {}) {
14418
14878
  // src/hook/init/hook-init-command.ts
14419
14879
  var BOT_OPTIONS = [{ label: "\u59AE\u853B", value: "\u59AE\u853B" }];
14420
14880
  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" }
14881
+ { label: "\u672C\u673A\u4E00\u4F53", value: "local", description: "\u540C\u4E00\u53F0\u673A\u5668\u542F\u52A8\u4E3B\u8282\u70B9\u548C\u672C\u5730\u4ECE\u8282\u70B9" },
14882
+ { label: "\u4ECE\u8282\u70B9", value: "slave", description: "\u8FDE\u63A5\u5DF2\u6709\u8FDC\u7AEF\u4E3B\u8282\u70B9" }
14422
14883
  ];
14423
14884
  var HookInitCommand = class {
14424
14885
  constructor(logger3) {
@@ -14432,22 +14893,32 @@ var HookInitCommand = class {
14432
14893
  const existingAggregateConfig = this.readJson(DEFAULT_CONFIG_PATH);
14433
14894
  const existingMasterConfig = this.readJson(MASTER_CONFIG_PATH);
14434
14895
  const existingWorkerConfig = this.readJson(WORKER_CONFIG_PATH);
14435
- const existingWorkerStatePath = path27.join(STATE_DIR, "worker.json");
14896
+ const existingWorkerStatePath = path28.join(STATE_DIR, "worker.json");
14436
14897
  const existingWorkerState = this.readJson(existingWorkerStatePath);
14437
14898
  const rl = createInterface3({ input: process11.stdin, output: process11.stdout });
14438
14899
  try {
14439
14900
  this.printEnvironmentSummary(source.path, { existingAggregateConfig, existingMasterConfig, existingWorkerConfig }, migration.summary);
14440
14901
  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);
14902
+ const deploymentDefaults = resolveHookInitDeploymentDefaults(source.config);
14903
+ const deploymentDefaultIndex = DEPLOYMENT_OPTIONS.findIndex((item) => item.value === deploymentDefaults.deploymentMode);
14904
+ const deploymentMode = await this.askChoice(
14905
+ rl,
14906
+ "\u7B2C\u4E8C\u6B65\uFF1A\u9009\u62E9\u90E8\u7F72\u65B9\u5F0F",
14907
+ DEPLOYMENT_OPTIONS,
14908
+ deploymentDefaultIndex >= 0 ? deploymentDefaultIndex : 0
14909
+ );
14910
+ const defaultMasterHost = deploymentMode === "local" ? LOCAL_MASTER_HOST : deploymentDefaults.masterHost;
14911
+ const masterHost = await this.askMasterHost(rl, source.config, defaultMasterHost);
14443
14912
  const normalized = this.normalizeSourceConfig(
14444
- mergeHookInitMasterHost(source.config, masterHost)
14913
+ mergeHookInitMasterHost(source.config, masterHost),
14914
+ deploymentMode
14445
14915
  );
14446
14916
  const nextMasterConfig = this.buildMasterConfigPayload(normalized, botName);
14447
14917
  const nextWorkerConfig = this.buildWorkerConfigPayload(normalized, botName);
14448
14918
  const nextWorkerState = this.buildSeedWorkerState({
14449
14919
  botName,
14450
14920
  configPath: WORKER_CONFIG_PATH,
14921
+ deploymentMode,
14451
14922
  normalized,
14452
14923
  existingWorkerState,
14453
14924
  legacyState: migration.state
@@ -14456,6 +14927,7 @@ var HookInitCommand = class {
14456
14927
  nextMasterConfig,
14457
14928
  nextWorkerConfig,
14458
14929
  nextWorkerState,
14930
+ deploymentMode,
14459
14931
  sourcePath: source.path,
14460
14932
  migrationSummary: migration.summary,
14461
14933
  existingAggregateConfig,
@@ -14480,7 +14952,10 @@ var HookInitCommand = class {
14480
14952
  console.log(chalk9.dim(`\u4E3B\u8282\u70B9\u914D\u7F6E: ${MASTER_CONFIG_PATH}`));
14481
14953
  console.log(chalk9.dim(`\u4ECE\u8282\u70B9\u914D\u7F6E: ${WORKER_CONFIG_PATH}`));
14482
14954
  console.log(chalk9.dim(`\u72B6\u6001\u6587\u4EF6: ${existingWorkerStatePath}`));
14483
- console.log(chalk9.dim("\n\u4E0B\u4E00\u6B65: nikou-cli hook worker codex\n"));
14955
+ const nextCommand = deploymentMode === "local" ? "ai-hook local" : "nikou-cli hook worker codex";
14956
+ console.log(chalk9.dim(`
14957
+ \u4E0B\u4E00\u6B65: ${nextCommand}
14958
+ `));
14484
14959
  } finally {
14485
14960
  rl.close();
14486
14961
  }
@@ -14493,11 +14968,11 @@ var HookInitCommand = class {
14493
14968
  resolveSourceConfig() {
14494
14969
  const candidates = [LEGACY_CONFIG_PATH, DEFAULT_CONFIG_PATH, MASTER_CONFIG_PATH, WORKER_CONFIG_PATH];
14495
14970
  for (const candidate of candidates) {
14496
- if (!fs28.existsSync(candidate)) {
14971
+ if (!fs29.existsSync(candidate)) {
14497
14972
  continue;
14498
14973
  }
14499
14974
  try {
14500
- const parsed = parseJsonWithLineComments(fs28.readFileSync(candidate, "utf-8"), candidate);
14975
+ const parsed = parseJsonWithLineComments(fs29.readFileSync(candidate, "utf-8"), candidate);
14501
14976
  if (parsed && typeof parsed === "object") {
14502
14977
  return { config: parsed, path: candidate };
14503
14978
  }
@@ -14508,9 +14983,10 @@ var HookInitCommand = class {
14508
14983
  }
14509
14984
  throw new Error(`\u672A\u627E\u5230\u53EF\u521D\u59CB\u5316\u7684\u914D\u7F6E\u6E90\uFF0C\u8BF7\u5148\u51C6\u5907 ${LEGACY_CONFIG_PATH}\u3001${DEFAULT_CONFIG_PATH} \u6216\u89D2\u8272\u914D\u7F6E\u6587\u4EF6`);
14510
14985
  }
14511
- normalizeSourceConfig(config) {
14986
+ normalizeSourceConfig(config, deploymentMode) {
14512
14987
  return normalizeHookInitConfig(config, {
14513
- defaultMasterHost: resolveDefaultMasterHost()
14988
+ defaultMasterHost: resolveDefaultMasterHost(),
14989
+ deploymentMode
14514
14990
  });
14515
14991
  }
14516
14992
  printEnvironmentSummary(sourcePath, existingConfig, migrationSummary) {
@@ -14559,8 +15035,8 @@ var HookInitCommand = class {
14559
15035
  console.log(chalk9.yellow("\u8BF7\u8F93\u5165 y \u6216 n\u3002\n"));
14560
15036
  }
14561
15037
  }
14562
- async askMasterHost(rl, config) {
14563
- const defaultHost = resolveConfiguredMasterHost(config) || resolveDefaultMasterHost();
15038
+ async askMasterHost(rl, config, fallbackHost) {
15039
+ const defaultHost = fallbackHost || resolveConfiguredMasterHost(config) || resolveDefaultMasterHost();
14564
15040
  while (true) {
14565
15041
  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
15042
  const answer = (await rl.question(chalk9.dim(label))).trim();
@@ -14650,7 +15126,7 @@ var HookInitCommand = class {
14650
15126
  contextConfig: params.normalized.contextConfig,
14651
15127
  slaveConnectionStatus: "offline",
14652
15128
  status: "stopped",
14653
- lastCommand: "nikou-cli hook worker codex"
15129
+ lastCommand: params.deploymentMode === "local" ? "ai-hook local" : "nikou-cli hook worker codex"
14654
15130
  });
14655
15131
  const mergedState = mergeWorkerState(
14656
15132
  initialState,
@@ -14661,7 +15137,7 @@ var HookInitCommand = class {
14661
15137
  mergedState.initializedAt = now;
14662
15138
  mergedState.initMode = "interactive";
14663
15139
  mergedState.migration = params.legacyState ? {
14664
- source: path27.join(os17.homedir(), "ops", "ai-hook.json"),
15140
+ source: path28.join(os18.homedir(), "ops", "ai-hook.json"),
14665
15141
  migratedAt: now,
14666
15142
  legacyBindingsCount: countObjectKeys2(params.legacyState.bindings),
14667
15143
  legacyChatIndexCount: countObjectKeys2(params.legacyState.chatIndex),
@@ -14682,10 +15158,10 @@ var HookInitCommand = class {
14682
15158
  console.log(chalk9.dim(`- \u4ECE\u8282\u70B9\u914D\u7F6E: ${WORKER_CONFIG_PATH}`));
14683
15159
  console.log(chalk9.dim(`- \u98DE\u4E66 AppId: ${String(params.nextMasterConfig.feishu?.app_id || "")}`));
14684
15160
  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`));
15161
+ console.log(chalk9.dim(`- \u90E8\u7F72\u65B9\u5F0F: ${params.deploymentMode === "local" ? "\u672C\u673A\u4E00\u4F53" : "\u4ECE\u8282\u70B9"}`));
14686
15162
  console.log(chalk9.dim(`- \u4E3B\u8282\u70B9: ${String(masterHook.master_host || "")}:${String(masterHook.master_port || "")}`));
14687
15163
  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")}`));
15164
+ console.log(chalk9.dim(`- \u79CD\u5B50\u72B6\u6001: ${path28.join(STATE_DIR, "worker.json")}`));
14689
15165
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E\u7FA4\u7ED1\u5B9A\u6570: ${Object.keys(params.nextWorkerState.bindings || {}).length}`));
14690
15166
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E chatIndex \u6570: ${Object.keys(params.nextWorkerState.chatIndex || {}).length}`));
14691
15167
  console.log(chalk9.dim(`- \u8FC1\u79FB\u540E P2P \u4F1A\u8BDD\u6570: ${countObjectKeys2(params.nextWorkerState.p2pSessions)}`));
@@ -14711,28 +15187,28 @@ var HookInitCommand = class {
14711
15187
  });
14712
15188
  }
14713
15189
  backupIfNeeded(targetPath, prefix) {
14714
- if (!fs28.existsSync(targetPath)) {
15190
+ if (!fs29.existsSync(targetPath)) {
14715
15191
  return;
14716
15192
  }
14717
- const backupDir = path27.join(STATE_DIR, "backups");
15193
+ const backupDir = path28.join(STATE_DIR, "backups");
14718
15194
  ensureDirExists(backupDir);
14719
15195
  const fileName = `${prefix}.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`;
14720
- fs28.copyFileSync(targetPath, path27.join(backupDir, fileName));
15196
+ fs29.copyFileSync(targetPath, path28.join(backupDir, fileName));
14721
15197
  }
14722
15198
  readJson(filePath) {
14723
- if (!fs28.existsSync(filePath)) {
15199
+ if (!fs29.existsSync(filePath)) {
14724
15200
  return null;
14725
15201
  }
14726
15202
  try {
14727
- const parsed = parseJsonWithLineComments(fs28.readFileSync(filePath, "utf-8"), filePath);
15203
+ const parsed = parseJsonWithLineComments(fs29.readFileSync(filePath, "utf-8"), filePath);
14728
15204
  return parsed && typeof parsed === "object" ? parsed : null;
14729
15205
  } catch {
14730
15206
  return null;
14731
15207
  }
14732
15208
  }
14733
15209
  writeJson(filePath, value) {
14734
- ensureDirExists(path27.dirname(filePath));
14735
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2));
15210
+ ensureDirExists(path28.dirname(filePath));
15211
+ fs29.writeFileSync(filePath, JSON.stringify(value, null, 2));
14736
15212
  }
14737
15213
  };
14738
15214
  function countObjectKeys2(value) {
@@ -14756,12 +15232,12 @@ async function hookInitAction() {
14756
15232
  }
14757
15233
 
14758
15234
  // src/commands/hook/hook-log.ts
14759
- import fs30 from "fs";
15235
+ import fs31 from "fs";
14760
15236
  import chalk10 from "chalk";
14761
15237
 
14762
15238
  // src/hook/log-searcher.ts
14763
- import fs29 from "fs";
14764
- import path28 from "path";
15239
+ import fs30 from "fs";
15240
+ import path29 from "path";
14765
15241
  function normalizeString(value) {
14766
15242
  if (value === null || value === void 0) {
14767
15243
  return "";
@@ -14799,8 +15275,8 @@ var HookLogSearcher = class {
14799
15275
  logPaths;
14800
15276
  constructor(options = {}) {
14801
15277
  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")
15278
+ master: options.masterPath || path29.join(LOGS_DIR, "master", "ai-hook-master.service.log"),
15279
+ worker: options.workerPath || path29.join(LOGS_DIR, "worker", "ai-hook-worker.service.log")
14804
15280
  };
14805
15281
  }
14806
15282
  resolveScope(scope) {
@@ -14863,7 +15339,7 @@ var HookLogSearcher = class {
14863
15339
  }
14864
15340
  searchSingleFile(role, query = {}, options = {}) {
14865
15341
  const filePath = this.logPaths[role];
14866
- if (!filePath || !fs29.existsSync(filePath)) {
15342
+ if (!filePath || !fs30.existsSync(filePath)) {
14867
15343
  return {
14868
15344
  role,
14869
15345
  path: filePath,
@@ -14874,7 +15350,7 @@ var HookLogSearcher = class {
14874
15350
  lines: []
14875
15351
  };
14876
15352
  }
14877
- const raw = fs29.readFileSync(filePath, "utf8");
15353
+ const raw = fs30.readFileSync(filePath, "utf8");
14878
15354
  const lines = raw.split(/\r?\n/);
14879
15355
  if (lines.length > 0 && lines[lines.length - 1] === "") {
14880
15356
  lines.pop();
@@ -14992,7 +15468,7 @@ function buildFollowState(file) {
14992
15468
  return {
14993
15469
  role: file.role,
14994
15470
  path: file.path,
14995
- offset: fs30.existsSync(file.path) ? fs30.statSync(file.path).size : 0,
15471
+ offset: fs31.existsSync(file.path) ? fs31.statSync(file.path).size : 0,
14996
15472
  remainder: ""
14997
15473
  };
14998
15474
  }
@@ -15019,10 +15495,10 @@ function flushChunk(state, chunk, queryText) {
15019
15495
  output.forEach((line) => console.log(line));
15020
15496
  }
15021
15497
  function readAppendedChunk(state, queryText) {
15022
- if (!fs30.existsSync(state.path)) {
15498
+ if (!fs31.existsSync(state.path)) {
15023
15499
  return;
15024
15500
  }
15025
- const stat = fs30.statSync(state.path);
15501
+ const stat = fs31.statSync(state.path);
15026
15502
  if (stat.size < state.offset) {
15027
15503
  state.offset = 0;
15028
15504
  state.remainder = "";
@@ -15030,15 +15506,15 @@ function readAppendedChunk(state, queryText) {
15030
15506
  if (stat.size === state.offset) {
15031
15507
  return;
15032
15508
  }
15033
- const fd = fs30.openSync(state.path, "r");
15509
+ const fd = fs31.openSync(state.path, "r");
15034
15510
  try {
15035
15511
  const length = stat.size - state.offset;
15036
15512
  const buffer = Buffer.alloc(length);
15037
- fs30.readSync(fd, buffer, 0, length, state.offset);
15513
+ fs31.readSync(fd, buffer, 0, length, state.offset);
15038
15514
  state.offset = stat.size;
15039
15515
  flushChunk(state, buffer.toString("utf8"), queryText);
15040
15516
  } finally {
15041
- fs30.closeSync(fd);
15517
+ fs31.closeSync(fd);
15042
15518
  }
15043
15519
  }
15044
15520
  async function followLogs(queryText, options = {}) {
@@ -15079,60 +15555,6 @@ async function hookLogAction(query = "", options = {}) {
15079
15555
 
15080
15556
  // src/index.ts
15081
15557
  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
15558
  var program = new Command();
15137
15559
  program.name("nikou-cli").description("CLI to install and manage Agent Skills from GitHub repos").version("0.1.0");
15138
15560
  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 +15583,23 @@ mcp.command("search").argument("<keyword>", "Search keyword").description("Searc
15161
15583
  await mcpSearchCommand(keyword, options);
15162
15584
  });
15163
15585
  var hook = program.command("hook").description("\u4E3B\u4ECE\u8282\u70B9\u4EFB\u52A1\u5206\u53D1\u4E0E\u6267\u884C\uFF08\u98DE\u4E66\u96C6\u6210\uFF09");
15586
+ function addWorkerRuntimeOptions(command) {
15587
+ 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");
15588
+ }
15164
15589
  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
15590
  await hookMasterAction(options);
15166
15591
  });
15167
15592
  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
15593
  await hookInitAction();
15169
15594
  });
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);
15595
+ 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) => {
15596
+ const passthroughArgs = resolveWorkerPassthroughArgs(engine, "worker");
15172
15597
  await hookWorkerAction(engine, { ...options, passthroughArgs });
15173
15598
  });
15599
+ 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) => {
15600
+ const passthroughArgs = resolveWorkerPassthroughArgs(engine, "local");
15601
+ await hookLocalAction(engine, { ...options, passthroughArgs });
15602
+ });
15174
15603
  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
15604
  await hookLogAction(query, options);
15176
15605
  });