im-hub-pro 0.2.29
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/LICENSE +21 -0
- package/README.md +497 -0
- package/README.zh-CN.md +496 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +921 -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 +374 -0
- package/dist/core/agent-base.js.map +1 -0
- package/dist/core/agent-cwd.d.ts +45 -0
- package/dist/core/agent-cwd.d.ts.map +1 -0
- package/dist/core/agent-cwd.js +178 -0
- package/dist/core/agent-cwd.js.map +1 -0
- package/dist/core/agent-cwd.test.d.ts +2 -0
- package/dist/core/agent-cwd.test.d.ts.map +1 -0
- package/dist/core/agent-cwd.test.js +149 -0
- package/dist/core/agent-cwd.test.js.map +1 -0
- package/dist/core/approval-bus.d.ts +232 -0
- package/dist/core/approval-bus.d.ts.map +1 -0
- package/dist/core/approval-bus.js +703 -0
- package/dist/core/approval-bus.js.map +1 -0
- package/dist/core/approval-bus.synthetic.test.d.ts +2 -0
- package/dist/core/approval-bus.synthetic.test.d.ts.map +1 -0
- package/dist/core/approval-bus.synthetic.test.js +182 -0
- package/dist/core/approval-bus.synthetic.test.js.map +1 -0
- package/dist/core/approval-bus.test.d.ts +2 -0
- package/dist/core/approval-bus.test.d.ts.map +1 -0
- package/dist/core/approval-bus.test.js +537 -0
- package/dist/core/approval-bus.test.js.map +1 -0
- package/dist/core/approval-router.d.ts +95 -0
- package/dist/core/approval-router.d.ts.map +1 -0
- package/dist/core/approval-router.js +450 -0
- package/dist/core/approval-router.js.map +1 -0
- package/dist/core/approval-router.test.d.ts +2 -0
- package/dist/core/approval-router.test.d.ts.map +1 -0
- package/dist/core/approval-router.test.js +413 -0
- package/dist/core/approval-router.test.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/bgjob-reader.test.d.ts +2 -0
- package/dist/core/bgjob-reader.test.d.ts.map +1 -0
- package/dist/core/bgjob-reader.test.js +178 -0
- package/dist/core/bgjob-reader.test.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 +21 -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 +44 -0
- package/dist/core/commands/approval.js.map +1 -0
- package/dist/core/commands/approval.test.d.ts +2 -0
- package/dist/core/commands/approval.test.d.ts.map +1 -0
- package/dist/core/commands/approval.test.js +85 -0
- package/dist/core/commands/approval.test.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 +26 -0
- package/dist/core/commands/builtin.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/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/plan.test.d.ts +2 -0
- package/dist/core/commands/plan.test.d.ts.map +1 -0
- package/dist/core/commands/plan.test.js +122 -0
- package/dist/core/commands/plan.test.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/schedule.d.ts +3 -0
- package/dist/core/commands/schedule.d.ts.map +1 -0
- package/dist/core/commands/schedule.js +123 -0
- package/dist/core/commands/schedule.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 +58 -0
- package/dist/core/config-schema.d.ts.map +1 -0
- package/dist/core/config-schema.js +63 -0
- package/dist/core/config-schema.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 +84 -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/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/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 +94 -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/onboarding.test.d.ts +2 -0
- package/dist/core/onboarding.test.d.ts.map +1 -0
- package/dist/core/onboarding.test.js +112 -0
- package/dist/core/onboarding.test.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 +122 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/router.d.ts +41 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +431 -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 +316 -0
- package/dist/core/schedule.js.map +1 -0
- package/dist/core/session-subtasks.test.d.ts +2 -0
- package/dist/core/session-subtasks.test.d.ts.map +1 -0
- package/dist/core/session-subtasks.test.js +88 -0
- package/dist/core/session-subtasks.test.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 +774 -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/transcribe.test.d.ts +2 -0
- package/dist/core/transcribe.test.d.ts.map +1 -0
- package/dist/core/transcribe.test.js +163 -0
- package/dist/core/transcribe.test.js.map +1 -0
- package/dist/core/types.d.ts +352 -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 +175 -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 +109 -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/adapter.test.d.ts +2 -0
- package/dist/plugins/agents/claude-code/adapter.test.d.ts.map +1 -0
- package/dist/plugins/agents/claude-code/adapter.test.js +195 -0
- package/dist/plugins/agents/claude-code/adapter.test.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 +42 -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 +235 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.js.map +1 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.test.d.ts +2 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.test.d.ts.map +1 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.test.js +188 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.test.js.map +1 -0
- package/dist/plugins/agents/codex/adapter.test.d.ts +2 -0
- package/dist/plugins/agents/codex/adapter.test.d.ts.map +1 -0
- package/dist/plugins/agents/codex/adapter.test.js +192 -0
- package/dist/plugins/agents/codex/adapter.test.js.map +1 -0
- package/dist/plugins/agents/codex/index.d.ts +37 -0
- package/dist/plugins/agents/codex/index.d.ts.map +1 -0
- package/dist/plugins/agents/codex/index.js +254 -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/adapter.test.d.ts +2 -0
- package/dist/plugins/agents/opencode/adapter.test.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/adapter.test.js +139 -0
- package/dist/plugins/agents/opencode/adapter.test.js.map +1 -0
- package/dist/plugins/agents/opencode/http-adapter.test.d.ts +2 -0
- package/dist/plugins/agents/opencode/http-adapter.test.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/http-adapter.test.js +492 -0
- package/dist/plugins/agents/opencode/http-adapter.test.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 +138 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.js +549 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.js.map +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts +24 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js +103 -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 +190 -0
- package/dist/plugins/agents/opencode/serve-manager.js.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.d.ts +22 -0
- package/dist/plugins/messengers/discord/discord-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.js +241 -0
- package/dist/plugins/messengers/discord/discord-adapter.js.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.test.d.ts +2 -0
- package/dist/plugins/messengers/discord/discord-adapter.test.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.test.js +332 -0
- package/dist/plugins/messengers/discord/discord-adapter.test.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/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 +33 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.js +195 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.js.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-client.d.ts +44 -0
- package/dist/plugins/messengers/feishu/feishu-client.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-client.js +120 -0
- package/dist/plugins/messengers/feishu/feishu-client.js.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-dedup.test.d.ts +2 -0
- package/dist/plugins/messengers/feishu/feishu-dedup.test.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-dedup.test.js +70 -0
- package/dist/plugins/messengers/feishu/feishu-dedup.test.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 +51 -0
- package/dist/plugins/messengers/telegram/media-download.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.js +224 -0
- package/dist/plugins/messengers/telegram/media-download.js.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.test.d.ts +2 -0
- package/dist/plugins/messengers/telegram/media-download.test.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.test.js +125 -0
- package/dist/plugins/messengers/telegram/media-download.test.js.map +1 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.d.ts +62 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.js +653 -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/ilink-adapter.d.ts +68 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.js +483 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.js.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-client.d.ts +66 -0
- package/dist/plugins/messengers/wechat/ilink-client.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-client.js +288 -0
- package/dist/plugins/messengers/wechat/ilink-client.js.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-types.d.ts +173 -0
- package/dist/plugins/messengers/wechat/ilink-types.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-types.js +12 -0
- package/dist/plugins/messengers/wechat/ilink-types.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 +935 -0
- package/dist/web/public/settings.html +1181 -0
- package/dist/web/public/tasks.html +1827 -0
- package/dist/web/server.d.ts +11 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +1820 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Exponential backoff with jitter — used by IM adapter reconnect loops.
|
|
2
|
+
//
|
|
3
|
+
// Why: when an IM platform throws (auth revoked, network blip, server
|
|
4
|
+
// outage) the adapter retries. With fixed delays we have three problems:
|
|
5
|
+
//
|
|
6
|
+
// 1. Auth revoked → infinite fast hammering of the IM API at the same
|
|
7
|
+
// cadence; with backoff the cadence relaxes to capMs.
|
|
8
|
+
// 2. Multi-instance fleet recovering from a network event reconnects
|
|
9
|
+
// in lock-step at the same fixed delay — thundering herd hits the
|
|
10
|
+
// IM platform's load balancer; jitter spreads the storm.
|
|
11
|
+
// 3. Flaky network → CPU spent in tight reconnect loop; longer waits
|
|
12
|
+
// between attempts dampen this.
|
|
13
|
+
//
|
|
14
|
+
// Used by:
|
|
15
|
+
// - telegram-adapter runPollingLoop (was fixed 2s/5s delays)
|
|
16
|
+
// - wechat ilink-adapter polling loop (had exp backoff but no jitter)
|
|
17
|
+
const DEFAULT_BASE_MS = 1_000;
|
|
18
|
+
const DEFAULT_CAP_MS = 60_000;
|
|
19
|
+
const DEFAULT_JITTER = 0.5;
|
|
20
|
+
export class Backoff {
|
|
21
|
+
attempt = 0;
|
|
22
|
+
baseMs;
|
|
23
|
+
capMs;
|
|
24
|
+
jitter;
|
|
25
|
+
random;
|
|
26
|
+
constructor(opts = {}) {
|
|
27
|
+
this.baseMs = Math.max(0, opts.baseMs ?? DEFAULT_BASE_MS);
|
|
28
|
+
this.capMs = Math.max(this.baseMs, opts.capMs ?? DEFAULT_CAP_MS);
|
|
29
|
+
this.jitter = Math.max(0, Math.min(1, opts.jitter ?? DEFAULT_JITTER));
|
|
30
|
+
this.random = opts.random ?? Math.random;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Compute the next delay in ms and increment the attempt counter.
|
|
34
|
+
*
|
|
35
|
+
* - First call: ~baseMs (jittered)
|
|
36
|
+
* - Each call doubles the unjittered exponent until it hits capMs
|
|
37
|
+
* - Then every subsequent call returns ~capMs (jittered)
|
|
38
|
+
*
|
|
39
|
+
* Returns a non-negative integer.
|
|
40
|
+
*/
|
|
41
|
+
nextDelayMs() {
|
|
42
|
+
const exp = Math.min(this.capMs, this.baseMs * Math.pow(2, this.attempt));
|
|
43
|
+
this.attempt += 1;
|
|
44
|
+
if (this.jitter === 0)
|
|
45
|
+
return Math.floor(exp);
|
|
46
|
+
// Multiplicative jitter: factor ∈ [1 - jitter, 1 + jitter]
|
|
47
|
+
const factor = 1 + (this.random() * 2 - 1) * this.jitter;
|
|
48
|
+
return Math.max(0, Math.floor(exp * factor));
|
|
49
|
+
}
|
|
50
|
+
/** Reset attempt counter — call after a successful operation. */
|
|
51
|
+
reset() {
|
|
52
|
+
this.attempt = 0;
|
|
53
|
+
}
|
|
54
|
+
/** Diagnostic: current attempt count without bumping. */
|
|
55
|
+
currentAttempt() {
|
|
56
|
+
return this.attempt;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=backoff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../src/utils/backoff.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,EAAE;AACF,sEAAsE;AACtE,yEAAyE;AACzE,EAAE;AACF,wEAAwE;AACxE,2DAA2D;AAC3D,uEAAuE;AACvE,uEAAuE;AACvE,8DAA8D;AAC9D,uEAAuE;AACvE,qCAAqC;AACrC,EAAE;AACF,WAAW;AACX,+DAA+D;AAC/D,wEAAwE;AAexE,MAAM,eAAe,GAAG,KAAK,CAAA;AAC7B,MAAM,cAAc,GAAG,MAAM,CAAA;AAC7B,MAAM,cAAc,GAAG,GAAG,CAAA;AAE1B,MAAM,OAAO,OAAO;IACV,OAAO,GAAG,CAAC,CAAA;IACF,MAAM,CAAQ;IACd,KAAK,CAAQ;IACb,MAAM,CAAQ;IACd,MAAM,CAAc;IAErC,YAAY,OAAuB,EAAE;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,CAAA;QACzD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,IAAI,cAAc,CAAC,CAAA;QAChE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,cAAc,CAAC,CAAC,CAAA;QACrE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAA;IAC1C,CAAC;IAED;;;;;;;;OAQG;IACH,WAAW;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;QACzE,IAAI,CAAC,OAAO,IAAI,CAAC,CAAA;QACjB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC7C,2DAA2D;QAC3D,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAA;QACxD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,CAAA;IAC9C,CAAC;IAED,iEAAiE;IACjE,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,CAAC,CAAA;IAClB,CAAC;IAED,yDAAyD;IACzD,cAAc;QACZ,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SpawnOptions, ChildProcess } from 'child_process';
|
|
2
|
+
export declare const isWindows: boolean;
|
|
3
|
+
export declare const isMac: boolean;
|
|
4
|
+
export declare const isLinux: boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Cross-platform spawn wrapper that automatically handles Windows compatibility.
|
|
7
|
+
* On Windows, it sets shell: true to enable command execution.
|
|
8
|
+
*
|
|
9
|
+
* Note: When using stdio: ['ignore', 'pipe', 'pipe'], stdout and stderr are guaranteed to be non-null.
|
|
10
|
+
*/
|
|
11
|
+
export declare function crossSpawn(command: string, args?: string[], options?: SpawnOptions): ChildProcess;
|
|
12
|
+
/**
|
|
13
|
+
* Get Copilot CLI binary path based on the current platform.
|
|
14
|
+
* Copilot CLI is installed by VS Code extension in different locations per OS.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getCopilotBinPath(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Check if a command exists in PATH.
|
|
19
|
+
* Uses 'where' on Windows, 'which' on Unix.
|
|
20
|
+
*/
|
|
21
|
+
export declare function commandExists(command: string): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Get the correct newline character for the current platform.
|
|
24
|
+
*/
|
|
25
|
+
export declare const EOL: string;
|
|
26
|
+
//# sourceMappingURL=cross-platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-platform.d.ts","sourceRoot":"","sources":["../../src/utils/cross-platform.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAIjE,eAAO,MAAM,SAAS,SAAyB,CAAA;AAC/C,eAAO,MAAM,KAAK,SAA0B,CAAA;AAC5C,eAAO,MAAM,OAAO,SAAyB,CAAA;AAE7C;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,EAAO,EACnB,OAAO,GAAE,YAAiB,GACzB,YAAY,CAQd;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAuB1C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,eAAO,MAAM,GAAG,QAA4B,CAAA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Cross-platform utilities for Windows compatibility
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { platform, homedir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
export const isWindows = platform() === 'win32';
|
|
6
|
+
export const isMac = platform() === 'darwin';
|
|
7
|
+
export const isLinux = platform() === 'linux';
|
|
8
|
+
/**
|
|
9
|
+
* Cross-platform spawn wrapper that automatically handles Windows compatibility.
|
|
10
|
+
* On Windows, it sets shell: true to enable command execution.
|
|
11
|
+
*
|
|
12
|
+
* Note: When using stdio: ['ignore', 'pipe', 'pipe'], stdout and stderr are guaranteed to be non-null.
|
|
13
|
+
*/
|
|
14
|
+
export function crossSpawn(command, args = [], options = {}) {
|
|
15
|
+
const spawnOptions = {
|
|
16
|
+
...options,
|
|
17
|
+
// On Windows, we need shell: true for commands like 'claude', 'opencode', etc.
|
|
18
|
+
// to be found in PATH and executed properly.
|
|
19
|
+
shell: isWindows ? (options.shell ?? true) : options.shell,
|
|
20
|
+
};
|
|
21
|
+
return spawn(command, args, spawnOptions);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get Copilot CLI binary path based on the current platform.
|
|
25
|
+
* Copilot CLI is installed by VS Code extension in different locations per OS.
|
|
26
|
+
*/
|
|
27
|
+
export function getCopilotBinPath() {
|
|
28
|
+
if (isWindows) {
|
|
29
|
+
// Windows: VS Code extensions are in %USERPROFILE%\.vscode\extensions
|
|
30
|
+
// The copilot CLI should be in the global storage
|
|
31
|
+
return join(homedir(), '.vscode', 'extensions', 'github.copilot-chat-*/copilotCli/copilot.exe');
|
|
32
|
+
}
|
|
33
|
+
else if (isMac) {
|
|
34
|
+
// macOS
|
|
35
|
+
return join(homedir(), 'Library/Application Support/Code/User/globalStorage/github.copilot-chat/copilotCli/copilot');
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Linux
|
|
39
|
+
return join(homedir(), '.vscode/extensions/github.copilot-chat/copilotCli/copilot');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a command exists in PATH.
|
|
44
|
+
* Uses 'where' on Windows, 'which' on Unix.
|
|
45
|
+
*/
|
|
46
|
+
export function commandExists(command) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
const checkCmd = isWindows ? 'where' : 'which';
|
|
49
|
+
const proc = crossSpawn(checkCmd, [command], { stdio: 'ignore' });
|
|
50
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
51
|
+
proc.on('error', () => resolve(false));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the correct newline character for the current platform.
|
|
56
|
+
*/
|
|
57
|
+
export const EOL = isWindows ? '\r\n' : '\n';
|
|
58
|
+
//# sourceMappingURL=cross-platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-platform.js","sourceRoot":"","sources":["../../src/utils/cross-platform.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,OAAO,EAAE,KAAK,EAA8B,MAAM,eAAe,CAAA;AACjE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,EAAE,KAAK,OAAO,CAAA;AAC/C,MAAM,CAAC,MAAM,KAAK,GAAG,QAAQ,EAAE,KAAK,QAAQ,CAAA;AAC5C,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,EAAE,KAAK,OAAO,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,OAAe,EACf,OAAiB,EAAE,EACnB,UAAwB,EAAE;IAE1B,MAAM,YAAY,GAAiB;QACjC,GAAG,OAAO;QACV,+EAA+E;QAC/E,6CAA6C;QAC7C,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK;KAC3D,CAAA;IACD,OAAO,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,CAAA;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,SAAS,EAAE,CAAC;QACd,sEAAsE;QACtE,kDAAkD;QAClD,OAAO,IAAI,CACT,OAAO,EAAE,EACT,SAAS,EACT,YAAY,EACZ,8CAA8C,CAC/C,CAAA;IACH,CAAC;SAAM,IAAI,KAAK,EAAE,CAAC;QACjB,QAAQ;QACR,OAAO,IAAI,CACT,OAAO,EAAE,EACT,4FAA4F,CAC7F,CAAA;IACH,CAAC;SAAM,CAAC;QACN,QAAQ;QACR,OAAO,IAAI,CACT,OAAO,EAAE,EACT,2DAA2D,CAC5D,CAAA;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;QAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;QACjE,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAA;QAC/C,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IACxC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SplitOptions {
|
|
2
|
+
/** Maximum characters per chunk */
|
|
3
|
+
maxLength?: number;
|
|
4
|
+
/** Add continuation marker to split messages */
|
|
5
|
+
addContinuationMarker?: boolean;
|
|
6
|
+
/** Continuation marker text */
|
|
7
|
+
continuationMarker?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Split a long message into chunks that fit within messenger limits.
|
|
11
|
+
* Tries to split at natural boundaries (code blocks, paragraphs, lines).
|
|
12
|
+
*/
|
|
13
|
+
export declare function splitMessage(text: string, options?: SplitOptions): string[];
|
|
14
|
+
//# sourceMappingURL=message-split.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-split.d.ts","sourceRoot":"","sources":["../../src/utils/message-split.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gDAAgD;IAChD,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,+BAA+B;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAmBD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,YAAiB,GACzB,MAAM,EAAE,CAkDV"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Shared message splitting utility
|
|
2
|
+
// Used by WeChat, Telegram, and other messengers with character limits
|
|
3
|
+
/**
|
|
4
|
+
* If `idx` falls between a UTF-16 surrogate pair, step back by 1 so the
|
|
5
|
+
* pair stays whole. JavaScript strings are UTF-16; characters in plane 1+
|
|
6
|
+
* (most emoji, e.g. 😀 U+1F600) are encoded as a high-surrogate +
|
|
7
|
+
* low-surrogate pair (2 code units). Slicing through the boundary yields
|
|
8
|
+
* an orphan surrogate that renders as `□` (or worse, breaks Telegram's
|
|
9
|
+
* HTML parser when the orphan happens to be inside a tag value).
|
|
10
|
+
*
|
|
11
|
+
* Returns the safe split point. Idempotent on already-safe boundaries.
|
|
12
|
+
*/
|
|
13
|
+
function safeSplitPoint(s, idx) {
|
|
14
|
+
if (idx <= 0 || idx >= s.length)
|
|
15
|
+
return idx;
|
|
16
|
+
const code = s.charCodeAt(idx - 1);
|
|
17
|
+
if (code >= 0xD800 && code <= 0xDBFF)
|
|
18
|
+
return idx - 1;
|
|
19
|
+
return idx;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Split a long message into chunks that fit within messenger limits.
|
|
23
|
+
* Tries to split at natural boundaries (code blocks, paragraphs, lines).
|
|
24
|
+
*/
|
|
25
|
+
export function splitMessage(text, options = {}) {
|
|
26
|
+
const { maxLength = 2000, addContinuationMarker = true, continuationMarker = '\n\n[continued...]', } = options;
|
|
27
|
+
if (text.length <= maxLength) {
|
|
28
|
+
return [text];
|
|
29
|
+
}
|
|
30
|
+
const chunks = [];
|
|
31
|
+
let remaining = text;
|
|
32
|
+
while (remaining.length > maxLength) {
|
|
33
|
+
// Try to split at code block boundary first
|
|
34
|
+
let splitPoint = remaining.lastIndexOf('\n```\n', maxLength);
|
|
35
|
+
if (splitPoint < maxLength / 2) {
|
|
36
|
+
// Try paragraph break
|
|
37
|
+
splitPoint = remaining.lastIndexOf('\n\n', maxLength);
|
|
38
|
+
}
|
|
39
|
+
if (splitPoint < maxLength / 2) {
|
|
40
|
+
// Try line break
|
|
41
|
+
splitPoint = remaining.lastIndexOf('\n', maxLength);
|
|
42
|
+
}
|
|
43
|
+
if (splitPoint < maxLength / 2) {
|
|
44
|
+
// Force split at maxLength
|
|
45
|
+
splitPoint = maxLength;
|
|
46
|
+
}
|
|
47
|
+
// M10: surrogate-pair safety. The natural-boundary searches land on
|
|
48
|
+
// ASCII bytes (newline, backtick) so they're already safe; the forced
|
|
49
|
+
// split at maxLength is the only path that could land mid-pair.
|
|
50
|
+
splitPoint = safeSplitPoint(remaining, splitPoint);
|
|
51
|
+
chunks.push(remaining.slice(0, splitPoint).trim());
|
|
52
|
+
remaining = remaining.slice(splitPoint).trim();
|
|
53
|
+
}
|
|
54
|
+
if (remaining) {
|
|
55
|
+
chunks.push(remaining);
|
|
56
|
+
}
|
|
57
|
+
// Add continuation markers
|
|
58
|
+
if (addContinuationMarker && chunks.length > 1) {
|
|
59
|
+
for (let i = 0; i < chunks.length - 1; i++) {
|
|
60
|
+
chunks[i] += continuationMarker;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return chunks;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=message-split.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-split.js","sourceRoot":"","sources":["../../src/utils/message-split.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,uEAAuE;AAWvE;;;;;;;;;GASG;AACH,SAAS,cAAc,CAAC,CAAS,EAAE,GAAW;IAC5C,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,GAAG,CAAA;IAC3C,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;IAClC,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM;QAAE,OAAO,GAAG,GAAG,CAAC,CAAA;IACpD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,UAAwB,EAAE;IAE1B,MAAM,EACJ,SAAS,GAAG,IAAI,EAChB,qBAAqB,GAAG,IAAI,EAC5B,kBAAkB,GAAG,oBAAoB,GAC1C,GAAG,OAAO,CAAA;IAEX,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,CAAA;IACf,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,IAAI,SAAS,GAAG,IAAI,CAAA;IAEpB,OAAO,SAAS,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QACpC,4CAA4C;QAC5C,IAAI,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;QAC5D,IAAI,UAAU,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC;YAC/B,sBAAsB;YACtB,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;QACvD,CAAC;QACD,IAAI,UAAU,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC;YAC/B,iBAAiB;YACjB,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QACrD,CAAC;QACD,IAAI,UAAU,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC;YAC/B,2BAA2B;YAC3B,UAAU,GAAG,SAAS,CAAA;QACxB,CAAC;QACD,oEAAoE;QACpE,sEAAsE;QACtE,gEAAgE;QAChE,UAAU,GAAG,cAAc,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;QAElD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QAClD,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAA;IAChD,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACxB,CAAC;IAED,2BAA2B;IAC3B,IAAI,qBAAqB,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAA;QACjC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-equal.d.ts","sourceRoot":"","sources":["../../src/utils/safe-equal.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAKvD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Constant-time string compare for auth-token comparison.
|
|
2
|
+
// Returns false when lengths differ, otherwise defers to crypto.timingSafeEqual.
|
|
3
|
+
import { timingSafeEqual } from 'crypto';
|
|
4
|
+
export function safeEqual(a, b) {
|
|
5
|
+
const bufA = Buffer.from(a);
|
|
6
|
+
const bufB = Buffer.from(b);
|
|
7
|
+
if (bufA.length !== bufB.length)
|
|
8
|
+
return false;
|
|
9
|
+
return timingSafeEqual(bufA, bufB);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=safe-equal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-equal.js","sourceRoot":"","sources":["../../src/utils/safe-equal.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,iFAAiF;AAEjF,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAExC,MAAM,UAAU,SAAS,CAAC,CAAS,EAAE,CAAS;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC3B,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAC7C,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;AACpC,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// im-hub-pro web console — shared utilities (theme / i18n / error boundary / api).
|
|
2
|
+
//
|
|
3
|
+
// Loaded as a CLASSIC script in <head> by every page before any page-specific
|
|
4
|
+
// code, so it sits in the same global lexical scope. Wrapped in an IIFE so the
|
|
5
|
+
// only thing that leaks is `window.imhub`.
|
|
6
|
+
//
|
|
7
|
+
// IMPORTANT: this file is served as `/_app.js` by web/server.ts. It does NOT
|
|
8
|
+
// have access to `window.IMHUB_TOKEN` until the inline <script> tag inside
|
|
9
|
+
// each page <head> runs — make sure `imhub.api(...)` is only called after
|
|
10
|
+
// the inline token bootstrap has executed (i.e. from inside DOMContentLoaded
|
|
11
|
+
// or a deferred page script). Reading `window.__lang` follows the same rule.
|
|
12
|
+
|
|
13
|
+
(() => {
|
|
14
|
+
// ============================================================
|
|
15
|
+
// Error boundary
|
|
16
|
+
// ============================================================
|
|
17
|
+
// Sticky red banner at the top of the viewport whenever a script error
|
|
18
|
+
// or unhandled promise rejection escapes the page-level try/catch. The
|
|
19
|
+
// tasks.html SyntaxError that produced a fully blank page (because the
|
|
20
|
+
// script that fills tab labels / button text never ran) was the
|
|
21
|
+
// motivating example — silent JS death must surface visually.
|
|
22
|
+
let errorBar = null
|
|
23
|
+
function showError(msg) {
|
|
24
|
+
if (!errorBar) {
|
|
25
|
+
errorBar = document.createElement('div')
|
|
26
|
+
errorBar.id = 'imhub-error-bar'
|
|
27
|
+
errorBar.setAttribute('role', 'alert')
|
|
28
|
+
errorBar.style.cssText = [
|
|
29
|
+
'position:fixed', 'top:0', 'left:0', 'right:0', 'z-index:99999',
|
|
30
|
+
'padding:8px 16px', 'background:#dc3545', 'color:#fff',
|
|
31
|
+
'font:13px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif',
|
|
32
|
+
'box-shadow:0 2px 8px rgba(0,0,0,.25)',
|
|
33
|
+
'display:flex', 'align-items:center', 'gap:12px',
|
|
34
|
+
].join(';')
|
|
35
|
+
errorBar.innerHTML =
|
|
36
|
+
'<span style="flex:1;white-space:pre-wrap;word-break:break-word" data-imhub-error-msg></span>'
|
|
37
|
+
+ '<button data-imhub-error-close style="background:rgba(255,255,255,.18);'
|
|
38
|
+
+ 'color:#fff;border:none;padding:4px 10px;border-radius:3px;cursor:pointer">'
|
|
39
|
+
+ '×</button>'
|
|
40
|
+
const init = () => {
|
|
41
|
+
if (!document.body) return setTimeout(init, 30)
|
|
42
|
+
document.body.prepend(errorBar)
|
|
43
|
+
errorBar.querySelector('[data-imhub-error-close]')
|
|
44
|
+
.addEventListener('click', () => { errorBar.remove(); errorBar = null })
|
|
45
|
+
}
|
|
46
|
+
init()
|
|
47
|
+
}
|
|
48
|
+
const slot = errorBar?.querySelector('[data-imhub-error-msg]')
|
|
49
|
+
if (slot) slot.textContent = String(msg).slice(0, 600)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function installErrorBoundary() {
|
|
53
|
+
window.addEventListener('error', (e) => {
|
|
54
|
+
const file = (e.filename || '?').split('/').pop()
|
|
55
|
+
showError(`JS error: ${e.message} (${file}:${e.lineno})`)
|
|
56
|
+
})
|
|
57
|
+
window.addEventListener('unhandledrejection', (e) => {
|
|
58
|
+
const r = e.reason
|
|
59
|
+
const msg = r && (r.message || r.toString && r.toString())
|
|
60
|
+
showError(`Unhandled rejection: ${msg ?? '(no message)'}`)
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
installErrorBoundary()
|
|
64
|
+
|
|
65
|
+
// ============================================================
|
|
66
|
+
// Theme manager (light / dark / system three-state)
|
|
67
|
+
// ============================================================
|
|
68
|
+
// Three-state toggle persists in localStorage. `system` removes
|
|
69
|
+
// `[data-theme]` and lets `prefers-color-scheme` decide; `light` /
|
|
70
|
+
// `dark` set the attribute explicitly so CSS `:root[data-theme="..."]`
|
|
71
|
+
// selectors win regardless of OS preference.
|
|
72
|
+
//
|
|
73
|
+
// Applied as early as possible (before first paint) so the page doesn't
|
|
74
|
+
// flash the wrong theme on load. This file is loaded synchronously in
|
|
75
|
+
// <head> precisely for that reason.
|
|
76
|
+
const THEME_KEY = 'im-hub-pro-theme'
|
|
77
|
+
const VALID_MODES = ['system', 'light', 'dark']
|
|
78
|
+
|
|
79
|
+
function getThemeMode() {
|
|
80
|
+
const stored = localStorage.getItem(THEME_KEY)
|
|
81
|
+
return VALID_MODES.includes(stored) ? stored : 'system'
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function applyTheme() {
|
|
85
|
+
const mode = getThemeMode()
|
|
86
|
+
if (mode === 'system') document.documentElement.removeAttribute('data-theme')
|
|
87
|
+
else document.documentElement.setAttribute('data-theme', mode)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function setThemeMode(mode) {
|
|
91
|
+
if (!VALID_MODES.includes(mode)) mode = 'system'
|
|
92
|
+
if (mode === 'system') localStorage.removeItem(THEME_KEY)
|
|
93
|
+
else localStorage.setItem(THEME_KEY, mode)
|
|
94
|
+
applyTheme()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function cycleThemeMode() {
|
|
98
|
+
const cur = getThemeMode()
|
|
99
|
+
const next = VALID_MODES[(VALID_MODES.indexOf(cur) + 1) % VALID_MODES.length]
|
|
100
|
+
setThemeMode(next)
|
|
101
|
+
return next
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function bindThemeToggle(el) {
|
|
105
|
+
if (!el) return
|
|
106
|
+
const ICONS = { system: '🖥', light: '☀', dark: '🌙' }
|
|
107
|
+
const LABELS = {
|
|
108
|
+
en: { system: 'System', light: 'Light', dark: 'Dark', tip: 'Click to cycle theme' },
|
|
109
|
+
zh: { system: '跟随系统', light: '浅色', dark: '深色', tip: '点击切换主题' },
|
|
110
|
+
}
|
|
111
|
+
const lang = (window.__lang === 'zh') ? 'zh' : 'en'
|
|
112
|
+
const L = LABELS[lang]
|
|
113
|
+
function refresh() {
|
|
114
|
+
const m = getThemeMode()
|
|
115
|
+
el.innerHTML = `<span style="font-size:14px">${ICONS[m]}</span> <span>${L[m]}</span>`
|
|
116
|
+
el.title = `${L[m]} — ${L.tip}`
|
|
117
|
+
el.setAttribute('data-theme-mode', m)
|
|
118
|
+
}
|
|
119
|
+
el.style.cursor = 'pointer'
|
|
120
|
+
el.addEventListener('click', () => { cycleThemeMode(); refresh() })
|
|
121
|
+
// Refresh when the OS preference flips (relevant only when mode='system').
|
|
122
|
+
if (window.matchMedia) {
|
|
123
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener?.('change', refresh)
|
|
124
|
+
}
|
|
125
|
+
refresh()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
applyTheme()
|
|
129
|
+
|
|
130
|
+
// ============================================================
|
|
131
|
+
// i18n (data-attribute driven)
|
|
132
|
+
// ============================================================
|
|
133
|
+
// Replaces the previous N×getElementById().textContent= boilerplate.
|
|
134
|
+
// Pages mark elements with `data-i18n="key"` (sets textContent) and
|
|
135
|
+
// optional `data-i18n-attr="placeholder:key1;title:key2"` (sets one or
|
|
136
|
+
// more attributes from the same dictionary). Missing keys leave the
|
|
137
|
+
// element's existing text/attribute intact, so a partial dictionary
|
|
138
|
+
// doesn't blank out the UI.
|
|
139
|
+
function applyI18n(T, root = document) {
|
|
140
|
+
if (!T || typeof T !== 'object') return
|
|
141
|
+
root.querySelectorAll('[data-i18n]').forEach((el) => {
|
|
142
|
+
const key = el.getAttribute('data-i18n')
|
|
143
|
+
if (key && T[key] !== undefined) el.textContent = T[key]
|
|
144
|
+
})
|
|
145
|
+
root.querySelectorAll('[data-i18n-attr]').forEach((el) => {
|
|
146
|
+
const spec = el.getAttribute('data-i18n-attr') || ''
|
|
147
|
+
for (const pair of spec.split(';')) {
|
|
148
|
+
const [attr, key] = pair.split(':').map((s) => (s || '').trim())
|
|
149
|
+
if (attr && key && T[key] !== undefined) el.setAttribute(attr, T[key])
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================
|
|
155
|
+
// API helper
|
|
156
|
+
// ============================================================
|
|
157
|
+
// Auto-attaches X-IM-Hub-Token header. Throws on non-2xx with the
|
|
158
|
+
// server-provided `error` field as the message when present. Always
|
|
159
|
+
// returns parsed JSON on success — pages handle data shape themselves.
|
|
160
|
+
async function apiFetch(path, init = {}) {
|
|
161
|
+
const headers = {
|
|
162
|
+
'X-IM-Hub-Token': window.IMHUB_TOKEN || '',
|
|
163
|
+
...(init.headers || {}),
|
|
164
|
+
}
|
|
165
|
+
if (init.body && !headers['Content-Type']) {
|
|
166
|
+
headers['Content-Type'] = 'application/json'
|
|
167
|
+
}
|
|
168
|
+
const res = await fetch(path, { ...init, headers })
|
|
169
|
+
if (!res.ok) {
|
|
170
|
+
let msg = `${res.status} ${res.statusText}`
|
|
171
|
+
try { const j = await res.json(); if (j && j.error) msg = j.error } catch { /* ignore */ }
|
|
172
|
+
const err = new Error(msg)
|
|
173
|
+
err.status = res.status
|
|
174
|
+
throw err
|
|
175
|
+
}
|
|
176
|
+
if (res.status === 204) return null
|
|
177
|
+
const ct = res.headers.get('content-type') || ''
|
|
178
|
+
return ct.includes('application/json') ? res.json() : res.text()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ============================================================
|
|
182
|
+
// Expose
|
|
183
|
+
// ============================================================
|
|
184
|
+
window.imhub = Object.freeze({
|
|
185
|
+
showError,
|
|
186
|
+
theme: {
|
|
187
|
+
get: getThemeMode,
|
|
188
|
+
set: setThemeMode,
|
|
189
|
+
cycle: cycleThemeMode,
|
|
190
|
+
bindToggle: bindThemeToggle,
|
|
191
|
+
apply: applyTheme,
|
|
192
|
+
},
|
|
193
|
+
i18n: { apply: applyI18n },
|
|
194
|
+
api: apiFetch,
|
|
195
|
+
})
|
|
196
|
+
})()
|