agim-cli 1.0.1
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 +1234 -0
- package/LICENSE +21 -0
- package/README.md +422 -0
- package/README.zh-CN.md +414 -0
- package/dist/cli-ui/cmd-handlers.d.ts +11 -0
- package/dist/cli-ui/cmd-handlers.d.ts.map +1 -0
- package/dist/cli-ui/cmd-handlers.js +240 -0
- package/dist/cli-ui/cmd-handlers.js.map +1 -0
- package/dist/cli-ui/config-wizard.d.ts +3 -0
- package/dist/cli-ui/config-wizard.d.ts.map +1 -0
- package/dist/cli-ui/config-wizard.js +851 -0
- package/dist/cli-ui/config-wizard.js.map +1 -0
- package/dist/cli-ui/entry-menu.d.ts +28 -0
- package/dist/cli-ui/entry-menu.d.ts.map +1 -0
- package/dist/cli-ui/entry-menu.js +50 -0
- package/dist/cli-ui/entry-menu.js.map +1 -0
- package/dist/cli-ui/env-file.d.ts +35 -0
- package/dist/cli-ui/env-file.d.ts.map +1 -0
- package/dist/cli-ui/env-file.js +163 -0
- package/dist/cli-ui/env-file.js.map +1 -0
- package/dist/cli-ui/i18n.d.ts +204 -0
- package/dist/cli-ui/i18n.d.ts.map +1 -0
- package/dist/cli-ui/i18n.js +455 -0
- package/dist/cli-ui/i18n.js.map +1 -0
- package/dist/cli-ui/lang-picker.d.ts +10 -0
- package/dist/cli-ui/lang-picker.d.ts.map +1 -0
- package/dist/cli-ui/lang-picker.js +33 -0
- package/dist/cli-ui/lang-picker.js.map +1 -0
- package/dist/cli-ui/paths.d.ts +4 -0
- package/dist/cli-ui/paths.d.ts.map +1 -0
- package/dist/cli-ui/paths.js +11 -0
- package/dist/cli-ui/paths.js.map +1 -0
- package/dist/cli-ui/prompts.d.ts +65 -0
- package/dist/cli-ui/prompts.d.ts.map +1 -0
- package/dist/cli-ui/prompts.js +125 -0
- package/dist/cli-ui/prompts.js.map +1 -0
- package/dist/cli-ui/service.d.ts +41 -0
- package/dist/cli-ui/service.d.ts.map +1 -0
- package/dist/cli-ui/service.js +241 -0
- package/dist/cli-ui/service.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1143 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/acp-server.d.ts +8 -0
- package/dist/core/acp-server.d.ts.map +1 -0
- package/dist/core/acp-server.js +266 -0
- package/dist/core/acp-server.js.map +1 -0
- package/dist/core/agent-base.d.ts +94 -0
- package/dist/core/agent-base.d.ts.map +1 -0
- package/dist/core/agent-base.js +373 -0
- package/dist/core/agent-base.js.map +1 -0
- package/dist/core/agent-cwd.d.ts +48 -0
- package/dist/core/agent-cwd.d.ts.map +1 -0
- package/dist/core/agent-cwd.js +181 -0
- package/dist/core/agent-cwd.js.map +1 -0
- package/dist/core/agent-helper.d.ts +65 -0
- package/dist/core/agent-helper.d.ts.map +1 -0
- package/dist/core/agent-helper.js +150 -0
- package/dist/core/agent-helper.js.map +1 -0
- package/dist/core/agim-paths.d.ts +10 -0
- package/dist/core/agim-paths.d.ts.map +1 -0
- package/dist/core/agim-paths.js +64 -0
- package/dist/core/agim-paths.js.map +1 -0
- package/dist/core/approval-bus.d.ts +300 -0
- package/dist/core/approval-bus.d.ts.map +1 -0
- package/dist/core/approval-bus.js +990 -0
- package/dist/core/approval-bus.js.map +1 -0
- package/dist/core/approval-router.d.ts +101 -0
- package/dist/core/approval-router.d.ts.map +1 -0
- package/dist/core/approval-router.js +540 -0
- package/dist/core/approval-router.js.map +1 -0
- package/dist/core/audit-log.d.ts +55 -0
- package/dist/core/audit-log.d.ts.map +1 -0
- package/dist/core/audit-log.js +203 -0
- package/dist/core/audit-log.js.map +1 -0
- package/dist/core/bgjob-reader.d.ts +65 -0
- package/dist/core/bgjob-reader.d.ts.map +1 -0
- package/dist/core/bgjob-reader.js +212 -0
- package/dist/core/bgjob-reader.js.map +1 -0
- package/dist/core/circuit-breaker.d.ts +37 -0
- package/dist/core/circuit-breaker.d.ts.map +1 -0
- package/dist/core/circuit-breaker.js +115 -0
- package/dist/core/circuit-breaker.js.map +1 -0
- package/dist/core/commands/agent.d.ts +4 -0
- package/dist/core/commands/agent.d.ts.map +1 -0
- package/dist/core/commands/agent.js +40 -0
- package/dist/core/commands/agent.js.map +1 -0
- package/dist/core/commands/approval.d.ts +3 -0
- package/dist/core/commands/approval.d.ts.map +1 -0
- package/dist/core/commands/approval.js +85 -0
- package/dist/core/commands/approval.js.map +1 -0
- package/dist/core/commands/audit.d.ts +3 -0
- package/dist/core/commands/audit.d.ts.map +1 -0
- package/dist/core/commands/audit.js +84 -0
- package/dist/core/commands/audit.js.map +1 -0
- package/dist/core/commands/builtin.d.ts +3 -0
- package/dist/core/commands/builtin.d.ts.map +1 -0
- package/dist/core/commands/builtin.js +304 -0
- package/dist/core/commands/builtin.js.map +1 -0
- package/dist/core/commands/cron.d.ts +3 -0
- package/dist/core/commands/cron.d.ts.map +1 -0
- package/dist/core/commands/cron.js +128 -0
- package/dist/core/commands/cron.js.map +1 -0
- package/dist/core/commands/job.d.ts +3 -0
- package/dist/core/commands/job.d.ts.map +1 -0
- package/dist/core/commands/job.js +195 -0
- package/dist/core/commands/job.js.map +1 -0
- package/dist/core/commands/memo.d.ts +3 -0
- package/dist/core/commands/memo.d.ts.map +1 -0
- package/dist/core/commands/memo.js +151 -0
- package/dist/core/commands/memo.js.map +1 -0
- package/dist/core/commands/model.d.ts +9 -0
- package/dist/core/commands/model.d.ts.map +1 -0
- package/dist/core/commands/model.js +183 -0
- package/dist/core/commands/model.js.map +1 -0
- package/dist/core/commands/plan.d.ts +3 -0
- package/dist/core/commands/plan.d.ts.map +1 -0
- package/dist/core/commands/plan.js +75 -0
- package/dist/core/commands/plan.js.map +1 -0
- package/dist/core/commands/remind.d.ts +3 -0
- package/dist/core/commands/remind.d.ts.map +1 -0
- package/dist/core/commands/remind.js +271 -0
- package/dist/core/commands/remind.js.map +1 -0
- package/dist/core/commands/router.d.ts +3 -0
- package/dist/core/commands/router.d.ts.map +1 -0
- package/dist/core/commands/router.js +71 -0
- package/dist/core/commands/router.js.map +1 -0
- package/dist/core/commands/sessions.d.ts +3 -0
- package/dist/core/commands/sessions.d.ts.map +1 -0
- package/dist/core/commands/sessions.js +88 -0
- package/dist/core/commands/sessions.js.map +1 -0
- package/dist/core/commands/stats.d.ts +3 -0
- package/dist/core/commands/stats.d.ts.map +1 -0
- package/dist/core/commands/stats.js +73 -0
- package/dist/core/commands/stats.js.map +1 -0
- package/dist/core/commands/think.d.ts +3 -0
- package/dist/core/commands/think.d.ts.map +1 -0
- package/dist/core/commands/think.js +28 -0
- package/dist/core/commands/think.js.map +1 -0
- package/dist/core/commands/workspaces.d.ts +3 -0
- package/dist/core/commands/workspaces.d.ts.map +1 -0
- package/dist/core/commands/workspaces.js +47 -0
- package/dist/core/commands/workspaces.js.map +1 -0
- package/dist/core/config-schema.d.ts +60 -0
- package/dist/core/config-schema.d.ts.map +1 -0
- package/dist/core/config-schema.js +75 -0
- package/dist/core/config-schema.js.map +1 -0
- package/dist/core/coord-systems.d.ts +65 -0
- package/dist/core/coord-systems.d.ts.map +1 -0
- package/dist/core/coord-systems.js +229 -0
- package/dist/core/coord-systems.js.map +1 -0
- package/dist/core/cron.d.ts +29 -0
- package/dist/core/cron.d.ts.map +1 -0
- package/dist/core/cron.js +184 -0
- package/dist/core/cron.js.map +1 -0
- package/dist/core/event-bus.d.ts +80 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +62 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/intent-llm.d.ts +27 -0
- package/dist/core/intent-llm.d.ts.map +1 -0
- package/dist/core/intent-llm.js +170 -0
- package/dist/core/intent-llm.js.map +1 -0
- package/dist/core/intent.d.ts +12 -0
- package/dist/core/intent.d.ts.map +1 -0
- package/dist/core/intent.js +187 -0
- package/dist/core/intent.js.map +1 -0
- package/dist/core/job-board.d.ts +82 -0
- package/dist/core/job-board.d.ts.map +1 -0
- package/dist/core/job-board.js +379 -0
- package/dist/core/job-board.js.map +1 -0
- package/dist/core/location-context.d.ts +32 -0
- package/dist/core/location-context.d.ts.map +1 -0
- package/dist/core/location-context.js +69 -0
- package/dist/core/location-context.js.map +1 -0
- package/dist/core/location-token.d.ts +57 -0
- package/dist/core/location-token.d.ts.map +1 -0
- package/dist/core/location-token.js +128 -0
- package/dist/core/location-token.js.map +1 -0
- package/dist/core/logger.d.ts +6 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +54 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/memo-rpc.d.ts +13 -0
- package/dist/core/memo-rpc.d.ts.map +1 -0
- package/dist/core/memo-rpc.js +288 -0
- package/dist/core/memo-rpc.js.map +1 -0
- package/dist/core/memos.d.ts +163 -0
- package/dist/core/memos.d.ts.map +1 -0
- package/dist/core/memos.js +502 -0
- package/dist/core/memos.js.map +1 -0
- package/dist/core/metrics.d.ts +55 -0
- package/dist/core/metrics.d.ts.map +1 -0
- package/dist/core/metrics.js +291 -0
- package/dist/core/metrics.js.map +1 -0
- package/dist/core/onboarding.d.ts +99 -0
- package/dist/core/onboarding.d.ts.map +1 -0
- package/dist/core/onboarding.js +426 -0
- package/dist/core/onboarding.js.map +1 -0
- package/dist/core/pending-reminder.d.ts +25 -0
- package/dist/core/pending-reminder.d.ts.map +1 -0
- package/dist/core/pending-reminder.js +53 -0
- package/dist/core/pending-reminder.js.map +1 -0
- package/dist/core/rate-limiter.d.ts +44 -0
- package/dist/core/rate-limiter.d.ts.map +1 -0
- package/dist/core/rate-limiter.js +115 -0
- package/dist/core/rate-limiter.js.map +1 -0
- package/dist/core/registry.d.ts +32 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +126 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/remind-intent.d.ts +25 -0
- package/dist/core/remind-intent.d.ts.map +1 -0
- package/dist/core/remind-intent.js +196 -0
- package/dist/core/remind-intent.js.map +1 -0
- package/dist/core/reminder-rpc.d.ts +17 -0
- package/dist/core/reminder-rpc.d.ts.map +1 -0
- package/dist/core/reminder-rpc.js +169 -0
- package/dist/core/reminder-rpc.js.map +1 -0
- package/dist/core/reminders.d.ts +159 -0
- package/dist/core/reminders.d.ts.map +1 -0
- package/dist/core/reminders.js +977 -0
- package/dist/core/reminders.js.map +1 -0
- package/dist/core/router.d.ts +55 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +497 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/schedule.d.ts +65 -0
- package/dist/core/schedule.d.ts.map +1 -0
- package/dist/core/schedule.js +323 -0
- package/dist/core/schedule.js.map +1 -0
- package/dist/core/session.d.ts +182 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +807 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/sqlite-helper.d.ts +37 -0
- package/dist/core/sqlite-helper.d.ts.map +1 -0
- package/dist/core/sqlite-helper.js +79 -0
- package/dist/core/sqlite-helper.js.map +1 -0
- package/dist/core/transcribe.d.ts +25 -0
- package/dist/core/transcribe.d.ts.map +1 -0
- package/dist/core/transcribe.js +217 -0
- package/dist/core/transcribe.js.map +1 -0
- package/dist/core/types.d.ts +360 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/workspace.d.ts +67 -0
- package/dist/core/workspace.d.ts.map +1 -0
- package/dist/core/workspace.js +113 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/agents/acp/acp-adapter.d.ts +16 -0
- package/dist/plugins/agents/acp/acp-adapter.d.ts.map +1 -0
- package/dist/plugins/agents/acp/acp-adapter.js +49 -0
- package/dist/plugins/agents/acp/acp-adapter.js.map +1 -0
- package/dist/plugins/agents/acp/acp-client.d.ts +32 -0
- package/dist/plugins/agents/acp/acp-client.d.ts.map +1 -0
- package/dist/plugins/agents/acp/acp-client.js +177 -0
- package/dist/plugins/agents/acp/acp-client.js.map +1 -0
- package/dist/plugins/agents/acp/discovery.d.ts +19 -0
- package/dist/plugins/agents/acp/discovery.d.ts.map +1 -0
- package/dist/plugins/agents/acp/discovery.js +111 -0
- package/dist/plugins/agents/acp/discovery.js.map +1 -0
- package/dist/plugins/agents/acp/index.d.ts +4 -0
- package/dist/plugins/agents/acp/index.d.ts.map +1 -0
- package/dist/plugins/agents/acp/index.js +4 -0
- package/dist/plugins/agents/acp/index.js.map +1 -0
- package/dist/plugins/agents/acp/types.d.ts +62 -0
- package/dist/plugins/agents/acp/types.d.ts.map +1 -0
- package/dist/plugins/agents/acp/types.js +5 -0
- package/dist/plugins/agents/acp/types.js.map +1 -0
- package/dist/plugins/agents/claude-code/index.d.ts +25 -0
- package/dist/plugins/agents/claude-code/index.d.ts.map +1 -0
- package/dist/plugins/agents/claude-code/index.js +184 -0
- package/dist/plugins/agents/claude-code/index.js.map +1 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts +59 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts.map +1 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.js +645 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.js.map +1 -0
- package/dist/plugins/agents/codex/build-mcp-cli-args.d.ts +28 -0
- package/dist/plugins/agents/codex/build-mcp-cli-args.d.ts.map +1 -0
- package/dist/plugins/agents/codex/build-mcp-cli-args.js +74 -0
- package/dist/plugins/agents/codex/build-mcp-cli-args.js.map +1 -0
- package/dist/plugins/agents/codex/index.d.ts +53 -0
- package/dist/plugins/agents/codex/index.d.ts.map +1 -0
- package/dist/plugins/agents/codex/index.js +341 -0
- package/dist/plugins/agents/codex/index.js.map +1 -0
- package/dist/plugins/agents/copilot/index.d.ts +35 -0
- package/dist/plugins/agents/copilot/index.d.ts.map +1 -0
- package/dist/plugins/agents/copilot/index.js +182 -0
- package/dist/plugins/agents/copilot/index.js.map +1 -0
- package/dist/plugins/agents/opencode/ensure-mcp-config.d.ts +11 -0
- package/dist/plugins/agents/opencode/ensure-mcp-config.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/ensure-mcp-config.js +100 -0
- package/dist/plugins/agents/opencode/ensure-mcp-config.js.map +1 -0
- package/dist/plugins/agents/opencode/index.d.ts +5 -0
- package/dist/plugins/agents/opencode/index.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/index.js +30 -0
- package/dist/plugins/agents/opencode/index.js.map +1 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts +166 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.js +682 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.js.map +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts +32 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js +137 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js.map +1 -0
- package/dist/plugins/agents/opencode/serve-manager.d.ts +27 -0
- package/dist/plugins/agents/opencode/serve-manager.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/serve-manager.js +194 -0
- package/dist/plugins/agents/opencode/serve-manager.js.map +1 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-adapter.d.ts +57 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-adapter.js +409 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-adapter.js.map +1 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-client.d.ts +48 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-client.d.ts.map +1 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-client.js +236 -0
- package/dist/plugins/messengers/dingtalk/dingtalk-client.js.map +1 -0
- package/dist/plugins/messengers/dingtalk/index.d.ts +3 -0
- package/dist/plugins/messengers/dingtalk/index.d.ts.map +1 -0
- package/dist/plugins/messengers/dingtalk/index.js +3 -0
- package/dist/plugins/messengers/dingtalk/index.js.map +1 -0
- package/dist/plugins/messengers/dingtalk/link-coords.d.ts +23 -0
- package/dist/plugins/messengers/dingtalk/link-coords.d.ts.map +1 -0
- package/dist/plugins/messengers/dingtalk/link-coords.js +89 -0
- package/dist/plugins/messengers/dingtalk/link-coords.js.map +1 -0
- package/dist/plugins/messengers/dingtalk/media-store.d.ts +16 -0
- package/dist/plugins/messengers/dingtalk/media-store.d.ts.map +1 -0
- package/dist/plugins/messengers/dingtalk/media-store.js +77 -0
- package/dist/plugins/messengers/dingtalk/media-store.js.map +1 -0
- package/dist/plugins/messengers/dingtalk/types.d.ts +82 -0
- package/dist/plugins/messengers/dingtalk/types.d.ts.map +1 -0
- package/dist/plugins/messengers/dingtalk/types.js +14 -0
- package/dist/plugins/messengers/dingtalk/types.js.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.d.ts +21 -0
- package/dist/plugins/messengers/discord/discord-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.js +238 -0
- package/dist/plugins/messengers/discord/discord-adapter.js.map +1 -0
- package/dist/plugins/messengers/discord/index.d.ts +4 -0
- package/dist/plugins/messengers/discord/index.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/index.js +4 -0
- package/dist/plugins/messengers/discord/index.js.map +1 -0
- package/dist/plugins/messengers/discord/markdown-to-discord.d.ts +11 -0
- package/dist/plugins/messengers/discord/markdown-to-discord.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/markdown-to-discord.js +59 -0
- package/dist/plugins/messengers/discord/markdown-to-discord.js.map +1 -0
- package/dist/plugins/messengers/discord/types.d.ts +9 -0
- package/dist/plugins/messengers/discord/types.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/types.js +3 -0
- package/dist/plugins/messengers/discord/types.js.map +1 -0
- package/dist/plugins/messengers/email/email-adapter.d.ts +33 -0
- package/dist/plugins/messengers/email/email-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/email/email-adapter.js +137 -0
- package/dist/plugins/messengers/email/email-adapter.js.map +1 -0
- package/dist/plugins/messengers/feishu/card-builder.d.ts +23 -0
- package/dist/plugins/messengers/feishu/card-builder.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/card-builder.js +89 -0
- package/dist/plugins/messengers/feishu/card-builder.js.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.d.ts +23 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.js +250 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.js.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-client.d.ts +43 -0
- package/dist/plugins/messengers/feishu/feishu-client.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-client.js +118 -0
- package/dist/plugins/messengers/feishu/feishu-client.js.map +1 -0
- package/dist/plugins/messengers/feishu/index.d.ts +4 -0
- package/dist/plugins/messengers/feishu/index.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/index.js +4 -0
- package/dist/plugins/messengers/feishu/index.js.map +1 -0
- package/dist/plugins/messengers/feishu/types.d.ts +113 -0
- package/dist/plugins/messengers/feishu/types.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/types.js +4 -0
- package/dist/plugins/messengers/feishu/types.js.map +1 -0
- package/dist/plugins/messengers/telegram/index.d.ts +4 -0
- package/dist/plugins/messengers/telegram/index.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/index.js +4 -0
- package/dist/plugins/messengers/telegram/index.js.map +1 -0
- package/dist/plugins/messengers/telegram/markdown-to-html.d.ts +5 -0
- package/dist/plugins/messengers/telegram/markdown-to-html.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/markdown-to-html.js +186 -0
- package/dist/plugins/messengers/telegram/markdown-to-html.js.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.d.ts +59 -0
- package/dist/plugins/messengers/telegram/media-download.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.js +228 -0
- package/dist/plugins/messengers/telegram/media-download.js.map +1 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.d.ts +77 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.js +880 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.js.map +1 -0
- package/dist/plugins/messengers/telegram/types.d.ts +47 -0
- package/dist/plugins/messengers/telegram/types.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/types.js +3 -0
- package/dist/plugins/messengers/telegram/types.js.map +1 -0
- package/dist/plugins/messengers/wechat/context-store.d.ts +18 -0
- package/dist/plugins/messengers/wechat/context-store.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/context-store.js +105 -0
- package/dist/plugins/messengers/wechat/context-store.js.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.d.ts +71 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.js +664 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.js.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-client.d.ts +75 -0
- package/dist/plugins/messengers/wechat/ilink-client.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-client.js +331 -0
- package/dist/plugins/messengers/wechat/ilink-client.js.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-types.d.ts +181 -0
- package/dist/plugins/messengers/wechat/ilink-types.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-types.js +22 -0
- package/dist/plugins/messengers/wechat/ilink-types.js.map +1 -0
- package/dist/plugins/messengers/wechat/media-download.d.ts +32 -0
- package/dist/plugins/messengers/wechat/media-download.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/media-download.js +78 -0
- package/dist/plugins/messengers/wechat/media-download.js.map +1 -0
- package/dist/scripts/migrate-gcj02-to-wgs84.d.ts +3 -0
- package/dist/scripts/migrate-gcj02-to-wgs84.d.ts.map +1 -0
- package/dist/scripts/migrate-gcj02-to-wgs84.js +52 -0
- package/dist/scripts/migrate-gcj02-to-wgs84.js.map +1 -0
- package/dist/utils/backoff.d.ts +35 -0
- package/dist/utils/backoff.d.ts.map +1 -0
- package/dist/utils/backoff.js +59 -0
- package/dist/utils/backoff.js.map +1 -0
- package/dist/utils/cross-platform.d.ts +26 -0
- package/dist/utils/cross-platform.d.ts.map +1 -0
- package/dist/utils/cross-platform.js +58 -0
- package/dist/utils/cross-platform.js.map +1 -0
- package/dist/utils/message-split.d.ts +14 -0
- package/dist/utils/message-split.d.ts.map +1 -0
- package/dist/utils/message-split.js +65 -0
- package/dist/utils/message-split.js.map +1 -0
- package/dist/utils/safe-equal.d.ts +2 -0
- package/dist/utils/safe-equal.d.ts.map +1 -0
- package/dist/utils/safe-equal.js +11 -0
- package/dist/utils/safe-equal.js.map +1 -0
- package/dist/web/public/_app.js +196 -0
- package/dist/web/public/index.html +936 -0
- package/dist/web/public/loc.html +305 -0
- package/dist/web/public/login.html +106 -0
- package/dist/web/public/memos.html +271 -0
- package/dist/web/public/reminders.html +234 -0
- package/dist/web/public/settings.html +1355 -0
- package/dist/web/public/tasks.html +1835 -0
- package/dist/web/server.d.ts +12 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +2399 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +92 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Agim (阿吉姆) CLI — rebranded from im-hub-pro at v1.0.0.
|
|
3
|
+
//
|
|
4
|
+
// Brand: "Agim" (English) / "阿吉姆" (Chinese). Binary is `agim`; the
|
|
5
|
+
// legacy `im-hub-pro` bin name is kept as an alias for systemd units +
|
|
6
|
+
// shell aliases users have already wired up.
|
|
7
|
+
//
|
|
8
|
+
// On-disk paths (~/.im-hub/, ~/.im-hub-workspaces/), env vars (IMHUB_*),
|
|
9
|
+
// HTTP headers (X-IM-Hub-Token), and the systemd unit `im-hub.service`
|
|
10
|
+
// keep their legacy "im-hub" identifier for drop-in compatibility with
|
|
11
|
+
// existing installations. Do not "fix" them — millions of bytes of user
|
|
12
|
+
// data + every existing deployment's systemd config depend on those
|
|
13
|
+
// names being stable. The rename is a brand surface only.
|
|
14
|
+
import { program } from 'commander';
|
|
15
|
+
import { dirname, join } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
import { randomUUID } from 'node:crypto';
|
|
19
|
+
import { registry } from './core/registry.js';
|
|
20
|
+
import { sessionManager } from './core/session.js';
|
|
21
|
+
import { parseMessage, routeMessage } from './core/router.js';
|
|
22
|
+
import { crossSpawn, isMac, isWindows } from './utils/cross-platform.js';
|
|
23
|
+
import { generateTraceId, createLogger } from './core/logger.js';
|
|
24
|
+
import { validateConfig } from './core/config-schema.js';
|
|
25
|
+
import { workspaceRegistry } from './core/workspace.js';
|
|
26
|
+
import { bootstrapAgentWorkspaces } from './core/agent-cwd.js';
|
|
27
|
+
import { approvalBus, threadKey } from './core/approval-bus.js';
|
|
28
|
+
import { install as installApprovalRouter, tryHandleApprovalReply, platformToMessengerName } from './core/approval-router.js';
|
|
29
|
+
import { tryConsumeReply as tryConsumePendingReminderReply } from './core/pending-reminder.js';
|
|
30
|
+
import { consumeLocationContext, formatLocationAnnotation } from './core/location-context.js';
|
|
31
|
+
import { tryDetectReminderIntent } from './core/remind-intent.js';
|
|
32
|
+
import { createReminder } from './core/reminders.js';
|
|
33
|
+
import { setReminderConfirmNotifier } from './core/reminder-rpc.js';
|
|
34
|
+
import { checkMessengerConfig, checkAgentAvailability, runMessengerOnboarding, formatAgentInstallHint, formatMessengerStartError, loadConfig as loadOnboardingConfig, saveConfig as saveOnboardingConfig, } from './core/onboarding.js';
|
|
35
|
+
import { startWebServer } from './web/server.js';
|
|
36
|
+
import { startACPServer } from './core/acp-server.js';
|
|
37
|
+
import { AGIM_HOME } from './core/agim-paths.js';
|
|
38
|
+
// Helper to format agent install hint for missing agents
|
|
39
|
+
function _formatMissingAgentHint(missing) {
|
|
40
|
+
return formatAgentInstallHint(missing);
|
|
41
|
+
}
|
|
42
|
+
const CONFIG_DIR = AGIM_HOME;
|
|
43
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
44
|
+
const PACKAGE_JSON = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
45
|
+
async function loadConfig() {
|
|
46
|
+
return loadOnboardingConfig();
|
|
47
|
+
}
|
|
48
|
+
async function saveConfig(config) {
|
|
49
|
+
return saveOnboardingConfig(config);
|
|
50
|
+
}
|
|
51
|
+
function readPackageVersion() {
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(readFileSync(PACKAGE_JSON, 'utf-8'));
|
|
54
|
+
if (typeof parsed.version === 'string' && parsed.version.trim()) {
|
|
55
|
+
return parsed.version;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Keep --help/--version usable even in unusual source-tree layouts.
|
|
60
|
+
}
|
|
61
|
+
return '0.0.0';
|
|
62
|
+
}
|
|
63
|
+
program
|
|
64
|
+
.name('agim')
|
|
65
|
+
.description('Universal messenger-to-agent bridge')
|
|
66
|
+
.version(readPackageVersion());
|
|
67
|
+
program
|
|
68
|
+
.command('start')
|
|
69
|
+
.description('Start the Agim server (foreground by default; --bg for detached daemon)')
|
|
70
|
+
.option('--bg, --background', 'Start as a detached background daemon (writes pid file + log to ~/.im-hub/)')
|
|
71
|
+
.action(async (opts) => {
|
|
72
|
+
// `--bg` (or its alias `--background`): hand off to the bg-spawner.
|
|
73
|
+
// It re-execs us with `start` (no flag) inside a detached child so
|
|
74
|
+
// the actual foreground start logic below runs in that child.
|
|
75
|
+
if (opts.bg || opts.background) {
|
|
76
|
+
const { cmdStart } = await import('./cli-ui/cmd-handlers.js');
|
|
77
|
+
await cmdStart(true, async () => { });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
console.log('🚀 Starting Agim (阿吉姆)…');
|
|
81
|
+
let config = await loadConfig();
|
|
82
|
+
console.log(`Config loaded from ${CONFIG_FILE}`);
|
|
83
|
+
// Validate config schema — always use the zod-validated output (which
|
|
84
|
+
// applies defaults and strips invalid fields) instead of the raw config.
|
|
85
|
+
const validation = validateConfig(config);
|
|
86
|
+
if (!validation.ok) {
|
|
87
|
+
console.warn('⚠️ Config schema issues detected:');
|
|
88
|
+
for (const err of validation.errors) {
|
|
89
|
+
console.warn(` - ${err}`);
|
|
90
|
+
}
|
|
91
|
+
console.warn(' Agim will continue with defaults for invalid fields.\n');
|
|
92
|
+
}
|
|
93
|
+
config = validation.config;
|
|
94
|
+
// Initialize workspace registry
|
|
95
|
+
const rawConfig = config;
|
|
96
|
+
const workspaces = rawConfig.workspaces;
|
|
97
|
+
workspaceRegistry.load({ workspaces: workspaces });
|
|
98
|
+
const wsCount = workspaces?.length || 1;
|
|
99
|
+
console.log(`Workspaces loaded: ${wsCount} workspace(s)`);
|
|
100
|
+
// Initialize session manager
|
|
101
|
+
await sessionManager.start();
|
|
102
|
+
// Start approval-bus before any agent can spawn — failure here is
|
|
103
|
+
// non-fatal: claude-code falls back to legacy --permission-mode dontAsk.
|
|
104
|
+
if (process.env.IMHUB_APPROVAL_DISABLED === '1') {
|
|
105
|
+
console.log('🛑 Approval bus disabled via IMHUB_APPROVAL_DISABLED=1');
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
try {
|
|
109
|
+
const sockPath = await approvalBus.start();
|
|
110
|
+
console.log(`✅ Approval bus listening on ${sockPath}`);
|
|
111
|
+
// Expose the path through env so long-lived child processes
|
|
112
|
+
// (notably `opencode serve`, which spawns the MCP sidecar with
|
|
113
|
+
// `env: process.env`) can connect without per-spawn config.
|
|
114
|
+
// claude-code and opencode stdio still set this per-spawn via
|
|
115
|
+
// extraEnv — that override wins inside their spawn scope.
|
|
116
|
+
process.env.IMHUB_APPROVAL_SOCK = sockPath;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
120
|
+
console.warn(`⚠️ Approval bus failed to start (${msg}); claude-code will fall back to dontAsk mode`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Wire the bus's lifetime counters into /api/metrics (M14). Done even
|
|
124
|
+
// when the bus failed to start — getMetrics() returns zeros, which is
|
|
125
|
+
// the correct Prometheus snapshot for "no approvals processed".
|
|
126
|
+
const { setApprovalBusSnapshotProvider } = await import('./core/metrics.js');
|
|
127
|
+
setApprovalBusSnapshotProvider(() => approvalBus.getMetrics());
|
|
128
|
+
// Load plugins FIRST (agents won't be registered until this runs)
|
|
129
|
+
await registry.loadBuiltInPlugins();
|
|
130
|
+
// Bootstrap per-agent IM workspaces. Idempotent — creates
|
|
131
|
+
// ~/.im-hub-workspaces/<agent>/ and seeds CLAUDE.md / AGENTS.md only if
|
|
132
|
+
// they don't exist yet. See docs/architecture/agent-cwd-and-memory.md.
|
|
133
|
+
try {
|
|
134
|
+
const bootstrapped = await bootstrapAgentWorkspaces();
|
|
135
|
+
for (const { agent, dir } of bootstrapped) {
|
|
136
|
+
console.log(`📁 Agent workspace: ${agent} → ${dir}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
141
|
+
console.warn(`⚠️ Agent workspace bootstrap failed: ${msg}`);
|
|
142
|
+
console.warn(' Agents will fall back to Agim cwd ("/" under systemd)');
|
|
143
|
+
}
|
|
144
|
+
// Register the imhub MCP server in opencode's global config so opencode
|
|
145
|
+
// (stdio mode) can see the reminder tools. Idempotent; quietly skips if
|
|
146
|
+
// opencode isn't installed / no config dir / write fails. HTTP mode
|
|
147
|
+
// can't use this — see ensure-mcp-config.ts comments.
|
|
148
|
+
try {
|
|
149
|
+
const { ensureOpencodeMcpRegistered } = await import('./plugins/agents/opencode/ensure-mcp-config.js');
|
|
150
|
+
const wrote = await ensureOpencodeMcpRegistered();
|
|
151
|
+
if (wrote)
|
|
152
|
+
console.log('🔧 Registered imhub MCP in opencode global config');
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
156
|
+
console.warn(`⚠️ opencode MCP registration skipped: ${msg}`);
|
|
157
|
+
}
|
|
158
|
+
// Load ACP (remote) agents from config
|
|
159
|
+
if (config.acpAgents?.length) {
|
|
160
|
+
await registry.loadACPAgents(config.acpAgents);
|
|
161
|
+
}
|
|
162
|
+
// Discover ACP agents via .well-known/acp on configured base URLs
|
|
163
|
+
const discoveryUrls = config.acpDiscoveryUrls;
|
|
164
|
+
if (discoveryUrls?.length) {
|
|
165
|
+
await registry.loadDiscoveredACPAgents(discoveryUrls);
|
|
166
|
+
}
|
|
167
|
+
// Start the scheduler (runs cron-due schedules every 30s)
|
|
168
|
+
const { startScheduler } = await import('./core/schedule.js');
|
|
169
|
+
startScheduler();
|
|
170
|
+
// Start the reminder engine (one-shot lightweight timers, 5s tick)
|
|
171
|
+
const { startReminderEngine } = await import('./core/reminders.js');
|
|
172
|
+
startReminderEngine();
|
|
173
|
+
// Start the memo expiry sweeper (every 5 min — purges memos with
|
|
174
|
+
// expires_at <= now). Idempotent: safe even if startSweepTimer is
|
|
175
|
+
// called twice. Lazy import keeps memos.ts out of the cold-start path
|
|
176
|
+
// until it's actually needed.
|
|
177
|
+
const { startSweepTimer: startMemoSweep } = await import('./core/memos.js');
|
|
178
|
+
startMemoSweep();
|
|
179
|
+
// ============================================
|
|
180
|
+
// ONBOARDING CHECKS (before default fill!)
|
|
181
|
+
// ============================================
|
|
182
|
+
// Check messengers BEFORE the default fill
|
|
183
|
+
const onboardingResult = checkMessengerConfig(config);
|
|
184
|
+
if (onboardingResult.needsOnboarding) {
|
|
185
|
+
console.log('👋 No messengers configured.\n');
|
|
186
|
+
const newConfig = await runMessengerOnboarding(config);
|
|
187
|
+
if (!newConfig) {
|
|
188
|
+
console.log('\n❌ Onboarding cancelled.');
|
|
189
|
+
console.log('Run "agim config <messenger>" to configure manually.');
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
config = newConfig;
|
|
193
|
+
await saveConfig(config);
|
|
194
|
+
}
|
|
195
|
+
// Check agents (async, AFTER plugins loaded)
|
|
196
|
+
const agentResult = await checkAgentAvailability();
|
|
197
|
+
if (agentResult.allMissing) {
|
|
198
|
+
console.log('\n⚠️ No coding agents found!');
|
|
199
|
+
console.log(formatAgentInstallHint(agentResult.missing));
|
|
200
|
+
console.log('\nInstall at least one agent, then run agim start again.');
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
else if (agentResult.missing.length > 0) {
|
|
204
|
+
console.log('⚠️ Some agents not available:', agentResult.missing.join(', '));
|
|
205
|
+
console.log(` ${formatAgentInstallHint(agentResult.missing)}`);
|
|
206
|
+
console.log('');
|
|
207
|
+
}
|
|
208
|
+
// Set defaultAgent to first available installed agent
|
|
209
|
+
if (agentResult.available.length > 0) {
|
|
210
|
+
config.defaultAgent = agentResult.available[0];
|
|
211
|
+
}
|
|
212
|
+
// ============================================
|
|
213
|
+
// START MESSENGERS
|
|
214
|
+
// ============================================
|
|
215
|
+
// Get messengers to start (now config.messengers is populated)
|
|
216
|
+
const messengersToStart = config.messengers.length > 0
|
|
217
|
+
? [...config.messengers]
|
|
218
|
+
: ['wechat-ilink']; // Fallback default
|
|
219
|
+
// Auto-include the email adapter so /remind email ... works without
|
|
220
|
+
// explicit `agim config email`. The adapter's start() is fail-soft
|
|
221
|
+
// when SMTP env vars are missing — it stays "registered but disabled"
|
|
222
|
+
// and any actual send call throws a clear "not configured" error.
|
|
223
|
+
if (!messengersToStart.includes('email')) {
|
|
224
|
+
messengersToStart.push('email');
|
|
225
|
+
}
|
|
226
|
+
// Start messenger adapters
|
|
227
|
+
for (const name of messengersToStart) {
|
|
228
|
+
const messenger = registry.getMessenger(name);
|
|
229
|
+
if (!messenger) {
|
|
230
|
+
console.warn(`⚠️ Messenger "${name}" not found, skipping`);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
// Set up message handler
|
|
234
|
+
messenger.onMessage(async (ctx) => {
|
|
235
|
+
const traceId = generateTraceId();
|
|
236
|
+
ctx.traceId = traceId;
|
|
237
|
+
ctx.logger = createLogger({ traceId, platform: ctx.platform, component: 'cli' });
|
|
238
|
+
ctx.logger.info({ event: 'message.received', text: ctx.message.text.substring(0, 120), userId: ctx.message.userId });
|
|
239
|
+
// Approval interception comes BEFORE the agent router. If a pending
|
|
240
|
+
// approval exists for this thread and the message is a y/n-style
|
|
241
|
+
// reply, we resolve the approval and stop. Anything else routes
|
|
242
|
+
// normally (with the side effect of auto-denying any stale pending —
|
|
243
|
+
// see approval-router.ts).
|
|
244
|
+
if (tryHandleApprovalReply(threadKey(ctx.platform, ctx.channelId, ctx.message.threadId), ctx.message.text)) {
|
|
245
|
+
ctx.logger.info({ event: 'message.consumed_by_approval' });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// Reminder-intent confirmation reply ("y"/"n" to a previously-
|
|
249
|
+
// proposed reminder card). Must come BEFORE the detector so a
|
|
250
|
+
// user replying "y" doesn't get re-detected as another reminder.
|
|
251
|
+
const tk = `${ctx.platform}:${ctx.channelId}:${ctx.message.threadId}`;
|
|
252
|
+
// Location-context injection: if the user shared a native pin /
|
|
253
|
+
// venue on Telegram or Feishu within the last 5 min, the adapter
|
|
254
|
+
// stashed the WGS-84 coords in core/location-context. Prepend a
|
|
255
|
+
// structured annotation to the user's text so the agent can decide
|
|
256
|
+
// what to do (save_memo with these coords, ignore, ask follow-up).
|
|
257
|
+
// Single-shot: consuming clears the stash so the next turn doesn't
|
|
258
|
+
// accidentally re-inject. Matches the WeChat H5-flow architecture:
|
|
259
|
+
// the agent is the decision-maker; adapters just surface data.
|
|
260
|
+
const locCtxStash = consumeLocationContext(tk);
|
|
261
|
+
if (locCtxStash) {
|
|
262
|
+
const annotation = formatLocationAnnotation(locCtxStash);
|
|
263
|
+
ctx.message.text = `${annotation}\n\n${ctx.message.text}`;
|
|
264
|
+
ctx.logger.info({
|
|
265
|
+
event: 'message.location_context_injected',
|
|
266
|
+
lat: locCtxStash.lat,
|
|
267
|
+
lng: locCtxStash.lng,
|
|
268
|
+
hasVenue: !!locCtxStash.venueLabel,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
const replyDecision = tryConsumePendingReminderReply(tk, ctx.message.text);
|
|
272
|
+
if (replyDecision) {
|
|
273
|
+
if (replyDecision.decision === 'cancel') {
|
|
274
|
+
await messenger.sendMessage(ctx.message.threadId, '✅ 已忽略这条提醒建议');
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
const p = replyDecision.pending;
|
|
278
|
+
try {
|
|
279
|
+
const id = createReminder({
|
|
280
|
+
fireAt: p.fireAt,
|
|
281
|
+
text: p.text,
|
|
282
|
+
platform: ctx.platform,
|
|
283
|
+
channelId: ctx.channelId,
|
|
284
|
+
threadId: ctx.message.threadId,
|
|
285
|
+
userId: ctx.message.userId || '',
|
|
286
|
+
source: 'slash',
|
|
287
|
+
recurrence: p.recurrence,
|
|
288
|
+
});
|
|
289
|
+
const recurLine = p.recurrence ? `\n 循环:${p.recurrence}` : '';
|
|
290
|
+
await messenger.sendMessage(ctx.message.threadId, `✅ 已创建提醒 #${id}\n 触发:${p.fireAt.toLocaleString()}${recurLine}\n 内容:${p.text}`);
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
294
|
+
await messenger.sendMessage(ctx.message.threadId, `❌ 创建失败:${msg}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
ctx.logger.info({ event: 'message.consumed_by_pending_reminder', decision: replyDecision.decision });
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
// Reminder-intent detector: only on non-slash messages and only
|
|
301
|
+
// when the user has aiwatch on. Cheap regex pre-filter inside the
|
|
302
|
+
// detector skips most messages without a time hint, so the LLM
|
|
303
|
+
// call only fires on plausible candidates.
|
|
304
|
+
if (!ctx.message.text.trim().startsWith('/')) {
|
|
305
|
+
try {
|
|
306
|
+
const detected = await tryDetectReminderIntent({
|
|
307
|
+
platform: ctx.platform,
|
|
308
|
+
channelId: ctx.channelId,
|
|
309
|
+
threadId: ctx.message.threadId,
|
|
310
|
+
userId: ctx.message.userId || '',
|
|
311
|
+
text: ctx.message.text,
|
|
312
|
+
});
|
|
313
|
+
if (detected.handled && detected.reply) {
|
|
314
|
+
await messenger.sendMessage(ctx.message.threadId, detected.reply);
|
|
315
|
+
ctx.logger.info({ event: 'message.proposed_reminder' });
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
// Detector must never break the message pipeline. Log and
|
|
321
|
+
// fall through to normal agent dispatch.
|
|
322
|
+
ctx.logger.warn({
|
|
323
|
+
event: 'remind-intent.crashed',
|
|
324
|
+
err: err instanceof Error ? err.message : String(err),
|
|
325
|
+
}, 'detector crashed — falling through to normal agent flow');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
await handleMessage(ctx, config.defaultAgent);
|
|
329
|
+
});
|
|
330
|
+
try {
|
|
331
|
+
await messenger.start();
|
|
332
|
+
console.log(`✅ Started messenger: ${name}`);
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
336
|
+
console.error(`❌ Failed to start messenger ${name}:`);
|
|
337
|
+
console.error(` ${errMsg}`);
|
|
338
|
+
// Show actionable next step, not stack trace
|
|
339
|
+
const hint = formatMessengerStartError(name, error);
|
|
340
|
+
if (hint !== errMsg) {
|
|
341
|
+
console.error(` ${hint}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// ============================================
|
|
346
|
+
// WIRE APPROVAL ROUTER (after messengers are up)
|
|
347
|
+
// ============================================
|
|
348
|
+
if (approvalBus.getSocketPath()) {
|
|
349
|
+
installApprovalRouter({
|
|
350
|
+
resolveMessenger: (platform) => registry.getMessenger(platformToMessengerName(platform)),
|
|
351
|
+
});
|
|
352
|
+
console.log('✅ Approval router wired to messengers');
|
|
353
|
+
}
|
|
354
|
+
// Reminder confirm notifier — used by reminder-rpc.ts when an agent
|
|
355
|
+
// creates a high-frequency + LLM-polish reminder (those go through a
|
|
356
|
+
// y/n card before landing in the DB). Plain message via the same
|
|
357
|
+
// messenger the agent is talking on.
|
|
358
|
+
setReminderConfirmNotifier(async (ctx, message) => {
|
|
359
|
+
const messenger = registry.getMessenger(platformToMessengerName(ctx.platform))
|
|
360
|
+
?? registry.getMessenger(ctx.platform);
|
|
361
|
+
if (!messenger) {
|
|
362
|
+
console.warn(`reminder-confirm: no messenger for platform "${ctx.platform}"`);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
await messenger.sendMessage(ctx.threadId, message);
|
|
366
|
+
});
|
|
367
|
+
// ============================================
|
|
368
|
+
// START WEB CHAT SERVER
|
|
369
|
+
// ============================================
|
|
370
|
+
let webServer;
|
|
371
|
+
try {
|
|
372
|
+
webServer = await startWebServer({
|
|
373
|
+
port: config.webPort,
|
|
374
|
+
defaultAgent: config.defaultAgent,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
379
|
+
console.warn(`⚠️ Web chat server failed to start: ${errMsg}`);
|
|
380
|
+
}
|
|
381
|
+
// ============================================
|
|
382
|
+
// START ACP SERVER
|
|
383
|
+
// ============================================
|
|
384
|
+
let acpServer;
|
|
385
|
+
const acpPort = config.acpPort || undefined;
|
|
386
|
+
try {
|
|
387
|
+
acpServer = await startACPServer({
|
|
388
|
+
port: acpPort,
|
|
389
|
+
defaultAgent: config.defaultAgent,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
catch (err) {
|
|
393
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
394
|
+
console.warn(`⚠️ ACP server failed to start: ${errMsg}`);
|
|
395
|
+
}
|
|
396
|
+
console.log('\n✅ IM hub is running!');
|
|
397
|
+
if (webServer) {
|
|
398
|
+
console.log(` Chat UI: http://localhost:${webServer.port}`);
|
|
399
|
+
}
|
|
400
|
+
if (acpServer) {
|
|
401
|
+
console.log(` ACP Endpoint: http://localhost:${acpServer.port}`);
|
|
402
|
+
}
|
|
403
|
+
console.log('Press Ctrl+C to stop');
|
|
404
|
+
// Keep process alive
|
|
405
|
+
process.on('SIGINT', async () => {
|
|
406
|
+
console.log('\n👋 Shutting down...');
|
|
407
|
+
sessionManager.stop();
|
|
408
|
+
webServer?.close();
|
|
409
|
+
acpServer?.close();
|
|
410
|
+
// Stop all messengers
|
|
411
|
+
for (const name of registry.listMessengers()) {
|
|
412
|
+
const messenger = registry.getMessenger(name);
|
|
413
|
+
if (messenger) {
|
|
414
|
+
await messenger.stop();
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Stop approval bus last — denies any in-flight approvals so sidecar
|
|
418
|
+
// processes don't hang. Always called even if start() failed earlier.
|
|
419
|
+
try {
|
|
420
|
+
await approvalBus.stop();
|
|
421
|
+
}
|
|
422
|
+
catch { /* ignore */ }
|
|
423
|
+
// L2: stop background sweepers and checkpoint SQLite WAL files before
|
|
424
|
+
// exiting. Without these, the next startup has to recover the WAL
|
|
425
|
+
// (cheap but visible in logs and slows boot).
|
|
426
|
+
try {
|
|
427
|
+
const { stopRetentionSweep, closeAuditDb } = await import('./core/audit-log.js');
|
|
428
|
+
stopRetentionSweep();
|
|
429
|
+
closeAuditDb();
|
|
430
|
+
}
|
|
431
|
+
catch { /* ignore */ }
|
|
432
|
+
try {
|
|
433
|
+
const { stopJobRetentionSweep, closeJobBoardDb } = await import('./core/job-board.js');
|
|
434
|
+
stopJobRetentionSweep();
|
|
435
|
+
closeJobBoardDb();
|
|
436
|
+
}
|
|
437
|
+
catch { /* ignore */ }
|
|
438
|
+
try {
|
|
439
|
+
const { stopScheduler, closeScheduleDb } = await import('./core/schedule.js');
|
|
440
|
+
stopScheduler();
|
|
441
|
+
closeScheduleDb();
|
|
442
|
+
}
|
|
443
|
+
catch { /* ignore */ }
|
|
444
|
+
try {
|
|
445
|
+
const { stopReminderEngine, closeReminderDb } = await import('./core/reminders.js');
|
|
446
|
+
stopReminderEngine();
|
|
447
|
+
closeReminderDb();
|
|
448
|
+
}
|
|
449
|
+
catch { /* ignore */ }
|
|
450
|
+
try {
|
|
451
|
+
const { closeMemosDb } = await import('./core/memos.js');
|
|
452
|
+
closeMemosDb(); // also stops the sweep timer
|
|
453
|
+
}
|
|
454
|
+
catch { /* ignore */ }
|
|
455
|
+
process.exit(0);
|
|
456
|
+
});
|
|
457
|
+
// Wait forever
|
|
458
|
+
await new Promise(() => { });
|
|
459
|
+
});
|
|
460
|
+
/**
|
|
461
|
+
* Handle incoming message from any messenger
|
|
462
|
+
*/
|
|
463
|
+
async function handleMessage(ctx, defaultAgent) {
|
|
464
|
+
const { message, platform } = ctx;
|
|
465
|
+
const traceId = ctx.traceId || generateTraceId();
|
|
466
|
+
const logger = ctx.logger || createLogger({ traceId, platform, component: 'cli' });
|
|
467
|
+
const messengerName = platform === 'wechat' ? 'wechat-ilink' : platform;
|
|
468
|
+
const messenger = registry.getMessenger(messengerName);
|
|
469
|
+
if (!messenger) {
|
|
470
|
+
console.error(`No messenger found for platform: ${platform}`);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
// Agent-name prefix removed — historically we prepended "[opencode]\n\n"
|
|
474
|
+
// / "[codex]\n\n" / etc. so users could see which agent answered when
|
|
475
|
+
// they were on a non-default sticky session. In practice it cluttered
|
|
476
|
+
// every reply (most users only ever stick with one agent per channel)
|
|
477
|
+
// and made memo/reminder confirmations look noisy. If the user wants to
|
|
478
|
+
// know the active agent they can run `/status` or `/router status`.
|
|
479
|
+
const maybePrefix = async (text) => text;
|
|
480
|
+
const stopTyping = async () => {
|
|
481
|
+
if (messenger.sendTyping) {
|
|
482
|
+
try {
|
|
483
|
+
await messenger.sendTyping(message.threadId, false);
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
// Ignore typing errors
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
// Build route context with trace
|
|
491
|
+
const routeCtx = {
|
|
492
|
+
threadId: message.threadId,
|
|
493
|
+
channelId: ctx.channelId,
|
|
494
|
+
platform,
|
|
495
|
+
defaultAgent,
|
|
496
|
+
traceId,
|
|
497
|
+
logger,
|
|
498
|
+
userId: message.userId,
|
|
499
|
+
};
|
|
500
|
+
// Thinking placeholder — sent if the adapter supports it, dismissed (when
|
|
501
|
+
// the adapter knows how) just before the real response goes out. Skip for
|
|
502
|
+
// y/n-style approval replies which already get a same-thread effect from
|
|
503
|
+
// the resolved approval card; sending "🤔 思考中…" there would just add
|
|
504
|
+
// noise. We approximate that filter by skipping placeholders for messages
|
|
505
|
+
// that look like single-token approval words.
|
|
506
|
+
let dismissThinking;
|
|
507
|
+
const looksLikeApproval = /^\s*[yn]\s*$/i.test(message.text) ||
|
|
508
|
+
/^\s*(批准|拒绝|同意|不同意|通过|可以|不可以|不行|✅|❌)\s*$/.test(message.text);
|
|
509
|
+
try {
|
|
510
|
+
if (messenger.sendTyping) {
|
|
511
|
+
messenger.sendTyping(message.threadId, true).catch(() => { });
|
|
512
|
+
}
|
|
513
|
+
const parsed = parseMessage(message.text);
|
|
514
|
+
logger.debug({ event: 'router.parse', parsed: parsed.type });
|
|
515
|
+
// Only show "🤔 思考中…" for messages that will actually go through the
|
|
516
|
+
// agent — built-in/system commands (/help /status /audit /router etc.)
|
|
517
|
+
// respond instantly so a placeholder would race the real reply.
|
|
518
|
+
const willInvokeAgent = (parsed.type === 'default' || parsed.type === 'agent' || parsed.type === 'agentCommand') &&
|
|
519
|
+
!looksLikeApproval;
|
|
520
|
+
// Prepare native agent sessions lazily — the router calls
|
|
521
|
+
// onAgentResolved once intent classification picks the final agent,
|
|
522
|
+
// so we never allocate a Claude UUID for an opencode run (or vice versa).
|
|
523
|
+
//
|
|
524
|
+
// Important: claude-code writes its session jsonl as soon as the run
|
|
525
|
+
// starts, so markClaudeSessionPrimed MUST happen before the adapter is
|
|
526
|
+
// invoked. If the run later errors, the next turn must use --resume or
|
|
527
|
+
// claude refuses with "session already exists". We therefore mark
|
|
528
|
+
// primed inside onAgentResolved (right after we wire agentSessionId)
|
|
529
|
+
// rather than after routeMessage returns.
|
|
530
|
+
if (willInvokeAgent) {
|
|
531
|
+
routeCtx.onAgentResolved = async (resolvedAgent) => {
|
|
532
|
+
const stickySession = await sessionManager.getExistingSession(platform, ctx.channelId, message.threadId);
|
|
533
|
+
if (resolvedAgent === 'claude-code') {
|
|
534
|
+
let claudeId = stickySession?.claudeSessionId;
|
|
535
|
+
if (!claudeId) {
|
|
536
|
+
claudeId = randomUUID();
|
|
537
|
+
await sessionManager.getOrCreateSession(platform, ctx.channelId, message.threadId, resolvedAgent);
|
|
538
|
+
await sessionManager.setClaudeSessionId(platform, ctx.channelId, message.threadId, claudeId);
|
|
539
|
+
}
|
|
540
|
+
routeCtx.agentSessionId = claudeId;
|
|
541
|
+
const willResume = !!stickySession?.claudeSessionPrimed;
|
|
542
|
+
routeCtx.agentSessionResume = willResume;
|
|
543
|
+
if (!willResume) {
|
|
544
|
+
try {
|
|
545
|
+
await sessionManager.markClaudeSessionPrimed(platform, ctx.channelId, message.threadId);
|
|
546
|
+
}
|
|
547
|
+
catch (err) {
|
|
548
|
+
logger.debug({ err: String(err) }, 'markClaudeSessionPrimed failed');
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else if (resolvedAgent === 'opencode') {
|
|
553
|
+
const ocId = stickySession?.opencodeSessionId;
|
|
554
|
+
if (ocId) {
|
|
555
|
+
routeCtx.agentSessionId = ocId;
|
|
556
|
+
routeCtx.agentSessionResume = true;
|
|
557
|
+
}
|
|
558
|
+
await sessionManager.getOrCreateSession(platform, ctx.channelId, message.threadId, resolvedAgent);
|
|
559
|
+
}
|
|
560
|
+
else if (resolvedAgent === 'codex') {
|
|
561
|
+
const cxId = stickySession?.codexSessionId;
|
|
562
|
+
if (cxId) {
|
|
563
|
+
routeCtx.agentSessionId = cxId;
|
|
564
|
+
routeCtx.agentSessionResume = true;
|
|
565
|
+
}
|
|
566
|
+
await sessionManager.getOrCreateSession(platform, ctx.channelId, message.threadId, resolvedAgent);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
if (willInvokeAgent && messenger.sendThinking) {
|
|
571
|
+
try {
|
|
572
|
+
dismissThinking = await messenger.sendThinking(message.threadId, '🤔 思考中…');
|
|
573
|
+
}
|
|
574
|
+
catch (err) {
|
|
575
|
+
logger.debug({ err: String(err) }, 'sendThinking failed');
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const result = await routeMessage(parsed, routeCtx);
|
|
579
|
+
const dismiss = async () => {
|
|
580
|
+
if (dismissThinking) {
|
|
581
|
+
try {
|
|
582
|
+
await dismissThinking();
|
|
583
|
+
}
|
|
584
|
+
catch { /* ignore */ }
|
|
585
|
+
dismissThinking = undefined;
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
// Handle response (string or async generator)
|
|
589
|
+
if (typeof result === 'string') {
|
|
590
|
+
await stopTyping();
|
|
591
|
+
await dismiss();
|
|
592
|
+
await messenger.sendMessage(message.threadId, await maybePrefix(result));
|
|
593
|
+
logger.info({ event: 'message.sent', responseLen: result.length });
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
// Stream response chunks
|
|
597
|
+
let fullResponse = '';
|
|
598
|
+
for await (const chunk of result) {
|
|
599
|
+
fullResponse += chunk;
|
|
600
|
+
}
|
|
601
|
+
await stopTyping();
|
|
602
|
+
await dismiss();
|
|
603
|
+
if (fullResponse) {
|
|
604
|
+
await messenger.sendMessage(message.threadId, await maybePrefix(fullResponse));
|
|
605
|
+
logger.info({ event: 'message.sent', responseLen: fullResponse.length });
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
logger.warn({ event: 'message.empty_response' });
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
614
|
+
logger.error({ event: 'message.error', error: errMsg });
|
|
615
|
+
await stopTyping();
|
|
616
|
+
if (dismissThinking) {
|
|
617
|
+
try {
|
|
618
|
+
await dismissThinking();
|
|
619
|
+
}
|
|
620
|
+
catch { /* ignore */ }
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
await messenger.sendMessage(message.threadId, '❌ An error occurred processing your message.');
|
|
624
|
+
}
|
|
625
|
+
catch {
|
|
626
|
+
// Ignore
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
program
|
|
631
|
+
.command('config [component]')
|
|
632
|
+
.description('Interactive config wizard (or `config <component>` for the per-component legacy flow)')
|
|
633
|
+
.action(async (component) => {
|
|
634
|
+
if (!component) {
|
|
635
|
+
// New interactive wizard (since v0.5.0). Falls through to the
|
|
636
|
+
// per-component switch below when an explicit `config wechat` etc.
|
|
637
|
+
// is passed — keeps scripted setups working.
|
|
638
|
+
const { pickLang } = await import('./cli-ui/lang-picker.js');
|
|
639
|
+
const { runConfigWizard } = await import('./cli-ui/config-wizard.js');
|
|
640
|
+
const lang = await pickLang();
|
|
641
|
+
await runConfigWizard(lang);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const config = await loadConfig();
|
|
645
|
+
switch (component) {
|
|
646
|
+
case 'wechat': {
|
|
647
|
+
console.log('📱 Configuring WeChat adapter...');
|
|
648
|
+
console.log('Fetching QR code...\n');
|
|
649
|
+
// Import the iLink adapter for QR login
|
|
650
|
+
const { ILinkWeChatAdapter } = await import('./plugins/messengers/wechat/ilink-adapter.js');
|
|
651
|
+
const adapter = new ILinkWeChatAdapter();
|
|
652
|
+
try {
|
|
653
|
+
// Get QR code URL and token
|
|
654
|
+
const { qrUrl, qrToken } = await adapter.startQRLogin();
|
|
655
|
+
console.log('📱 Scan this QR code with WeChat:\n');
|
|
656
|
+
console.log(qrUrl);
|
|
657
|
+
console.log('\n');
|
|
658
|
+
// Poll for login status
|
|
659
|
+
const credentials = await adapter.waitForQRLogin(qrToken, (status) => {
|
|
660
|
+
console.log(`[${new Date().toLocaleTimeString()}] ${status}`);
|
|
661
|
+
});
|
|
662
|
+
if (credentials) {
|
|
663
|
+
console.log(`\n✅ Logged in as ${credentials.userId}`);
|
|
664
|
+
console.log(` Bot ID: ${credentials.accountId}`);
|
|
665
|
+
// Add wechat-ilink to config
|
|
666
|
+
if (!config.messengers.includes('wechat-ilink')) {
|
|
667
|
+
config.messengers.push('wechat-ilink');
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
console.log('\n❌ Login failed or timed out');
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
catch (error) {
|
|
676
|
+
console.error('\n❌ Failed to configure WeChat:', error);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
break;
|
|
680
|
+
}
|
|
681
|
+
case 'claude': {
|
|
682
|
+
console.log('🤖 Configuring Claude Code agent...');
|
|
683
|
+
// Check if claude CLI is available
|
|
684
|
+
const checkClaude = crossSpawn('claude', ['--version'], { stdio: 'ignore' });
|
|
685
|
+
checkClaude.on('close', (code) => {
|
|
686
|
+
if (code === 0) {
|
|
687
|
+
console.log('✅ Claude Code CLI found!');
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
console.log('❌ Claude Code CLI not found.');
|
|
691
|
+
console.log('Install with: npm install -g @anthropic-ai/claude-code');
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
if (!config.agents.includes('claude-code')) {
|
|
695
|
+
config.agents.push('claude-code');
|
|
696
|
+
}
|
|
697
|
+
config.defaultAgent = 'claude-code';
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
case 'codex': {
|
|
701
|
+
console.log('🤖 Configuring Codex agent...');
|
|
702
|
+
const checkCodex = crossSpawn('codex', ['--version'], { stdio: 'ignore' });
|
|
703
|
+
checkCodex.on('close', (code) => {
|
|
704
|
+
if (code === 0) {
|
|
705
|
+
console.log('✅ Codex CLI found!');
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
console.log('❌ Codex CLI not found.');
|
|
709
|
+
console.log('Install with: npm install -g @openai/codex');
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
if (!config.agents.includes('codex')) {
|
|
713
|
+
config.agents.push('codex');
|
|
714
|
+
}
|
|
715
|
+
config.defaultAgent = 'codex';
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
case 'telegram': {
|
|
719
|
+
console.log('📱 Configuring Telegram adapter...');
|
|
720
|
+
console.log('To get a bot token:');
|
|
721
|
+
console.log('1. Open Telegram and search for @BotFather');
|
|
722
|
+
console.log('2. Send /newbot and follow instructions');
|
|
723
|
+
console.log('3. Copy the bot token\n');
|
|
724
|
+
const { createInterface } = await import('node:readline');
|
|
725
|
+
const rl = createInterface({
|
|
726
|
+
input: process.stdin,
|
|
727
|
+
output: process.stdout,
|
|
728
|
+
});
|
|
729
|
+
const token = await new Promise((resolve) => {
|
|
730
|
+
rl.question('Enter your bot token: ', (answer) => {
|
|
731
|
+
resolve(answer.trim());
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
if (!token) {
|
|
735
|
+
console.log('❌ Bot token is required');
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
const channelId = await new Promise((resolve) => {
|
|
739
|
+
rl.question('Enter channel ID (optional, press Enter for default): ', (answer) => {
|
|
740
|
+
resolve(answer.trim() || 'default');
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
rl.close();
|
|
744
|
+
config.telegram = { botToken: token, channelId };
|
|
745
|
+
if (!config.messengers.includes('telegram')) {
|
|
746
|
+
config.messengers.push('telegram');
|
|
747
|
+
}
|
|
748
|
+
console.log('✅ Telegram bot token saved');
|
|
749
|
+
console.log(` Channel ID: ${channelId}`);
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
case 'feishu': {
|
|
753
|
+
console.log('📱 Configuring Feishu adapter (WebSocket long polling mode)...');
|
|
754
|
+
console.log('To create a Feishu bot:');
|
|
755
|
+
console.log('1. Go to https://open.feishu.cn/app');
|
|
756
|
+
console.log('2. Create a custom bot app');
|
|
757
|
+
console.log('3. Enable Bot capability');
|
|
758
|
+
console.log('4. Configure event subscriptions (Subscribe to "im.message.receive_v1" event)');
|
|
759
|
+
console.log('5. Go to Permissions management and enable: im:message, im:message.p2p_msg:readonly, im:message:send_as_bot');
|
|
760
|
+
console.log('6. Create a version and publish it');
|
|
761
|
+
console.log('7. Copy App ID and App Secret\n');
|
|
762
|
+
const { createInterface: createRl } = await import('node:readline');
|
|
763
|
+
const feishuRl = createRl({
|
|
764
|
+
input: process.stdin,
|
|
765
|
+
output: process.stdout,
|
|
766
|
+
});
|
|
767
|
+
const appId = await new Promise((resolve) => {
|
|
768
|
+
feishuRl.question('Enter App ID: ', (answer) => {
|
|
769
|
+
resolve(answer.trim());
|
|
770
|
+
});
|
|
771
|
+
});
|
|
772
|
+
const appSecret = await new Promise((resolve) => {
|
|
773
|
+
feishuRl.question('Enter App Secret: ', (answer) => {
|
|
774
|
+
resolve(answer.trim());
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
feishuRl.close();
|
|
778
|
+
if (!appId || !appSecret) {
|
|
779
|
+
console.log('❌ App ID and App Secret are required');
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
config.feishu = {
|
|
783
|
+
appId,
|
|
784
|
+
appSecret
|
|
785
|
+
};
|
|
786
|
+
if (!config.messengers.includes('feishu')) {
|
|
787
|
+
config.messengers.push('feishu');
|
|
788
|
+
}
|
|
789
|
+
console.log('✅ Feishu bot credentials saved');
|
|
790
|
+
console.log(`\n✅ Using WebSocket long polling mode - no webhook configuration needed!`);
|
|
791
|
+
console.log(` The bot will automatically connect to Feishu servers.`);
|
|
792
|
+
break;
|
|
793
|
+
}
|
|
794
|
+
case 'discord': {
|
|
795
|
+
console.log('📱 Configuring Discord adapter...');
|
|
796
|
+
console.log('To create a Discord bot:');
|
|
797
|
+
console.log('1. Go to https://discord.com/developers/applications');
|
|
798
|
+
console.log('2. Click "New Application" and give it a name');
|
|
799
|
+
console.log('3. Go to "Bot" tab and click "Add Bot"');
|
|
800
|
+
console.log('4. IMPORTANT: Enable "MESSAGE CONTENT INTENT" under Privileged Gateway Intents');
|
|
801
|
+
console.log('5. Click "Reset Token" to get your bot token');
|
|
802
|
+
console.log('6. Use OAuth2 URL Generator to invite the bot to your server\n');
|
|
803
|
+
const { createInterface: createDiscordRl } = await import('node:readline');
|
|
804
|
+
const discordRl = createDiscordRl({
|
|
805
|
+
input: process.stdin,
|
|
806
|
+
output: process.stdout,
|
|
807
|
+
});
|
|
808
|
+
const discordToken = await new Promise((resolve) => {
|
|
809
|
+
discordRl.question('Enter your bot token: ', (answer) => {
|
|
810
|
+
resolve(answer.trim());
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
if (!discordToken) {
|
|
814
|
+
console.log('❌ Bot token is required');
|
|
815
|
+
discordRl.close();
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
const discordChannelId = await new Promise((resolve) => {
|
|
819
|
+
discordRl.question('Enter channel ID (optional, press Enter for default): ', (answer) => {
|
|
820
|
+
resolve(answer.trim() || 'default');
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
const allowedGuilds = await new Promise((resolve) => {
|
|
824
|
+
discordRl.question('Allowed guild IDs (comma-separated, optional): ', (answer) => {
|
|
825
|
+
resolve(answer.trim());
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
const allowedChannels = await new Promise((resolve) => {
|
|
829
|
+
discordRl.question('Allowed channel IDs (comma-separated, optional): ', (answer) => {
|
|
830
|
+
resolve(answer.trim());
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
discordRl.close();
|
|
834
|
+
config.discord = {
|
|
835
|
+
botToken: discordToken,
|
|
836
|
+
channelId: discordChannelId,
|
|
837
|
+
allowedGuilds: allowedGuilds ? allowedGuilds.split(',').map(s => s.trim()).filter(Boolean) : undefined,
|
|
838
|
+
allowedChannels: allowedChannels ? allowedChannels.split(',').map(s => s.trim()).filter(Boolean) : undefined,
|
|
839
|
+
};
|
|
840
|
+
if (!config.messengers.includes('discord')) {
|
|
841
|
+
config.messengers.push('discord');
|
|
842
|
+
}
|
|
843
|
+
console.log('✅ Discord bot token saved');
|
|
844
|
+
console.log(` Channel ID: ${discordChannelId}`);
|
|
845
|
+
if (config.discord.allowedGuilds?.length) {
|
|
846
|
+
console.log(` Allowed guilds: ${config.discord.allowedGuilds.join(', ')}`);
|
|
847
|
+
}
|
|
848
|
+
if (config.discord.allowedChannels?.length) {
|
|
849
|
+
console.log(` Allowed channels: ${config.discord.allowedChannels.join(', ')}`);
|
|
850
|
+
}
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
case 'opencode': {
|
|
854
|
+
console.log('🤖 Configuring OpenCode agent...');
|
|
855
|
+
// Check if opencode CLI is available
|
|
856
|
+
const openCodeAvailable = await new Promise((resolve) => {
|
|
857
|
+
const proc = crossSpawn('opencode', ['--version'], { stdio: 'ignore' });
|
|
858
|
+
proc.on('error', () => resolve(false));
|
|
859
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
860
|
+
});
|
|
861
|
+
if (openCodeAvailable) {
|
|
862
|
+
console.log('✅ OpenCode CLI found!');
|
|
863
|
+
console.log('\nTo authenticate, run: opencode auth login');
|
|
864
|
+
}
|
|
865
|
+
else {
|
|
866
|
+
console.log('❌ OpenCode CLI not found.');
|
|
867
|
+
console.log('Install with: npm i -g opencode-ai@latest');
|
|
868
|
+
console.log('Or visit: https://github.com/anomalyco/opencode');
|
|
869
|
+
}
|
|
870
|
+
if (!config.agents.includes('opencode')) {
|
|
871
|
+
config.agents.push('opencode');
|
|
872
|
+
}
|
|
873
|
+
config.defaultAgent = 'opencode';
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
case 'copilot': {
|
|
877
|
+
console.log('🤖 Configuring GitHub Copilot CLI agent...');
|
|
878
|
+
// Check if copilot CLI is available (multiple installation methods)
|
|
879
|
+
const { copilotAdapter } = await import('./plugins/agents/copilot/index.js');
|
|
880
|
+
const copilotAvailable = await copilotAdapter.isAvailable();
|
|
881
|
+
if (copilotAvailable) {
|
|
882
|
+
console.log('✅ GitHub Copilot CLI found!');
|
|
883
|
+
}
|
|
884
|
+
else {
|
|
885
|
+
console.log('❌ GitHub Copilot CLI not found.');
|
|
886
|
+
console.log('\n安装方式 (选择其一):');
|
|
887
|
+
console.log(' npm i -g @github/copilot');
|
|
888
|
+
console.log(' gh extension install github/gh-copilot');
|
|
889
|
+
if (isMac) {
|
|
890
|
+
console.log(' brew install copilot-cli');
|
|
891
|
+
}
|
|
892
|
+
if (isWindows) {
|
|
893
|
+
console.log(' winget install GitHub.Copilot');
|
|
894
|
+
}
|
|
895
|
+
console.log(' 或安装 VS Code Copilot Chat 扩展');
|
|
896
|
+
console.log('\n详情: https://github.com/features/copilot/cli');
|
|
897
|
+
}
|
|
898
|
+
if (!config.agents.includes('copilot')) {
|
|
899
|
+
config.agents.push('copilot');
|
|
900
|
+
}
|
|
901
|
+
config.defaultAgent = 'copilot';
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
case 'agent': {
|
|
905
|
+
console.log('🔌 Configuring remote ACP agent...');
|
|
906
|
+
console.log('This adds a remote agent that speaks the Agent Communication Protocol.');
|
|
907
|
+
console.log('ACP is an open standard (https://agentcommunicationprotocol.dev)\n');
|
|
908
|
+
const { createInterface: createAgentRl } = await import('node:readline');
|
|
909
|
+
const agentRl = createAgentRl({ input: process.stdin, output: process.stdout });
|
|
910
|
+
const agentName = await new Promise((resolve) => {
|
|
911
|
+
agentRl.question('Agent name (e.g. openclaw-dev): ', (answer) => resolve(answer.trim()));
|
|
912
|
+
});
|
|
913
|
+
if (!agentName) {
|
|
914
|
+
console.log('❌ Name is required');
|
|
915
|
+
agentRl.close();
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const agentAlias = await new Promise((resolve) => {
|
|
919
|
+
agentRl.question('Aliases, comma-separated (optional): ', (answer) => resolve(answer.trim()));
|
|
920
|
+
});
|
|
921
|
+
const endpoint = await new Promise((resolve) => {
|
|
922
|
+
agentRl.question('ACP endpoint URL (e.g. http://localhost:8080): ', (answer) => resolve(answer.trim()));
|
|
923
|
+
});
|
|
924
|
+
if (!endpoint) {
|
|
925
|
+
console.log('❌ Endpoint is required');
|
|
926
|
+
agentRl.close();
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
console.log('\nAuthentication type:');
|
|
930
|
+
console.log(' 1. none');
|
|
931
|
+
console.log(' 2. apikey');
|
|
932
|
+
console.log(' 3. bearer');
|
|
933
|
+
const authTypeInput = await new Promise((resolve) => {
|
|
934
|
+
agentRl.question('Choose (1-3, default: none): ', (answer) => resolve(answer.trim() || '1'));
|
|
935
|
+
});
|
|
936
|
+
const authTypeMap = { '1': 'none', '2': 'apikey', '3': 'bearer' };
|
|
937
|
+
const authType = authTypeMap[authTypeInput] || 'none';
|
|
938
|
+
let auth;
|
|
939
|
+
if (authType !== 'none') {
|
|
940
|
+
const token = await new Promise((resolve) => {
|
|
941
|
+
agentRl.question('Auth token: ', (answer) => resolve(answer.trim()));
|
|
942
|
+
});
|
|
943
|
+
if (!token) {
|
|
944
|
+
console.log('❌ Token is required when auth is enabled');
|
|
945
|
+
agentRl.close();
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
auth = { type: authType, token };
|
|
949
|
+
}
|
|
950
|
+
agentRl.close();
|
|
951
|
+
// Validate connection
|
|
952
|
+
console.log('\n🔍 Testing connection...');
|
|
953
|
+
const { ACPClient } = await import('./plugins/agents/acp/acp-client.js');
|
|
954
|
+
const testClient = new ACPClient({ name: agentName, endpoint, auth });
|
|
955
|
+
try {
|
|
956
|
+
const manifest = await testClient.fetchManifest();
|
|
957
|
+
console.log(`✅ Connected! Agent: ${manifest.name}`);
|
|
958
|
+
if (manifest.description)
|
|
959
|
+
console.log(` ${manifest.description}`);
|
|
960
|
+
}
|
|
961
|
+
catch (e) {
|
|
962
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
963
|
+
console.log(`⚠️ Connection failed: ${msg}`);
|
|
964
|
+
console.log(' Agent will be saved but may not work until endpoint is available.');
|
|
965
|
+
}
|
|
966
|
+
// Save
|
|
967
|
+
if (!config.acpAgents)
|
|
968
|
+
config.acpAgents = [];
|
|
969
|
+
const existing = config.acpAgents.findIndex(a => a.name === agentName);
|
|
970
|
+
const agentConfig = {
|
|
971
|
+
name: agentName,
|
|
972
|
+
aliases: agentAlias?.split(',').map(s => s.trim()).filter(Boolean) || [],
|
|
973
|
+
endpoint,
|
|
974
|
+
auth,
|
|
975
|
+
enabled: true
|
|
976
|
+
};
|
|
977
|
+
if (existing >= 0) {
|
|
978
|
+
config.acpAgents[existing] = agentConfig;
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
config.acpAgents.push(agentConfig);
|
|
982
|
+
}
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
default:
|
|
986
|
+
console.log(`Unknown component: ${component}`);
|
|
987
|
+
console.log('Run "agim config" to see available components.');
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
await saveConfig(config);
|
|
991
|
+
console.log(`\n✅ Configuration saved to ${CONFIG_FILE}`);
|
|
992
|
+
});
|
|
993
|
+
program
|
|
994
|
+
.command('agents')
|
|
995
|
+
.description('List available agents')
|
|
996
|
+
.action(async () => {
|
|
997
|
+
await registry.loadBuiltInPlugins();
|
|
998
|
+
const config = await loadConfig();
|
|
999
|
+
if (config.acpAgents?.length) {
|
|
1000
|
+
await registry.loadACPAgents(config.acpAgents);
|
|
1001
|
+
}
|
|
1002
|
+
const agents = registry.listAgents();
|
|
1003
|
+
if (agents.length === 0) {
|
|
1004
|
+
console.log('No agents registered yet.');
|
|
1005
|
+
console.log('Run "agim config claude" to configure Claude Code.');
|
|
1006
|
+
console.log('Run "agim config agent" to add a remote ACP agent.');
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
console.log('🤖 Checking agents...\n');
|
|
1010
|
+
// Check all agents in parallel to avoid slow sequential timeouts
|
|
1011
|
+
const results = await Promise.allSettled(agents.map(async (name) => {
|
|
1012
|
+
const agent = registry.getAgent(name);
|
|
1013
|
+
const available = await agent?.isAvailable().catch(() => false);
|
|
1014
|
+
// Check if this is an ACP agent with extra info
|
|
1015
|
+
let info = '';
|
|
1016
|
+
try {
|
|
1017
|
+
const { ACPAdapter } = await import('./plugins/agents/acp/acp-adapter.js');
|
|
1018
|
+
if (agent instanceof ACPAdapter) {
|
|
1019
|
+
const manifest = await agent.getManifest().catch(() => undefined);
|
|
1020
|
+
if (manifest) {
|
|
1021
|
+
info = ` — ${manifest.description || 'Remote ACP agent'}`;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
catch { /* not an ACP agent */ }
|
|
1026
|
+
return { name, available, aliases: agent?.aliases || [], info };
|
|
1027
|
+
}));
|
|
1028
|
+
for (const result of results) {
|
|
1029
|
+
if (result.status === 'fulfilled') {
|
|
1030
|
+
const { name, available, aliases, info } = result.value;
|
|
1031
|
+
const aliasStr = aliases.length ? ` (${aliases.join(', ')})` : '';
|
|
1032
|
+
console.log(` ${available ? '✅' : '❌'} ${name}${aliasStr}${info}`);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
program
|
|
1037
|
+
.command('messengers')
|
|
1038
|
+
.description('List available messengers')
|
|
1039
|
+
.action(async () => {
|
|
1040
|
+
await registry.loadBuiltInPlugins();
|
|
1041
|
+
const messengers = registry.listMessengers();
|
|
1042
|
+
if (messengers.length === 0) {
|
|
1043
|
+
console.log('No messengers registered yet.');
|
|
1044
|
+
console.log('Run "agim config wechat" to configure WeChat.');
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
console.log('📱 Available Messengers:\n');
|
|
1048
|
+
for (const name of messengers) {
|
|
1049
|
+
console.log(` ${name}`);
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
// ─── service-management commands (v0.5.0) ──────────────────────────────
|
|
1053
|
+
// status / restart / stop / uninstall — all delegate to cli-ui/cmd-handlers.ts.
|
|
1054
|
+
program
|
|
1055
|
+
.command('status')
|
|
1056
|
+
.description('Show whether the bridge is running (systemd / background / foreground)')
|
|
1057
|
+
.action(async () => {
|
|
1058
|
+
const { cmdStatus } = await import('./cli-ui/cmd-handlers.js');
|
|
1059
|
+
cmdStatus();
|
|
1060
|
+
});
|
|
1061
|
+
program
|
|
1062
|
+
.command('restart')
|
|
1063
|
+
.description('Restart the bridge (autodetects systemd vs background daemon)')
|
|
1064
|
+
.action(async () => {
|
|
1065
|
+
const { cmdRestart } = await import('./cli-ui/cmd-handlers.js');
|
|
1066
|
+
await cmdRestart();
|
|
1067
|
+
});
|
|
1068
|
+
program
|
|
1069
|
+
.command('stop')
|
|
1070
|
+
.description('Stop the bridge (systemd unit or background daemon)')
|
|
1071
|
+
.action(async () => {
|
|
1072
|
+
const { cmdStop } = await import('./cli-ui/cmd-handlers.js');
|
|
1073
|
+
await cmdStop();
|
|
1074
|
+
});
|
|
1075
|
+
program
|
|
1076
|
+
.command('uninstall')
|
|
1077
|
+
.description('Stop service, remove npm install + config; keeps ~/.im-hub-workspaces/')
|
|
1078
|
+
.action(async () => {
|
|
1079
|
+
const { cmdUninstall } = await import('./cli-ui/cmd-handlers.js');
|
|
1080
|
+
await cmdUninstall();
|
|
1081
|
+
});
|
|
1082
|
+
// ─── bare `agim` → entry menu (v0.5.0+) ─────────────────────────────
|
|
1083
|
+
// If the user invoked us with no args (just `agim`), drop them at
|
|
1084
|
+
// the language picker + maintenance menu instead of dumping --help text.
|
|
1085
|
+
//
|
|
1086
|
+
// This is intentionally BEFORE program.parse() so the menu runs even
|
|
1087
|
+
// though commander would otherwise fall through to its default no-command
|
|
1088
|
+
// branch. We check process.argv length to detect "no subcommand passed".
|
|
1089
|
+
async function maybeRunEntryMenu() {
|
|
1090
|
+
// argv layout: [node, /path/to/cli.js, ...rest]. No subcommand = length 2.
|
|
1091
|
+
if (process.argv.length > 2)
|
|
1092
|
+
return false;
|
|
1093
|
+
if (!process.stdin.isTTY)
|
|
1094
|
+
return false; // piped / non-interactive — fall back to commander's help
|
|
1095
|
+
const { runEntryMenu } = await import('./cli-ui/entry-menu.js');
|
|
1096
|
+
const { cmdStatus, cmdStart, cmdStop, cmdRestart, cmdUninstall } = await import('./cli-ui/cmd-handlers.js');
|
|
1097
|
+
const { action, lang } = await runEntryMenu();
|
|
1098
|
+
switch (action.kind) {
|
|
1099
|
+
case 'quit':
|
|
1100
|
+
return true;
|
|
1101
|
+
case 'status':
|
|
1102
|
+
cmdStatus();
|
|
1103
|
+
return true;
|
|
1104
|
+
case 'config': {
|
|
1105
|
+
// Reuse the lang the user just picked in runEntryMenu — re-prompting
|
|
1106
|
+
// here was a v0.5.1 regression (user reported: "已经选过语言不要再问").
|
|
1107
|
+
const { runConfigWizard } = await import('./cli-ui/config-wizard.js');
|
|
1108
|
+
await runConfigWizard(lang);
|
|
1109
|
+
return true;
|
|
1110
|
+
}
|
|
1111
|
+
case 'start':
|
|
1112
|
+
// For foreground start from the menu, re-exec ourselves with the
|
|
1113
|
+
// proper subcommand so commander's `start` action runs. Easier than
|
|
1114
|
+
// duplicating the 500-line start body.
|
|
1115
|
+
if (action.bg) {
|
|
1116
|
+
await cmdStart(true, async () => { });
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
// Re-exec in the same shell so Ctrl-C maps cleanly. Dynamic
|
|
1120
|
+
// `await import` works in ESM (require() doesn't).
|
|
1121
|
+
const { spawn } = await import('node:child_process');
|
|
1122
|
+
await cmdStart(false, () => new Promise((resolve) => {
|
|
1123
|
+
const child = spawn(process.execPath, [process.argv[1], 'start'], { stdio: 'inherit' });
|
|
1124
|
+
child.on('exit', () => resolve());
|
|
1125
|
+
}));
|
|
1126
|
+
}
|
|
1127
|
+
return true;
|
|
1128
|
+
case 'restart':
|
|
1129
|
+
await cmdRestart();
|
|
1130
|
+
return true;
|
|
1131
|
+
case 'stop':
|
|
1132
|
+
await cmdStop();
|
|
1133
|
+
return true;
|
|
1134
|
+
case 'uninstall':
|
|
1135
|
+
await cmdUninstall();
|
|
1136
|
+
return true;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
void maybeRunEntryMenu().then((handled) => {
|
|
1140
|
+
if (!handled)
|
|
1141
|
+
program.parse();
|
|
1142
|
+
});
|
|
1143
|
+
//# sourceMappingURL=cli.js.map
|