opclawtm 1.4.0
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 +136 -0
- package/dist/bin/team-manager.d.ts +6 -0
- package/dist/bin/team-manager.d.ts.map +1 -0
- package/dist/bin/team-manager.js +36 -0
- package/dist/bin/team-manager.js.map +1 -0
- package/dist/bin/team-setup.d.ts +7 -0
- package/dist/bin/team-setup.d.ts.map +1 -0
- package/dist/bin/team-setup.js +66 -0
- package/dist/bin/team-setup.js.map +1 -0
- package/dist/bin/team-uninstall.d.ts +6 -0
- package/dist/bin/team-uninstall.d.ts.map +1 -0
- package/dist/bin/team-uninstall.js +91 -0
- package/dist/bin/team-uninstall.js.map +1 -0
- package/dist/cli/commands/agent.command.d.ts +43 -0
- package/dist/cli/commands/agent.command.d.ts.map +1 -0
- package/dist/cli/commands/agent.command.js +223 -0
- package/dist/cli/commands/agent.command.js.map +1 -0
- package/dist/cli/commands/checklist.command.d.ts +35 -0
- package/dist/cli/commands/checklist.command.d.ts.map +1 -0
- package/dist/cli/commands/checklist.command.js +77 -0
- package/dist/cli/commands/checklist.command.js.map +1 -0
- package/dist/cli/commands/dept.command.d.ts +32 -0
- package/dist/cli/commands/dept.command.d.ts.map +1 -0
- package/dist/cli/commands/dept.command.js +92 -0
- package/dist/cli/commands/dept.command.js.map +1 -0
- package/dist/cli/commands/document.command.d.ts +37 -0
- package/dist/cli/commands/document.command.d.ts.map +1 -0
- package/dist/cli/commands/document.command.js +146 -0
- package/dist/cli/commands/document.command.js.map +1 -0
- package/dist/cli/commands/domain.command.d.ts +36 -0
- package/dist/cli/commands/domain.command.d.ts.map +1 -0
- package/dist/cli/commands/domain.command.js +97 -0
- package/dist/cli/commands/domain.command.js.map +1 -0
- package/dist/cli/commands/feishu.command.d.ts +63 -0
- package/dist/cli/commands/feishu.command.d.ts.map +1 -0
- package/dist/cli/commands/feishu.command.js +433 -0
- package/dist/cli/commands/feishu.command.js.map +1 -0
- package/dist/cli/commands/job.command.d.ts +39 -0
- package/dist/cli/commands/job.command.d.ts.map +1 -0
- package/dist/cli/commands/job.command.js +168 -0
- package/dist/cli/commands/job.command.js.map +1 -0
- package/dist/cli/commands/license.command.d.ts +22 -0
- package/dist/cli/commands/license.command.d.ts.map +1 -0
- package/dist/cli/commands/license.command.js +68 -0
- package/dist/cli/commands/license.command.js.map +1 -0
- package/dist/cli/commands/message-failure.command.d.ts +44 -0
- package/dist/cli/commands/message-failure.command.d.ts.map +1 -0
- package/dist/cli/commands/message-failure.command.js +137 -0
- package/dist/cli/commands/message-failure.command.js.map +1 -0
- package/dist/cli/commands/message.command.d.ts +47 -0
- package/dist/cli/commands/message.command.d.ts.map +1 -0
- package/dist/cli/commands/message.command.js +129 -0
- package/dist/cli/commands/message.command.js.map +1 -0
- package/dist/cli/commands/node.command.d.ts +76 -0
- package/dist/cli/commands/node.command.d.ts.map +1 -0
- package/dist/cli/commands/node.command.js +251 -0
- package/dist/cli/commands/node.command.js.map +1 -0
- package/dist/cli/commands/role-flow.command.d.ts +12 -0
- package/dist/cli/commands/role-flow.command.d.ts.map +1 -0
- package/dist/cli/commands/role-flow.command.js +54 -0
- package/dist/cli/commands/role-flow.command.js.map +1 -0
- package/dist/cli/commands/skill-pack.command.d.ts +41 -0
- package/dist/cli/commands/skill-pack.command.d.ts.map +1 -0
- package/dist/cli/commands/skill-pack.command.js +137 -0
- package/dist/cli/commands/skill-pack.command.js.map +1 -0
- package/dist/cli/commands/status.command.d.ts +8 -0
- package/dist/cli/commands/status.command.d.ts.map +1 -0
- package/dist/cli/commands/status.command.js +61 -0
- package/dist/cli/commands/status.command.js.map +1 -0
- package/dist/cli/commands/task.command.d.ts +105 -0
- package/dist/cli/commands/task.command.d.ts.map +1 -0
- package/dist/cli/commands/task.command.js +402 -0
- package/dist/cli/commands/task.command.js.map +1 -0
- package/dist/cli/commands/user.command.d.ts +43 -0
- package/dist/cli/commands/user.command.d.ts.map +1 -0
- package/dist/cli/commands/user.command.js +134 -0
- package/dist/cli/commands/user.command.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +863 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/tui/index.d.ts +45 -0
- package/dist/cli/tui/index.d.ts.map +1 -0
- package/dist/cli/tui/index.js +470 -0
- package/dist/cli/tui/index.js.map +1 -0
- package/dist/cli/tui/menus/agent-manage.menu.d.ts +8 -0
- package/dist/cli/tui/menus/agent-manage.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/agent-manage.menu.js +614 -0
- package/dist/cli/tui/menus/agent-manage.menu.js.map +1 -0
- package/dist/cli/tui/menus/dept-manage.menu.d.ts +8 -0
- package/dist/cli/tui/menus/dept-manage.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/dept-manage.menu.js +299 -0
- package/dist/cli/tui/menus/dept-manage.menu.js.map +1 -0
- package/dist/cli/tui/menus/domain-manage.menu.d.ts +8 -0
- package/dist/cli/tui/menus/domain-manage.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/domain-manage.menu.js +208 -0
- package/dist/cli/tui/menus/domain-manage.menu.js.map +1 -0
- package/dist/cli/tui/menus/feishu.menu.d.ts +8 -0
- package/dist/cli/tui/menus/feishu.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/feishu.menu.js +1727 -0
- package/dist/cli/tui/menus/feishu.menu.js.map +1 -0
- package/dist/cli/tui/menus/job-manage.menu.d.ts +16 -0
- package/dist/cli/tui/menus/job-manage.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/job-manage.menu.js +734 -0
- package/dist/cli/tui/menus/job-manage.menu.js.map +1 -0
- package/dist/cli/tui/menus/license.menu.d.ts +12 -0
- package/dist/cli/tui/menus/license.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/license.menu.js +164 -0
- package/dist/cli/tui/menus/license.menu.js.map +1 -0
- package/dist/cli/tui/menus/main.menu.d.ts +10 -0
- package/dist/cli/tui/menus/main.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/main.menu.js +94 -0
- package/dist/cli/tui/menus/main.menu.js.map +1 -0
- package/dist/cli/tui/menus/reset.menu.d.ts +10 -0
- package/dist/cli/tui/menus/reset.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/reset.menu.js +767 -0
- package/dist/cli/tui/menus/reset.menu.js.map +1 -0
- package/dist/cli/tui/menus/status.menu.d.ts +8 -0
- package/dist/cli/tui/menus/status.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/status.menu.js +123 -0
- package/dist/cli/tui/menus/status.menu.js.map +1 -0
- package/dist/cli/tui/menus/task-manage.menu.d.ts +11 -0
- package/dist/cli/tui/menus/task-manage.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/task-manage.menu.js +129 -0
- package/dist/cli/tui/menus/task-manage.menu.js.map +1 -0
- package/dist/cli/tui/menus/team-create.menu.d.ts +8 -0
- package/dist/cli/tui/menus/team-create.menu.d.ts.map +1 -0
- package/dist/cli/tui/menus/team-create.menu.js +353 -0
- package/dist/cli/tui/menus/team-create.menu.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +74 -0
- package/dist/config.js.map +1 -0
- package/dist/core/auth/index.d.ts +6 -0
- package/dist/core/auth/index.d.ts.map +1 -0
- package/dist/core/auth/index.js +22 -0
- package/dist/core/auth/index.js.map +1 -0
- package/dist/core/auth/middleware.d.ts +73 -0
- package/dist/core/auth/middleware.d.ts.map +1 -0
- package/dist/core/auth/middleware.js +456 -0
- package/dist/core/auth/middleware.js.map +1 -0
- package/dist/core/auth/storage.d.ts +38 -0
- package/dist/core/auth/storage.d.ts.map +1 -0
- package/dist/core/auth/storage.js +280 -0
- package/dist/core/auth/storage.js.map +1 -0
- package/dist/core/keys/public.pem +9 -0
- package/dist/core/models/types.d.ts +426 -0
- package/dist/core/models/types.d.ts.map +1 -0
- package/dist/core/models/types.js +9 -0
- package/dist/core/models/types.js.map +1 -0
- package/dist/core/services/agent-template.service.d.ts +49 -0
- package/dist/core/services/agent-template.service.d.ts.map +1 -0
- package/dist/core/services/agent-template.service.js +88 -0
- package/dist/core/services/agent-template.service.js.map +1 -0
- package/dist/core/services/agent.service.d.ts +120 -0
- package/dist/core/services/agent.service.d.ts.map +1 -0
- package/dist/core/services/agent.service.js +381 -0
- package/dist/core/services/agent.service.js.map +1 -0
- package/dist/core/services/auth-profiles.service.d.ts +93 -0
- package/dist/core/services/auth-profiles.service.d.ts.map +1 -0
- package/dist/core/services/auth-profiles.service.js +220 -0
- package/dist/core/services/auth-profiles.service.js.map +1 -0
- package/dist/core/services/checklist.service.d.ts +58 -0
- package/dist/core/services/checklist.service.d.ts.map +1 -0
- package/dist/core/services/checklist.service.js +240 -0
- package/dist/core/services/checklist.service.js.map +1 -0
- package/dist/core/services/config-tracker.service.d.ts +119 -0
- package/dist/core/services/config-tracker.service.d.ts.map +1 -0
- package/dist/core/services/config-tracker.service.js +1093 -0
- package/dist/core/services/config-tracker.service.js.map +1 -0
- package/dist/core/services/crypto.service.d.ts +102 -0
- package/dist/core/services/crypto.service.d.ts.map +1 -0
- package/dist/core/services/crypto.service.js +377 -0
- package/dist/core/services/crypto.service.js.map +1 -0
- package/dist/core/services/dept.service.d.ts +92 -0
- package/dist/core/services/dept.service.d.ts.map +1 -0
- package/dist/core/services/dept.service.js +260 -0
- package/dist/core/services/dept.service.js.map +1 -0
- package/dist/core/services/document.service.d.ts +131 -0
- package/dist/core/services/document.service.d.ts.map +1 -0
- package/dist/core/services/document.service.js +368 -0
- package/dist/core/services/document.service.js.map +1 -0
- package/dist/core/services/domain.service.d.ts +50 -0
- package/dist/core/services/domain.service.d.ts.map +1 -0
- package/dist/core/services/domain.service.js +98 -0
- package/dist/core/services/domain.service.js.map +1 -0
- package/dist/core/services/feishu.service.d.ts +124 -0
- package/dist/core/services/feishu.service.d.ts.map +1 -0
- package/dist/core/services/feishu.service.js +165 -0
- package/dist/core/services/feishu.service.js.map +1 -0
- package/dist/core/services/index.d.ts +27 -0
- package/dist/core/services/index.d.ts.map +1 -0
- package/dist/core/services/index.js +89 -0
- package/dist/core/services/index.js.map +1 -0
- package/dist/core/services/job.service.d.ts +60 -0
- package/dist/core/services/job.service.d.ts.map +1 -0
- package/dist/core/services/job.service.js +190 -0
- package/dist/core/services/job.service.js.map +1 -0
- package/dist/core/services/log.service.d.ts +111 -0
- package/dist/core/services/log.service.d.ts.map +1 -0
- package/dist/core/services/log.service.js +237 -0
- package/dist/core/services/log.service.js.map +1 -0
- package/dist/core/services/message-failure.service.d.ts +65 -0
- package/dist/core/services/message-failure.service.d.ts.map +1 -0
- package/dist/core/services/message-failure.service.js +112 -0
- package/dist/core/services/message-failure.service.js.map +1 -0
- package/dist/core/services/message.service.d.ts +122 -0
- package/dist/core/services/message.service.d.ts.map +1 -0
- package/dist/core/services/message.service.js +374 -0
- package/dist/core/services/message.service.js.map +1 -0
- package/dist/core/services/node.service.d.ts +150 -0
- package/dist/core/services/node.service.d.ts.map +1 -0
- package/dist/core/services/node.service.js +257 -0
- package/dist/core/services/node.service.js.map +1 -0
- package/dist/core/services/openclaw-config.service.d.ts +187 -0
- package/dist/core/services/openclaw-config.service.d.ts.map +1 -0
- package/dist/core/services/openclaw-config.service.js +268 -0
- package/dist/core/services/openclaw-config.service.js.map +1 -0
- package/dist/core/services/preset-loader.service.d.ts +80 -0
- package/dist/core/services/preset-loader.service.d.ts.map +1 -0
- package/dist/core/services/preset-loader.service.js +379 -0
- package/dist/core/services/preset-loader.service.js.map +1 -0
- package/dist/core/services/role-flow.service.d.ts +21 -0
- package/dist/core/services/role-flow.service.d.ts.map +1 -0
- package/dist/core/services/role-flow.service.js +47 -0
- package/dist/core/services/role-flow.service.js.map +1 -0
- package/dist/core/services/setup.service.d.ts +46 -0
- package/dist/core/services/setup.service.d.ts.map +1 -0
- package/dist/core/services/setup.service.js +336 -0
- package/dist/core/services/setup.service.js.map +1 -0
- package/dist/core/services/skill-pack.service.d.ts +56 -0
- package/dist/core/services/skill-pack.service.d.ts.map +1 -0
- package/dist/core/services/skill-pack.service.js +113 -0
- package/dist/core/services/skill-pack.service.js.map +1 -0
- package/dist/core/services/task.service.d.ts +177 -0
- package/dist/core/services/task.service.d.ts.map +1 -0
- package/dist/core/services/task.service.js +397 -0
- package/dist/core/services/task.service.js.map +1 -0
- package/dist/core/services/template.service.d.ts +51 -0
- package/dist/core/services/template.service.d.ts.map +1 -0
- package/dist/core/services/template.service.js +88 -0
- package/dist/core/services/template.service.js.map +1 -0
- package/dist/core/services/user.service.d.ts +50 -0
- package/dist/core/services/user.service.d.ts.map +1 -0
- package/dist/core/services/user.service.js +111 -0
- package/dist/core/services/user.service.js.map +1 -0
- package/dist/core/utils/agent-guide-generator.d.ts +38 -0
- package/dist/core/utils/agent-guide-generator.d.ts.map +1 -0
- package/dist/core/utils/agent-guide-generator.js +187 -0
- package/dist/core/utils/agent-guide-generator.js.map +1 -0
- package/dist/core/utils/credentials-cleanup.d.ts +81 -0
- package/dist/core/utils/credentials-cleanup.d.ts.map +1 -0
- package/dist/core/utils/credentials-cleanup.js +256 -0
- package/dist/core/utils/credentials-cleanup.js.map +1 -0
- package/dist/core/utils/index.d.ts +215 -0
- package/dist/core/utils/index.d.ts.map +1 -0
- package/dist/core/utils/index.js +462 -0
- package/dist/core/utils/index.js.map +1 -0
- package/dist/core/utils/openclaw-helper.d.ts +250 -0
- package/dist/core/utils/openclaw-helper.d.ts.map +1 -0
- package/dist/core/utils/openclaw-helper.js +1629 -0
- package/dist/core/utils/openclaw-helper.js.map +1 -0
- package/dist/core/utils/template-generator.d.ts +67 -0
- package/dist/core/utils/template-generator.d.ts.map +1 -0
- package/dist/core/utils/template-generator.js +170 -0
- package/dist/core/utils/template-generator.js.map +1 -0
- package/dist/db/index.d.ts +54 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +403 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/repositories/agent-template.repo.d.ts +47 -0
- package/dist/db/repositories/agent-template.repo.d.ts.map +1 -0
- package/dist/db/repositories/agent-template.repo.js +108 -0
- package/dist/db/repositories/agent-template.repo.js.map +1 -0
- package/dist/db/repositories/agent.repo.d.ts +64 -0
- package/dist/db/repositories/agent.repo.d.ts.map +1 -0
- package/dist/db/repositories/agent.repo.js +103 -0
- package/dist/db/repositories/agent.repo.js.map +1 -0
- package/dist/db/repositories/base.repository.d.ts +51 -0
- package/dist/db/repositories/base.repository.d.ts.map +1 -0
- package/dist/db/repositories/base.repository.js +107 -0
- package/dist/db/repositories/base.repository.js.map +1 -0
- package/dist/db/repositories/company.repo.d.ts +18 -0
- package/dist/db/repositories/company.repo.d.ts.map +1 -0
- package/dist/db/repositories/company.repo.js +33 -0
- package/dist/db/repositories/company.repo.js.map +1 -0
- package/dist/db/repositories/config-change.repo.d.ts +65 -0
- package/dist/db/repositories/config-change.repo.d.ts.map +1 -0
- package/dist/db/repositories/config-change.repo.js +119 -0
- package/dist/db/repositories/config-change.repo.js.map +1 -0
- package/dist/db/repositories/dept.repo.d.ts +37 -0
- package/dist/db/repositories/dept.repo.d.ts.map +1 -0
- package/dist/db/repositories/dept.repo.js +66 -0
- package/dist/db/repositories/dept.repo.js.map +1 -0
- package/dist/db/repositories/document.repo.d.ts +25 -0
- package/dist/db/repositories/document.repo.d.ts.map +1 -0
- package/dist/db/repositories/document.repo.js +51 -0
- package/dist/db/repositories/document.repo.js.map +1 -0
- package/dist/db/repositories/domain.repo.d.ts +42 -0
- package/dist/db/repositories/domain.repo.d.ts.map +1 -0
- package/dist/db/repositories/domain.repo.js +79 -0
- package/dist/db/repositories/domain.repo.js.map +1 -0
- package/dist/db/repositories/index.d.ts +24 -0
- package/dist/db/repositories/index.d.ts.map +1 -0
- package/dist/db/repositories/index.js +81 -0
- package/dist/db/repositories/index.js.map +1 -0
- package/dist/db/repositories/init-session.repo.d.ts +38 -0
- package/dist/db/repositories/init-session.repo.d.ts.map +1 -0
- package/dist/db/repositories/init-session.repo.js +112 -0
- package/dist/db/repositories/init-session.repo.js.map +1 -0
- package/dist/db/repositories/job.repo.d.ts +54 -0
- package/dist/db/repositories/job.repo.d.ts.map +1 -0
- package/dist/db/repositories/job.repo.js +119 -0
- package/dist/db/repositories/job.repo.js.map +1 -0
- package/dist/db/repositories/message-failure.repo.d.ts +92 -0
- package/dist/db/repositories/message-failure.repo.d.ts.map +1 -0
- package/dist/db/repositories/message-failure.repo.js +141 -0
- package/dist/db/repositories/message-failure.repo.js.map +1 -0
- package/dist/db/repositories/message-log.repo.d.ts +36 -0
- package/dist/db/repositories/message-log.repo.d.ts.map +1 -0
- package/dist/db/repositories/message-log.repo.js +64 -0
- package/dist/db/repositories/message-log.repo.js.map +1 -0
- package/dist/db/repositories/node.repo.d.ts +107 -0
- package/dist/db/repositories/node.repo.d.ts.map +1 -0
- package/dist/db/repositories/node.repo.js +276 -0
- package/dist/db/repositories/node.repo.js.map +1 -0
- package/dist/db/repositories/role-flow.repo.d.ts +43 -0
- package/dist/db/repositories/role-flow.repo.d.ts.map +1 -0
- package/dist/db/repositories/role-flow.repo.js +83 -0
- package/dist/db/repositories/role-flow.repo.js.map +1 -0
- package/dist/db/repositories/skill-pack.repo.d.ts +45 -0
- package/dist/db/repositories/skill-pack.repo.d.ts.map +1 -0
- package/dist/db/repositories/skill-pack.repo.js +149 -0
- package/dist/db/repositories/skill-pack.repo.js.map +1 -0
- package/dist/db/repositories/task.repo.d.ts +168 -0
- package/dist/db/repositories/task.repo.d.ts.map +1 -0
- package/dist/db/repositories/task.repo.js +381 -0
- package/dist/db/repositories/task.repo.js.map +1 -0
- package/dist/db/repositories/template.repo.d.ts +40 -0
- package/dist/db/repositories/template.repo.d.ts.map +1 -0
- package/dist/db/repositories/template.repo.js +66 -0
- package/dist/db/repositories/template.repo.js.map +1 -0
- package/dist/db/repositories/user.repo.d.ts +46 -0
- package/dist/db/repositories/user.repo.d.ts.map +1 -0
- package/dist/db/repositories/user.repo.js +75 -0
- package/dist/db/repositories/user.repo.js.map +1 -0
- package/dist/db/schema.sql +364 -0
- package/package.json +90 -0
- package/resources/preset-data-hash.enc +1 -0
- package/resources/preset-data.enc +1 -0
|
@@ -0,0 +1,1727 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Feishu Configuration Menu
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.showFeishuConfigMenu = showFeishuConfigMenu;
|
|
43
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
44
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const index_1 = require("../index");
|
|
48
|
+
const services_1 = require("../../../core/services");
|
|
49
|
+
const utils_1 = require("../../../core/utils");
|
|
50
|
+
const configTracker = __importStar(require("../../../core/services/config-tracker.service"));
|
|
51
|
+
const dept_repo_1 = require("../../../db/repositories/dept.repo");
|
|
52
|
+
const job_repo_1 = require("../../../db/repositories/job.repo");
|
|
53
|
+
/**
|
|
54
|
+
* 显示飞书配置菜单
|
|
55
|
+
*/
|
|
56
|
+
async function showFeishuConfigMenu() {
|
|
57
|
+
while (true) {
|
|
58
|
+
const configuredAgents = getConfiguredAgents();
|
|
59
|
+
const agentCount = Object.keys(configuredAgents).length;
|
|
60
|
+
const choices = [
|
|
61
|
+
new inquirer_1.default.Separator(chalk_1.default.cyan.bold('📱 飞书 Bot 配置')),
|
|
62
|
+
new inquirer_1.default.Separator(),
|
|
63
|
+
{ name: `📋 查看已配置的 Agent Bot 列表 (${agentCount} 个)`, value: 'list' },
|
|
64
|
+
{ name: '➕ 为 Agent 添加飞书 Bot', value: 'add' },
|
|
65
|
+
{ name: '✏️ 修改 Agent 的飞书配置', value: 'edit' },
|
|
66
|
+
{ name: '🗑️ 删除 Agent 的飞书配置', value: 'delete' },
|
|
67
|
+
new inquirer_1.default.Separator(),
|
|
68
|
+
new inquirer_1.default.Separator(chalk_1.default.cyan.bold('🏢 飞书群绑定')),
|
|
69
|
+
new inquirer_1.default.Separator(),
|
|
70
|
+
{ name: '📋 查看飞书群绑定列表', value: 'listGroups' },
|
|
71
|
+
{ name: '🔗 绑定飞书群到事业部', value: 'bindGroup' },
|
|
72
|
+
new inquirer_1.default.Separator(),
|
|
73
|
+
new inquirer_1.default.Separator(chalk_1.default.cyan.bold('👤 用户飞书绑定')),
|
|
74
|
+
new inquirer_1.default.Separator(),
|
|
75
|
+
{ name: '📋 查看用户飞书绑定状态', value: 'showUserBinding' },
|
|
76
|
+
{ name: '🔗 绑定用户飞书 Open ID', value: 'bindUser' },
|
|
77
|
+
{ name: '🔓 解绑用户飞书', value: 'unbindUser' },
|
|
78
|
+
new inquirer_1.default.Separator(),
|
|
79
|
+
new inquirer_1.default.Separator(chalk_1.default.cyan.bold('🔐 配对管理')),
|
|
80
|
+
new inquirer_1.default.Separator(),
|
|
81
|
+
{ name: '🔐 配对管理(批准用户对话权限)', value: 'pairing' },
|
|
82
|
+
new inquirer_1.default.Separator(),
|
|
83
|
+
{ name: '🔙 返回主菜单', value: 'back' }
|
|
84
|
+
];
|
|
85
|
+
const { action } = await inquirer_1.default.prompt([
|
|
86
|
+
{
|
|
87
|
+
type: 'list',
|
|
88
|
+
name: 'action',
|
|
89
|
+
message: '请选择操作:',
|
|
90
|
+
choices,
|
|
91
|
+
loop: true,
|
|
92
|
+
pageSize: 20
|
|
93
|
+
}
|
|
94
|
+
]);
|
|
95
|
+
switch (action) {
|
|
96
|
+
case 'list':
|
|
97
|
+
await listConfiguredAgents();
|
|
98
|
+
break;
|
|
99
|
+
case 'add':
|
|
100
|
+
await addFeishuBot();
|
|
101
|
+
break;
|
|
102
|
+
case 'edit':
|
|
103
|
+
await editFeishuBot();
|
|
104
|
+
break;
|
|
105
|
+
case 'delete':
|
|
106
|
+
await deleteFeishuBot();
|
|
107
|
+
break;
|
|
108
|
+
case 'listGroups':
|
|
109
|
+
await listFeishuGroups();
|
|
110
|
+
break;
|
|
111
|
+
case 'bindGroup':
|
|
112
|
+
await bindFeishuGroup();
|
|
113
|
+
break;
|
|
114
|
+
case 'showUserBinding':
|
|
115
|
+
await showUserFeishuBinding();
|
|
116
|
+
break;
|
|
117
|
+
case 'bindUser':
|
|
118
|
+
await bindUserFeishuId();
|
|
119
|
+
break;
|
|
120
|
+
case 'unbindUser':
|
|
121
|
+
await unbindUserFeishu();
|
|
122
|
+
break;
|
|
123
|
+
case 'pairing':
|
|
124
|
+
await showPairingMenu();
|
|
125
|
+
break;
|
|
126
|
+
case 'back':
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 安全读取 JSON 配置文件
|
|
133
|
+
*/
|
|
134
|
+
function safeReadJsonConfig(configPath) {
|
|
135
|
+
if (!fs.existsSync(configPath)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
let content = fs.readFileSync(configPath, 'utf-8');
|
|
140
|
+
// 移除 BOM
|
|
141
|
+
if (content.charCodeAt(0) === 0xFEFF) {
|
|
142
|
+
content = content.slice(1);
|
|
143
|
+
}
|
|
144
|
+
// 移除控制字符
|
|
145
|
+
content = content.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
|
|
146
|
+
return JSON.parse(content);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('JSON 解析错误:', error instanceof Error ? error.message : String(error));
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* 获取已配置飞书 Bot 的 Agent 列表
|
|
155
|
+
* 排除 'default' 键(它是别名,不是真正的 Agent)
|
|
156
|
+
*/
|
|
157
|
+
function getConfiguredAgents() {
|
|
158
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
159
|
+
const config = safeReadJsonConfig(configPath);
|
|
160
|
+
if (!config)
|
|
161
|
+
return {};
|
|
162
|
+
const channels = config.channels;
|
|
163
|
+
const feishuConfig = channels?.feishu;
|
|
164
|
+
const accounts = feishuConfig?.accounts;
|
|
165
|
+
if (!accounts)
|
|
166
|
+
return {};
|
|
167
|
+
const result = {};
|
|
168
|
+
for (const [agentId, account] of Object.entries(accounts)) {
|
|
169
|
+
// 跳过 'default' 键(它是别名)
|
|
170
|
+
if (agentId === 'default')
|
|
171
|
+
continue;
|
|
172
|
+
const acc = account;
|
|
173
|
+
if (acc?.appId) {
|
|
174
|
+
result[agentId] = {
|
|
175
|
+
appId: acc.appId,
|
|
176
|
+
name: acc.name || acc.botName // 兼容旧字段名
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 查看已配置的 Agent Bot 列表
|
|
184
|
+
*/
|
|
185
|
+
async function listConfiguredAgents() {
|
|
186
|
+
const configuredAgents = getConfiguredAgents();
|
|
187
|
+
const agentIds = Object.keys(configuredAgents);
|
|
188
|
+
if (agentIds.length === 0) {
|
|
189
|
+
console.log(chalk_1.default.yellow('\n暂无已配置的飞书 Bot\n'));
|
|
190
|
+
console.log(chalk_1.default.dim('使用"为 Agent 添加飞书 Bot"进行配置\n'));
|
|
191
|
+
await index_1.tuiUtils.waitForKey();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
console.log(chalk_1.default.bold('\n📋 已配置飞书 Bot 的 Agent 列表\n'));
|
|
195
|
+
console.log(chalk_1.default.dim('Agent ID'.padEnd(30)) + chalk_1.default.dim('Bot 名称'.padEnd(20)) + chalk_1.default.dim('App ID'));
|
|
196
|
+
index_1.tuiUtils.printDivider();
|
|
197
|
+
for (const agentId of agentIds) {
|
|
198
|
+
const info = configuredAgents[agentId];
|
|
199
|
+
const agent = services_1.agentService.getById(agentId);
|
|
200
|
+
const agentName = agent?.name || agentId;
|
|
201
|
+
const botName = info.name || '-';
|
|
202
|
+
console.log(`${agentId.padEnd(30)}${botName.padEnd(20)}${info.appId}`);
|
|
203
|
+
}
|
|
204
|
+
console.log('');
|
|
205
|
+
await index_1.tuiUtils.waitForKey();
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* 为 Agent 添加飞书 Bot
|
|
209
|
+
*/
|
|
210
|
+
async function addFeishuBot() {
|
|
211
|
+
const agents = services_1.agentService.listAll();
|
|
212
|
+
if (agents.length === 0) {
|
|
213
|
+
index_1.tuiUtils.printWarning('暂无 Agent,请先创建 Agent');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const configuredAgents = getConfiguredAgents();
|
|
217
|
+
// 过滤出未配置的 Agent
|
|
218
|
+
const unconfiguredAgents = agents.filter(a => !configuredAgents[a.id]);
|
|
219
|
+
if (unconfiguredAgents.length === 0) {
|
|
220
|
+
console.log(chalk_1.default.yellow('\n所有 Agent 都已配置飞书 Bot\n'));
|
|
221
|
+
console.log(chalk_1.default.dim('如需修改,请使用"修改 Agent 的飞书配置"\n'));
|
|
222
|
+
await index_1.tuiUtils.waitForKey();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// Enrich agent info with department and job names
|
|
226
|
+
const agentsWithDetails = unconfiguredAgents.map(a => {
|
|
227
|
+
const dept = a.department_id ? dept_repo_1.departmentRepository.findById(a.department_id) : null;
|
|
228
|
+
const job = a.job_id ? job_repo_1.jobRepository.findById(a.job_id) : null;
|
|
229
|
+
const roleMap = {
|
|
230
|
+
manager: '管理者',
|
|
231
|
+
executor: '执行者',
|
|
232
|
+
reviewer: '审核者',
|
|
233
|
+
assistant: '总助理',
|
|
234
|
+
watchdog: '监控者'
|
|
235
|
+
};
|
|
236
|
+
return {
|
|
237
|
+
...a,
|
|
238
|
+
deptName: dept?.name || '未分配',
|
|
239
|
+
jobName: job?.name || '未指定',
|
|
240
|
+
roleDisplay: roleMap[a.role] || a.role
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
console.log(chalk_1.default.bold.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
244
|
+
console.log(chalk_1.default.bold('➕ 为 Agent 添加飞书 Bot'));
|
|
245
|
+
console.log(chalk_1.default.bold.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
246
|
+
console.log(chalk_1.default.bold.yellow('📖 详细教程:'));
|
|
247
|
+
console.log(chalk_1.default.cyan(' https://www.feishu.cn/content/article/7602519239445974205\n'));
|
|
248
|
+
console.log(chalk_1.default.bold.yellow('📋 前置准备(在飞书开放平台完成):\n'));
|
|
249
|
+
console.log(chalk_1.default.bold('步骤 1:创建企业自建应用'));
|
|
250
|
+
console.log(chalk_1.default.dim(' • 进入飞书开放平台:https://open.feishu.cn/app'));
|
|
251
|
+
console.log(chalk_1.default.dim(' • 点击「创建企业自建应用」,填写应用信息\n'));
|
|
252
|
+
console.log(chalk_1.default.bold('步骤 2:启用机器人能力'));
|
|
253
|
+
console.log(chalk_1.default.dim(' • 进入「应用能力」→「机器人」'));
|
|
254
|
+
console.log(chalk_1.default.dim(' • 开启机器人能力,设置机器人名称\n'));
|
|
255
|
+
console.log(chalk_1.default.bold('步骤 3:配置应用权限'));
|
|
256
|
+
console.log(chalk_1.default.dim(' • 进入「权限管理」→ 点击「批量导入」'));
|
|
257
|
+
console.log(chalk_1.default.dim(' • 复制以下 JSON 内容直接导入:\n'));
|
|
258
|
+
console.log(chalk_1.default.cyan(' {'));
|
|
259
|
+
console.log(chalk_1.default.cyan(' "scopes": {'));
|
|
260
|
+
console.log(chalk_1.default.cyan(' "tenant": ['));
|
|
261
|
+
console.log(chalk_1.default.cyan(' "contact:user.base:readonly",'));
|
|
262
|
+
console.log(chalk_1.default.cyan(' "im:message",'));
|
|
263
|
+
console.log(chalk_1.default.cyan(' "im:message.p2p_msg:readonly",'));
|
|
264
|
+
console.log(chalk_1.default.cyan(' "im:message.group_at_msg:readonly",'));
|
|
265
|
+
console.log(chalk_1.default.cyan(' "im:message:send_as_bot",'));
|
|
266
|
+
console.log(chalk_1.default.cyan(' "im:resource"'));
|
|
267
|
+
console.log(chalk_1.default.cyan(' ]'));
|
|
268
|
+
console.log(chalk_1.default.cyan(' }'));
|
|
269
|
+
console.log(chalk_1.default.cyan(' }\n'));
|
|
270
|
+
console.log(chalk_1.default.dim(' 提示:可根据实际需求添加更多权限\n'));
|
|
271
|
+
console.log(chalk_1.default.bold('步骤 4:获取凭证'));
|
|
272
|
+
console.log(chalk_1.default.dim(' • 进入「凭证与基础信息」页面'));
|
|
273
|
+
console.log(chalk_1.default.dim(' • 复制 App ID 和 App Secret\n'));
|
|
274
|
+
console.log(chalk_1.default.bold.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
275
|
+
// 选择 Agent
|
|
276
|
+
const { agentId } = await inquirer_1.default.prompt([
|
|
277
|
+
{
|
|
278
|
+
type: 'list',
|
|
279
|
+
name: 'agentId',
|
|
280
|
+
message: '选择要配置飞书 Bot 的 Agent:',
|
|
281
|
+
choices: [
|
|
282
|
+
...agentsWithDetails.map(a => ({
|
|
283
|
+
name: `${a.name} | ${a.deptName} | ${a.roleDisplay} | ${a.jobName}`,
|
|
284
|
+
value: a.id
|
|
285
|
+
})),
|
|
286
|
+
new inquirer_1.default.Separator(),
|
|
287
|
+
{ name: '🔙 取消', value: 'cancel' }
|
|
288
|
+
],
|
|
289
|
+
loop: true
|
|
290
|
+
}
|
|
291
|
+
]);
|
|
292
|
+
if (agentId === 'cancel') {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const agent = services_1.agentService.getById(agentId);
|
|
296
|
+
console.log(chalk_1.default.dim('\n请输入飞书开放平台的应用凭证:'));
|
|
297
|
+
console.log(chalk_1.default.dim(' 飞书开放平台:https://open.feishu.cn/app\n'));
|
|
298
|
+
// 输入凭证
|
|
299
|
+
const { appId, appSecret, dmPolicy } = await inquirer_1.default.prompt([
|
|
300
|
+
{
|
|
301
|
+
type: 'input',
|
|
302
|
+
name: 'appId',
|
|
303
|
+
message: 'App ID (cli_xxx 格式):'
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
type: 'password',
|
|
307
|
+
name: 'appSecret',
|
|
308
|
+
message: 'App Secret:'
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
type: 'list',
|
|
312
|
+
name: 'dmPolicy',
|
|
313
|
+
message: '私聊策略:',
|
|
314
|
+
choices: [
|
|
315
|
+
{ name: 'pairing - 需配对批准(推荐,最安全)', value: 'pairing' },
|
|
316
|
+
{ name: 'open - 允许所有用户私聊', value: 'open' },
|
|
317
|
+
{ name: 'disabled - 禁用私聊', value: 'disabled' }
|
|
318
|
+
],
|
|
319
|
+
default: 'pairing',
|
|
320
|
+
loop: true
|
|
321
|
+
}
|
|
322
|
+
]);
|
|
323
|
+
if (!appId?.trim() || !appId.startsWith('cli_')) {
|
|
324
|
+
index_1.tuiUtils.printError('App ID 格式错误,应以 cli_ 开头');
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (!appSecret?.trim()) {
|
|
328
|
+
index_1.tuiUtils.printError('App Secret 不能为空');
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
// 更新配置
|
|
332
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
333
|
+
const config = safeReadJsonConfig(configPath) || {
|
|
334
|
+
gateway: { port: 28789, mode: 'local', bind: 'loopback', auth: { mode: 'token', token: 'default-token' } },
|
|
335
|
+
agents: { defaults: {}, list: [] },
|
|
336
|
+
bindings: [],
|
|
337
|
+
models: { providers: {} },
|
|
338
|
+
channels: {}
|
|
339
|
+
};
|
|
340
|
+
// 确保 channels.feishu 结构存在
|
|
341
|
+
if (!config.channels)
|
|
342
|
+
config.channels = {};
|
|
343
|
+
const channels = config.channels;
|
|
344
|
+
// 记录是否是新创建的飞书配置(用于重置时记录变更)
|
|
345
|
+
const isNewFeishuConfig = !channels.feishu;
|
|
346
|
+
// 重要:不能覆盖现有的 feishu 配置,只更新需要的字段
|
|
347
|
+
if (!channels.feishu) {
|
|
348
|
+
channels.feishu = {
|
|
349
|
+
enabled: true,
|
|
350
|
+
dmPolicy: 'pairing',
|
|
351
|
+
connectionMode: 'websocket',
|
|
352
|
+
accounts: {}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
const feishuConfig = channels.feishu;
|
|
356
|
+
// 重要:确保 accounts 不被覆盖
|
|
357
|
+
if (!feishuConfig.accounts) {
|
|
358
|
+
feishuConfig.accounts = {};
|
|
359
|
+
}
|
|
360
|
+
const accounts = feishuConfig.accounts;
|
|
361
|
+
// 调试:显示当前 accounts
|
|
362
|
+
console.log(chalk_1.default.dim(`[调试] 保存前 accounts 中的账号: ${Object.keys(accounts).join(', ') || '无'}`));
|
|
363
|
+
// 记录旧配置(用于重置时撤销)
|
|
364
|
+
const oldAccountConfig = accounts[agentId] ? { [agentId]: accounts[agentId] } : undefined;
|
|
365
|
+
// 添加账号配置(OpenClaw 官方 schema)
|
|
366
|
+
// accounts.<account_id> 只允许 appId 和 appSecret
|
|
367
|
+
// dmPolicy 等字段必须在顶层 channels.feishu 下
|
|
368
|
+
accounts[agentId] = {
|
|
369
|
+
appId: appId.trim(),
|
|
370
|
+
appSecret: appSecret.trim()
|
|
371
|
+
};
|
|
372
|
+
// 顶层设置(这些字段在 channels.feishu 层级)
|
|
373
|
+
if (!feishuConfig.enabled) {
|
|
374
|
+
feishuConfig.enabled = true;
|
|
375
|
+
}
|
|
376
|
+
if (!feishuConfig.dmPolicy) {
|
|
377
|
+
feishuConfig.dmPolicy = dmPolicy;
|
|
378
|
+
}
|
|
379
|
+
if (!feishuConfig.connectionMode) {
|
|
380
|
+
feishuConfig.connectionMode = 'websocket';
|
|
381
|
+
}
|
|
382
|
+
// 调试:显示保存后的 accounts
|
|
383
|
+
console.log(chalk_1.default.dim(`[调试] 保存后 accounts 中的账号: ${Object.keys(accounts).join(', ')}`));
|
|
384
|
+
// 确保 bindings 存在
|
|
385
|
+
if (!config.bindings)
|
|
386
|
+
config.bindings = [];
|
|
387
|
+
const bindings = config.bindings;
|
|
388
|
+
// 检查是否已有该 Agent 的绑定
|
|
389
|
+
const existingBindingIndex = bindings.findIndex((b) => b.agentId === agentId && b.match?.channel === 'feishu');
|
|
390
|
+
// 创建默认绑定(该 Bot 的所有消息路由到此 Agent)
|
|
391
|
+
const binding = {
|
|
392
|
+
agentId,
|
|
393
|
+
match: {
|
|
394
|
+
channel: 'feishu',
|
|
395
|
+
accountId: agentId
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
if (existingBindingIndex >= 0) {
|
|
399
|
+
bindings[existingBindingIndex] = binding;
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
bindings.push(binding);
|
|
403
|
+
}
|
|
404
|
+
// 保存配置
|
|
405
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
406
|
+
// 记录变更(用于重置时撤销)
|
|
407
|
+
// 1. 如果是首次创建飞书配置,记录整体结构变更
|
|
408
|
+
if (isNewFeishuConfig) {
|
|
409
|
+
configTracker.recordConfigChange({
|
|
410
|
+
change_type: 'channel_config',
|
|
411
|
+
action: 'add',
|
|
412
|
+
target_path: 'channels.feishu',
|
|
413
|
+
target_type: 'channels.feishu',
|
|
414
|
+
new_value: JSON.stringify(channels.feishu),
|
|
415
|
+
description: '创建飞书渠道配置'
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
// 2. 记录飞书账号配置变更
|
|
419
|
+
configTracker.recordConfigChange({
|
|
420
|
+
change_type: 'channel_config',
|
|
421
|
+
action: oldAccountConfig ? 'update' : 'add',
|
|
422
|
+
target_path: `channels.feishu.accounts.${agentId}`,
|
|
423
|
+
target_type: 'channels.feishu',
|
|
424
|
+
old_value: oldAccountConfig ? JSON.stringify(oldAccountConfig) : undefined,
|
|
425
|
+
new_value: JSON.stringify({ [agentId]: accounts[agentId] }),
|
|
426
|
+
related_id: agentId,
|
|
427
|
+
description: `配置飞书账号: ${agentId}`
|
|
428
|
+
});
|
|
429
|
+
// 3. 记录绑定变更
|
|
430
|
+
configTracker.recordBindingCreate(binding, agentId);
|
|
431
|
+
console.log(chalk_1.default.green(`\n✓ 已为 Agent "${agent?.name}" 配置飞书 Bot`));
|
|
432
|
+
console.log(chalk_1.default.dim(` App ID: ${appId}`));
|
|
433
|
+
console.log(chalk_1.default.dim(` 私聊策略: ${dmPolicy}`));
|
|
434
|
+
console.log(chalk_1.default.bold.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
435
|
+
console.log(chalk_1.default.bold.yellow('📋 后续步骤:\n'));
|
|
436
|
+
console.log(chalk_1.default.bold('步骤 1:启动 Gateway'));
|
|
437
|
+
console.log(chalk_1.default.cyan(' openclaw gateway'));
|
|
438
|
+
console.log(chalk_1.default.dim(' ⚠️ 必须先启动 Gateway,否则长连接配置会失败\n'));
|
|
439
|
+
console.log(chalk_1.default.bold('步骤 2:配置事件订阅(飞书开放平台)'));
|
|
440
|
+
console.log(chalk_1.default.dim(' • 进入「事件订阅」页面'));
|
|
441
|
+
console.log(chalk_1.default.dim(' • 开启「使用长连接接收事件」开关'));
|
|
442
|
+
console.log(chalk_1.default.dim(' • 添加事件:im.message.receive_v1'));
|
|
443
|
+
console.log(chalk_1.default.dim(' • 保存配置\n'));
|
|
444
|
+
console.log(chalk_1.default.bold('步骤 3:配置消息卡片回调(飞书开放平台)'));
|
|
445
|
+
console.log(chalk_1.default.dim(' • 进入「消息卡片」→「回调配置」页面'));
|
|
446
|
+
console.log(chalk_1.default.dim(' • 开启「使用长连接接收事件」开关'));
|
|
447
|
+
console.log(chalk_1.default.dim(' • 保存配置'));
|
|
448
|
+
console.log(chalk_1.default.dim(' 说明:用于接收消息卡片按钮点击等交互事件\n'));
|
|
449
|
+
console.log(chalk_1.default.bold('步骤 4:创建版本'));
|
|
450
|
+
console.log(chalk_1.default.dim(' • 进入「版本管理与发布」页面'));
|
|
451
|
+
console.log(chalk_1.default.dim(' • 点击「创建版本」,填写版本号和更新说明'));
|
|
452
|
+
console.log(chalk_1.default.dim(' • 保存版本\n'));
|
|
453
|
+
console.log(chalk_1.default.bold('步骤 5:提交审核'));
|
|
454
|
+
console.log(chalk_1.default.dim(' • 选择刚创建的版本,点击「申请上线」'));
|
|
455
|
+
console.log(chalk_1.default.dim(' • 等待管理员审批(企业自建应用通常自动通过)\n'));
|
|
456
|
+
console.log(chalk_1.default.bold('步骤 6:用户配对'));
|
|
457
|
+
console.log(chalk_1.default.dim(' • 用户在飞书搜索并添加机器人'));
|
|
458
|
+
console.log(chalk_1.default.dim(' • 用户发送私聊消息,机器人返回配对码'));
|
|
459
|
+
console.log(chalk_1.default.dim(' • 使用本工具「配对管理」输入配对码完成授权\n'));
|
|
460
|
+
console.log(chalk_1.default.bold.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
461
|
+
await index_1.tuiUtils.waitForKey();
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* 修改 Agent 的飞书配置
|
|
465
|
+
*/
|
|
466
|
+
async function editFeishuBot() {
|
|
467
|
+
const configuredAgents = getConfiguredAgents();
|
|
468
|
+
const agentIds = Object.keys(configuredAgents);
|
|
469
|
+
if (agentIds.length === 0) {
|
|
470
|
+
console.log(chalk_1.default.yellow('\n暂无已配置的飞书 Bot\n'));
|
|
471
|
+
await index_1.tuiUtils.waitForKey();
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
console.log(chalk_1.default.bold('\n✏️ 修改 Agent 的飞书配置\n'));
|
|
475
|
+
// Enrich agent info
|
|
476
|
+
const agentsWithDetails = agentIds.map(id => {
|
|
477
|
+
const agent = services_1.agentService.getById(id);
|
|
478
|
+
const info = configuredAgents[id];
|
|
479
|
+
const dept = agent?.department_id ? dept_repo_1.departmentRepository.findById(agent.department_id) : null;
|
|
480
|
+
const job = agent?.job_id ? job_repo_1.jobRepository.findById(agent.job_id) : null;
|
|
481
|
+
const roleMap = {
|
|
482
|
+
manager: '管理者',
|
|
483
|
+
executor: '执行者',
|
|
484
|
+
reviewer: '审核者',
|
|
485
|
+
assistant: '总助理',
|
|
486
|
+
watchdog: '监控者'
|
|
487
|
+
};
|
|
488
|
+
return {
|
|
489
|
+
id,
|
|
490
|
+
agent,
|
|
491
|
+
info,
|
|
492
|
+
deptName: dept?.name || '未分配',
|
|
493
|
+
jobName: job?.name || '未指定',
|
|
494
|
+
roleDisplay: roleMap[agent?.role || ''] || agent?.role || '未知'
|
|
495
|
+
};
|
|
496
|
+
});
|
|
497
|
+
// 选择 Agent
|
|
498
|
+
const { agentId } = await inquirer_1.default.prompt([
|
|
499
|
+
{
|
|
500
|
+
type: 'list',
|
|
501
|
+
name: 'agentId',
|
|
502
|
+
message: '选择要修改的 Agent:',
|
|
503
|
+
choices: [
|
|
504
|
+
...agentsWithDetails.map(a => ({
|
|
505
|
+
name: `${a.agent?.name || a.id} | ${a.deptName} | ${a.roleDisplay} | ${a.jobName} (${a.info.name || a.info.appId})`,
|
|
506
|
+
value: a.id
|
|
507
|
+
})),
|
|
508
|
+
new inquirer_1.default.Separator(),
|
|
509
|
+
{ name: '🔙 取消', value: 'cancel' }
|
|
510
|
+
],
|
|
511
|
+
loop: true
|
|
512
|
+
}
|
|
513
|
+
]);
|
|
514
|
+
if (agentId === 'cancel') {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
// 获取当前配置
|
|
518
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
519
|
+
const config = safeReadJsonConfig(configPath);
|
|
520
|
+
const channels = config?.channels;
|
|
521
|
+
const feishuConfig = channels?.feishu;
|
|
522
|
+
const accounts = feishuConfig?.accounts;
|
|
523
|
+
const currentAccount = accounts?.[agentId];
|
|
524
|
+
if (!feishuConfig) {
|
|
525
|
+
index_1.tuiUtils.printError('飞书配置不存在');
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (!currentAccount) {
|
|
529
|
+
index_1.tuiUtils.printError('配置读取失败');
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
// 获取 agent 信息用于显示
|
|
533
|
+
const agent = services_1.agentService.getById(agentId);
|
|
534
|
+
// dmPolicy 在顶层 channels.feishu 下
|
|
535
|
+
const currentDmPolicy = feishuConfig?.dmPolicy || 'pairing';
|
|
536
|
+
console.log(chalk_1.default.dim('\n当前配置:'));
|
|
537
|
+
console.log(chalk_1.default.dim(` App ID: ${currentAccount.appId}`));
|
|
538
|
+
console.log(chalk_1.default.dim(` 私聊策略: ${currentDmPolicy}\n`));
|
|
539
|
+
// 选择要修改的内容
|
|
540
|
+
const { fields } = await inquirer_1.default.prompt([
|
|
541
|
+
{
|
|
542
|
+
type: 'checkbox',
|
|
543
|
+
name: 'fields',
|
|
544
|
+
message: '选择要修改的内容:',
|
|
545
|
+
choices: [
|
|
546
|
+
{ name: 'App Secret(重新输入)', value: 'appSecret' },
|
|
547
|
+
{ name: '私聊策略', value: 'dmPolicy' }
|
|
548
|
+
]
|
|
549
|
+
}
|
|
550
|
+
]);
|
|
551
|
+
if (fields.length === 0) {
|
|
552
|
+
console.log(chalk_1.default.dim('\n未做任何修改\n'));
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
// 逐个修改
|
|
556
|
+
if (fields.includes('appSecret')) {
|
|
557
|
+
const { appSecret } = await inquirer_1.default.prompt([
|
|
558
|
+
{
|
|
559
|
+
type: 'password',
|
|
560
|
+
name: 'appSecret',
|
|
561
|
+
message: '新的 App Secret:'
|
|
562
|
+
}
|
|
563
|
+
]);
|
|
564
|
+
if (appSecret?.trim()) {
|
|
565
|
+
currentAccount.appSecret = appSecret.trim();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (fields.includes('dmPolicy')) {
|
|
569
|
+
const { dmPolicy } = await inquirer_1.default.prompt([
|
|
570
|
+
{
|
|
571
|
+
type: 'list',
|
|
572
|
+
name: 'dmPolicy',
|
|
573
|
+
message: '新的私聊策略:',
|
|
574
|
+
choices: [
|
|
575
|
+
{ name: 'pairing - 需配对批准', value: 'pairing' },
|
|
576
|
+
{ name: 'open - 允许所有用户私聊', value: 'open' },
|
|
577
|
+
{ name: 'disabled - 禁用私聊', value: 'disabled' }
|
|
578
|
+
],
|
|
579
|
+
default: currentDmPolicy,
|
|
580
|
+
loop: true
|
|
581
|
+
}
|
|
582
|
+
]);
|
|
583
|
+
// dmPolicy 在顶层 channels.feishu 下
|
|
584
|
+
feishuConfig.dmPolicy = dmPolicy;
|
|
585
|
+
}
|
|
586
|
+
// 保存
|
|
587
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
588
|
+
console.log(chalk_1.default.green('\n✓ 配置已更新\n'));
|
|
589
|
+
await index_1.tuiUtils.waitForKey();
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* 删除 Agent 的飞书配置
|
|
593
|
+
*/
|
|
594
|
+
async function deleteFeishuBot() {
|
|
595
|
+
const configuredAgents = getConfiguredAgents();
|
|
596
|
+
const agentIds = Object.keys(configuredAgents);
|
|
597
|
+
if (agentIds.length === 0) {
|
|
598
|
+
console.log(chalk_1.default.yellow('\n暂无已配置的飞书 Bot\n'));
|
|
599
|
+
await index_1.tuiUtils.waitForKey();
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
console.log(chalk_1.default.bold('\n🗑️ 删除 Agent 的飞书配置\n'));
|
|
603
|
+
console.log(chalk_1.default.yellow('⚠️ 警告:删除后该 Agent 的飞书 Bot 将无法使用\n'));
|
|
604
|
+
// 选择 Agent
|
|
605
|
+
const { agentId } = await inquirer_1.default.prompt([
|
|
606
|
+
{
|
|
607
|
+
type: 'list',
|
|
608
|
+
name: 'agentId',
|
|
609
|
+
message: '选择要删除的 Agent 配置:',
|
|
610
|
+
choices: [
|
|
611
|
+
...agentIds.map(id => {
|
|
612
|
+
const agent = services_1.agentService.getById(id);
|
|
613
|
+
const info = configuredAgents[id];
|
|
614
|
+
return {
|
|
615
|
+
name: `${agent?.name || id} (${id}) - ${info.name || info.appId}`,
|
|
616
|
+
value: id
|
|
617
|
+
};
|
|
618
|
+
}),
|
|
619
|
+
new inquirer_1.default.Separator(),
|
|
620
|
+
{ name: '🔙 取消', value: 'cancel' }
|
|
621
|
+
],
|
|
622
|
+
loop: true
|
|
623
|
+
}
|
|
624
|
+
]);
|
|
625
|
+
if (agentId === 'cancel') {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
// 确认删除
|
|
629
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
630
|
+
{
|
|
631
|
+
type: 'confirm',
|
|
632
|
+
name: 'confirm',
|
|
633
|
+
message: '确认删除该飞书 Bot 配置?',
|
|
634
|
+
default: false
|
|
635
|
+
}
|
|
636
|
+
]);
|
|
637
|
+
if (!confirm) {
|
|
638
|
+
console.log(chalk_1.default.dim('\n已取消\n'));
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
// 更新配置
|
|
642
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
643
|
+
const config = safeReadJsonConfig(configPath);
|
|
644
|
+
if (!config) {
|
|
645
|
+
index_1.tuiUtils.printError('配置文件读取失败');
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
// 删除账号配置
|
|
649
|
+
const channels = config.channels;
|
|
650
|
+
const feishuConfig = channels?.feishu;
|
|
651
|
+
const accounts = feishuConfig?.accounts;
|
|
652
|
+
// 保存旧配置(用于追踪记录)
|
|
653
|
+
const oldAccountConfig = accounts?.[agentId] ? JSON.stringify(accounts[agentId]) : undefined;
|
|
654
|
+
const oldBindings = Array.isArray(config.bindings) ? [...config.bindings] : [];
|
|
655
|
+
if (accounts && accounts[agentId]) {
|
|
656
|
+
delete accounts[agentId];
|
|
657
|
+
}
|
|
658
|
+
// 删除相关绑定
|
|
659
|
+
if (config.bindings && Array.isArray(config.bindings)) {
|
|
660
|
+
config.bindings = config.bindings.filter((b) => !(b.agentId === agentId && b.match?.channel === 'feishu'));
|
|
661
|
+
}
|
|
662
|
+
// 记录变更(用于重置时恢复)
|
|
663
|
+
if (oldAccountConfig) {
|
|
664
|
+
configTracker.recordConfigChange({
|
|
665
|
+
change_type: 'channel_config',
|
|
666
|
+
action: 'remove',
|
|
667
|
+
target_path: `channels.feishu.accounts.${agentId}`,
|
|
668
|
+
target_type: 'channels.feishu',
|
|
669
|
+
old_value: oldAccountConfig,
|
|
670
|
+
related_id: agentId,
|
|
671
|
+
description: `删除飞书账号配置: ${agentId}`
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
// 记录绑定删除
|
|
675
|
+
const currentBindings = Array.isArray(config.bindings) ? config.bindings : [];
|
|
676
|
+
if (oldBindings.length > currentBindings.length) {
|
|
677
|
+
configTracker.recordConfigChange({
|
|
678
|
+
change_type: 'binding_delete',
|
|
679
|
+
action: 'remove',
|
|
680
|
+
target_path: 'bindings',
|
|
681
|
+
target_type: 'bindings',
|
|
682
|
+
old_value: JSON.stringify(oldBindings),
|
|
683
|
+
new_value: JSON.stringify(currentBindings),
|
|
684
|
+
related_id: agentId,
|
|
685
|
+
description: `删除 Agent ${agentId} 的飞书绑定`
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
// 保存
|
|
689
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
690
|
+
console.log(chalk_1.default.green('\n✓ 飞书 Bot 配置已删除'));
|
|
691
|
+
console.log(chalk_1.default.yellow('\n⚠️ 请重启 Gateway 使飞书长连接断开:'));
|
|
692
|
+
console.log(chalk_1.default.cyan(' openclaw gateway restart\n'));
|
|
693
|
+
await index_1.tuiUtils.waitForKey();
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* 查看飞书群绑定列表
|
|
697
|
+
*/
|
|
698
|
+
async function listFeishuGroups() {
|
|
699
|
+
console.log(chalk_1.default.bold.cyan('\n🏢 飞书群绑定列表\n'));
|
|
700
|
+
// 从数据库读取事业部列表
|
|
701
|
+
const { departmentRepository } = await Promise.resolve().then(() => __importStar(require('../../../db/repositories/dept.repo')));
|
|
702
|
+
const departments = departmentRepository.findAll();
|
|
703
|
+
if (departments.length === 0) {
|
|
704
|
+
console.log(chalk_1.default.dim('暂无事业部\n'));
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
console.log(chalk_1.default.dim('事业部'.padEnd(20)) + chalk_1.default.dim('飞书群ID'));
|
|
708
|
+
index_1.tuiUtils.printDivider();
|
|
709
|
+
for (const dept of departments) {
|
|
710
|
+
const groupId = dept.feishu_group_id || '-';
|
|
711
|
+
console.log(`${(dept.name || '-').padEnd(20)}${groupId}`);
|
|
712
|
+
}
|
|
713
|
+
console.log('');
|
|
714
|
+
}
|
|
715
|
+
// 显示 openclaw.json 中的 bindings
|
|
716
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
717
|
+
const config = safeReadJsonConfig(configPath);
|
|
718
|
+
const bindings = (config?.bindings || []);
|
|
719
|
+
const feishuBindings = bindings.filter(b => b.match?.channel === 'feishu');
|
|
720
|
+
if (feishuBindings.length > 0) {
|
|
721
|
+
console.log(chalk_1.default.bold('飞书路由绑定(openclaw.json):\n'));
|
|
722
|
+
console.log(chalk_1.default.dim('Agent ID'.padEnd(25)) + chalk_1.default.dim('类型'.padEnd(10)) + chalk_1.default.dim('ID'));
|
|
723
|
+
index_1.tuiUtils.printDivider();
|
|
724
|
+
for (const binding of feishuBindings) {
|
|
725
|
+
const type = binding.match?.peer?.kind || 'unknown';
|
|
726
|
+
const id = binding.match?.peer?.id || binding.match?.accountId || '*';
|
|
727
|
+
console.log(`${(binding.agentId || '-').padEnd(25)}${type.padEnd(10)}${id}`);
|
|
728
|
+
}
|
|
729
|
+
console.log('');
|
|
730
|
+
}
|
|
731
|
+
await index_1.tuiUtils.waitForKey();
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* 绑定飞书群到事业部
|
|
735
|
+
*
|
|
736
|
+
* 绑定规则:
|
|
737
|
+
* 1. 该事业部内的所有 Agent 都绑定到群
|
|
738
|
+
* 2. 默认增加总助理(assistant_main)
|
|
739
|
+
* 3. 配置群组设置(requireMention: true 支持 @ 沟通)
|
|
740
|
+
*/
|
|
741
|
+
async function bindFeishuGroup() {
|
|
742
|
+
try {
|
|
743
|
+
console.log(chalk_1.default.bold.cyan('\n🔗 绑定飞书群到事业部\n'));
|
|
744
|
+
// 从数据库读取事业部列表
|
|
745
|
+
const { departmentRepository } = await Promise.resolve().then(() => __importStar(require('../../../db/repositories/dept.repo')));
|
|
746
|
+
const departments = departmentRepository.findAll();
|
|
747
|
+
if (departments.length === 0) {
|
|
748
|
+
console.log(chalk_1.default.yellow('暂无事业部,请先创建事业部\n'));
|
|
749
|
+
await index_1.tuiUtils.waitForKey();
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
// 选择事业部
|
|
753
|
+
const { deptId } = await inquirer_1.default.prompt([
|
|
754
|
+
{
|
|
755
|
+
type: 'list',
|
|
756
|
+
name: 'deptId',
|
|
757
|
+
message: '选择要绑定的事业部:',
|
|
758
|
+
choices: [
|
|
759
|
+
...departments.map((d) => ({
|
|
760
|
+
name: `${d.name} (${d.id})${d.feishu_group_id ? ' [已绑定: ' + d.feishu_group_id + ']' : ''}`,
|
|
761
|
+
value: d.id
|
|
762
|
+
})),
|
|
763
|
+
new inquirer_1.default.Separator(),
|
|
764
|
+
{ name: '🔙 取消', value: 'cancel' }
|
|
765
|
+
],
|
|
766
|
+
loop: true
|
|
767
|
+
}
|
|
768
|
+
]);
|
|
769
|
+
if (deptId === 'cancel') {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
// 输入飞书群ID
|
|
773
|
+
const { groupId } = await inquirer_1.default.prompt([
|
|
774
|
+
{
|
|
775
|
+
type: 'input',
|
|
776
|
+
name: 'groupId',
|
|
777
|
+
message: '请输入飞书群ID(oc_xxx 格式):',
|
|
778
|
+
validate: (input) => {
|
|
779
|
+
if (!input.trim()) {
|
|
780
|
+
return '请输入飞书群ID';
|
|
781
|
+
}
|
|
782
|
+
if (!input.startsWith('oc_')) {
|
|
783
|
+
return '飞书群ID格式错误,应以 oc_ 开头';
|
|
784
|
+
}
|
|
785
|
+
return true;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
]);
|
|
789
|
+
// 更新数据库
|
|
790
|
+
const dept = departments.find((d) => d.id === deptId);
|
|
791
|
+
const oldGroupId = dept?.feishu_group_id;
|
|
792
|
+
departmentRepository.update(deptId, { feishu_group_id: groupId });
|
|
793
|
+
// 记录变更(用于重置时撤销)
|
|
794
|
+
configTracker.recordFeishuGroupBind(deptId, dept?.name || deptId, groupId, oldGroupId);
|
|
795
|
+
// 更新 openclaw.json
|
|
796
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
797
|
+
const config = safeReadJsonConfig(configPath) || {};
|
|
798
|
+
// 1. 配置群组设置(支持 @ 沟通)
|
|
799
|
+
if (!config.channels)
|
|
800
|
+
config.channels = {};
|
|
801
|
+
const channels = config.channels;
|
|
802
|
+
// 重要:不能覆盖现有的 feishu 配置,只更新需要的字段
|
|
803
|
+
if (!channels.feishu) {
|
|
804
|
+
channels.feishu = {
|
|
805
|
+
enabled: true,
|
|
806
|
+
dmPolicy: 'pairing',
|
|
807
|
+
groupPolicy: 'allowlist',
|
|
808
|
+
accounts: {} // 确保 accounts 存在
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
const feishuConfig = channels.feishu;
|
|
812
|
+
// 确保 accounts 不被覆盖
|
|
813
|
+
if (!feishuConfig.accounts) {
|
|
814
|
+
feishuConfig.accounts = {};
|
|
815
|
+
}
|
|
816
|
+
// 设置群组访问策略
|
|
817
|
+
feishuConfig.groupPolicy = 'allowlist';
|
|
818
|
+
if (!feishuConfig.groupAllowFrom)
|
|
819
|
+
feishuConfig.groupAllowFrom = [];
|
|
820
|
+
const groupAllowFrom = feishuConfig.groupAllowFrom;
|
|
821
|
+
// 记录旧的 groupAllowFrom(用于追踪)
|
|
822
|
+
const oldGroupAllowFrom = [...groupAllowFrom];
|
|
823
|
+
if (!groupAllowFrom.includes(groupId)) {
|
|
824
|
+
groupAllowFrom.push(groupId);
|
|
825
|
+
// 记录 groupAllowFrom 变更(用于重置时撤销)
|
|
826
|
+
configTracker.recordConfigChange({
|
|
827
|
+
change_type: 'channel_config',
|
|
828
|
+
action: 'update',
|
|
829
|
+
target_path: 'channels.feishu.groupAllowFrom',
|
|
830
|
+
target_type: 'channels.feishu',
|
|
831
|
+
old_value: JSON.stringify(oldGroupAllowFrom),
|
|
832
|
+
new_value: JSON.stringify(groupAllowFrom),
|
|
833
|
+
related_id: groupId,
|
|
834
|
+
description: `添加群到白名单: ${groupId}`
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
// 群组详细配置
|
|
838
|
+
if (!feishuConfig.groups)
|
|
839
|
+
feishuConfig.groups = {};
|
|
840
|
+
const groups = feishuConfig.groups;
|
|
841
|
+
// 群组配置:requireMention: true 表示需要 @ 才能触发
|
|
842
|
+
// 默认配置:respondToMentionAll=true, enabled=true
|
|
843
|
+
groups[groupId] = {
|
|
844
|
+
requireMention: true,
|
|
845
|
+
respondToMentionAll: true,
|
|
846
|
+
enabled: true
|
|
847
|
+
};
|
|
848
|
+
// 2. 创建绑定:该事业部所有 Agent + 总助理
|
|
849
|
+
if (!config.bindings)
|
|
850
|
+
config.bindings = [];
|
|
851
|
+
const bindings = config.bindings;
|
|
852
|
+
// 获取该事业部的所有 Agent
|
|
853
|
+
const allAgents = services_1.agentService.listAll();
|
|
854
|
+
console.log(chalk_1.default.dim(`[调试] 总 Agent 数: ${allAgents.length}`));
|
|
855
|
+
const deptAgents = allAgents.filter((a) => a.department_id === deptId);
|
|
856
|
+
console.log(chalk_1.default.dim(`[调试] 事业部 ${deptId} 的 Agent 数: ${deptAgents.length}`));
|
|
857
|
+
// 获取总助理
|
|
858
|
+
const assistantMain = allAgents.find((a) => a.id === 'assistant_main' || a.role === 'assistant');
|
|
859
|
+
console.log(chalk_1.default.dim(`[调试] 总助理: ${assistantMain?.id || '未找到'}`));
|
|
860
|
+
// 要绑定的 Agent 列表:事业部所有 Agent + 总助理
|
|
861
|
+
const agentsToBind = [...deptAgents];
|
|
862
|
+
if (assistantMain && !agentsToBind.find(a => a.id === assistantMain.id)) {
|
|
863
|
+
agentsToBind.push(assistantMain);
|
|
864
|
+
}
|
|
865
|
+
console.log(chalk_1.default.dim(`[调试] 最终绑定 Agent 数: ${agentsToBind.length}`));
|
|
866
|
+
if (agentsToBind.length === 0) {
|
|
867
|
+
console.log(chalk_1.default.yellow('\n⚠️ 未找到可绑定的 Agent\n'));
|
|
868
|
+
console.log(chalk_1.default.dim('请确保该事业部已创建 Agent(管理者、审核者、执行者)\n'));
|
|
869
|
+
await index_1.tuiUtils.waitForKey();
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const typedBindings = bindings;
|
|
873
|
+
const oldBindings = typedBindings.filter((b) => b.match?.peer?.id !== groupId);
|
|
874
|
+
// 添加新绑定
|
|
875
|
+
for (const agent of agentsToBind) {
|
|
876
|
+
const binding = {
|
|
877
|
+
agentId: agent.id,
|
|
878
|
+
match: {
|
|
879
|
+
channel: 'feishu',
|
|
880
|
+
peer: {
|
|
881
|
+
kind: 'group',
|
|
882
|
+
id: groupId
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
oldBindings.push(binding);
|
|
887
|
+
// 记录每个 binding 的创建(用于重置时撤销)
|
|
888
|
+
configTracker.recordBindingCreate(binding);
|
|
889
|
+
}
|
|
890
|
+
config.bindings = oldBindings;
|
|
891
|
+
// 保存配置
|
|
892
|
+
console.log(chalk_1.default.dim(`[调试] 保存配置到: ${configPath}`));
|
|
893
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
894
|
+
console.log(chalk_1.default.dim(`[调试] 配置已保存`));
|
|
895
|
+
console.log(chalk_1.default.green('\n✓ 飞书群已绑定\n'));
|
|
896
|
+
console.log(chalk_1.default.dim(`事业部: ${dept?.name || deptId}`));
|
|
897
|
+
console.log(chalk_1.default.dim(`群ID: ${groupId}`));
|
|
898
|
+
console.log(chalk_1.default.dim(`\n绑定的 Agent(共 ${agentsToBind.length} 个):`));
|
|
899
|
+
for (const agent of agentsToBind) {
|
|
900
|
+
const roleLabel = agent.role === 'assistant' ? '[总助理]' :
|
|
901
|
+
agent.role === 'manager' ? '[管理者]' :
|
|
902
|
+
agent.role === 'reviewer' ? '[审核者]' : '[执行者]';
|
|
903
|
+
console.log(chalk_1.default.dim(` - ${agent.name} ${roleLabel}`));
|
|
904
|
+
}
|
|
905
|
+
console.log(chalk_1.default.dim('\n群组访问配置:'));
|
|
906
|
+
console.log(chalk_1.default.dim(` - groupPolicy: allowlist(仅允许绑定的群)`));
|
|
907
|
+
console.log(chalk_1.default.dim(` - requireMention: true(需要 @ 机器人才能触发)`));
|
|
908
|
+
console.log(chalk_1.default.dim('\n使用说明:'));
|
|
909
|
+
console.log(chalk_1.default.dim(` - 在群内 @机器人 发送消息,绑定的 Agent 会响应`));
|
|
910
|
+
console.log(chalk_1.default.dim(` - Agent 可以主动向群发送消息`));
|
|
911
|
+
console.log(chalk_1.default.yellow('\n⚠️ 请重启 Gateway 使配置生效'));
|
|
912
|
+
console.log(chalk_1.default.dim(' 1. 关闭当前运行的 Gateway(Ctrl+C)'));
|
|
913
|
+
console.log(chalk_1.default.dim(' 2. 重新启动 Gateway'));
|
|
914
|
+
console.log(chalk_1.default.dim(' 启动命令: openclaw gateway run\n'));
|
|
915
|
+
await index_1.tuiUtils.waitForKey('按回车键返回...');
|
|
916
|
+
}
|
|
917
|
+
catch (error) {
|
|
918
|
+
console.log(chalk_1.default.red('\n✗ 绑定失败'));
|
|
919
|
+
console.log(chalk_1.default.dim(`错误: ${error instanceof Error ? error.message : String(error)}\n`));
|
|
920
|
+
await index_1.tuiUtils.waitForKey();
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* 配对管理菜单
|
|
925
|
+
*
|
|
926
|
+
* 配对流程说明:
|
|
927
|
+
* 1. 用户在飞书私聊机器人
|
|
928
|
+
* 2. 机器人返回配对码(如 ABC123)
|
|
929
|
+
* 3. 管理员在命令行执行配对命令
|
|
930
|
+
* 4. 用户获得对话权限,可以正常对话
|
|
931
|
+
*
|
|
932
|
+
* 这是安全机制,防止未授权用户与机器人对话
|
|
933
|
+
*/
|
|
934
|
+
async function showPairingMenu() {
|
|
935
|
+
const { execSync } = require('child_process');
|
|
936
|
+
while (true) {
|
|
937
|
+
// 读取配对请求
|
|
938
|
+
const requests = readFeishuPairingRequests();
|
|
939
|
+
console.log(chalk_1.default.bold.cyan('\n🔐 配对管理\n'));
|
|
940
|
+
console.log(chalk_1.default.dim(`凭证目录: ${getCredentialsDir()}\n`));
|
|
941
|
+
if (requests.length === 0) {
|
|
942
|
+
console.log(chalk_1.default.dim('暂无待批准的配对请求\n'));
|
|
943
|
+
console.log(chalk_1.default.dim('提示:用户在飞书私聊机器人后,会收到配对码\n'));
|
|
944
|
+
await index_1.tuiUtils.waitForKey();
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
console.log(chalk_1.default.bold(`待批准的配对请求(共 ${requests.length} 个):\n`));
|
|
948
|
+
console.log(chalk_1.default.dim('配对码'.padEnd(15)) + chalk_1.default.dim('用户ID'.padEnd(45)) + chalk_1.default.dim('账号'));
|
|
949
|
+
index_1.tuiUtils.printDivider();
|
|
950
|
+
for (const req of requests) {
|
|
951
|
+
const code = req.code || '-';
|
|
952
|
+
const userId = req.id || '-';
|
|
953
|
+
const accountId = req.meta?.accountId || req.accountId || 'default';
|
|
954
|
+
console.log(code.padEnd(15) + userId.padEnd(45) + accountId);
|
|
955
|
+
}
|
|
956
|
+
console.log('');
|
|
957
|
+
// 提供选择菜单
|
|
958
|
+
const choices = [
|
|
959
|
+
...requests.map(req => {
|
|
960
|
+
const code = req.code || '-';
|
|
961
|
+
const accountId = req.meta?.accountId || req.accountId || 'default';
|
|
962
|
+
return {
|
|
963
|
+
name: `批准: ${code} (${accountId})`,
|
|
964
|
+
value: { code: req.code, userId: req.id, accountId: req.meta?.accountId || req.accountId || 'default' }
|
|
965
|
+
};
|
|
966
|
+
}),
|
|
967
|
+
new inquirer_1.default.Separator(),
|
|
968
|
+
{ name: '🔄 刷新列表', value: 'refresh' },
|
|
969
|
+
{ name: '🔙 返回', value: 'back' }
|
|
970
|
+
];
|
|
971
|
+
const { action } = await inquirer_1.default.prompt([
|
|
972
|
+
{
|
|
973
|
+
type: 'list',
|
|
974
|
+
name: 'action',
|
|
975
|
+
message: '请选择操作:',
|
|
976
|
+
choices,
|
|
977
|
+
loop: true
|
|
978
|
+
}
|
|
979
|
+
]);
|
|
980
|
+
if (action === 'back') {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
if (action === 'refresh') {
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
// 通过 openclaw 命令批准配对
|
|
987
|
+
const { code, userId, accountId } = action;
|
|
988
|
+
const command = `openclaw pairing approve feishu ${code}`;
|
|
989
|
+
console.log(chalk_1.default.dim(`\n执行命令: ${command}\n`));
|
|
990
|
+
try {
|
|
991
|
+
const output = execSync(command, {
|
|
992
|
+
encoding: 'utf-8',
|
|
993
|
+
cwd: (0, utils_1.getOpenClawRoot)() || undefined
|
|
994
|
+
});
|
|
995
|
+
console.log(chalk_1.default.green('\n✓ 配对授权成功!\n'));
|
|
996
|
+
console.log(chalk_1.default.dim(output));
|
|
997
|
+
console.log(chalk_1.default.yellow('提示:用户现在可以在飞书中与机器人对话。\n'));
|
|
998
|
+
// 记录配对变更(用于重置时清除)
|
|
999
|
+
configTracker.recordConfigChange({
|
|
1000
|
+
change_type: 'feishu_pairing_approve',
|
|
1001
|
+
action: 'add',
|
|
1002
|
+
target_path: `credentials.feishu.allowFrom.${accountId}`,
|
|
1003
|
+
target_type: 'credentials',
|
|
1004
|
+
new_value: JSON.stringify({ accountId, userId }),
|
|
1005
|
+
related_id: accountId,
|
|
1006
|
+
description: `批准飞书配对: ${userId} -> ${accountId}`
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
catch (error) {
|
|
1010
|
+
console.log(chalk_1.default.red('\n✗ 配对授权失败\n'));
|
|
1011
|
+
const errorMsg = error instanceof Error
|
|
1012
|
+
? error.stderr || error.message
|
|
1013
|
+
: String(error);
|
|
1014
|
+
console.log(chalk_1.default.dim(`错误信息: ${errorMsg}\n`));
|
|
1015
|
+
console.log(chalk_1.default.dim('可能原因:'));
|
|
1016
|
+
console.log(chalk_1.default.dim(' • 配对码无效或已过期'));
|
|
1017
|
+
console.log(chalk_1.default.dim(' • 配对码已被使用'));
|
|
1018
|
+
console.log(chalk_1.default.dim(' • Gateway 未运行'));
|
|
1019
|
+
console.log(chalk_1.default.dim(' • openclaw 命令未安装或不在 PATH 中\n'));
|
|
1020
|
+
}
|
|
1021
|
+
await index_1.tuiUtils.waitForKey();
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* 扫描本地端口查找活跃的 Gateway
|
|
1026
|
+
*/
|
|
1027
|
+
async function scanGateways() {
|
|
1028
|
+
const http = require('http');
|
|
1029
|
+
const gateways = [];
|
|
1030
|
+
// 常用端口列表
|
|
1031
|
+
const commonPorts = [28789, 18789, 38789, 48789];
|
|
1032
|
+
// 先从 openclaw.json 读取已配置的端口
|
|
1033
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
1034
|
+
const config = safeReadJsonConfig(configPath);
|
|
1035
|
+
const gateway = config?.gateway;
|
|
1036
|
+
const configPort = gateway?.port || 28789;
|
|
1037
|
+
const configToken = gateway?.auth?.token;
|
|
1038
|
+
// 将配置的端口放在最前面
|
|
1039
|
+
const portsToScan = [configPort, ...commonPorts.filter(p => p !== configPort)];
|
|
1040
|
+
for (const port of portsToScan) {
|
|
1041
|
+
const isRunning = await checkGatewayPort(port);
|
|
1042
|
+
if (isRunning) {
|
|
1043
|
+
const token = port === configPort ? configToken : undefined;
|
|
1044
|
+
const source = port === configPort ? '当前配置' : '自动发现';
|
|
1045
|
+
gateways.push({ port, status: 'running', token, source });
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return gateways;
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* 检查指定端口是否有 Gateway 运行
|
|
1052
|
+
*/
|
|
1053
|
+
function checkGatewayPort(port) {
|
|
1054
|
+
const http = require('http');
|
|
1055
|
+
return new Promise((resolve) => {
|
|
1056
|
+
const req = http.request({
|
|
1057
|
+
hostname: '127.0.0.1',
|
|
1058
|
+
port: port,
|
|
1059
|
+
path: '/health',
|
|
1060
|
+
method: 'GET',
|
|
1061
|
+
timeout: 2000
|
|
1062
|
+
}, (res) => {
|
|
1063
|
+
resolve(res.statusCode === 200);
|
|
1064
|
+
});
|
|
1065
|
+
req.on('error', () => resolve(false));
|
|
1066
|
+
req.on('timeout', () => {
|
|
1067
|
+
req.destroy();
|
|
1068
|
+
resolve(false);
|
|
1069
|
+
});
|
|
1070
|
+
req.end();
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* 让用户选择 Gateway
|
|
1075
|
+
*/
|
|
1076
|
+
async function selectGateway() {
|
|
1077
|
+
console.log(chalk_1.default.dim('\n正在扫描活跃的 Gateway...\n'));
|
|
1078
|
+
const gateways = await scanGateways();
|
|
1079
|
+
if (gateways.length === 0) {
|
|
1080
|
+
console.log(chalk_1.default.yellow('未发现活跃的 Gateway\n'));
|
|
1081
|
+
console.log(chalk_1.default.dim('请确保 Gateway 正在运行:'));
|
|
1082
|
+
console.log(chalk_1.default.cyan(' openclaw gateway\n'));
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
if (gateways.length === 1) {
|
|
1086
|
+
const gw = gateways[0];
|
|
1087
|
+
console.log(chalk_1.default.green(`✓ 发现 Gateway: 端口 ${gw.port} (${gw.source})\n`));
|
|
1088
|
+
// 如果有 token,直接返回
|
|
1089
|
+
if (gw.token) {
|
|
1090
|
+
return { port: gw.port, token: gw.token };
|
|
1091
|
+
}
|
|
1092
|
+
// 否则让用户输入 token
|
|
1093
|
+
const { token } = await inquirer_1.default.prompt([
|
|
1094
|
+
{
|
|
1095
|
+
type: 'password',
|
|
1096
|
+
name: 'token',
|
|
1097
|
+
message: `请输入 Gateway Token(端口 ${gw.port}):`
|
|
1098
|
+
}
|
|
1099
|
+
]);
|
|
1100
|
+
if (!token?.trim()) {
|
|
1101
|
+
console.log(chalk_1.default.yellow('\n未输入 Token,已取消\n'));
|
|
1102
|
+
return null;
|
|
1103
|
+
}
|
|
1104
|
+
return { port: gw.port, token: token.trim() };
|
|
1105
|
+
}
|
|
1106
|
+
// 多个 Gateway,让用户选择
|
|
1107
|
+
console.log(chalk_1.default.dim(`发现 ${gateways.length} 个活跃的 Gateway:\n`));
|
|
1108
|
+
const choices = gateways.map(gw => ({
|
|
1109
|
+
name: `端口 ${gw.port} (${gw.source})${gw.token ? ' [已配置Token]' : ''}`,
|
|
1110
|
+
value: gw
|
|
1111
|
+
}));
|
|
1112
|
+
const { selected } = await inquirer_1.default.prompt([
|
|
1113
|
+
{
|
|
1114
|
+
type: 'list',
|
|
1115
|
+
name: 'selected',
|
|
1116
|
+
message: '选择要使用的 Gateway:',
|
|
1117
|
+
choices,
|
|
1118
|
+
loop: true
|
|
1119
|
+
}
|
|
1120
|
+
]);
|
|
1121
|
+
if (selected.token) {
|
|
1122
|
+
return { port: selected.port, token: selected.token };
|
|
1123
|
+
}
|
|
1124
|
+
// 让用户输入 token
|
|
1125
|
+
const { token } = await inquirer_1.default.prompt([
|
|
1126
|
+
{
|
|
1127
|
+
type: 'password',
|
|
1128
|
+
name: 'token',
|
|
1129
|
+
message: `请输入 Gateway Token(端口 ${selected.port}):`
|
|
1130
|
+
}
|
|
1131
|
+
]);
|
|
1132
|
+
if (!token?.trim()) {
|
|
1133
|
+
console.log(chalk_1.default.yellow('\n未输入 Token,已取消\n'));
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
return { port: selected.port, token: token.trim() };
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Gateway WebSocket 客户端
|
|
1140
|
+
* 用于调用 Gateway 的配对 API
|
|
1141
|
+
*/
|
|
1142
|
+
class GatewayClient {
|
|
1143
|
+
gateway = null;
|
|
1144
|
+
/**
|
|
1145
|
+
* 设置 Gateway 信息
|
|
1146
|
+
*/
|
|
1147
|
+
setGateway(gateway) {
|
|
1148
|
+
this.gateway = gateway;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* 初始化:选择或配置 Gateway
|
|
1152
|
+
*/
|
|
1153
|
+
async init() {
|
|
1154
|
+
if (this.gateway)
|
|
1155
|
+
return true;
|
|
1156
|
+
const gateway = await selectGateway();
|
|
1157
|
+
if (!gateway)
|
|
1158
|
+
return false;
|
|
1159
|
+
this.gateway = gateway;
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* 调用 Gateway WebSocket 方法
|
|
1164
|
+
*/
|
|
1165
|
+
async callMethod(method, params = {}) {
|
|
1166
|
+
if (!this.gateway) {
|
|
1167
|
+
return { success: false, error: 'Gateway 未配置' };
|
|
1168
|
+
}
|
|
1169
|
+
// 保存引用,避免在 Promise 回调中访问 this.gateway
|
|
1170
|
+
const gateway = this.gateway;
|
|
1171
|
+
const WebSocket = require('ws');
|
|
1172
|
+
const url = `ws://127.0.0.1:${gateway.port}/ws`;
|
|
1173
|
+
const origin = `http://127.0.0.1:${gateway.port}`;
|
|
1174
|
+
return new Promise((resolve) => {
|
|
1175
|
+
// 创建 WebSocket 连接,添加 origin header
|
|
1176
|
+
const ws = new WebSocket(url, {
|
|
1177
|
+
headers: {
|
|
1178
|
+
'Origin': origin
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
let resolved = false;
|
|
1182
|
+
const connectId = `connect_${Date.now()}`;
|
|
1183
|
+
const methodId = `method_${Date.now()}`;
|
|
1184
|
+
const timeout = setTimeout(() => {
|
|
1185
|
+
if (!resolved) {
|
|
1186
|
+
resolved = true;
|
|
1187
|
+
ws.close();
|
|
1188
|
+
resolve({ success: false, error: '连接超时' });
|
|
1189
|
+
}
|
|
1190
|
+
}, 15000);
|
|
1191
|
+
ws.on('open', () => {
|
|
1192
|
+
// 等待 challenge 事件,不立即发送
|
|
1193
|
+
});
|
|
1194
|
+
ws.on('message', (data) => {
|
|
1195
|
+
try {
|
|
1196
|
+
const msg = JSON.parse(data.toString());
|
|
1197
|
+
// 1. 收到 challenge 事件
|
|
1198
|
+
if (msg.type === 'event' && msg.event === 'connect.challenge') {
|
|
1199
|
+
// 发送 connect 请求(使用正确的客户端 ID 和模式)
|
|
1200
|
+
ws.send(JSON.stringify({
|
|
1201
|
+
type: 'req',
|
|
1202
|
+
id: connectId,
|
|
1203
|
+
method: 'connect',
|
|
1204
|
+
params: {
|
|
1205
|
+
minProtocol: 3,
|
|
1206
|
+
maxProtocol: 3,
|
|
1207
|
+
client: {
|
|
1208
|
+
id: 'openclaw-control-ui', // 官方定义的控制 UI 客户端 ID
|
|
1209
|
+
version: '1.0.0',
|
|
1210
|
+
platform: process.platform,
|
|
1211
|
+
mode: 'ui' // UI 模式
|
|
1212
|
+
},
|
|
1213
|
+
role: 'operator',
|
|
1214
|
+
scopes: ['operator.pairing'],
|
|
1215
|
+
auth: { token: gateway.token }
|
|
1216
|
+
}
|
|
1217
|
+
}));
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
// 2. connect 响应 (type: "res")
|
|
1221
|
+
if (msg.type === 'res' && msg.id === connectId) {
|
|
1222
|
+
if (msg.ok) {
|
|
1223
|
+
// 连接成功,发送实际的方法调用
|
|
1224
|
+
ws.send(JSON.stringify({
|
|
1225
|
+
type: 'req',
|
|
1226
|
+
id: methodId,
|
|
1227
|
+
method: method,
|
|
1228
|
+
params: params
|
|
1229
|
+
}));
|
|
1230
|
+
}
|
|
1231
|
+
else {
|
|
1232
|
+
clearTimeout(timeout);
|
|
1233
|
+
resolved = true;
|
|
1234
|
+
ws.close();
|
|
1235
|
+
const errMsg = msg.error?.message || msg.error?.text || JSON.stringify(msg.error);
|
|
1236
|
+
resolve({ success: false, error: `认证失败: ${errMsg}` });
|
|
1237
|
+
}
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
// 3. 方法调用响应 (type: "res")
|
|
1241
|
+
if (msg.type === 'res' && msg.id === methodId) {
|
|
1242
|
+
clearTimeout(timeout);
|
|
1243
|
+
resolved = true;
|
|
1244
|
+
ws.close();
|
|
1245
|
+
if (msg.ok) {
|
|
1246
|
+
resolve({ success: true, result: msg.result || msg.payload });
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
const errMsg = msg.error?.message || msg.error?.text || JSON.stringify(msg.error);
|
|
1250
|
+
resolve({ success: false, error: errMsg });
|
|
1251
|
+
}
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
catch (e) {
|
|
1256
|
+
// 忽略解析错误
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
ws.on('error', (err) => {
|
|
1260
|
+
clearTimeout(timeout);
|
|
1261
|
+
if (!resolved) {
|
|
1262
|
+
resolved = true;
|
|
1263
|
+
resolve({ success: false, error: `WebSocket错误: ${err.message}` });
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
ws.on('close', (code, reason) => {
|
|
1267
|
+
clearTimeout(timeout);
|
|
1268
|
+
if (!resolved) {
|
|
1269
|
+
resolved = true;
|
|
1270
|
+
const reasonText = reason.toString() || `code=${code}`;
|
|
1271
|
+
resolve({ success: false, error: `连接已关闭: ${reasonText}` });
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* 检查并提示用户配置 Gateway
|
|
1278
|
+
*/
|
|
1279
|
+
checkGatewayConfig() {
|
|
1280
|
+
if (!this.gateway) {
|
|
1281
|
+
return { needsConfig: false, message: '' };
|
|
1282
|
+
}
|
|
1283
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
1284
|
+
const config = safeReadJsonConfig(configPath);
|
|
1285
|
+
if (!config) {
|
|
1286
|
+
return {
|
|
1287
|
+
needsConfig: true,
|
|
1288
|
+
message: '无法读取配置文件'
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
const gateway = config.gateway;
|
|
1292
|
+
const controlUi = gateway?.controlUi;
|
|
1293
|
+
// 检查是否配置了 allowInsecureAuth
|
|
1294
|
+
if (controlUi?.allowInsecureAuth !== true) {
|
|
1295
|
+
return {
|
|
1296
|
+
needsConfig: true,
|
|
1297
|
+
message: `需要在 openclaw.json 中添加以下配置:
|
|
1298
|
+
|
|
1299
|
+
"gateway": {
|
|
1300
|
+
"controlUi": {
|
|
1301
|
+
"allowInsecureAuth": true
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
或者配置允许的 origin:
|
|
1306
|
+
|
|
1307
|
+
"gateway": {
|
|
1308
|
+
"controlUi": {
|
|
1309
|
+
"allowedOrigins": ["http://127.0.0.1:${this.gateway.port}"]
|
|
1310
|
+
}
|
|
1311
|
+
}`
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
return { needsConfig: false, message: '' };
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* 列出配对请求
|
|
1318
|
+
*/
|
|
1319
|
+
async listPairingRequests() {
|
|
1320
|
+
// node.pair.list 不需要参数
|
|
1321
|
+
const result = await this.callMethod('node.pair.list', {});
|
|
1322
|
+
if (!result.success) {
|
|
1323
|
+
return { success: false, error: result.error };
|
|
1324
|
+
}
|
|
1325
|
+
// 返回格式: { pending: [...], paired: [...] }
|
|
1326
|
+
const response = result.result;
|
|
1327
|
+
return { success: true, pending: response.pending || [] };
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* 批准配对请求
|
|
1331
|
+
*/
|
|
1332
|
+
async approvePairing(requestId) {
|
|
1333
|
+
const result = await this.callMethod('node.pair.approve', { requestId });
|
|
1334
|
+
if (!result.success) {
|
|
1335
|
+
return { success: false, error: result.error };
|
|
1336
|
+
}
|
|
1337
|
+
return { success: true };
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
// 全局 Gateway 客户端实例
|
|
1341
|
+
const gatewayClient = new GatewayClient();
|
|
1342
|
+
/**
|
|
1343
|
+
* 获取凭证目录路径
|
|
1344
|
+
*
|
|
1345
|
+
* 基于 TUI 启动时选择的工作目录计算:
|
|
1346
|
+
* - 工作目录: <project>/.openclaw
|
|
1347
|
+
* - 凭证目录: <project>/credentials
|
|
1348
|
+
*
|
|
1349
|
+
* 即:凭证目录 = 工作目录/credentials
|
|
1350
|
+
*
|
|
1351
|
+
* Gateway 运行时会在工作目录下创建 credentials 子目录存储配对信息
|
|
1352
|
+
*/
|
|
1353
|
+
function getCredentialsDir() {
|
|
1354
|
+
const path = require('path');
|
|
1355
|
+
const os = require('os');
|
|
1356
|
+
// 获取 TUI 启动时用户选择的工作目录
|
|
1357
|
+
const openclawRoot = (0, utils_1.getOpenClawRoot)();
|
|
1358
|
+
// 凭证目录直接在工作目录下
|
|
1359
|
+
// 工作目录:~/.openclaw 或 <project>/.openclaw
|
|
1360
|
+
// 凭证目录:<工作目录>/credentials
|
|
1361
|
+
if (openclawRoot) {
|
|
1362
|
+
return path.join(openclawRoot, 'credentials');
|
|
1363
|
+
}
|
|
1364
|
+
// 如果工作目录未设置,尝试从配置文件路径推导
|
|
1365
|
+
const configPath = (0, utils_1.getOpenClawJsonPath)();
|
|
1366
|
+
if (configPath) {
|
|
1367
|
+
const openclawDir = path.dirname(configPath);
|
|
1368
|
+
return path.join(openclawDir, 'credentials');
|
|
1369
|
+
}
|
|
1370
|
+
// 尝试环境变量
|
|
1371
|
+
if (process.env.OPENCLAW_CREDENTIALS_DIR) {
|
|
1372
|
+
return process.env.OPENCLAW_CREDENTIALS_DIR;
|
|
1373
|
+
}
|
|
1374
|
+
if (process.env.OPENCLAW_STATE_DIR) {
|
|
1375
|
+
return path.join(process.env.OPENCLAW_STATE_DIR, 'credentials');
|
|
1376
|
+
}
|
|
1377
|
+
// 默认:全局目录
|
|
1378
|
+
return path.join(os.homedir(), '.openclaw', 'credentials');
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* 获取飞书配对文件路径
|
|
1382
|
+
*/
|
|
1383
|
+
function getFeishuPairingFilePath() {
|
|
1384
|
+
const path = require('path');
|
|
1385
|
+
return path.join(getCredentialsDir(), 'feishu-pairing.json');
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* 读取飞书配对请求
|
|
1389
|
+
*/
|
|
1390
|
+
function readFeishuPairingRequests() {
|
|
1391
|
+
const filePath = getFeishuPairingFilePath();
|
|
1392
|
+
if (!fs.existsSync(filePath)) {
|
|
1393
|
+
return [];
|
|
1394
|
+
}
|
|
1395
|
+
try {
|
|
1396
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
1397
|
+
const data = JSON.parse(content);
|
|
1398
|
+
return data.requests || [];
|
|
1399
|
+
}
|
|
1400
|
+
catch {
|
|
1401
|
+
return [];
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* 批准飞书配对请求
|
|
1406
|
+
*
|
|
1407
|
+
* 执行流程(遵循 OpenClaw 标准):
|
|
1408
|
+
* 1. 验证配对码是否有效
|
|
1409
|
+
* 2. 查找对应的配对请求(匹配 code 和 accountId)
|
|
1410
|
+
* 3. 从待处理配对列表中删除该请求
|
|
1411
|
+
* 4. 将发送者 ID 添加到 allowFrom 存储文件
|
|
1412
|
+
*
|
|
1413
|
+
* 存储文件位置:
|
|
1414
|
+
* - {credentials_dir}/feishu-allowFrom.json(默认账户)
|
|
1415
|
+
* - {credentials_dir}/feishu-{accountId}-allowFrom.json(指定账户)
|
|
1416
|
+
*
|
|
1417
|
+
* 同时会记录变更到 config_changes 表,用于重置时清除
|
|
1418
|
+
*/
|
|
1419
|
+
function approveFeishuPairing(code, accountId) {
|
|
1420
|
+
const credentialsDir = getCredentialsDir();
|
|
1421
|
+
const filePath = getFeishuPairingFilePath();
|
|
1422
|
+
console.log(chalk_1.default.dim(`凭证目录: ${credentialsDir}`));
|
|
1423
|
+
console.log(chalk_1.default.dim(`配对文件: ${filePath}`));
|
|
1424
|
+
if (!fs.existsSync(filePath)) {
|
|
1425
|
+
return { success: false, error: '配对请求文件不存在' };
|
|
1426
|
+
}
|
|
1427
|
+
try {
|
|
1428
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
1429
|
+
const data = JSON.parse(content);
|
|
1430
|
+
const requests = data.requests || [];
|
|
1431
|
+
console.log(chalk_1.default.dim(`当前请求数: ${requests.length}`));
|
|
1432
|
+
// 查找匹配的配对请求
|
|
1433
|
+
const requestIndex = requests.findIndex((r) => r.code && r.code.toUpperCase() === code.toUpperCase());
|
|
1434
|
+
if (requestIndex < 0) {
|
|
1435
|
+
return { success: false, error: `配对码 "${code}" 不存在或已过期` };
|
|
1436
|
+
}
|
|
1437
|
+
const request = requests[requestIndex];
|
|
1438
|
+
console.log(chalk_1.default.dim(`用户ID: ${request.id}`));
|
|
1439
|
+
// 从请求列表中移除
|
|
1440
|
+
requests.splice(requestIndex, 1);
|
|
1441
|
+
data.requests = requests;
|
|
1442
|
+
// 确保凭证目录存在
|
|
1443
|
+
if (!fs.existsSync(credentialsDir)) {
|
|
1444
|
+
fs.mkdirSync(credentialsDir, { recursive: true });
|
|
1445
|
+
}
|
|
1446
|
+
// 写回请求文件
|
|
1447
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
1448
|
+
console.log(chalk_1.default.dim(`已从配对列表移除配对码`));
|
|
1449
|
+
// 获取请求中的 accountId
|
|
1450
|
+
const requestAccountId = (request.meta?.accountId || request.accountId);
|
|
1451
|
+
// 确定要写入的 accountId(优先级:传入参数 > 请求中的 accountId > default)
|
|
1452
|
+
let targetAccountId;
|
|
1453
|
+
if (accountId && accountId !== 'default') {
|
|
1454
|
+
targetAccountId = accountId;
|
|
1455
|
+
}
|
|
1456
|
+
else if (requestAccountId) {
|
|
1457
|
+
targetAccountId = requestAccountId;
|
|
1458
|
+
}
|
|
1459
|
+
else {
|
|
1460
|
+
targetAccountId = 'default';
|
|
1461
|
+
}
|
|
1462
|
+
console.log(chalk_1.default.dim(`目标账户: ${targetAccountId}`));
|
|
1463
|
+
// 写入多种格式的 allowFrom 文件,确保 Gateway 能找到
|
|
1464
|
+
const userId = request.id;
|
|
1465
|
+
const fileNames = [
|
|
1466
|
+
'feishu-allowFrom.json', // 默认格式
|
|
1467
|
+
`feishu-${targetAccountId}-allowFrom.json`, // 指定账户
|
|
1468
|
+
'feishu-default-allowFrom.json' // default 账户
|
|
1469
|
+
];
|
|
1470
|
+
for (const fileName of fileNames) {
|
|
1471
|
+
const allowFromPath = path.join(credentialsDir, fileName);
|
|
1472
|
+
let allowFrom = [];
|
|
1473
|
+
if (fs.existsSync(allowFromPath)) {
|
|
1474
|
+
try {
|
|
1475
|
+
allowFrom = JSON.parse(fs.readFileSync(allowFromPath, 'utf-8')) || [];
|
|
1476
|
+
}
|
|
1477
|
+
catch {
|
|
1478
|
+
allowFrom = [];
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
if (!allowFrom.includes(userId)) {
|
|
1482
|
+
allowFrom.push(userId);
|
|
1483
|
+
fs.writeFileSync(allowFromPath, JSON.stringify(allowFrom, null, 2), 'utf-8');
|
|
1484
|
+
console.log(chalk_1.default.dim(`已写入: ${fileName}`));
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
// 记录变更(用于重置时清除)
|
|
1488
|
+
configTracker.recordConfigChange({
|
|
1489
|
+
change_type: 'feishu_pairing_approve',
|
|
1490
|
+
action: 'add',
|
|
1491
|
+
target_path: `credentials.feishu.allowFrom.${targetAccountId}`,
|
|
1492
|
+
target_type: 'credentials',
|
|
1493
|
+
new_value: JSON.stringify({ accountId: targetAccountId, userId }),
|
|
1494
|
+
related_id: targetAccountId,
|
|
1495
|
+
description: `批准飞书配对: ${userId} -> ${targetAccountId}`
|
|
1496
|
+
});
|
|
1497
|
+
return { success: true, userId, targetAccountId };
|
|
1498
|
+
}
|
|
1499
|
+
catch (error) {
|
|
1500
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* 列出配对请求
|
|
1505
|
+
*/
|
|
1506
|
+
async function listPairingRequests() {
|
|
1507
|
+
console.log(chalk_1.default.dim('\n正在查询配对请求...\n'));
|
|
1508
|
+
console.log(chalk_1.default.dim(`凭证目录: ${getCredentialsDir()}\n`));
|
|
1509
|
+
const requests = readFeishuPairingRequests();
|
|
1510
|
+
if (requests.length === 0) {
|
|
1511
|
+
console.log(chalk_1.default.dim('暂无待批准的配对请求\n'));
|
|
1512
|
+
console.log(chalk_1.default.dim('提示:让用户在飞书私聊机器人,机器人会返回配对码\n'));
|
|
1513
|
+
}
|
|
1514
|
+
else {
|
|
1515
|
+
console.log(chalk_1.default.bold(`待批准的配对请求(共 ${requests.length} 个):\n`));
|
|
1516
|
+
console.log(chalk_1.default.dim('配对码'.padEnd(15)) + chalk_1.default.dim('用户ID'.padEnd(45)) + chalk_1.default.dim('账号'));
|
|
1517
|
+
index_1.tuiUtils.printDivider();
|
|
1518
|
+
for (const req of requests) {
|
|
1519
|
+
const code = req.code || '-';
|
|
1520
|
+
const userId = req.id || '-';
|
|
1521
|
+
const accountId = req.accountId || req.meta?.accountId || 'default';
|
|
1522
|
+
console.log(code.padEnd(15) + userId.padEnd(45) + accountId);
|
|
1523
|
+
}
|
|
1524
|
+
console.log('');
|
|
1525
|
+
}
|
|
1526
|
+
await index_1.tuiUtils.waitForKey();
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* 批准配对请求
|
|
1530
|
+
*
|
|
1531
|
+
* 直接调用 approveFeishuPairing 函数完成授权
|
|
1532
|
+
*/
|
|
1533
|
+
async function approvePairingRequest() {
|
|
1534
|
+
console.log(chalk_1.default.bold.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
1535
|
+
console.log(chalk_1.default.bold('🔐 飞书配对授权'));
|
|
1536
|
+
console.log(chalk_1.default.bold.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
1537
|
+
console.log(chalk_1.default.dim('用户在飞书私聊机器人后,会收到配对码。\n'));
|
|
1538
|
+
console.log(chalk_1.default.dim(`凭证目录: ${getCredentialsDir()}\n`));
|
|
1539
|
+
const { pairingCode } = await inquirer_1.default.prompt([
|
|
1540
|
+
{
|
|
1541
|
+
type: 'input',
|
|
1542
|
+
name: 'pairingCode',
|
|
1543
|
+
message: '请输入配对码(留空取消):'
|
|
1544
|
+
}
|
|
1545
|
+
]);
|
|
1546
|
+
if (!pairingCode || !pairingCode.trim()) {
|
|
1547
|
+
console.log(chalk_1.default.dim('\n已取消\n'));
|
|
1548
|
+
await index_1.tuiUtils.waitForKey();
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
const code = pairingCode.trim().toUpperCase();
|
|
1552
|
+
console.log(chalk_1.default.dim(`\n正在批准配对码: ${code}...\n`));
|
|
1553
|
+
// 直接调用配对函数
|
|
1554
|
+
const result = approveFeishuPairing(code);
|
|
1555
|
+
if (result.success) {
|
|
1556
|
+
console.log(chalk_1.default.green('\n✓ 配对授权成功!\n'));
|
|
1557
|
+
console.log(chalk_1.default.dim(`用户ID: ${result.userId}`));
|
|
1558
|
+
console.log(chalk_1.default.dim(`账号: ${result.targetAccountId}\n`));
|
|
1559
|
+
console.log(chalk_1.default.yellow('提示:用户现在可以在飞书中与机器人对话。\n'));
|
|
1560
|
+
}
|
|
1561
|
+
else {
|
|
1562
|
+
console.log(chalk_1.default.red('\n✗ 配对授权失败\n'));
|
|
1563
|
+
console.log(chalk_1.default.dim(`错误: ${result.error || '未知错误'}\n`));
|
|
1564
|
+
console.log(chalk_1.default.dim('可能原因:'));
|
|
1565
|
+
console.log(chalk_1.default.dim(' • 配对码无效或已过期'));
|
|
1566
|
+
console.log(chalk_1.default.dim(' • 配对码已被使用'));
|
|
1567
|
+
console.log(chalk_1.default.dim(' • 凭证目录配置不正确\n'));
|
|
1568
|
+
}
|
|
1569
|
+
await index_1.tuiUtils.waitForKey();
|
|
1570
|
+
}
|
|
1571
|
+
// ============================================
|
|
1572
|
+
// 用户飞书绑定功能 (v1.1.5)
|
|
1573
|
+
// ============================================
|
|
1574
|
+
/**
|
|
1575
|
+
* 查看用户飞书绑定状态
|
|
1576
|
+
*/
|
|
1577
|
+
async function showUserFeishuBinding() {
|
|
1578
|
+
console.log(chalk_1.default.bold.cyan('\n👤 用户飞书绑定状态\n'));
|
|
1579
|
+
const userInfo = services_1.userService.getUserInfo();
|
|
1580
|
+
console.log(chalk_1.default.dim('用户信息:'));
|
|
1581
|
+
console.log(chalk_1.default.dim(` 用户ID: ${userInfo.id}`));
|
|
1582
|
+
console.log(chalk_1.default.dim(` 用户名: ${userInfo.name}`));
|
|
1583
|
+
console.log('');
|
|
1584
|
+
if (userInfo.feishuBound) {
|
|
1585
|
+
console.log(chalk_1.default.green('飞书绑定状态: 已绑定'));
|
|
1586
|
+
console.log(chalk_1.default.dim(` Open ID: ${userInfo.feishuOpenId}`));
|
|
1587
|
+
console.log('');
|
|
1588
|
+
console.log(chalk_1.default.dim('总助理可以通过飞书私聊通知你任务完成情况。'));
|
|
1589
|
+
}
|
|
1590
|
+
else {
|
|
1591
|
+
console.log(chalk_1.default.yellow('飞书绑定状态: 未绑定'));
|
|
1592
|
+
console.log('');
|
|
1593
|
+
console.log(chalk_1.default.dim('绑定后,总助理可以通过飞书私聊通知你任务完成情况。'));
|
|
1594
|
+
console.log(chalk_1.default.dim('使用"绑定用户飞书 Open ID"进行绑定。'));
|
|
1595
|
+
}
|
|
1596
|
+
console.log('');
|
|
1597
|
+
await index_1.tuiUtils.waitForKey();
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* 绑定用户飞书 Open ID
|
|
1601
|
+
*/
|
|
1602
|
+
async function bindUserFeishuId() {
|
|
1603
|
+
console.log(chalk_1.default.bold.cyan('\n🔗 绑定用户飞书 Open ID\n'));
|
|
1604
|
+
// 显示当前状态
|
|
1605
|
+
const userInfo = services_1.userService.getUserInfo();
|
|
1606
|
+
if (userInfo.feishuBound) {
|
|
1607
|
+
console.log(chalk_1.default.yellow('当前已绑定:'));
|
|
1608
|
+
console.log(chalk_1.default.dim(` Open ID: ${userInfo.feishuOpenId}`));
|
|
1609
|
+
console.log('');
|
|
1610
|
+
const { overwrite } = await inquirer_1.default.prompt([
|
|
1611
|
+
{
|
|
1612
|
+
type: 'confirm',
|
|
1613
|
+
name: 'overwrite',
|
|
1614
|
+
message: '是否要覆盖现有绑定?',
|
|
1615
|
+
default: false
|
|
1616
|
+
}
|
|
1617
|
+
]);
|
|
1618
|
+
if (!overwrite) {
|
|
1619
|
+
console.log(chalk_1.default.dim('\n已取消\n'));
|
|
1620
|
+
await index_1.tuiUtils.waitForKey();
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
// 说明如何获取 open_id
|
|
1625
|
+
console.log(chalk_1.default.bold.yellow('📖 如何获取飞书 Open ID:\n'));
|
|
1626
|
+
console.log(chalk_1.default.dim('方法一:查看 Gateway 日志'));
|
|
1627
|
+
console.log(chalk_1.default.cyan(' 1. 用户在飞书私聊机器人'));
|
|
1628
|
+
console.log(chalk_1.default.cyan(' 2. 查看 Gateway 日志,找到 open_id 字段'));
|
|
1629
|
+
console.log(chalk_1.default.cyan(' 命令: openclaw logs --follow'));
|
|
1630
|
+
console.log('');
|
|
1631
|
+
console.log(chalk_1.default.dim('方法二:通过飞书管理后台'));
|
|
1632
|
+
console.log(chalk_1.default.cyan(' 1. 进入飞书管理后台 > 成员管理'));
|
|
1633
|
+
console.log(chalk_1.default.cyan(' 2. 找到目标用户,查看用户详情'));
|
|
1634
|
+
console.log(chalk_1.default.cyan(' 3. 复制 Open ID(ou_xxx 格式)'));
|
|
1635
|
+
console.log('');
|
|
1636
|
+
// 输入 open_id
|
|
1637
|
+
const { feishuOpenId } = await inquirer_1.default.prompt([
|
|
1638
|
+
{
|
|
1639
|
+
type: 'input',
|
|
1640
|
+
name: 'feishuOpenId',
|
|
1641
|
+
message: '请输入飞书 Open ID(ou_xxx 格式):',
|
|
1642
|
+
validate: (input) => {
|
|
1643
|
+
if (!input.trim()) {
|
|
1644
|
+
return '请输入 Open ID';
|
|
1645
|
+
}
|
|
1646
|
+
if (!input.trim().startsWith('ou_')) {
|
|
1647
|
+
return 'Open ID 格式错误,应以 ou_ 开头';
|
|
1648
|
+
}
|
|
1649
|
+
return true;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
]);
|
|
1653
|
+
// 可选:输入用户名
|
|
1654
|
+
const { updateName, userName } = await inquirer_1.default.prompt([
|
|
1655
|
+
{
|
|
1656
|
+
type: 'confirm',
|
|
1657
|
+
name: 'updateName',
|
|
1658
|
+
message: '是否更新用户名?',
|
|
1659
|
+
default: false
|
|
1660
|
+
},
|
|
1661
|
+
{
|
|
1662
|
+
type: 'input',
|
|
1663
|
+
name: 'userName',
|
|
1664
|
+
message: '请输入用户名:',
|
|
1665
|
+
when: (answers) => answers.updateName
|
|
1666
|
+
}
|
|
1667
|
+
]);
|
|
1668
|
+
// 执行绑定
|
|
1669
|
+
const success = services_1.userService.bindFeishu(feishuOpenId.trim());
|
|
1670
|
+
if (success) {
|
|
1671
|
+
// 更新用户名
|
|
1672
|
+
if (userName?.trim()) {
|
|
1673
|
+
services_1.userService.updateName(userName.trim());
|
|
1674
|
+
}
|
|
1675
|
+
console.log(chalk_1.default.green('\n✓ 飞书账号绑定成功!\n'));
|
|
1676
|
+
console.log(chalk_1.default.dim(` Open ID: ${feishuOpenId}`));
|
|
1677
|
+
if (userName?.trim()) {
|
|
1678
|
+
console.log(chalk_1.default.dim(` 用户名: ${userName}`));
|
|
1679
|
+
}
|
|
1680
|
+
console.log('');
|
|
1681
|
+
console.log(chalk_1.default.dim('现在总助理可以通过飞书私聊通知你了!'));
|
|
1682
|
+
}
|
|
1683
|
+
else {
|
|
1684
|
+
console.log(chalk_1.default.red('\n✗ 绑定失败\n'));
|
|
1685
|
+
}
|
|
1686
|
+
console.log('');
|
|
1687
|
+
await index_1.tuiUtils.waitForKey();
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* 解绑用户飞书
|
|
1691
|
+
*/
|
|
1692
|
+
async function unbindUserFeishu() {
|
|
1693
|
+
console.log(chalk_1.default.bold.cyan('\n🔓 解绑用户飞书\n'));
|
|
1694
|
+
const userInfo = services_1.userService.getUserInfo();
|
|
1695
|
+
if (!userInfo.feishuBound) {
|
|
1696
|
+
console.log(chalk_1.default.yellow('当前未绑定飞书账号\n'));
|
|
1697
|
+
await index_1.tuiUtils.waitForKey();
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
console.log(chalk_1.default.dim('当前绑定信息:'));
|
|
1701
|
+
console.log(chalk_1.default.dim(` Open ID: ${userInfo.feishuOpenId}`));
|
|
1702
|
+
console.log('');
|
|
1703
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
1704
|
+
{
|
|
1705
|
+
type: 'confirm',
|
|
1706
|
+
name: 'confirm',
|
|
1707
|
+
message: '确认解绑飞书账号?',
|
|
1708
|
+
default: false
|
|
1709
|
+
}
|
|
1710
|
+
]);
|
|
1711
|
+
if (!confirm) {
|
|
1712
|
+
console.log(chalk_1.default.dim('\n已取消\n'));
|
|
1713
|
+
await index_1.tuiUtils.waitForKey();
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
const success = services_1.userService.unbindFeishu();
|
|
1717
|
+
if (success) {
|
|
1718
|
+
console.log(chalk_1.default.green('\n✓ 飞书账号已解绑\n'));
|
|
1719
|
+
console.log(chalk_1.default.dim('总助理将无法通过飞书私聊通知你。'));
|
|
1720
|
+
}
|
|
1721
|
+
else {
|
|
1722
|
+
console.log(chalk_1.default.red('\n✗ 解绑失败\n'));
|
|
1723
|
+
}
|
|
1724
|
+
console.log('');
|
|
1725
|
+
await index_1.tuiUtils.waitForKey();
|
|
1726
|
+
}
|
|
1727
|
+
//# sourceMappingURL=feishu.menu.js.map
|