@keyou007/agent-bot 0.1.0 → 0.1.2
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/CHANGELOG.md +11 -0
- package/README.md +7 -1
- package/README.zh.md +7 -1
- package/config.example.yaml +0 -2
- package/dist/cli/Initializer.js +155 -11
- package/dist/cli/Initializer.js.map +1 -1
- package/dist/cli/LocalControlClient.js +14 -1
- package/dist/cli/LocalControlClient.js.map +1 -1
- package/dist/cli/help.js +91 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/packageVersion.js +10 -0
- package/dist/cli/packageVersion.js.map +1 -0
- package/dist/cli.js +77 -83
- package/dist/cli.js.map +1 -1
- package/dist/config/loadConfig.js +0 -2
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/schema.js +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +79 -13
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/feishu/transport.js +4 -11
- package/dist/feishu/transport.js.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/startup/startFeishu.js +2 -1
- package/dist/startup/startFeishu.js.map +1 -1
- package/dist/supervisor.js +7 -4
- package/dist/supervisor.js.map +1 -1
- package/docs/technical-reference.md +19 -18
- package/docs/technical-reference.zh.md +19 -18
- package/npm-shrinkwrap.json +2 -2
- package/package.json +3 -2
- package/skills/agent-bot/SKILL.md +4 -1
- package/src/cli/Initializer.ts +164 -11
- package/src/cli/LocalControlClient.ts +12 -1
- package/src/cli/help.ts +99 -0
- package/src/cli/packageVersion.ts +14 -0
- package/src/cli.ts +84 -79
- package/src/config/loadConfig.ts +0 -2
- package/src/config/schema.ts +1 -1
- package/src/feishu/FeishuConnector.ts +89 -12
- package/src/feishu/transport.ts +7 -16
- package/src/index.ts +14 -3
- package/src/startup/startFeishu.ts +2 -0
- package/src/supervisor.ts +7 -4
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { ConsoleFeishuClient } from "./feishu/ConsoleFeishuClient.js";
|
|
|
15
15
|
import { FeishuConnector } from "./feishu/FeishuConnector.js";
|
|
16
16
|
import { FeishuMessageClient } from "./feishu/FeishuMessageClient.js";
|
|
17
17
|
import { FeishuTurnPresenter } from "./feishu/FeishuTurnPresenter.js";
|
|
18
|
-
import {
|
|
18
|
+
import { requireServerFeishuTransport } from "./feishu/transport.js";
|
|
19
19
|
import { createLogger } from "./logging/logger.js";
|
|
20
20
|
import { OutboundRouter } from "./presentation/OutboundRouter.js";
|
|
21
21
|
import { ProxySessionController } from "./proxy/ProxySessionController.js";
|
|
@@ -35,6 +35,7 @@ const startupReason = process.env.AGENT_BOT_RESTART_REASON?.trim()
|
|
|
35
35
|
|| (supervised ? "Supervisor 启动" : "直接启动");
|
|
36
36
|
const consoleOnly = process.env.AGENT_BOT_CONSOLE_ONLY === "1";
|
|
37
37
|
const config = loadConfig();
|
|
38
|
+
const transport = consoleOnly ? "console" : requireServerFeishuTransport(config.feishu);
|
|
38
39
|
const logger = createLogger(config);
|
|
39
40
|
const store = new StateStore(config.storage.sqlitePath);
|
|
40
41
|
const acpProcessManager = new AcpProcessManager(logger);
|
|
@@ -44,13 +45,13 @@ const codexAgent = Object.values(config.agents).find((agent) => agent.kind === "
|
|
|
44
45
|
const codexProcessManager = new CodexProcessManager(codexAgent?.command ?? "codex", codexAgent?.args ?? ["app-server", "--listen", "stdio://"], codexAgent?.env ?? {}, logger);
|
|
45
46
|
const codexRuntime = new CodexRuntime(codexProcessManager, logger);
|
|
46
47
|
const runtimes = new AgentRuntimeRegistry({ acp: acpRuntime, codex: codexRuntime });
|
|
47
|
-
const transport = consoleOnly ? "console" : resolveFeishuTransport(config.feishu);
|
|
48
48
|
const routes = [];
|
|
49
49
|
let feishuConnector;
|
|
50
50
|
let consoleConnector;
|
|
51
51
|
let startupNotifier;
|
|
52
52
|
let safeRestartNotifier;
|
|
53
53
|
let controlServer;
|
|
54
|
+
let serverReady = false;
|
|
54
55
|
const feishuOutbound = transport === "sdk" ? new FeishuMessageClient(config, logger) : undefined;
|
|
55
56
|
if (feishuOutbound) {
|
|
56
57
|
const renderer = new CardRenderer();
|
|
@@ -109,11 +110,12 @@ const startControlServer = async () => {
|
|
|
109
110
|
logger.info({ endpoint: controlEndpoint(config.storage.sqlitePath) }, "Local Agent Bot control endpoint started.");
|
|
110
111
|
};
|
|
111
112
|
if (feishuConnector && startupNotifier) {
|
|
112
|
-
await startFeishu(feishuConnector, startupNotifier, processStartedAt, startupReason, startControlServer);
|
|
113
|
+
await startFeishu(feishuConnector, startupNotifier, processStartedAt, startupReason, startControlServer, () => { serverReady = true; });
|
|
113
114
|
}
|
|
114
115
|
else {
|
|
115
116
|
await startControlServer();
|
|
116
117
|
await feishuConnector?.start();
|
|
118
|
+
serverReady = true;
|
|
117
119
|
}
|
|
118
120
|
consoleConnector?.start();
|
|
119
121
|
async function requestRestart(contextKey) {
|
|
@@ -141,6 +143,8 @@ async function handleControlRequest(request) {
|
|
|
141
143
|
return {
|
|
142
144
|
ok: true,
|
|
143
145
|
data: {
|
|
146
|
+
ready: serverReady,
|
|
147
|
+
phase: serverReady ? "ready" : "connecting_feishu",
|
|
144
148
|
pid: process.pid,
|
|
145
149
|
startedAt: processStartedAt.toISOString(),
|
|
146
150
|
supervised,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,eAAe,EAA6C,MAAM,0BAA0B,CAAC;AACtG,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,eAAe,EAA6C,MAAM,0BAA0B,CAAC;AACtG,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAsB,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,gCAAgC,EAAE,MAAM,wCAAwC,CAAC;AAC1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAE3E,MAAM,gBAAgB,GAAG,IAAI,IAAI,EAAE,CAAC;AACpC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,GAAG,CAAC;AAC5D,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE;OAC7D,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,GAAG,CAAC;AAC/D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,4BAA4B,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACxF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AACpC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAExD,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACxD,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAC;AACnF,MAAM,UAAU,GAAG,IAAI,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;AAC5D,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;AACxF,MAAM,mBAAmB,GAAG,IAAI,mBAAmB,CACjD,UAAU,EAAE,OAAO,IAAI,OAAO,EAC9B,UAAU,EAAE,IAAI,IAAI,CAAC,YAAY,EAAE,UAAU,EAAE,UAAU,CAAC,EAC1D,UAAU,EAAE,GAAG,IAAI,EAAE,EACrB,MAAM,CACP,CAAC;AACF,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;AACnE,MAAM,QAAQ,GAAG,IAAI,oBAAoB,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;AAEpF,MAAM,MAAM,GAAoB,EAAE,CAAC;AACnC,IAAI,eAA4C,CAAC;AACjD,IAAI,gBAA8C,CAAC;AACnD,IAAI,eAA4C,CAAC;AACjD,IAAI,mBAAoD,CAAC;AACzD,IAAI,aAA6C,CAAC;AAClD,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB,MAAM,cAAc,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjG,IAAI,cAAc,EAAE,CAAC;IACnB,MAAM,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,mBAAmB,CAAC,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE;QACzE,gBAAgB,EAAE,KAAK;QACvB,aAAa,EAAE,GAAG;QAClB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,uCAAuC,CAAC;KACpF,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC,CAAC;IAClH,MAAM,gBAAgB,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAM,CAAC;IAChD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrD,MAAM,gBAAgB,GAAG,IAAI,uBAAuB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACtE,eAAe,GAAG,IAAI,eAAe,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE;QAC7E,gBAAgB;QAChB,iBAAiB,EAAE,YAAY,EAAE,KAAK,IAAI,gBAAgB;QAC1D,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QACtC,aAAa,EAAE,YAAY,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;KAC1E,EAAE,gBAAgB,CAAC,CAAC;IACrB,mBAAmB,GAAG,IAAI,mBAAmB,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,cAAc,GAAG,WAAW,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,SAAS,KAAK,SAAS,CAAC;AACxF,MAAM,eAAe,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,mBAAmB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/E,IAAI,eAAe,EAAE,CAAC;IACpB,MAAM,CAAC,IAAI,CAAC;QACV,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC;QAC1D,QAAQ,EAAE,eAAe;QACzB,SAAS,EAAE,IAAI,oBAAoB,CAAC,eAAe,CAAC;KACrD,CAAC,CAAC;AACL,CAAC;AAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;IAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;AACzF,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AAC5C,IAAI,YAAY,GAAG,KAAK,CAAC;AACzB,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAC7B,MAAM,UAAU,GAAG,IAAI,sBAAsB,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE;IACvF,UAAU;IACV,OAAO,EAAE,cAAc;CACxB,CAAC,CAAC;AACH,MAAM,WAAW,GAAG,IAAI,oBAAoB,CAAC;IAC3C,YAAY,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,sBAAsB,EAAE;IAClD,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC;IAC5C,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAmB,EAAE,MAAM,CAAC,MAAM,CAAC;IACzD,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,wCAAwC,CAAC;CAC3F,CAAC,CAAC;AAEH,IAAI,cAAc;IAAE,eAAe,GAAG,IAAI,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AACtF,IAAI,cAAc;IAAE,gBAAgB,GAAG,IAAI,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAEhF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9C,MAAM,kBAAkB,GAAG,KAAK,IAAmB,EAAE;IACnD,IAAI,WAAW,IAAI,aAAa;QAAE,OAAO;IACzC,aAAa,GAAG,IAAI,kBAAkB,CACpC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAC1C,oBAAoB,CACrB,CAAC;IACF,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC;IAC5B,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,2CAA2C,CAAC,CAAC;AACrH,CAAC,CAAC;AAEF,IAAI,eAAe,IAAI,eAAe,EAAE,CAAC;IACvC,MAAM,WAAW,CACf,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,GAAG,EAAE,GAAG,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAC9B,CAAC;AACJ,CAAC;KAAM,CAAC;IACN,MAAM,kBAAkB,EAAE,CAAC;IAC3B,MAAM,eAAe,EAAE,KAAK,EAAE,CAAC;IAC/B,WAAW,GAAG,IAAI,CAAC;AACrB,CAAC;AACD,gBAAgB,EAAE,KAAK,EAAE,CAAC;AAE1B,KAAK,UAAU,cAAc,CAAC,UAAkB;IAC9C,MAAM,eAAe,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;AACxD,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,UAAmB;IAChE,IAAI,gBAAgB,EAAE,CAAC;QACrB,IAAI,UAAU;YAAE,MAAM,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACnG,OAAO;IACT,CAAC;IACD,gBAAgB,GAAG,IAAI,CAAC;IACxB,WAAW,CAAC,MAAM,EAAE,CAAC;IACrB,MAAM,mBAAmB,EAAE,KAAK,EAAE,CAAC;IACnC,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,gCAAgC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YAC7F,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,yCAAyC,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;IACL,CAAC;IACD,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;AAChF,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,OAAuB;IACzD,QAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;QACvB,KAAK,QAAQ;YACX,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE;oBACJ,KAAK,EAAE,WAAW;oBAClB,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB;oBAClD,GAAG,EAAE,OAAO,CAAC,GAAG;oBAChB,SAAS,EAAE,gBAAgB,CAAC,WAAW,EAAE;oBACzC,UAAU;oBACV,oBAAoB,EAAE,WAAW,CAAC,SAAS;oBAC3C,iBAAiB,EAAE,WAAW,CAAC,aAAa;oBAC5C,QAAQ,EAAE,KAAK,CAAC,sBAAsB,EAAE;iBACzC;aACF,CAAC;QACJ,KAAK,gBAAgB;YACnB,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,cAAc,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBAC5D,OAAO;oBACL,EAAE,EAAE,IAAI;oBACR,OAAO,EAAE,cAAc;wBACrB,CAAC,CAAC,gCAAgC;wBAClC,CAAC,CAAC,oBAAoB;iBACzB,CAAC;YACJ,CAAC;YACD,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;QAC3C,KAAK,aAAa;YAChB,WAAW,CAAC,MAAM,EAAE,CAAC;YACrB,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC,CAAC;YACpD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;QAC1D,KAAK,aAAa;YAChB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC,oBAAoB,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC3F,KAAK,WAAW;YACd,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC,eAAe,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QACzF,KAAK,YAAY;YACf,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC,mBAAmB,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5G,KAAK,aAAa;YAChB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC,qBAAqB,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;IAC/G,CAAC;AACH,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,QAAgB,EAAE,aAAsB;IAC9D,IAAI,YAAY;QAAE,OAAO;IACzB,YAAY,GAAG,IAAI,CAAC;IACpB,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,0BAA0B,CAAC,CAAC;IACtD,WAAW,CAAC,MAAM,EAAE,CAAC;IACrB,eAAe,EAAE,IAAI,EAAE,CAAC;IACxB,gBAAgB,EAAE,IAAI,EAAE,CAAC;IACzB,UAAU,CAAC,KAAK,EAAE,CAAC;IACnB,MAAM,OAAO,CAAC,IAAI,CAAC;QACjB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,6BAA6B,CAAC,CAAC;QACpG,KAAK,CAAC,KAAK,CAAC;KACb,CAAC,CAAC;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjB,iBAAiB,CAAC,OAAO,EAAE,CAAC;IAC5B,MAAM,aAAa,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,yCAAyC,CAAC,CAAC,CAAC;IAC1H,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,IAAI,aAAa;QAAE,0BAA0B,CAAC,aAAa,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,0BAA0B,CAAC,aAAqB;IACvD,MAAM,eAAe,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACnF,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,eAAe,CAAC,EAAE;QAC5D,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,IAAI;QACjB,KAAK,EAAE,QAAQ;QACf,GAAG,EAAE,gCAAgC,CAAC,aAAa,CAAC;KACrD,CAAC,CAAC;IACH,UAAU,CAAC,KAAK,EAAE,CAAC;AACrB,CAAC;AAED,SAAS,KAAK,CAAC,YAAoB;IACjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export async function startFeishu(connector, notifier, startedAt, restartReason, prepare) {
|
|
1
|
+
export async function startFeishu(connector, notifier, startedAt, restartReason, prepare, onConnected) {
|
|
2
2
|
await prepare?.();
|
|
3
3
|
await connector.start();
|
|
4
|
+
onConnected?.();
|
|
4
5
|
await notifier.notify(startedAt, restartReason);
|
|
5
6
|
}
|
|
6
7
|
//# sourceMappingURL=startFeishu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startFeishu.js","sourceRoot":"","sources":["../../src/startup/startFeishu.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAmC,EACnC,QAAmC,EACnC,SAAe,EACf,aAAqB,EACrB,OAA6B;
|
|
1
|
+
{"version":3,"file":"startFeishu.js","sourceRoot":"","sources":["../../src/startup/startFeishu.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAmC,EACnC,QAAmC,EACnC,SAAe,EACf,aAAqB,EACrB,OAA6B,EAC7B,WAAwB;IAExB,MAAM,OAAO,EAAE,EAAE,CAAC;IAClB,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;IACxB,WAAW,EAAE,EAAE,CAAC;IAChB,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAClD,CAAC"}
|
package/dist/supervisor.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import {
|
|
3
|
+
import { isServerReachable } from "./cli/LocalControlClient.js";
|
|
4
4
|
import { controlEndpoint } from "./cli/controlProtocol.js";
|
|
5
5
|
import { loadConfig } from "./config/loadConfig.js";
|
|
6
|
+
import { requireServerFeishuTransport } from "./feishu/transport.js";
|
|
6
7
|
import { crashRestartDelayMs, describeRestartReason, INTENTIONAL_RESTART_DELAY_MS, RESTART_EXIT_CODE, STABLE_UPTIME_MS, STOP_EXIT_CODE, } from "./supervision/restartPolicy.js";
|
|
7
8
|
const childEntry = fileURLToPath(new URL("./index.js", import.meta.url));
|
|
8
|
-
const
|
|
9
|
+
const config = loadConfig();
|
|
10
|
+
requireServerFeishuTransport(config.feishu);
|
|
11
|
+
const sqlitePath = config.storage.sqlitePath;
|
|
9
12
|
const serverEndpoint = controlEndpoint(sqlitePath);
|
|
10
13
|
let child;
|
|
11
14
|
let restartTimer;
|
|
@@ -15,7 +18,7 @@ let nextStartReason = process.env.AGENT_BOT_RESTART_REASON?.trim() || "Superviso
|
|
|
15
18
|
async function startChild() {
|
|
16
19
|
if (stopping || child)
|
|
17
20
|
return;
|
|
18
|
-
if (await
|
|
21
|
+
if (await isServerReachable(serverEndpoint)) {
|
|
19
22
|
writeSupervisorLog("existing_server_detected", { endpoint: serverEndpoint });
|
|
20
23
|
process.exit(0);
|
|
21
24
|
return;
|
|
@@ -50,7 +53,7 @@ async function handleChildExit(code, signal, startedAt) {
|
|
|
50
53
|
process.exit(0);
|
|
51
54
|
return;
|
|
52
55
|
}
|
|
53
|
-
if (await
|
|
56
|
+
if (await isServerReachable(serverEndpoint)) {
|
|
54
57
|
writeSupervisorLog("duplicate_supervisor_stopped", { endpoint: serverEndpoint });
|
|
55
58
|
process.exit(0);
|
|
56
59
|
return;
|
package/dist/supervisor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -60,10 +60,12 @@ Credential behavior:
|
|
|
60
60
|
- Environment values take precedence over values in `~/.agent-bot/.env`.
|
|
61
61
|
- Both `FEISHU_APP_ID` and `FEISHU_APP_SECRET` are required.
|
|
62
62
|
- Complete credentials are preserved unless `--reconfigure-feishu` is used.
|
|
63
|
-
-
|
|
64
|
-
- Newly registered credentials are
|
|
63
|
+
- Missing or incomplete credentials cause a new app registration.
|
|
64
|
+
- Newly registered credentials are fsynced, atomically replaced in `.env`, and read back before configuration begins.
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
Initialization holds `~/.agent-bot/init.lock` so concurrent commands cannot create multiple apps, including commands that use different config paths. A lock left by a dead process is recovered on the next run, and credential temporary files left by an interrupted write are removed.
|
|
67
|
+
|
|
68
|
+
When complete credentials are absent, initialization uses Feishu one-click registration and reports its verification URL as text and a QR code. In-progress registration codes are not resumed: if the process exits before the complete credential pair is persisted, the next run starts a new app registration. After credentials are persisted, initialization audits the currently published app version and can safely resume that audit after interruption.
|
|
67
69
|
|
|
68
70
|
Missing app configuration is handled in two stages:
|
|
69
71
|
|
|
@@ -103,10 +105,8 @@ The complete example is [config.example.yaml](../config.example.yaml). The main
|
|
|
103
105
|
|
|
104
106
|
```yaml
|
|
105
107
|
feishu:
|
|
106
|
-
transport: "auto"
|
|
107
108
|
appId: "${FEISHU_APP_ID}"
|
|
108
109
|
appSecret: "${FEISHU_APP_SECRET}"
|
|
109
|
-
useConsoleWhenMissingCredentials: true
|
|
110
110
|
|
|
111
111
|
console:
|
|
112
112
|
enabled: true
|
|
@@ -131,11 +131,7 @@ logging:
|
|
|
131
131
|
path: "./logs/agent-bot.log"
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
- `auto`: use the SDK when credentials exist; otherwise follow `useConsoleWhenMissingCredentials`
|
|
137
|
-
- `sdk`: require Feishu credentials and start the WebSocket client
|
|
138
|
-
- `console`: disable Feishu transport
|
|
134
|
+
`agent-bot server start` requires both Feishu credentials. The command waits for the SDK's persistent WebSocket connection before reporting the server as ready. Missing credentials fail startup with an initialization hint. `agent-bot console` is the explicit local-only path and does not require Feishu credentials.
|
|
139
135
|
|
|
140
136
|
At least one agent must be configured. `defaults.agent` must name a configured agent.
|
|
141
137
|
|
|
@@ -261,24 +257,29 @@ Package lifecycle:
|
|
|
261
257
|
- `prepack` builds a clean `dist/` and validates the tarball manifest.
|
|
262
258
|
- `npm run package:smoke` packs the project, installs the tarball into a temporary directory, runs the CLI, and performs Console-only initialization.
|
|
263
259
|
|
|
264
|
-
|
|
260
|
+
Prepare the next patch release from a clean worktree:
|
|
265
261
|
|
|
266
262
|
```powershell
|
|
267
|
-
npm
|
|
268
|
-
npm
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
npm publish --access public
|
|
263
|
+
npm run release
|
|
264
|
+
git add package.json npm-shrinkwrap.json CHANGELOG.md
|
|
265
|
+
git commit -m "release: v0.1.1"
|
|
266
|
+
git push origin master
|
|
272
267
|
```
|
|
273
268
|
|
|
274
|
-
|
|
269
|
+
`npm run release` increments the patch version by default and moves the current `Unreleased` entries into a dated version section. Use `npm run release -- minor`, `npm run release -- major`, or `npm run release -- 0.2.0` to select another stable version. The command refuses to modify a dirty worktree or publish an empty changelog.
|
|
270
|
+
|
|
271
|
+
No local publish command is required after the push. When CI succeeds for a first-party push to `master`, `publish.yml` checks the package version against npm. An existing version is skipped successfully. An unpublished stable version requires a matching `CHANGELOG.md` section, then runs verification and the package smoke test before publishing. After npm accepts the package, the workflow creates the matching `v<package version>` GitHub Release.
|
|
272
|
+
|
|
273
|
+
The workflow can also be retried manually from **GitHub Actions → Publish to npm → Run workflow** on `master`. Pull requests, forked repositories, failed CI runs, and pushes to other branches cannot enter the publish job.
|
|
274
|
+
|
|
275
|
+
The npm Trusted Publisher is configured for:
|
|
275
276
|
|
|
276
277
|
- GitHub owner: `keyou`
|
|
277
278
|
- Repository: `agent-bot`
|
|
278
279
|
- Workflow: `publish.yml`
|
|
279
280
|
- Allowed action: `npm publish`
|
|
280
281
|
|
|
281
|
-
The publish workflow
|
|
282
|
+
The publish workflow uses GitHub OIDC instead of a long-lived npm token and publishes from a GitHub-hosted Node.js 24 runner.
|
|
282
283
|
|
|
283
284
|
## Development And Source Installation
|
|
284
285
|
|
|
@@ -60,10 +60,12 @@ Agent Bot 是基于 Node.js 22+、ESM 和 TypeScript 的应用,主要组件如
|
|
|
60
60
|
- 进程环境变量优先于 `~/.agent-bot/.env`
|
|
61
61
|
- `FEISHU_APP_ID` 和 `FEISHU_APP_SECRET` 必须同时存在
|
|
62
62
|
- 除非使用 `--reconfigure-feishu`,完整凭据不会被替换
|
|
63
|
-
-
|
|
64
|
-
-
|
|
63
|
+
- 缺少凭据或只有一项时会重新创建应用
|
|
64
|
+
- 新创建的凭据经过 fsync、原子替换写入 `.env`,并在配置权限前读回校验
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
初始化期间会持有 `~/.agent-bot/init.lock`,避免使用不同配置路径的并发命令重复创建应用。进程异常退出遗留的锁会在下次运行时恢复,凭据写入中断留下的临时文件也会被清理。
|
|
67
|
+
|
|
68
|
+
没有完整凭据时,初始化会启动飞书一键创建流程,并以文本和二维码显示验证链接。注册中的设备码不会恢复:如果进程在完整凭据持久化前退出,下次会重新创建应用。凭据持久化后会检查应用当前已发布版本;这一权限审计阶段中断后可以安全继续。
|
|
67
69
|
|
|
68
70
|
缺失配置分两阶段处理:
|
|
69
71
|
|
|
@@ -103,10 +105,8 @@ Agent Bot 是基于 Node.js 22+、ESM 和 TypeScript 的应用,主要组件如
|
|
|
103
105
|
|
|
104
106
|
```yaml
|
|
105
107
|
feishu:
|
|
106
|
-
transport: "auto"
|
|
107
108
|
appId: "${FEISHU_APP_ID}"
|
|
108
109
|
appSecret: "${FEISHU_APP_SECRET}"
|
|
109
|
-
useConsoleWhenMissingCredentials: true
|
|
110
110
|
|
|
111
111
|
console:
|
|
112
112
|
enabled: true
|
|
@@ -131,11 +131,7 @@ logging:
|
|
|
131
131
|
path: "./logs/agent-bot.log"
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
- `auto`:存在凭据时使用 SDK,否则按 `useConsoleWhenMissingCredentials` 决定是否回退 Console
|
|
137
|
-
- `sdk`:要求飞书凭据并启动长连接客户端
|
|
138
|
-
- `console`:禁用飞书传输
|
|
134
|
+
`agent-bot server start` 要求同时配置飞书 `appId` 和 `appSecret`。命令会等待 SDK 的 WebSocket 长连接建立后才报告 Server 就绪;缺少凭据时启动失败并提示先初始化。`agent-bot console` 是明确的纯本地入口,不需要飞书凭据。
|
|
139
135
|
|
|
140
136
|
必须至少配置一个 Agent,`defaults.agent` 必须指向已配置的 Agent。
|
|
141
137
|
|
|
@@ -261,24 +257,29 @@ CLI 随包发布 `npm-shrinkwrap.json`,以固定传递运行依赖;直接运
|
|
|
261
257
|
- `prepack` 清理并构建 `dist/`,然后检查 tarball 清单
|
|
262
258
|
- `npm run package:smoke` 打包项目、在临时目录安装 tarball、运行 CLI,并执行仅 Console 的初始化
|
|
263
259
|
|
|
264
|
-
|
|
260
|
+
在干净工作树中准备下一个 patch 版本:
|
|
265
261
|
|
|
266
262
|
```powershell
|
|
267
|
-
npm
|
|
268
|
-
npm
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
npm publish --access public
|
|
263
|
+
npm run release
|
|
264
|
+
git add package.json npm-shrinkwrap.json CHANGELOG.md
|
|
265
|
+
git commit -m "release: v0.1.1"
|
|
266
|
+
git push origin master
|
|
272
267
|
```
|
|
273
268
|
|
|
274
|
-
|
|
269
|
+
`npm run release` 默认递增 patch 版本,并把当前 `Unreleased` 条目归档到带日期的版本小节。可以使用 `npm run release -- minor`、`npm run release -- major` 或 `npm run release -- 0.2.0` 指定其他稳定版本。工作树不干净或 `Unreleased` 没有内容时,命令会拒绝修改文件。
|
|
270
|
+
|
|
271
|
+
推送完成后无需再执行本地发布命令。本仓库自身对 `master` 的 push 通过 CI 后,`publish.yml` 会查询 npm 中的包版本:版本已存在时正常跳过;发现尚未发布的稳定版本时,要求 `CHANGELOG.md` 存在匹配小节,并在发布前再次执行完整验证和包安装 smoke test。npm 接受新包后,workflow 自动创建对应的 `v<包版本>` GitHub Release。
|
|
272
|
+
|
|
273
|
+
需要补跑时,可以在 **GitHub Actions → Publish to npm → Run workflow** 中选择 `master` 手动执行。Pull Request、外部 fork、CI 失败以及其他分支的 push 都不能进入发布 job。
|
|
274
|
+
|
|
275
|
+
npm Trusted Publisher 配置为:
|
|
275
276
|
|
|
276
277
|
- GitHub 所有者:`keyou`
|
|
277
278
|
- 仓库:`agent-bot`
|
|
278
279
|
- Workflow:`publish.yml`
|
|
279
280
|
- 允许操作:`npm publish`
|
|
280
281
|
|
|
281
|
-
|
|
282
|
+
发布 workflow 使用 GitHub OIDC,不保存长期 npm token,并从 GitHub 托管的 Node.js 24 runner 发布。
|
|
282
283
|
|
|
283
284
|
## 开发与源码安装
|
|
284
285
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyou007/agent-bot",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@keyou007/agent-bot",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.2",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@agentclientprotocol/sdk": "1.2.1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyou007/agent-bot",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Using Codex in Lark/Feishu feels just like using native Codex.",
|
|
6
6
|
"license": "GPL-3.0-only",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"prepack": "npm run build && node scripts/check-package.mjs",
|
|
55
55
|
"prepublishOnly": "npm run verify",
|
|
56
56
|
"package:check": "npm run prepack",
|
|
57
|
-
"package:smoke": "node scripts/smoke-package.mjs"
|
|
57
|
+
"package:smoke": "node scripts/smoke-package.mjs",
|
|
58
|
+
"release": "node scripts/prepare-release.mjs"
|
|
58
59
|
},
|
|
59
60
|
"dependencies": {
|
|
60
61
|
"@agentclientprotocol/sdk": "1.2.1",
|
|
@@ -50,18 +50,21 @@ Initialization creates the Agent Bot home, `config.yaml`, `.env`, `data/`, and `
|
|
|
50
50
|
|
|
51
51
|
When running `init` on the user's behalf, relay every exact authorization or incremental-configuration URL and let the user complete it in their own browser. The command waits only for core message receipt and delivery capabilities. Optional capabilities such as group creation, group-title synchronization, reactions, images, and card actions do not block initialization; relay the final affected-feature warnings when they remain unavailable. Neither URL contains the app secret, and the secret itself is never printed.
|
|
52
52
|
|
|
53
|
-
Existing complete Feishu credentials are preserved but still audited and repaired when needed. Use `agent-bot init --skip-feishu` only for console-only initialization because it skips both registration and configuration auditing. Use `agent-bot init --reconfigure-feishu` only when the user explicitly wants to replace existing credentials. The command respects `AGENT_BOT_HOME`, `AGENT_BOT_CONFIG`, and `--config <path>`. Use `agent-bot init --json` when structured final output helps.
|
|
53
|
+
Existing complete Feishu credentials are preserved but still audited and repaired when needed. Missing or incomplete credentials mean app creation did not complete, so a later `init` starts a new registration instead of resuming the previous device code. Once both credentials are durably saved, an interrupted permission audit resumes against the same remote app. `init` also holds a local lock to prevent concurrent registrations. Use `agent-bot init --skip-feishu` only for console-only initialization because it skips both registration and configuration auditing. Use `agent-bot init --reconfigure-feishu` only when the user explicitly wants to replace existing complete credentials. The command respects `AGENT_BOT_HOME`, `AGENT_BOT_CONFIG`, and `--config <path>`. Use `agent-bot init --json` when structured final output helps.
|
|
54
54
|
|
|
55
55
|
## Start with inspection
|
|
56
56
|
|
|
57
57
|
Verify the CLI and current service state before changing anything:
|
|
58
58
|
|
|
59
59
|
```powershell
|
|
60
|
+
agent-bot --version
|
|
60
61
|
agent-bot --help
|
|
61
62
|
agent-bot server status
|
|
62
63
|
agent-bot task list
|
|
63
64
|
```
|
|
64
65
|
|
|
66
|
+
`agent-bot --help` uses Chinese when the system locale is Chinese and English otherwise.
|
|
67
|
+
|
|
65
68
|
Add `--json` to status and list commands when machine-readable output helps. If `agent-bot` is unavailable, report that the CLI is not installed or linked instead of guessing process state.
|
|
66
69
|
|
|
67
70
|
By default Agent Bot stores user-owned configuration and runtime state in `~/.agent-bot`: config at `~/.agent-bot/config.yaml`, environment variables at `~/.agent-bot/.env`, SQLite state at `~/.agent-bot/data/agent-bot.sqlite`, logs at `~/.agent-bot/logs/agent-bot.log`, and inbound image cache next to the SQLite database. `AGENT_BOT_HOME` changes this root. Use `--config <path>` only when controlling a non-default configuration.
|
package/src/cli/Initializer.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
2
4
|
import path from "node:path";
|
|
3
5
|
import { fileURLToPath } from "node:url";
|
|
4
6
|
import { parse as parseDotEnv } from "dotenv";
|
|
@@ -44,8 +46,15 @@ export interface FeishuCredentials {
|
|
|
44
46
|
appSecret: string;
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
export interface InitializationLock {
|
|
50
|
+
path: string;
|
|
51
|
+
release(): void;
|
|
52
|
+
}
|
|
53
|
+
|
|
47
54
|
const DEFAULT_CONFIG_TEMPLATE = fileURLToPath(new URL("../../config.example.yaml", import.meta.url));
|
|
48
55
|
const DEFAULT_ENV_TEMPLATE = fileURLToPath(new URL("../../.env.example", import.meta.url));
|
|
56
|
+
const INITIALIZATION_LOCK_NAME = "init.lock";
|
|
57
|
+
const MAX_INITIALIZATION_LOCK_AGE_MS = 30 * 60_000;
|
|
49
58
|
|
|
50
59
|
export function initializeAgentBot(options: InitializationOptions = {}): InitializationResult {
|
|
51
60
|
const env = options.env ?? process.env;
|
|
@@ -63,14 +72,7 @@ export function initializeAgentBot(options: InitializationOptions = {}): Initial
|
|
|
63
72
|
const home = ensureDirectory(homePath);
|
|
64
73
|
fs.mkdirSync(configDirectory, { recursive: true });
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
if (fs.existsSync(configPath)) {
|
|
68
|
-
config = { path: configPath, status: "existing" };
|
|
69
|
-
} else {
|
|
70
|
-
fs.copyFileSync(configTemplatePath, configPath, fs.constants.COPYFILE_EXCL);
|
|
71
|
-
config = { path: configPath, status: "created" };
|
|
72
|
-
}
|
|
73
|
-
|
|
75
|
+
const config = copyIfMissing(configTemplatePath, configPath);
|
|
74
76
|
const envFile = copyIfMissing(envTemplatePath, envPath);
|
|
75
77
|
restrictFilePermissions(envFile.path);
|
|
76
78
|
const data = ensureDirectory(path.join(configDirectory, "data"));
|
|
@@ -97,15 +99,93 @@ export function writeFeishuCredentials(envPath: string, credentials: FeishuCrede
|
|
|
97
99
|
|
|
98
100
|
fs.mkdirSync(path.dirname(envPath), { recursive: true });
|
|
99
101
|
const temporaryPath = `${envPath}.${process.pid}.${Date.now()}.tmp`;
|
|
102
|
+
let temporaryFile: number | undefined;
|
|
100
103
|
try {
|
|
101
|
-
fs.
|
|
104
|
+
temporaryFile = fs.openSync(temporaryPath, "wx", 0o600);
|
|
105
|
+
fs.writeFileSync(temporaryFile, updated, "utf8");
|
|
106
|
+
fs.fsyncSync(temporaryFile);
|
|
107
|
+
fs.closeSync(temporaryFile);
|
|
108
|
+
temporaryFile = undefined;
|
|
102
109
|
fs.renameSync(temporaryPath, envPath);
|
|
110
|
+
syncDirectory(path.dirname(envPath));
|
|
103
111
|
restrictFilePermissions(envPath);
|
|
112
|
+
const persisted = readFeishuCredentials(envPath, {});
|
|
113
|
+
if (
|
|
114
|
+
persisted.status !== "configured"
|
|
115
|
+
|| persisted.appId !== credentials.appId
|
|
116
|
+
|| persisted.appSecret !== credentials.appSecret
|
|
117
|
+
) {
|
|
118
|
+
throw new Error("飞书凭据写入后校验失败。");
|
|
119
|
+
}
|
|
104
120
|
} finally {
|
|
121
|
+
if (temporaryFile !== undefined) fs.closeSync(temporaryFile);
|
|
105
122
|
fs.rmSync(temporaryPath, { force: true });
|
|
106
123
|
}
|
|
107
124
|
}
|
|
108
125
|
|
|
126
|
+
export function shouldCreateFeishuApp(
|
|
127
|
+
credentials: FeishuCredentialState,
|
|
128
|
+
reconfigureFeishu: boolean,
|
|
129
|
+
): boolean {
|
|
130
|
+
return reconfigureFeishu || credentials.status !== "configured";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function acquireInitializationLock(lockDirectory: string): InitializationLock {
|
|
134
|
+
fs.mkdirSync(lockDirectory, { recursive: true });
|
|
135
|
+
const lockPath = path.join(lockDirectory, INITIALIZATION_LOCK_NAME);
|
|
136
|
+
const owner = {
|
|
137
|
+
token: randomUUID(),
|
|
138
|
+
pid: process.pid,
|
|
139
|
+
hostname: os.hostname(),
|
|
140
|
+
startedAt: new Date().toISOString(),
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
144
|
+
let lockFile: number | undefined;
|
|
145
|
+
let created = false;
|
|
146
|
+
try {
|
|
147
|
+
lockFile = fs.openSync(lockPath, "wx", 0o600);
|
|
148
|
+
created = true;
|
|
149
|
+
fs.writeFileSync(lockFile, `${JSON.stringify(owner)}\n`, "utf8");
|
|
150
|
+
fs.fsyncSync(lockFile);
|
|
151
|
+
fs.closeSync(lockFile);
|
|
152
|
+
lockFile = undefined;
|
|
153
|
+
return {
|
|
154
|
+
path: lockPath,
|
|
155
|
+
release: () => {
|
|
156
|
+
const current = readInitializationLock(lockPath);
|
|
157
|
+
if (current?.token === owner.token) fs.rmSync(lockPath, { force: true });
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (lockFile !== undefined) fs.closeSync(lockFile);
|
|
162
|
+
if (created) fs.rmSync(lockPath, { force: true });
|
|
163
|
+
if (!isFileExistsError(error)) throw error;
|
|
164
|
+
if (!isStaleInitializationLock(lockPath)) {
|
|
165
|
+
throw new Error(`另一个 agent-bot init 正在运行(锁文件:${lockPath})。`);
|
|
166
|
+
}
|
|
167
|
+
fs.rmSync(lockPath, { force: true });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
throw new Error(`无法获取初始化锁:${lockPath}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function cleanupFeishuCredentialTemporaryFiles(envPath: string): number {
|
|
175
|
+
const directory = path.dirname(envPath);
|
|
176
|
+
if (!fs.existsSync(directory)) return 0;
|
|
177
|
+
const prefix = `${path.basename(envPath)}.`;
|
|
178
|
+
let removed = 0;
|
|
179
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
180
|
+
if (!entry.isFile() || !entry.name.startsWith(prefix) || !entry.name.endsWith(".tmp")) continue;
|
|
181
|
+
const middle = entry.name.slice(prefix.length, -".tmp".length);
|
|
182
|
+
if (!/^\d+\.\d+$/.test(middle)) continue;
|
|
183
|
+
fs.rmSync(path.join(directory, entry.name), { force: true });
|
|
184
|
+
removed += 1;
|
|
185
|
+
}
|
|
186
|
+
return removed;
|
|
187
|
+
}
|
|
188
|
+
|
|
109
189
|
function assertTemplate(templatePath: string): void {
|
|
110
190
|
if (!fs.statSync(templatePath, { throwIfNoEntry: false })?.isFile()) {
|
|
111
191
|
throw new Error(`Initialization template does not exist: ${templatePath}`);
|
|
@@ -121,8 +201,13 @@ function ensureDirectory(directoryPath: string): InitializedPath {
|
|
|
121
201
|
function copyIfMissing(sourcePath: string, targetPath: string): InitializedPath {
|
|
122
202
|
if (fs.existsSync(targetPath)) return { path: targetPath, status: "existing" };
|
|
123
203
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
124
|
-
|
|
125
|
-
|
|
204
|
+
try {
|
|
205
|
+
fs.copyFileSync(sourcePath, targetPath, fs.constants.COPYFILE_EXCL);
|
|
206
|
+
return { path: targetPath, status: "created" };
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (isFileExistsError(error)) return { path: targetPath, status: "existing" };
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
126
211
|
}
|
|
127
212
|
|
|
128
213
|
function upsertDotEnvValue(contents: string, key: string, value: string): string {
|
|
@@ -154,6 +239,74 @@ function restrictFilePermissions(filePath: string): void {
|
|
|
154
239
|
}
|
|
155
240
|
}
|
|
156
241
|
|
|
242
|
+
function syncDirectory(directoryPath: string): void {
|
|
243
|
+
let directory: number | undefined;
|
|
244
|
+
try {
|
|
245
|
+
directory = fs.openSync(directoryPath, "r");
|
|
246
|
+
fs.fsyncSync(directory);
|
|
247
|
+
} catch {
|
|
248
|
+
// Directory fsync is not supported on every Windows or mounted filesystem.
|
|
249
|
+
} finally {
|
|
250
|
+
if (directory !== undefined) fs.closeSync(directory);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
interface InitializationLockOwner {
|
|
255
|
+
token: string;
|
|
256
|
+
pid: number;
|
|
257
|
+
hostname: string;
|
|
258
|
+
startedAt: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function readInitializationLock(lockPath: string): InitializationLockOwner | undefined {
|
|
262
|
+
try {
|
|
263
|
+
const parsed = JSON.parse(fs.readFileSync(lockPath, "utf8")) as unknown;
|
|
264
|
+
if (!parsed || typeof parsed !== "object") return undefined;
|
|
265
|
+
const value = parsed as Record<string, unknown>;
|
|
266
|
+
if (
|
|
267
|
+
typeof value.token !== "string"
|
|
268
|
+
|| typeof value.pid !== "number"
|
|
269
|
+
|| typeof value.hostname !== "string"
|
|
270
|
+
|| typeof value.startedAt !== "string"
|
|
271
|
+
) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
token: value.token,
|
|
276
|
+
pid: value.pid,
|
|
277
|
+
hostname: value.hostname,
|
|
278
|
+
startedAt: value.startedAt,
|
|
279
|
+
};
|
|
280
|
+
} catch {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function isStaleInitializationLock(lockPath: string): boolean {
|
|
286
|
+
const owner = readInitializationLock(lockPath);
|
|
287
|
+
const modifiedAt = fs.statSync(lockPath, { throwIfNoEntry: false })?.mtimeMs ?? Date.now();
|
|
288
|
+
const ageMs = Date.now() - modifiedAt;
|
|
289
|
+
if (!owner) return ageMs >= 5_000;
|
|
290
|
+
if (owner.hostname === os.hostname()) return !isProcessRunning(owner.pid);
|
|
291
|
+
const startedAt = Date.parse(owner.startedAt);
|
|
292
|
+
const ownerAgeMs = Number.isFinite(startedAt) ? Date.now() - startedAt : ageMs;
|
|
293
|
+
return ownerAgeMs >= MAX_INITIALIZATION_LOCK_AGE_MS;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function isProcessRunning(pid: number): boolean {
|
|
297
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
298
|
+
try {
|
|
299
|
+
process.kill(pid, 0);
|
|
300
|
+
return true;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
return (error as NodeJS.ErrnoException).code === "EPERM";
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function isFileExistsError(error: unknown): boolean {
|
|
307
|
+
return (error as NodeJS.ErrnoException)?.code === "EEXIST";
|
|
308
|
+
}
|
|
309
|
+
|
|
157
310
|
function firstNonBlank(...values: Array<string | undefined>): string | undefined {
|
|
158
311
|
return values.find((value) => value !== undefined && value.trim().length > 0);
|
|
159
312
|
}
|
|
@@ -41,7 +41,18 @@ export async function sendControlRequest(
|
|
|
41
41
|
export async function isServerRunning(endpoint: string): Promise<boolean> {
|
|
42
42
|
try {
|
|
43
43
|
const response = await sendControlRequest(endpoint, { action: "health" }, 1_000);
|
|
44
|
-
|
|
44
|
+
if (!response.ok) return false;
|
|
45
|
+
if (!response.data || typeof response.data !== "object") return true;
|
|
46
|
+
return (response.data as Record<string, unknown>).ready !== false;
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function isServerReachable(endpoint: string): Promise<boolean> {
|
|
53
|
+
try {
|
|
54
|
+
await sendControlRequest(endpoint, { action: "health" }, 1_000);
|
|
55
|
+
return true;
|
|
45
56
|
} catch {
|
|
46
57
|
return false;
|
|
47
58
|
}
|