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