@otto-assistant/otto 0.1.1 → 0.7.15
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/bin.js +2 -0
- package/dist/agent-model.e2e.test.js +755 -0
- package/dist/ai-tool-to-genai.js +233 -0
- package/dist/ai-tool-to-genai.test.js +267 -0
- package/dist/ai-tool.js +6 -0
- package/dist/anthropic-account-identity.js +62 -0
- package/dist/anthropic-account-identity.test.js +38 -0
- package/dist/anthropic-auth-plugin.js +917 -0
- package/dist/anthropic-auth-state.js +303 -0
- package/dist/anthropic-auth-state.test.js +150 -0
- package/dist/bin.js +152 -0
- package/dist/btw-prefix-detection.js +17 -0
- package/dist/btw-prefix-detection.test.js +63 -0
- package/dist/channel-management.js +259 -0
- package/dist/cli-parsing.test.js +142 -0
- package/dist/cli-send-thread.e2e.test.js +353 -0
- package/dist/cli-telegram-options.test.js +99 -0
- package/dist/cli.js +4210 -568
- package/dist/commands/abort.js +65 -0
- package/dist/commands/action-buttons.js +245 -0
- package/dist/commands/add-dir.js +124 -0
- package/dist/commands/add-dir.test.js +126 -0
- package/dist/commands/add-project.js +113 -0
- package/dist/commands/agent.js +355 -0
- package/dist/commands/ask-question.js +320 -0
- package/dist/commands/ask-question.test.js +92 -0
- package/dist/commands/btw.js +121 -0
- package/dist/commands/cli-commands-group-a.test.js +728 -0
- package/dist/commands/cli-commands-group-b.test.js +695 -0
- package/dist/commands/compact.js +120 -0
- package/dist/commands/context-usage.js +140 -0
- package/dist/commands/create-new-project.js +130 -0
- package/dist/commands/diff.js +63 -0
- package/dist/commands/discord-commands-group-a.test.js +621 -0
- package/dist/commands/discord-commands-group-b.test.js +595 -0
- package/dist/commands/discord-commands-group-c.test.js +739 -0
- package/dist/commands/file-upload.js +275 -0
- package/dist/commands/fork-subagent.js +177 -0
- package/dist/commands/fork.js +262 -0
- package/dist/commands/gemini-apikey.js +70 -0
- package/dist/commands/login.js +887 -0
- package/dist/commands/mcp.js +239 -0
- package/dist/commands/memory-snapshot.js +24 -0
- package/dist/commands/mention-mode.js +44 -0
- package/dist/commands/merge-worktree.js +162 -0
- package/dist/commands/model-variant.js +366 -0
- package/dist/commands/model.js +794 -0
- package/dist/commands/new-worktree.js +465 -0
- package/dist/commands/paginated-select.js +57 -0
- package/dist/commands/permissions.js +274 -0
- package/dist/commands/queue.js +223 -0
- package/dist/commands/remove-project.js +115 -0
- package/dist/commands/restart-opencode-server.js +127 -0
- package/dist/commands/resume.js +149 -0
- package/dist/commands/run-command.js +79 -0
- package/dist/commands/screenshare.js +303 -0
- package/dist/commands/screenshare.test.js +20 -0
- package/dist/commands/session-id.js +78 -0
- package/dist/commands/session.js +176 -0
- package/dist/commands/share.js +80 -0
- package/dist/commands/tasks.js +205 -0
- package/dist/commands/thread-deletion-sync.js +50 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/undo-redo.js +305 -0
- package/dist/commands/unset-model.js +139 -0
- package/dist/commands/upgrade.js +48 -0
- package/dist/commands/user-command.js +155 -0
- package/dist/commands/verbosity.js +125 -0
- package/dist/commands/vscode.js +269 -0
- package/dist/commands/worktree-settings.js +43 -0
- package/dist/commands/worktrees.js +468 -0
- package/dist/condense-memory.js +33 -0
- package/dist/config.js +100 -255
- package/dist/context-awareness-plugin.js +340 -0
- package/dist/context-awareness-plugin.test.js +126 -0
- package/dist/critique-utils.js +95 -0
- package/dist/database.js +1355 -0
- package/dist/db.js +260 -0
- package/dist/db.test.js +138 -0
- package/dist/debounce-timeout.js +28 -0
- package/dist/debounced-process-flush.js +77 -0
- package/dist/discord-bot.js +1124 -0
- package/dist/discord-command-registration.js +567 -0
- package/dist/discord-urls.js +82 -0
- package/dist/discord-utils.js +616 -0
- package/dist/discord-utils.test.js +134 -0
- package/dist/errors.js +157 -0
- package/dist/escape-backticks.test.js +429 -0
- package/dist/event-stream-real-capture.e2e.test.js +533 -0
- package/dist/eventsource-parser.test.js +327 -0
- package/dist/exec-async.js +26 -0
- package/dist/external-opencode-sync.js +480 -0
- package/dist/format-tables.js +491 -0
- package/dist/format-tables.test.js +478 -0
- package/dist/forum-sync/config.js +79 -0
- package/dist/forum-sync/discord-operations.js +154 -0
- package/dist/forum-sync/index.js +5 -0
- package/dist/forum-sync/markdown.js +113 -0
- package/dist/forum-sync/sync-to-discord.js +417 -0
- package/dist/forum-sync/sync-to-files.js +190 -0
- package/dist/forum-sync/types.js +53 -0
- package/dist/forum-sync/watchers.js +307 -0
- package/dist/gateway-proxy-reconnect.e2e.test.js +394 -0
- package/dist/gateway-proxy.e2e.test.js +485 -0
- package/dist/genai-worker-wrapper.js +111 -0
- package/dist/genai-worker.js +311 -0
- package/dist/genai.js +232 -0
- package/dist/generated/browser.js +17 -0
- package/dist/generated/client.js +37 -0
- package/dist/generated/commonInputTypes.js +10 -0
- package/dist/generated/enums.js +58 -0
- package/dist/generated/internal/class.js +49 -0
- package/dist/generated/internal/prismaNamespace.js +254 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +224 -0
- package/dist/generated/models/bot_api_keys.js +1 -0
- package/dist/generated/models/bot_tokens.js +1 -0
- package/dist/generated/models/channel_agents.js +1 -0
- package/dist/generated/models/channel_directories.js +1 -0
- package/dist/generated/models/channel_mention_mode.js +1 -0
- package/dist/generated/models/channel_models.js +1 -0
- package/dist/generated/models/channel_verbosity.js +1 -0
- package/dist/generated/models/channel_worktrees.js +1 -0
- package/dist/generated/models/forum_sync_configs.js +1 -0
- package/dist/generated/models/global_models.js +1 -0
- package/dist/generated/models/ipc_requests.js +1 -0
- package/dist/generated/models/part_messages.js +1 -0
- package/dist/generated/models/scheduled_tasks.js +1 -0
- package/dist/generated/models/session_agents.js +1 -0
- package/dist/generated/models/session_events.js +1 -0
- package/dist/generated/models/session_models.js +1 -0
- package/dist/generated/models/session_start_sources.js +1 -0
- package/dist/generated/models/thread_sessions.js +1 -0
- package/dist/generated/models/thread_worktrees.js +1 -0
- package/dist/generated/models.js +1 -0
- package/dist/heap-monitor.js +122 -0
- package/dist/hrana-server.js +251 -0
- package/dist/hrana-server.test.js +370 -0
- package/dist/html-actions.js +123 -0
- package/dist/html-actions.test.js +70 -0
- package/dist/html-components.js +117 -0
- package/dist/html-components.test.js +34 -0
- package/dist/image-optimizer-plugin.js +153 -0
- package/dist/image-utils.js +112 -0
- package/dist/interaction-handler.js +420 -0
- package/dist/ipc-polling.js +327 -0
- package/dist/ipc-tools-plugin.js +193 -0
- package/dist/ipc-utils.js +18 -0
- package/dist/limit-heading-depth.js +25 -0
- package/dist/limit-heading-depth.test.js +105 -0
- package/dist/logger.js +171 -0
- package/dist/markdown.js +342 -0
- package/dist/markdown.test.js +264 -0
- package/dist/memory-overview-plugin.js +128 -0
- package/dist/message-finish-field.e2e.test.js +168 -0
- package/dist/message-formatting.js +415 -0
- package/dist/message-formatting.test.js +115 -0
- package/dist/message-preprocessing.js +359 -0
- package/dist/onboarding-tutorial.js +163 -0
- package/dist/onboarding-welcome.js +37 -0
- package/dist/openai-realtime.js +224 -0
- package/dist/opencode-command-detection.js +65 -0
- package/dist/opencode-command-detection.test.js +240 -0
- package/dist/opencode-command.js +131 -0
- package/dist/opencode-command.test.js +48 -0
- package/dist/opencode-interrupt-plugin.js +388 -0
- package/dist/opencode-interrupt-plugin.test.js +463 -0
- package/dist/opencode.js +1117 -0
- package/dist/otto/branding.js +22 -0
- package/dist/otto/index.js +21 -0
- package/dist/otto-digital-twin.e2e.test.js +161 -0
- package/dist/otto-opencode-plugin-loading.e2e.test.js +94 -0
- package/dist/otto-opencode-plugin.js +21 -0
- package/dist/otto-opencode-plugin.test.js +98 -0
- package/dist/parse-permission-rules.test.js +117 -0
- package/dist/patch-text-parser.js +97 -0
- package/dist/plugin-logger.js +68 -0
- package/dist/privacy-sanitizer.js +105 -0
- package/dist/queue-advanced-abort.e2e.test.js +293 -0
- package/dist/queue-advanced-action-buttons.e2e.test.js +206 -0
- package/dist/queue-advanced-e2e-setup.js +790 -0
- package/dist/queue-advanced-footer.e2e.test.js +481 -0
- package/dist/queue-advanced-model-switch.e2e.test.js +299 -0
- package/dist/queue-advanced-permissions-typing.e2e.test.js +179 -0
- package/dist/queue-advanced-question.e2e.test.js +261 -0
- package/dist/queue-advanced-typing-interrupt.e2e.test.js +114 -0
- package/dist/queue-advanced-typing.e2e.test.js +153 -0
- package/dist/queue-drain-after-interactive-ui.e2e.test.js +119 -0
- package/dist/queue-interrupt-drain.e2e.test.js +135 -0
- package/dist/queue-question-select-drain.e2e.test.js +256 -0
- package/dist/runtime-idle-sweeper.js +52 -0
- package/dist/runtime-lifecycle.e2e.test.js +514 -0
- package/dist/sentry.js +23 -0
- package/dist/session-handler/agent-utils.js +67 -0
- package/dist/session-handler/event-stream-state.js +475 -0
- package/dist/session-handler/event-stream-state.test.js +632 -0
- package/dist/session-handler/model-utils.js +147 -0
- package/dist/session-handler/opencode-session-event-log.js +94 -0
- package/dist/session-handler/thread-runtime-state.js +131 -0
- package/dist/session-handler/thread-session-runtime.js +3390 -0
- package/dist/session-handler.js +9 -0
- package/dist/session-search.js +100 -0
- package/dist/session-search.test.js +40 -0
- package/dist/session-title-rename.test.js +92 -0
- package/dist/skill-filter.js +31 -0
- package/dist/skill-filter.test.js +65 -0
- package/dist/startup-service.js +153 -0
- package/dist/startup-time.e2e.test.js +296 -0
- package/dist/store.js +19 -0
- package/dist/subagent-rate-limit-plugin.js +175 -0
- package/dist/system-message.js +702 -0
- package/dist/system-message.test.js +697 -0
- package/dist/task-runner.js +530 -0
- package/dist/task-schedule.js +213 -0
- package/dist/task-schedule.test.js +71 -0
- package/dist/test-utils.js +313 -0
- package/dist/thinking-utils.js +35 -0
- package/dist/thread-message-queue.e2e.test.js +1111 -0
- package/dist/tools.js +357 -0
- package/dist/undo-redo.e2e.test.js +161 -0
- package/dist/unnest-code-blocks.js +146 -0
- package/dist/unnest-code-blocks.test.js +673 -0
- package/dist/upgrade.js +156 -0
- package/dist/utils.js +172 -0
- package/dist/utils.test.js +130 -0
- package/dist/voice-attachment.js +34 -0
- package/dist/voice-handler.js +646 -0
- package/dist/voice-message.e2e.test.js +1021 -0
- package/dist/voice.js +456 -0
- package/dist/voice.test.js +235 -0
- package/dist/wait-session.js +171 -0
- package/dist/websockify.js +69 -0
- package/dist/worker-types.js +4 -0
- package/dist/worktree-lifecycle.e2e.test.js +311 -0
- package/dist/worktree-utils.js +3 -0
- package/dist/worktrees.js +991 -0
- package/dist/worktrees.test.js +415 -0
- package/dist/xml.js +92 -0
- package/dist/xml.test.js +32 -0
- package/package.json +90 -38
- package/schema.prisma +303 -0
- package/skills/batch/SKILL.md +87 -0
- package/skills/critique/SKILL.md +112 -0
- package/skills/egaki/SKILL.md +100 -0
- package/skills/errore/SKILL.md +647 -0
- package/skills/event-sourcing-state/SKILL.md +252 -0
- package/skills/goke/SKILL.md +38 -0
- package/skills/jitter/EDITOR.md +219 -0
- package/skills/jitter/EXPORT-INTERNALS.md +309 -0
- package/skills/jitter/SKILL.md +158 -0
- package/skills/jitter/jitter-clipboard.json +1042 -0
- package/skills/jitter/package.json +14 -0
- package/skills/jitter/tsconfig.json +15 -0
- package/skills/jitter/utils/actions.ts +212 -0
- package/skills/jitter/utils/export.ts +114 -0
- package/skills/jitter/utils/index.ts +141 -0
- package/skills/jitter/utils/snapshot.ts +154 -0
- package/skills/jitter/utils/traverse.ts +246 -0
- package/skills/jitter/utils/types.ts +279 -0
- package/skills/jitter/utils/wait.ts +133 -0
- package/skills/lintcn/SKILL.md +873 -0
- package/skills/manual-kimaki-upstream-adapt/SKILL.md +114 -0
- package/skills/new-skill/SKILL.md +237 -0
- package/skills/npm-package/SKILL.md +617 -0
- package/skills/opensrc/SKILL.md +78 -0
- package/skills/otto-publish/SKILL.md +61 -0
- package/skills/playwriter/SKILL.md +35 -0
- package/skills/profano/SKILL.md +16 -0
- package/skills/proxyman/SKILL.md +215 -0
- package/skills/security-review/SKILL.md +208 -0
- package/skills/sigillo/SKILL.md +101 -0
- package/skills/simplify/SKILL.md +58 -0
- package/skills/spiceflow/SKILL.md +28 -0
- package/skills/termcast/SKILL.md +945 -0
- package/skills/tuistory/SKILL.md +98 -0
- package/skills/usecomputer/SKILL.md +264 -0
- package/skills/x-articles/SKILL.md +554 -0
- package/skills/zele/SKILL.md +49 -0
- package/skills/zustand-centralized-state/SKILL.md +1004 -0
- package/src/agent-model.e2e.test.ts +979 -0
- package/src/ai-tool-to-genai.test.ts +296 -0
- package/src/ai-tool-to-genai.ts +283 -0
- package/src/ai-tool.ts +39 -0
- package/src/anthropic-account-identity.test.ts +52 -0
- package/src/anthropic-account-identity.ts +77 -0
- package/src/anthropic-auth-plugin.ts +1139 -0
- package/src/anthropic-auth-state.test.ts +187 -0
- package/src/anthropic-auth-state.ts +386 -0
- package/src/bin.ts +182 -0
- package/src/btw-prefix-detection.test.ts +73 -0
- package/src/btw-prefix-detection.ts +23 -0
- package/src/channel-management.ts +376 -0
- package/src/cli-parsing.test.ts +197 -0
- package/src/cli-send-thread.e2e.test.ts +463 -0
- package/src/cli-telegram-options.test.ts +114 -0
- package/src/cli.ts +5718 -580
- package/src/commands/abort.ts +89 -0
- package/src/commands/action-buttons.ts +364 -0
- package/src/commands/add-dir.test.ts +154 -0
- package/src/commands/add-dir.ts +175 -0
- package/src/commands/add-project.ts +149 -0
- package/src/commands/agent.ts +496 -0
- package/src/commands/ask-question.test.ts +111 -0
- package/src/commands/ask-question.ts +455 -0
- package/src/commands/btw.ts +184 -0
- package/src/commands/cli-commands-group-a.test.ts +837 -0
- package/src/commands/cli-commands-group-b.test.ts +800 -0
- package/src/commands/compact.ts +157 -0
- package/src/commands/context-usage.ts +199 -0
- package/src/commands/create-new-project.ts +190 -0
- package/src/commands/diff.ts +91 -0
- package/src/commands/discord-commands-group-a.test.ts +751 -0
- package/src/commands/discord-commands-group-b.test.ts +648 -0
- package/src/commands/discord-commands-group-c.test.ts +882 -0
- package/src/commands/file-upload.ts +389 -0
- package/src/commands/fork-subagent.ts +263 -0
- package/src/commands/fork.ts +386 -0
- package/src/commands/gemini-apikey.ts +104 -0
- package/src/commands/login.ts +1175 -0
- package/src/commands/mcp.ts +307 -0
- package/src/commands/memory-snapshot.ts +30 -0
- package/src/commands/mention-mode.ts +68 -0
- package/src/commands/merge-worktree.ts +226 -0
- package/src/commands/model-variant.ts +485 -0
- package/src/commands/model.ts +1078 -0
- package/src/commands/new-worktree.ts +645 -0
- package/src/commands/paginated-select.ts +81 -0
- package/src/commands/permissions.ts +397 -0
- package/src/commands/queue.ts +293 -0
- package/src/commands/remove-project.ts +155 -0
- package/src/commands/restart-opencode-server.ts +162 -0
- package/src/commands/resume.ts +230 -0
- package/src/commands/run-command.ts +123 -0
- package/src/commands/screenshare.test.ts +30 -0
- package/src/commands/screenshare.ts +366 -0
- package/src/commands/session-id.ts +109 -0
- package/src/commands/session.ts +227 -0
- package/src/commands/share.ts +106 -0
- package/src/commands/tasks.ts +293 -0
- package/src/commands/thread-deletion-sync.ts +80 -0
- package/src/commands/types.ts +25 -0
- package/src/commands/undo-redo.ts +386 -0
- package/src/commands/unset-model.ts +174 -0
- package/src/commands/upgrade.ts +59 -0
- package/src/commands/user-command.ts +198 -0
- package/src/commands/verbosity.ts +173 -0
- package/src/commands/vscode.ts +342 -0
- package/src/commands/worktree-settings.ts +70 -0
- package/src/commands/worktrees.ts +645 -0
- package/src/condense-memory.ts +36 -0
- package/src/config.ts +103 -339
- package/src/context-awareness-plugin.test.ts +144 -0
- package/src/context-awareness-plugin.ts +469 -0
- package/src/critique-utils.ts +139 -0
- package/src/database.ts +1949 -0
- package/src/db.test.ts +162 -0
- package/src/db.ts +295 -0
- package/src/debounce-timeout.ts +43 -0
- package/src/debounced-process-flush.ts +104 -0
- package/src/discord-bot.ts +1505 -0
- package/src/discord-command-registration.ts +752 -0
- package/src/discord-urls.ts +89 -0
- package/src/discord-utils.test.ts +153 -0
- package/src/discord-utils.ts +846 -0
- package/src/errors.ts +201 -0
- package/src/escape-backticks.test.ts +469 -0
- package/src/event-stream-real-capture.e2e.test.ts +692 -0
- package/src/eventsource-parser.test.ts +351 -0
- package/src/exec-async.ts +35 -0
- package/src/external-opencode-sync.ts +685 -0
- package/src/format-tables.test.ts +515 -0
- package/src/format-tables.ts +718 -0
- package/src/forum-sync/config.ts +92 -0
- package/src/forum-sync/discord-operations.ts +241 -0
- package/src/forum-sync/index.ts +9 -0
- package/src/forum-sync/markdown.ts +172 -0
- package/src/forum-sync/sync-to-discord.ts +595 -0
- package/src/forum-sync/sync-to-files.ts +294 -0
- package/src/forum-sync/types.ts +175 -0
- package/src/forum-sync/watchers.ts +454 -0
- package/src/gateway-proxy-reconnect.e2e.test.ts +523 -0
- package/src/gateway-proxy.e2e.test.ts +644 -0
- package/src/genai-worker-wrapper.ts +164 -0
- package/src/genai-worker.ts +386 -0
- package/src/genai.ts +321 -0
- package/src/generated/browser.ts +114 -0
- package/src/generated/client.ts +138 -0
- package/src/generated/commonInputTypes.ts +770 -0
- package/src/generated/enums.ts +98 -0
- package/src/generated/internal/class.ts +384 -0
- package/src/generated/internal/prismaNamespace.ts +2394 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +327 -0
- package/src/generated/models/bot_api_keys.ts +1288 -0
- package/src/generated/models/bot_tokens.ts +1700 -0
- package/src/generated/models/channel_agents.ts +1256 -0
- package/src/generated/models/channel_directories.ts +1859 -0
- package/src/generated/models/channel_mention_mode.ts +1300 -0
- package/src/generated/models/channel_models.ts +1288 -0
- package/src/generated/models/channel_verbosity.ts +1228 -0
- package/src/generated/models/channel_worktrees.ts +1300 -0
- package/src/generated/models/forum_sync_configs.ts +1452 -0
- package/src/generated/models/global_models.ts +1288 -0
- package/src/generated/models/ipc_requests.ts +1485 -0
- package/src/generated/models/part_messages.ts +1302 -0
- package/src/generated/models/scheduled_tasks.ts +2320 -0
- package/src/generated/models/session_agents.ts +1086 -0
- package/src/generated/models/session_events.ts +1439 -0
- package/src/generated/models/session_models.ts +1114 -0
- package/src/generated/models/session_start_sources.ts +1408 -0
- package/src/generated/models/thread_sessions.ts +1781 -0
- package/src/generated/models/thread_worktrees.ts +1356 -0
- package/src/generated/models.ts +30 -0
- package/src/heap-monitor.ts +152 -0
- package/src/hrana-server.test.ts +434 -0
- package/src/hrana-server.ts +299 -0
- package/src/html-actions.test.ts +87 -0
- package/src/html-actions.ts +174 -0
- package/src/html-components.test.ts +38 -0
- package/src/html-components.ts +181 -0
- package/src/image-optimizer-plugin.ts +194 -0
- package/src/image-utils.ts +149 -0
- package/src/interaction-handler.ts +610 -0
- package/src/ipc-polling.ts +427 -0
- package/src/ipc-tools-plugin.ts +236 -0
- package/src/ipc-utils.ts +29 -0
- package/src/limit-heading-depth.test.ts +116 -0
- package/src/limit-heading-depth.ts +26 -0
- package/src/logger.ts +215 -0
- package/src/markdown.test.ts +315 -0
- package/src/markdown.ts +410 -0
- package/src/memory-overview-plugin.ts +163 -0
- package/src/message-finish-field.e2e.test.ts +195 -0
- package/src/message-formatting.test.ts +126 -0
- package/src/message-formatting.ts +535 -0
- package/src/message-preprocessing.ts +488 -0
- package/src/onboarding-tutorial.ts +167 -0
- package/src/onboarding-welcome.ts +49 -0
- package/src/openai-realtime.ts +358 -0
- package/src/opencode-command-detection.test.ts +307 -0
- package/src/opencode-command-detection.ts +76 -0
- package/src/opencode-command.test.ts +70 -0
- package/src/opencode-command.ts +191 -0
- package/src/opencode-interrupt-plugin.test.ts +682 -0
- package/src/opencode-interrupt-plugin.ts +507 -0
- package/src/opencode.ts +1453 -0
- package/src/otto/branding.ts +23 -0
- package/src/otto/index.ts +22 -0
- package/src/otto-digital-twin.e2e.test.ts +199 -0
- package/src/otto-opencode-plugin-loading.e2e.test.ts +117 -0
- package/src/otto-opencode-plugin.test.ts +108 -0
- package/src/otto-opencode-plugin.ts +22 -0
- package/src/parse-permission-rules.test.ts +127 -0
- package/src/patch-text-parser.ts +107 -0
- package/src/plugin-logger.ts +84 -0
- package/src/privacy-sanitizer.ts +142 -0
- package/src/queue-advanced-abort.e2e.test.ts +382 -0
- package/src/queue-advanced-action-buttons.e2e.test.ts +268 -0
- package/src/queue-advanced-e2e-setup.ts +877 -0
- package/src/queue-advanced-footer.e2e.test.ts +591 -0
- package/src/queue-advanced-model-switch.e2e.test.ts +383 -0
- package/src/queue-advanced-permissions-typing.e2e.test.ts +246 -0
- package/src/queue-advanced-question.e2e.test.ts +316 -0
- package/src/queue-advanced-typing-interrupt.e2e.test.ts +146 -0
- package/src/queue-advanced-typing.e2e.test.ts +199 -0
- package/src/queue-drain-after-interactive-ui.e2e.test.ts +151 -0
- package/src/queue-interrupt-drain.e2e.test.ts +166 -0
- package/src/queue-question-select-drain.e2e.test.ts +327 -0
- package/src/runtime-idle-sweeper.ts +76 -0
- package/src/runtime-lifecycle.e2e.test.ts +651 -0
- package/src/schema.sql +174 -0
- package/src/sentry.ts +26 -0
- package/src/session-handler/agent-utils.ts +99 -0
- package/src/session-handler/event-stream-fixtures/real-session-action-buttons.jsonl +45 -0
- package/src/session-handler/event-stream-fixtures/real-session-footer-suppressed-on-pre-idle-interrupt.jsonl +40 -0
- package/src/session-handler/event-stream-fixtures/real-session-permission-external-file.jsonl +23 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-normal.jsonl +22 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-three-parallel-sleeps.jsonl +277 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-user-interruption.jsonl +46 -0
- package/src/session-handler/event-stream-fixtures/session-abort-after-idle-race.jsonl +21 -0
- package/src/session-handler/event-stream-fixtures/session-concurrent-messages-serialized.jsonl +56 -0
- package/src/session-handler/event-stream-fixtures/session-explicit-abort.jsonl +44 -0
- package/src/session-handler/event-stream-fixtures/session-normal-completion.jsonl +29 -0
- package/src/session-handler/event-stream-fixtures/session-tool-call-noisy-stream.jsonl +29 -0
- package/src/session-handler/event-stream-fixtures/session-two-completions-same-session.jsonl +50 -0
- package/src/session-handler/event-stream-fixtures/session-user-interruption.jsonl +59 -0
- package/src/session-handler/event-stream-fixtures/session-voice-queued-followup.jsonl +52 -0
- package/src/session-handler/event-stream-state.test.ts +717 -0
- package/src/session-handler/event-stream-state.ts +706 -0
- package/src/session-handler/model-utils.ts +217 -0
- package/src/session-handler/opencode-session-event-log.ts +130 -0
- package/src/session-handler/thread-runtime-state.ts +247 -0
- package/src/session-handler/thread-session-runtime.ts +4440 -0
- package/src/session-handler.ts +15 -0
- package/src/session-search.test.ts +50 -0
- package/src/session-search.ts +148 -0
- package/src/session-title-rename.test.ts +130 -0
- package/src/skill-filter.test.ts +83 -0
- package/src/skill-filter.ts +42 -0
- package/src/startup-service.ts +200 -0
- package/src/startup-time.e2e.test.ts +373 -0
- package/src/store.ts +139 -0
- package/src/subagent-rate-limit-plugin.ts +218 -0
- package/src/system-message.test.ts +710 -0
- package/src/system-message.ts +814 -0
- package/src/task-runner.ts +725 -0
- package/src/task-schedule.test.ts +84 -0
- package/src/task-schedule.ts +317 -0
- package/src/test-utils.ts +451 -0
- package/src/thinking-utils.ts +61 -0
- package/src/thread-message-queue.e2e.test.ts +1350 -0
- package/src/tools.ts +430 -0
- package/src/undici.d.ts +12 -0
- package/src/undo-redo.e2e.test.ts +209 -0
- package/src/unnest-code-blocks.test.ts +713 -0
- package/src/unnest-code-blocks.ts +185 -0
- package/src/upgrade.ts +185 -0
- package/src/utils.test.ts +155 -0
- package/src/utils.ts +265 -0
- package/src/voice-attachment.ts +51 -0
- package/src/voice-handler.ts +908 -0
- package/src/voice-message.e2e.test.ts +1255 -0
- package/src/voice.test.ts +281 -0
- package/src/voice.ts +638 -0
- package/src/wait-session.ts +273 -0
- package/src/websockify.ts +101 -0
- package/src/worker-types.ts +64 -0
- package/src/worktree-lifecycle.e2e.test.ts +396 -0
- package/src/worktree-utils.ts +4 -0
- package/src/worktrees.test.ts +489 -0
- package/src/worktrees.ts +1370 -0
- package/src/xml.test.ts +38 -0
- package/src/xml.ts +121 -0
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config.d.ts +0 -39
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -202
- package/dist/config.test.js.map +0 -1
- package/dist/detect.d.ts +0 -9
- package/dist/detect.d.ts.map +0 -1
- package/dist/detect.js +0 -40
- package/dist/detect.js.map +0 -1
- package/dist/detect.test.d.ts +0 -2
- package/dist/detect.test.d.ts.map +0 -1
- package/dist/detect.test.js +0 -26
- package/dist/detect.test.js.map +0 -1
- package/dist/docker.d.ts +0 -7
- package/dist/docker.d.ts.map +0 -1
- package/dist/docker.js +0 -17
- package/dist/docker.js.map +0 -1
- package/dist/docker.test.d.ts +0 -2
- package/dist/docker.test.d.ts.map +0 -1
- package/dist/docker.test.js +0 -12
- package/dist/docker.test.js.map +0 -1
- package/dist/health.d.ts +0 -31
- package/dist/health.d.ts.map +0 -1
- package/dist/health.js +0 -117
- package/dist/health.js.map +0 -1
- package/dist/health.test.d.ts +0 -2
- package/dist/health.test.d.ts.map +0 -1
- package/dist/health.test.js +0 -52
- package/dist/health.test.js.map +0 -1
- package/dist/index.d.ts +0 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -15
- package/dist/index.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -8
- package/dist/index.test.js.map +0 -1
- package/dist/installer.d.ts +0 -10
- package/dist/installer.d.ts.map +0 -1
- package/dist/installer.js +0 -50
- package/dist/installer.js.map +0 -1
- package/dist/installer.test.d.ts +0 -2
- package/dist/installer.test.d.ts.map +0 -1
- package/dist/installer.test.js +0 -43
- package/dist/installer.test.js.map +0 -1
- package/dist/lifecycle.d.ts +0 -10
- package/dist/lifecycle.d.ts.map +0 -1
- package/dist/lifecycle.js +0 -45
- package/dist/lifecycle.js.map +0 -1
- package/dist/lifecycle.test.d.ts +0 -2
- package/dist/lifecycle.test.d.ts.map +0 -1
- package/dist/lifecycle.test.js +0 -20
- package/dist/lifecycle.test.js.map +0 -1
- package/dist/manifest.d.ts +0 -18
- package/dist/manifest.d.ts.map +0 -1
- package/dist/manifest.js +0 -30
- package/dist/manifest.js.map +0 -1
- package/dist/skills-baseline.d.ts +0 -7
- package/dist/skills-baseline.d.ts.map +0 -1
- package/dist/skills-baseline.js +0 -9
- package/dist/skills-baseline.js.map +0 -1
- package/dist/skills.d.ts +0 -110
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js +0 -429
- package/dist/skills.js.map +0 -1
- package/dist/skills.test.d.ts +0 -2
- package/dist/skills.test.d.ts.map +0 -1
- package/dist/skills.test.js +0 -416
- package/dist/skills.test.js.map +0 -1
- package/dist/sync.d.ts +0 -10
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js +0 -39
- package/dist/sync.js.map +0 -1
- package/dist/tenant.d.ts +0 -13
- package/dist/tenant.d.ts.map +0 -1
- package/dist/tenant.js +0 -105
- package/dist/tenant.js.map +0 -1
- package/dist/tenant.test.d.ts +0 -2
- package/dist/tenant.test.d.ts.map +0 -1
- package/dist/tenant.test.js +0 -37
- package/dist/tenant.test.js.map +0 -1
- package/src/config.test.ts +0 -237
- package/src/detect.test.ts +0 -29
- package/src/detect.ts +0 -52
- package/src/docker.test.ts +0 -12
- package/src/docker.ts +0 -23
- package/src/health.test.ts +0 -61
- package/src/health.ts +0 -158
- package/src/index.test.ts +0 -8
- package/src/index.ts +0 -62
- package/src/installer.test.ts +0 -52
- package/src/installer.ts +0 -62
- package/src/lifecycle.test.ts +0 -23
- package/src/lifecycle.ts +0 -49
- package/src/manifest.ts +0 -42
- package/src/skills-baseline.ts +0 -14
- package/src/skills.test.ts +0 -503
- package/src/skills.ts +0 -512
- package/src/sync.ts +0 -53
- package/src/tenant.test.ts +0 -49
- package/src/tenant.ts +0 -120
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// Discord channel and category management.
|
|
2
|
+
// Creates and manages Otto project channels (text + voice pairs),
|
|
3
|
+
// extracts channel metadata from topic tags, and ensures category structure.
|
|
4
|
+
import { ChannelType, } from 'discord.js';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { getChannelDirectory, setChannelDirectory, findChannelsByDirectory, } from './database.js';
|
|
8
|
+
import { getProjectsDir } from './config.js';
|
|
9
|
+
import { execAsync } from './worktrees.js';
|
|
10
|
+
import { createLogger, LogPrefix } from './logger.js';
|
|
11
|
+
const logger = createLogger(LogPrefix.CHANNEL);
|
|
12
|
+
// Legacy category names kept for backward-compat lookup on existing servers.
|
|
13
|
+
// New categories are created with the current "Otto" / "Otto Audio" names.
|
|
14
|
+
const CATEGORY_NAME = 'Otto';
|
|
15
|
+
const CATEGORY_NAME_AUDIO = 'Otto Audio';
|
|
16
|
+
const LEGACY_CATEGORY_NAME = 'Kimaki';
|
|
17
|
+
const LEGACY_CATEGORY_NAME_AUDIO = 'Kimaki Audio';
|
|
18
|
+
export async function ensureOttoCategory(guild, botName) {
|
|
19
|
+
// Skip appending bot name if it's already "otto" to avoid "Otto otto"
|
|
20
|
+
const isOttoBot = botName?.toLowerCase() === 'otto';
|
|
21
|
+
const categoryName = botName && !isOttoBot ? `${CATEGORY_NAME} ${botName}` : CATEGORY_NAME;
|
|
22
|
+
// Legacy names to check when looking up existing categories on older servers
|
|
23
|
+
const isLegacyKimakiBot = botName?.toLowerCase() === 'otto';
|
|
24
|
+
const legacyCategoryName = botName && !isLegacyKimakiBot ? `${LEGACY_CATEGORY_NAME} ${botName}` : LEGACY_CATEGORY_NAME;
|
|
25
|
+
const existingCategory = guild.channels.cache.find((channel) => {
|
|
26
|
+
if (channel.type !== ChannelType.GuildCategory) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const name = channel.name.toLowerCase();
|
|
30
|
+
return (name === categoryName.toLowerCase() ||
|
|
31
|
+
name === legacyCategoryName.toLowerCase());
|
|
32
|
+
});
|
|
33
|
+
if (existingCategory) {
|
|
34
|
+
return existingCategory;
|
|
35
|
+
}
|
|
36
|
+
return guild.channels.create({
|
|
37
|
+
name: categoryName,
|
|
38
|
+
type: ChannelType.GuildCategory,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Keep old export name as an alias for any callers that haven't been updated yet
|
|
42
|
+
export const ensureKimakiCategory = ensureOttoCategory;
|
|
43
|
+
export async function ensureOttoAudioCategory(guild, botName) {
|
|
44
|
+
// Skip appending bot name if it's already "otto" to avoid "Otto Audio otto"
|
|
45
|
+
const isOttoBot = botName?.toLowerCase() === 'otto';
|
|
46
|
+
const categoryName = botName && !isOttoBot ? `${CATEGORY_NAME_AUDIO} ${botName}` : CATEGORY_NAME_AUDIO;
|
|
47
|
+
const isLegacyKimakiBot = botName?.toLowerCase() === 'otto';
|
|
48
|
+
const legacyCategoryName = botName && !isLegacyKimakiBot
|
|
49
|
+
? `${LEGACY_CATEGORY_NAME_AUDIO} ${botName}`
|
|
50
|
+
: LEGACY_CATEGORY_NAME_AUDIO;
|
|
51
|
+
const existingCategory = guild.channels.cache.find((channel) => {
|
|
52
|
+
if (channel.type !== ChannelType.GuildCategory) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const name = channel.name.toLowerCase();
|
|
56
|
+
return (name === categoryName.toLowerCase() ||
|
|
57
|
+
name === legacyCategoryName.toLowerCase());
|
|
58
|
+
});
|
|
59
|
+
if (existingCategory) {
|
|
60
|
+
return existingCategory;
|
|
61
|
+
}
|
|
62
|
+
return guild.channels.create({
|
|
63
|
+
name: categoryName,
|
|
64
|
+
type: ChannelType.GuildCategory,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export const ensureKimakiAudioCategory = ensureOttoAudioCategory;
|
|
68
|
+
export async function createProjectChannels({ guild, projectDirectory, botName, enableVoiceChannels = false, }) {
|
|
69
|
+
const baseName = path.basename(projectDirectory);
|
|
70
|
+
const channelName = `${baseName}`
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
73
|
+
.slice(0, 100);
|
|
74
|
+
const ottoCategory = await ensureOttoCategory(guild, botName);
|
|
75
|
+
const textChannel = await guild.channels.create({
|
|
76
|
+
name: channelName,
|
|
77
|
+
type: ChannelType.GuildText,
|
|
78
|
+
parent: ottoCategory,
|
|
79
|
+
// Channel configuration is stored in SQLite, not in the topic
|
|
80
|
+
});
|
|
81
|
+
await setChannelDirectory({
|
|
82
|
+
channelId: textChannel.id,
|
|
83
|
+
directory: projectDirectory,
|
|
84
|
+
channelType: 'text',
|
|
85
|
+
});
|
|
86
|
+
let voiceChannelId = null;
|
|
87
|
+
if (enableVoiceChannels) {
|
|
88
|
+
const ottoAudioCategory = await ensureOttoAudioCategory(guild, botName);
|
|
89
|
+
const voiceChannel = await guild.channels.create({
|
|
90
|
+
name: channelName,
|
|
91
|
+
type: ChannelType.GuildVoice,
|
|
92
|
+
parent: ottoAudioCategory,
|
|
93
|
+
});
|
|
94
|
+
await setChannelDirectory({
|
|
95
|
+
channelId: voiceChannel.id,
|
|
96
|
+
directory: projectDirectory,
|
|
97
|
+
channelType: 'voice',
|
|
98
|
+
});
|
|
99
|
+
voiceChannelId = voiceChannel.id;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
textChannelId: textChannel.id,
|
|
103
|
+
voiceChannelId,
|
|
104
|
+
channelName,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export async function getChannelsWithDescriptions(guild) {
|
|
108
|
+
const channels = [];
|
|
109
|
+
const textChannels = guild.channels.cache.filter((channel) => channel.isTextBased());
|
|
110
|
+
for (const channel of textChannels.values()) {
|
|
111
|
+
const textChannel = channel;
|
|
112
|
+
const description = textChannel.topic || null;
|
|
113
|
+
// Get channel config from database instead of parsing XML from topic
|
|
114
|
+
const channelConfig = await getChannelDirectory(textChannel.id);
|
|
115
|
+
channels.push({
|
|
116
|
+
id: textChannel.id,
|
|
117
|
+
name: textChannel.name,
|
|
118
|
+
description,
|
|
119
|
+
ottoDirectory: channelConfig?.directory,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return channels;
|
|
123
|
+
}
|
|
124
|
+
const DEFAULT_GITIGNORE = `node_modules/
|
|
125
|
+
dist/
|
|
126
|
+
.env
|
|
127
|
+
.env.*
|
|
128
|
+
!.env.example
|
|
129
|
+
.DS_Store
|
|
130
|
+
tmp/
|
|
131
|
+
*.log
|
|
132
|
+
__pycache__/
|
|
133
|
+
*.pyc
|
|
134
|
+
.venv/
|
|
135
|
+
*.egg-info/
|
|
136
|
+
`;
|
|
137
|
+
const DEFAULT_CHANNEL_TOPIC = 'General channel for misc tasks with Otto. Not connected to a specific OpenCode project or repository.';
|
|
138
|
+
/**
|
|
139
|
+
* Create (or find) the default "otto" channel for general-purpose tasks.
|
|
140
|
+
* Channel name is "otto-{botName}" for self-hosted bots, "otto" for gateway.
|
|
141
|
+
* Directory is <dataDir>/projects/otto (or the legacy <dataDir>/projects/otto
|
|
142
|
+
* if that already exists on disk), git-initialized with a .gitignore.
|
|
143
|
+
*
|
|
144
|
+
* Idempotency: checks the database for an existing channel mapped to the
|
|
145
|
+
* otto (or legacy otto) projects directory. Also scans guild channels by
|
|
146
|
+
* name+category as a fallback for channels created before DB mapping existed.
|
|
147
|
+
*/
|
|
148
|
+
export async function createDefaultOttoChannel({ guild, botName, appId, isGatewayMode, }) {
|
|
149
|
+
// Use the legacy "otto" sub-directory if it already exists on disk so
|
|
150
|
+
// existing users keep their project history. New installs use "otto".
|
|
151
|
+
const legacyProjectDirectory = path.join(getProjectsDir(), 'otto');
|
|
152
|
+
const projectDirectory = fs.existsSync(legacyProjectDirectory)
|
|
153
|
+
? legacyProjectDirectory
|
|
154
|
+
: path.join(getProjectsDir(), 'otto');
|
|
155
|
+
// Ensure the project directory exists before any DB mapping restoration
|
|
156
|
+
// or git setup. Custom data dirs may not have <dataDir>/projects created
|
|
157
|
+
// yet, and later writes assume the full path is present.
|
|
158
|
+
if (!fs.existsSync(projectDirectory)) {
|
|
159
|
+
fs.mkdirSync(projectDirectory, { recursive: true });
|
|
160
|
+
logger.log(`Created default otto directory: ${projectDirectory}`);
|
|
161
|
+
}
|
|
162
|
+
// Hydrate guild channels from API so the cache scan is complete
|
|
163
|
+
try {
|
|
164
|
+
await guild.channels.fetch();
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
logger.warn(`Could not fetch guild channels for ${guild.name}: ${error instanceof Error ? error.stack : String(error)}`);
|
|
168
|
+
}
|
|
169
|
+
// 1. Check database for existing channel mapped to this directory.
|
|
170
|
+
// Check ALL mappings (not just the first) since the same directory could
|
|
171
|
+
// have stale rows from deleted channels or other guilds.
|
|
172
|
+
const existingMappings = await findChannelsByDirectory({
|
|
173
|
+
directory: projectDirectory,
|
|
174
|
+
channelType: 'text',
|
|
175
|
+
});
|
|
176
|
+
const mappedChannelInGuild = existingMappings
|
|
177
|
+
.map((row) => guild.channels.cache.get(row.channel_id))
|
|
178
|
+
.find((ch) => ch?.type === ChannelType.GuildText);
|
|
179
|
+
if (mappedChannelInGuild) {
|
|
180
|
+
logger.log(`Default otto channel already exists: ${mappedChannelInGuild.id}`);
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
// 2. Fallback: detect existing channel by name+category (handles both the
|
|
184
|
+
// current "otto"/"otto-*" names and legacy "otto"/"otto-*" names).
|
|
185
|
+
const ottoCategory = await ensureOttoCategory(guild, botName);
|
|
186
|
+
const existingByName = guild.channels.cache.find((ch) => {
|
|
187
|
+
if (ch.type !== ChannelType.GuildText) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (ch.parentId !== ottoCategory.id) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
return (ch.name === 'otto' ||
|
|
194
|
+
ch.name.startsWith('otto-') ||
|
|
195
|
+
ch.name === 'otto' ||
|
|
196
|
+
ch.name.startsWith('otto-'));
|
|
197
|
+
});
|
|
198
|
+
if (existingByName) {
|
|
199
|
+
logger.log(`Found existing default channel by name: ${existingByName.id}, restoring DB mapping`);
|
|
200
|
+
await setChannelDirectory({
|
|
201
|
+
channelId: existingByName.id,
|
|
202
|
+
directory: projectDirectory,
|
|
203
|
+
channelType: 'text',
|
|
204
|
+
skipIfExists: true,
|
|
205
|
+
});
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
// Git init — gracefully skip if git is not installed
|
|
209
|
+
const gitDir = path.join(projectDirectory, '.git');
|
|
210
|
+
if (!fs.existsSync(gitDir)) {
|
|
211
|
+
try {
|
|
212
|
+
await execAsync('git init', { cwd: projectDirectory, timeout: 10_000 });
|
|
213
|
+
logger.log(`Initialized git in: ${projectDirectory}`);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
logger.warn(`Could not initialize git in ${projectDirectory}: ${error instanceof Error ? error.stack : String(error)}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Write .gitignore if it doesn't exist
|
|
220
|
+
const gitignorePath = path.join(projectDirectory, '.gitignore');
|
|
221
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
222
|
+
fs.writeFileSync(gitignorePath, DEFAULT_GITIGNORE);
|
|
223
|
+
}
|
|
224
|
+
// Channel name: "otto-{botName}" for self-hosted, "otto" for gateway
|
|
225
|
+
const channelName = (() => {
|
|
226
|
+
if (isGatewayMode || !botName) {
|
|
227
|
+
return 'otto';
|
|
228
|
+
}
|
|
229
|
+
const sanitized = botName
|
|
230
|
+
.toLowerCase()
|
|
231
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
232
|
+
.replace(/-+/g, '-')
|
|
233
|
+
.replace(/^-|-$/g, '');
|
|
234
|
+
if (!sanitized || sanitized === 'otto') {
|
|
235
|
+
return 'otto';
|
|
236
|
+
}
|
|
237
|
+
return `otto-${sanitized}`.slice(0, 100);
|
|
238
|
+
})();
|
|
239
|
+
const textChannel = await guild.channels.create({
|
|
240
|
+
name: channelName,
|
|
241
|
+
type: ChannelType.GuildText,
|
|
242
|
+
parent: ottoCategory,
|
|
243
|
+
topic: DEFAULT_CHANNEL_TOPIC,
|
|
244
|
+
});
|
|
245
|
+
await setChannelDirectory({
|
|
246
|
+
channelId: textChannel.id,
|
|
247
|
+
directory: projectDirectory,
|
|
248
|
+
channelType: 'text',
|
|
249
|
+
});
|
|
250
|
+
logger.log(`Created default otto channel: #${channelName} (${textChannel.id})`);
|
|
251
|
+
return {
|
|
252
|
+
textChannel,
|
|
253
|
+
textChannelId: textChannel.id,
|
|
254
|
+
channelName,
|
|
255
|
+
projectDirectory,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
// Keep legacy export name so any unupdated callers still compile
|
|
259
|
+
export const createDefaultKimakiChannel = createDefaultOttoChannel;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Regression tests for CLI argument parsing around Discord ID string preservation.
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import { execAsync } from './exec-async.js';
|
|
4
|
+
async function parseWithGoke(argv) {
|
|
5
|
+
const script = [
|
|
6
|
+
"import { goke } from 'goke'",
|
|
7
|
+
'const cli = goke(\'otto\')',
|
|
8
|
+
"cli.command('send', 'Send a message').option('-c, --channel <channelId>', 'Discord channel ID').option('--thread <threadId>', 'Thread ID').option('--session <sessionId>', 'Session ID').option('--send-at <schedule>', 'Schedule').option('--gateway-platform <platform>', 'Gateway platform').option('--telegram-bot-token <token>', 'Telegram bot token').option('--telegram-chat-id <id>', 'Telegram chat ID')",
|
|
9
|
+
"cli.command('session archive <threadId>', 'Archive a thread')",
|
|
10
|
+
"cli.command('session search <query>', 'Search sessions').option('--channel <channelId>', 'Discord channel ID').option('--project <path>', 'Project path')",
|
|
11
|
+
"cli.command('session export-events-jsonl', 'Export in-memory events to JSONL').option('--session <sessionId>', 'Session ID').option('--out <file>', 'Output path')",
|
|
12
|
+
"cli.command('add-project', 'Add a project').option('-g, --guild <guildId>', 'Discord guild/server ID')",
|
|
13
|
+
"cli.command('task delete <id>', 'Delete task')",
|
|
14
|
+
"cli.command('anthropic-accounts list', 'List stored Anthropic accounts')",
|
|
15
|
+
"cli.command('anthropic-accounts remove <indexOrEmail>', 'Remove stored Anthropic account')",
|
|
16
|
+
`const result = cli.parse(${JSON.stringify(argv)}, { run: false })`,
|
|
17
|
+
'process.stdout.write(JSON.stringify({ args: result.args, options: result.options }))',
|
|
18
|
+
].join(';');
|
|
19
|
+
const { stdout } = await execAsync(`node --input-type=module -e ${JSON.stringify(script)}`, {
|
|
20
|
+
cwd: import.meta.dirname,
|
|
21
|
+
timeout: 10_000,
|
|
22
|
+
});
|
|
23
|
+
return JSON.parse(stdout);
|
|
24
|
+
}
|
|
25
|
+
async function getHelpOutput() {
|
|
26
|
+
const script = [
|
|
27
|
+
"import { goke } from 'goke'",
|
|
28
|
+
'const stdout = { text: \'\', write(data) { this.text += String(data) } }',
|
|
29
|
+
"const cli = goke('otto', { stdout })",
|
|
30
|
+
"cli.command('send', 'Send a message')",
|
|
31
|
+
"cli.command('anthropic-accounts list', 'List stored Anthropic accounts')",
|
|
32
|
+
'cli.help()',
|
|
33
|
+
"cli.parse(['node', 'otto', '--help'], { run: false })",
|
|
34
|
+
'process.stdout.write(stdout.text)',
|
|
35
|
+
].join(';');
|
|
36
|
+
const { stdout } = await execAsync(`node --input-type=module -e ${JSON.stringify(script)}`, {
|
|
37
|
+
cwd: import.meta.dirname,
|
|
38
|
+
timeout: 10_000,
|
|
39
|
+
});
|
|
40
|
+
return stdout;
|
|
41
|
+
}
|
|
42
|
+
describe('goke CLI ID parsing', () => {
|
|
43
|
+
test('keeps large Discord IDs as strings', async () => {
|
|
44
|
+
const channelId = '1234567890123456789';
|
|
45
|
+
const threadId = '9876543210987654321';
|
|
46
|
+
const sessionId = '1111222233334444555';
|
|
47
|
+
const channelResult = await parseWithGoke(['node', 'otto', 'send', '--channel', channelId]);
|
|
48
|
+
expect(channelResult.options.channel).toBe(channelId);
|
|
49
|
+
expect(typeof channelResult.options.channel).toBe('string');
|
|
50
|
+
const threadResult = await parseWithGoke(['node', 'otto', 'send', '--thread', threadId]);
|
|
51
|
+
expect(threadResult.options.thread).toBe(threadId);
|
|
52
|
+
expect(typeof threadResult.options.thread).toBe('string');
|
|
53
|
+
const sessionResult = await parseWithGoke(['node', 'otto', 'send', '--session', sessionId]);
|
|
54
|
+
expect(sessionResult.options.session).toBe(sessionId);
|
|
55
|
+
expect(typeof sessionResult.options.session).toBe('string');
|
|
56
|
+
});
|
|
57
|
+
test('preserves leading zeros in Discord IDs', async () => {
|
|
58
|
+
const guildId = '001230045600789';
|
|
59
|
+
const result = await parseWithGoke(['node', 'otto', 'add-project', '--guild', guildId]);
|
|
60
|
+
expect(result.options.guild).toBe(guildId);
|
|
61
|
+
expect(typeof result.options.guild).toBe('string');
|
|
62
|
+
});
|
|
63
|
+
test('keeps session archive thread ID as string', async () => {
|
|
64
|
+
const threadId = '0098765432109876543';
|
|
65
|
+
const result = await parseWithGoke(['node', 'otto', 'session', 'archive', threadId]);
|
|
66
|
+
expect(result.args[0]).toBe(threadId);
|
|
67
|
+
expect(typeof result.args[0]).toBe('string');
|
|
68
|
+
});
|
|
69
|
+
test('keeps session search regex and channel ID as strings', async () => {
|
|
70
|
+
const channelId = '0012345678901234567';
|
|
71
|
+
const query = '/error\\s+42/i';
|
|
72
|
+
const result = await parseWithGoke(['node', 'otto', 'session', 'search', query, '--channel', channelId]);
|
|
73
|
+
expect(result.args[0]).toBe(query);
|
|
74
|
+
expect(typeof result.args[0]).toBe('string');
|
|
75
|
+
expect(result.options.channel).toBe(channelId);
|
|
76
|
+
expect(typeof result.options.channel).toBe('string');
|
|
77
|
+
});
|
|
78
|
+
test('keeps session export options as strings', async () => {
|
|
79
|
+
const sessionId = '001111222233334444';
|
|
80
|
+
const outPath = './tmp/session-events.jsonl';
|
|
81
|
+
const result = await parseWithGoke([
|
|
82
|
+
'node',
|
|
83
|
+
'otto',
|
|
84
|
+
'session',
|
|
85
|
+
'export-events-jsonl',
|
|
86
|
+
'--session',
|
|
87
|
+
sessionId,
|
|
88
|
+
'--out',
|
|
89
|
+
outPath,
|
|
90
|
+
]);
|
|
91
|
+
expect(result.options.session).toBe(sessionId);
|
|
92
|
+
expect(typeof result.options.session).toBe('string');
|
|
93
|
+
expect(result.options.out).toBe(outPath);
|
|
94
|
+
expect(typeof result.options.out).toBe('string');
|
|
95
|
+
});
|
|
96
|
+
test('keeps --send-at cron string intact', async () => {
|
|
97
|
+
const cron = '0 9 * * 1';
|
|
98
|
+
const result = await parseWithGoke(['node', 'otto', 'send', '--send-at', cron]);
|
|
99
|
+
expect(result.options.sendAt).toBe(cron);
|
|
100
|
+
expect(typeof result.options.sendAt).toBe('string');
|
|
101
|
+
});
|
|
102
|
+
test('keeps gateway onboarding options as strings', async () => {
|
|
103
|
+
const platform = 'telegram';
|
|
104
|
+
const botToken = '123456:ABCDEF';
|
|
105
|
+
const chatId = '-1001234567890';
|
|
106
|
+
const result = await parseWithGoke([
|
|
107
|
+
'node',
|
|
108
|
+
'otto',
|
|
109
|
+
'send',
|
|
110
|
+
'--gateway-platform',
|
|
111
|
+
platform,
|
|
112
|
+
'--telegram-bot-token',
|
|
113
|
+
botToken,
|
|
114
|
+
`--telegram-chat-id=${chatId}`,
|
|
115
|
+
]);
|
|
116
|
+
expect(result.options.gatewayPlatform).toBe(platform);
|
|
117
|
+
expect(typeof result.options.gatewayPlatform).toBe('string');
|
|
118
|
+
expect(result.options.telegramBotToken).toBe(botToken);
|
|
119
|
+
expect(typeof result.options.telegramBotToken).toBe('string');
|
|
120
|
+
expect(result.options.telegramChatId).toBe(chatId);
|
|
121
|
+
expect(typeof result.options.telegramChatId).toBe('string');
|
|
122
|
+
});
|
|
123
|
+
test('keeps task delete ID as string before validation', async () => {
|
|
124
|
+
const taskId = '0012345';
|
|
125
|
+
const result = await parseWithGoke(['node', 'otto', 'task', 'delete', taskId]);
|
|
126
|
+
expect(result.args[0]).toBe(taskId);
|
|
127
|
+
expect(typeof result.args[0]).toBe('string');
|
|
128
|
+
});
|
|
129
|
+
test('anthropic account remove parses index and email as strings', async () => {
|
|
130
|
+
const indexResult = await parseWithGoke(['node', 'otto', 'anthropic-accounts', 'remove', '2']);
|
|
131
|
+
const emailResult = await parseWithGoke(['node', 'otto', 'anthropic-accounts', 'remove', 'user@example.com']);
|
|
132
|
+
expect(indexResult.args[0]).toBe('2');
|
|
133
|
+
expect(typeof indexResult.args[0]).toBe('string');
|
|
134
|
+
expect(emailResult.args[0]).toBe('user@example.com');
|
|
135
|
+
expect(typeof emailResult.args[0]).toBe('string');
|
|
136
|
+
});
|
|
137
|
+
test('anthropic account commands are included in help output', async () => {
|
|
138
|
+
const stdout = await getHelpOutput();
|
|
139
|
+
expect(stdout).toContain('send');
|
|
140
|
+
expect(stdout).toContain('anthropic-accounts');
|
|
141
|
+
});
|
|
142
|
+
});
|