@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,185 @@
|
|
|
1
|
+
// Unnest code blocks from list items for Discord.
|
|
2
|
+
// Discord doesn't render code blocks inside lists, so this hoists them
|
|
3
|
+
// to root level while preserving list structure.
|
|
4
|
+
|
|
5
|
+
import { Lexer, type Token, type Tokens } from 'marked'
|
|
6
|
+
|
|
7
|
+
type Segment =
|
|
8
|
+
| { type: 'list-item'; prefix: string; content: string }
|
|
9
|
+
| { type: 'code'; content: string }
|
|
10
|
+
|
|
11
|
+
export function unnestCodeBlocksFromLists(markdown: string): string {
|
|
12
|
+
const lexer = new Lexer()
|
|
13
|
+
const tokens = lexer.lex(markdown)
|
|
14
|
+
|
|
15
|
+
const result: string[] = []
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
18
|
+
const token = tokens[i]!
|
|
19
|
+
const next = tokens[i + 1]
|
|
20
|
+
|
|
21
|
+
const chunk = (() => {
|
|
22
|
+
if (token.type === 'list') {
|
|
23
|
+
const segments = processListToken(token as Tokens.List)
|
|
24
|
+
return renderSegments(segments)
|
|
25
|
+
}
|
|
26
|
+
return token.raw
|
|
27
|
+
})()
|
|
28
|
+
|
|
29
|
+
if (!chunk) {
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const nextRaw = next?.raw ?? ''
|
|
34
|
+
const needsNewline =
|
|
35
|
+
nextRaw &&
|
|
36
|
+
!chunk.endsWith('\n') &&
|
|
37
|
+
typeof nextRaw === 'string' &&
|
|
38
|
+
!nextRaw.startsWith('\n')
|
|
39
|
+
|
|
40
|
+
result.push(needsNewline ? chunk + '\n' : chunk)
|
|
41
|
+
}
|
|
42
|
+
return result.join('')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function processListToken(list: Tokens.List): Segment[] {
|
|
46
|
+
const segments: Segment[] = []
|
|
47
|
+
const start =
|
|
48
|
+
typeof list.start === 'number' ? list.start : parseInt(list.start, 10) || 1
|
|
49
|
+
const prefix = list.ordered ? (i: number) => `${start + i}. ` : () => '- '
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < list.items.length; i++) {
|
|
52
|
+
const item = list.items[i]!
|
|
53
|
+
const itemSegments = processListItem(item, prefix(i))
|
|
54
|
+
segments.push(...itemSegments)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return segments
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function processListItem(item: Tokens.ListItem, prefix: string): Segment[] {
|
|
61
|
+
const segments: Segment[] = []
|
|
62
|
+
let currentText: string[] = []
|
|
63
|
+
// Track if we've seen a code block - text after code uses continuation prefix
|
|
64
|
+
let seenCodeBlock = false
|
|
65
|
+
|
|
66
|
+
const taskMarker = item.task ? (item.checked ? '[x] ' : '[ ] ') : ''
|
|
67
|
+
let wroteFirstListItem = false
|
|
68
|
+
|
|
69
|
+
const flushText = (): void => {
|
|
70
|
+
const rawText = currentText.join('')
|
|
71
|
+
const text = rawText.trimEnd()
|
|
72
|
+
if (text.trim()) {
|
|
73
|
+
// After a code block, use '-' as continuation prefix to avoid repeating numbers
|
|
74
|
+
const effectivePrefix = seenCodeBlock ? '- ' : prefix
|
|
75
|
+
const marker = !wroteFirstListItem ? taskMarker : ''
|
|
76
|
+
const normalizedText = normalizeListItemText({
|
|
77
|
+
text,
|
|
78
|
+
isTaskItem: item.task,
|
|
79
|
+
})
|
|
80
|
+
segments.push({
|
|
81
|
+
type: 'list-item',
|
|
82
|
+
prefix: effectivePrefix,
|
|
83
|
+
content: marker + normalizedText,
|
|
84
|
+
})
|
|
85
|
+
wroteFirstListItem = true
|
|
86
|
+
}
|
|
87
|
+
currentText = []
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const token of item.tokens) {
|
|
91
|
+
if (token.type === 'code') {
|
|
92
|
+
flushText()
|
|
93
|
+
const codeToken = token as Tokens.Code
|
|
94
|
+
const lang = codeToken.lang || ''
|
|
95
|
+
segments.push({
|
|
96
|
+
type: 'code',
|
|
97
|
+
content: '```' + lang + '\n' + codeToken.text + '\n```\n',
|
|
98
|
+
})
|
|
99
|
+
seenCodeBlock = true
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (token.type === 'list') {
|
|
104
|
+
flushText()
|
|
105
|
+
// Recursively process nested list - segments bubble up
|
|
106
|
+
const nestedSegments = processListToken(token as Tokens.List)
|
|
107
|
+
segments.push(...nestedSegments)
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
currentText.push(extractText(token))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
flushText()
|
|
115
|
+
|
|
116
|
+
// If no segments were created (empty item), return empty
|
|
117
|
+
if (segments.length === 0) {
|
|
118
|
+
return []
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// If item had no code blocks (all segments are list-items from this level),
|
|
122
|
+
// return original raw to preserve formatting
|
|
123
|
+
const hasCode = segments.some((s) => s.type === 'code')
|
|
124
|
+
if (!hasCode) {
|
|
125
|
+
return [{ type: 'list-item', prefix: '', content: item.raw }]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return segments
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function extractText(token: Token): string {
|
|
132
|
+
// Prefer raw to preserve newlines and markdown markers.
|
|
133
|
+
if ('raw' in token && typeof token.raw === 'string') {
|
|
134
|
+
return token.raw
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (token.type === 'text') {
|
|
138
|
+
return (token as Tokens.Text).text
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return ''
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function normalizeListItemText({
|
|
145
|
+
text,
|
|
146
|
+
isTaskItem,
|
|
147
|
+
}: {
|
|
148
|
+
text: string
|
|
149
|
+
isTaskItem: boolean
|
|
150
|
+
}): string {
|
|
151
|
+
const withoutIndent = text.replace(/^\s+/, '')
|
|
152
|
+
if (!isTaskItem) {
|
|
153
|
+
return withoutIndent
|
|
154
|
+
}
|
|
155
|
+
return withoutIndent.replace(/^\[(?: |x|X)\]\s+/, '')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function renderSegments(segments: Segment[]): string {
|
|
159
|
+
const result: string[] = []
|
|
160
|
+
|
|
161
|
+
for (let i = 0; i < segments.length; i++) {
|
|
162
|
+
const segment = segments[i]!
|
|
163
|
+
const prev = segments[i - 1]
|
|
164
|
+
|
|
165
|
+
if (segment.type === 'code') {
|
|
166
|
+
// Add newline before code if previous was a list item
|
|
167
|
+
if (prev && prev.type === 'list-item') {
|
|
168
|
+
result.push('\n')
|
|
169
|
+
}
|
|
170
|
+
result.push(segment.content)
|
|
171
|
+
} else {
|
|
172
|
+
// list-item
|
|
173
|
+
if (segment.prefix) {
|
|
174
|
+
result.push(segment.prefix + segment.content + '\n')
|
|
175
|
+
} else {
|
|
176
|
+
// Raw content (no prefix means it's original raw)
|
|
177
|
+
// Ensure raw ends with newline for proper separation from next segment
|
|
178
|
+
const raw = segment.content.trimEnd()
|
|
179
|
+
result.push(raw + '\n')
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return result.join('').trimEnd()
|
|
185
|
+
}
|
package/src/upgrade.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Otto self-upgrade utilities.
|
|
2
|
+
// Detects the package manager used to install otto, checks npm for newer versions,
|
|
3
|
+
// and runs the global upgrade command. Used by both CLI `otto upgrade` and
|
|
4
|
+
// the Discord `/upgrade-and-restart` command, plus background auto-upgrade on startup.
|
|
5
|
+
|
|
6
|
+
import fs from 'node:fs'
|
|
7
|
+
import { createRequire } from 'node:module'
|
|
8
|
+
import { createLogger, LogPrefix } from './logger.js'
|
|
9
|
+
import { execAsync } from './worktrees.js'
|
|
10
|
+
|
|
11
|
+
const logger = createLogger(LogPrefix.CLI)
|
|
12
|
+
|
|
13
|
+
type Pm = 'bun' | 'pnpm' | 'npm'
|
|
14
|
+
type CliPackageJson = { version: string; name: string }
|
|
15
|
+
|
|
16
|
+
// Detects which package manager globally installed otto, used to run the
|
|
17
|
+
// correct `<pm> i -g @otto-assistant/otto@latest` upgrade command.
|
|
18
|
+
//
|
|
19
|
+
// Detection order:
|
|
20
|
+
// 1. npm_config_user_agent — set by npx/bunx/pnpm dlx, reliable for those cases
|
|
21
|
+
// 2. Realpath of the running script — resolve symlinks and check if the path
|
|
22
|
+
// lives under a known PM global directory (e.g. ~/.bun, ~/Library/pnpm,
|
|
23
|
+
// /usr/local/lib/node_modules). Inspired by sindresorhus/global-directory.
|
|
24
|
+
// 3. process.versions.bun — if the runtime itself is Bun, likely bun ecosystem
|
|
25
|
+
// 4. Default to npm — safest fallback since npm is the most common global installer
|
|
26
|
+
export function detectPm(): Pm {
|
|
27
|
+
const ua = process.env.npm_config_user_agent
|
|
28
|
+
if (ua?.startsWith('bun/')) {
|
|
29
|
+
return 'bun'
|
|
30
|
+
}
|
|
31
|
+
if (ua?.startsWith('pnpm/')) {
|
|
32
|
+
return 'pnpm'
|
|
33
|
+
}
|
|
34
|
+
if (ua?.startsWith('npm/')) {
|
|
35
|
+
return 'npm'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const scriptPath = resolveScriptRealpath()
|
|
39
|
+
if (scriptPath) {
|
|
40
|
+
const p = scriptPath.toLowerCase()
|
|
41
|
+
// bun global installs live under ~/.bun or $BUN_INSTALL
|
|
42
|
+
if (p.includes('.bun/') || p.includes('/bun/install/')) {
|
|
43
|
+
return 'bun'
|
|
44
|
+
}
|
|
45
|
+
// pnpm global installs live under ~/Library/pnpm, ~/.local/share/pnpm, or $PNPM_HOME
|
|
46
|
+
if (p.includes('/pnpm/')) {
|
|
47
|
+
return 'pnpm'
|
|
48
|
+
}
|
|
49
|
+
// npm global installs typically live under lib/node_modules/@otto-assistant without
|
|
50
|
+
// any pnpm or bun path segments, so if we reach here it's likely npm
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (process.versions.bun) {
|
|
54
|
+
return 'bun'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return 'npm'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveScriptRealpath(): string | null {
|
|
61
|
+
try {
|
|
62
|
+
const script = process.argv[1]
|
|
63
|
+
if (!script) {
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
return fs.realpathSync(script)
|
|
67
|
+
} catch {
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getCliPackageJson(): CliPackageJson {
|
|
73
|
+
const require = createRequire(import.meta.url)
|
|
74
|
+
return require('../package.json') as CliPackageJson
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getCurrentVersion(): string {
|
|
78
|
+
return getCliPackageJson().version
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getPackageName(): string {
|
|
82
|
+
return getCliPackageJson().name
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function getLatestNpmVersion(): Promise<string | null> {
|
|
86
|
+
try {
|
|
87
|
+
const packageName = encodeURIComponent(getPackageName())
|
|
88
|
+
const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
89
|
+
signal: AbortSignal.timeout(15_000),
|
|
90
|
+
})
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
const data = (await res.json()) as { version: string }
|
|
95
|
+
return data.version
|
|
96
|
+
} catch {
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const LEGACY_PACKAGE_NAME = '@otto-assistant/bridge'
|
|
102
|
+
const CURRENT_PACKAGE_NAME = '@otto-assistant/otto'
|
|
103
|
+
|
|
104
|
+
// Returns true if running under the old @otto-assistant/bridge package name.
|
|
105
|
+
// When true, `upgrade()` will migrate to @otto-assistant/otto automatically.
|
|
106
|
+
export function isLegacyPackageName(): boolean {
|
|
107
|
+
return getPackageName() === LEGACY_PACKAGE_NAME
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Returns the new version string if upgraded (or migrated), null if already up to date.
|
|
111
|
+
// If running as the legacy @otto-assistant/bridge package, migrates to
|
|
112
|
+
// @otto-assistant/otto by installing the new package name globally.
|
|
113
|
+
export async function upgrade(): Promise<string | null> {
|
|
114
|
+
const current = getCurrentVersion()
|
|
115
|
+
|
|
116
|
+
// Migrate legacy package name: install @otto-assistant/otto and remove @otto-assistant/bridge
|
|
117
|
+
if (isLegacyPackageName()) {
|
|
118
|
+
const latest = await fetch(
|
|
119
|
+
`https://registry.npmjs.org/${encodeURIComponent(CURRENT_PACKAGE_NAME)}/latest`,
|
|
120
|
+
{ signal: AbortSignal.timeout(15_000) },
|
|
121
|
+
)
|
|
122
|
+
.then((r) => (r.ok ? (r.json() as Promise<{ version: string }>) : null))
|
|
123
|
+
.then((d) => d?.version ?? null)
|
|
124
|
+
.catch(() => null)
|
|
125
|
+
|
|
126
|
+
const pm = detectPm()
|
|
127
|
+
logger.log(
|
|
128
|
+
`Migrating from ${LEGACY_PACKAGE_NAME} to ${CURRENT_PACKAGE_NAME}${latest ? ` v${latest}` : ''} using ${pm}...`,
|
|
129
|
+
)
|
|
130
|
+
await execAsync(`${pm} i -g ${CURRENT_PACKAGE_NAME}@latest`, {
|
|
131
|
+
timeout: 120_000,
|
|
132
|
+
})
|
|
133
|
+
// Best-effort removal of the old package — non-fatal if it fails
|
|
134
|
+
await execAsync(`${pm} remove -g ${LEGACY_PACKAGE_NAME}`, {
|
|
135
|
+
timeout: 60_000,
|
|
136
|
+
}).catch(() => {})
|
|
137
|
+
return latest ?? current
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const latest = await getLatestNpmVersion()
|
|
141
|
+
if (!latest) {
|
|
142
|
+
throw new Error('Failed to check latest version from npm')
|
|
143
|
+
}
|
|
144
|
+
if (current === latest) {
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const pm = detectPm()
|
|
149
|
+
const packageName = getPackageName()
|
|
150
|
+
logger.log(`Upgrading ${packageName} from v${current} to v${latest} using ${pm}...`)
|
|
151
|
+
await execAsync(`${pm} i -g ${packageName}@latest`, { timeout: 120_000 })
|
|
152
|
+
|
|
153
|
+
return latest
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Fire-and-forget background upgrade check on bot startup.
|
|
157
|
+
// Only upgrades if a newer version is available. Errors are silently ignored.
|
|
158
|
+
export async function backgroundUpgradeOtto(): Promise<void> {
|
|
159
|
+
try {
|
|
160
|
+
// Silently migrate legacy package name in the background
|
|
161
|
+
if (isLegacyPackageName()) {
|
|
162
|
+
const pm = detectPm()
|
|
163
|
+
logger.debug(`Background migration from ${LEGACY_PACKAGE_NAME} to ${CURRENT_PACKAGE_NAME}`)
|
|
164
|
+
await execAsync(`${pm} i -g ${CURRENT_PACKAGE_NAME}@latest`, { timeout: 120_000 })
|
|
165
|
+
await execAsync(`${pm} remove -g ${LEGACY_PACKAGE_NAME}`, { timeout: 60_000 }).catch(() => {})
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const current = getCurrentVersion()
|
|
170
|
+
const latest = await getLatestNpmVersion()
|
|
171
|
+
if (!latest || current === latest) {
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const pm = detectPm()
|
|
176
|
+
const packageName = getPackageName()
|
|
177
|
+
logger.debug(
|
|
178
|
+
`Background ${packageName} upgrade started: v${current} -> v${latest}`,
|
|
179
|
+
)
|
|
180
|
+
await execAsync(`${pm} i -g ${packageName}@latest`, { timeout: 120_000 })
|
|
181
|
+
logger.debug(`Background ${packageName} upgrade completed: v${latest}`)
|
|
182
|
+
} catch {
|
|
183
|
+
// silently ignored, non-critical
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
buildOpenUrlCommand,
|
|
4
|
+
generateDiscordInstallUrlForBot,
|
|
5
|
+
getGatewayInstallTargetName,
|
|
6
|
+
getGatewayPlatformDisplayName,
|
|
7
|
+
shouldRegisterSlashCommands,
|
|
8
|
+
} from './utils.js'
|
|
9
|
+
|
|
10
|
+
describe('generateDiscordInstallUrlForBot', () => {
|
|
11
|
+
test('uses telegram install path in gateway mode', () => {
|
|
12
|
+
const result = generateDiscordInstallUrlForBot({
|
|
13
|
+
appId: 'app-id',
|
|
14
|
+
mode: 'gateway',
|
|
15
|
+
clientId: 'client-id',
|
|
16
|
+
clientSecret: 'client-secret',
|
|
17
|
+
gatewayPlatform: 'telegram',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
expect(result).toBeTypeOf('string')
|
|
21
|
+
if (typeof result !== 'string') {
|
|
22
|
+
throw new Error('Expected install URL string')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const url = new URL(result)
|
|
26
|
+
expect(url.pathname).toBe('/telegram-install')
|
|
27
|
+
expect(url.searchParams.get('clientId')).toBe('client-id')
|
|
28
|
+
expect(url.searchParams.get('clientSecret')).toBe('client-secret')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('uses discord install path by default in gateway mode', () => {
|
|
32
|
+
const result = generateDiscordInstallUrlForBot({
|
|
33
|
+
appId: 'app-id',
|
|
34
|
+
mode: 'gateway',
|
|
35
|
+
clientId: 'client-id',
|
|
36
|
+
clientSecret: 'client-secret',
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
expect(result).toBeTypeOf('string')
|
|
40
|
+
if (typeof result !== 'string') {
|
|
41
|
+
throw new Error('Expected install URL string')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const url = new URL(result)
|
|
45
|
+
expect(url.pathname).toBe('/discord-install')
|
|
46
|
+
expect(url.searchParams.get('clientId')).toBe('client-id')
|
|
47
|
+
expect(url.searchParams.get('clientSecret')).toBe('client-secret')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('includes callback and reachable URL query params when provided', () => {
|
|
51
|
+
const callbackUrl = 'https://example.com/callback?foo=bar'
|
|
52
|
+
const reachableUrl = 'https://example-ngrok.test'
|
|
53
|
+
const result = generateDiscordInstallUrlForBot({
|
|
54
|
+
appId: 'app-id',
|
|
55
|
+
mode: 'gateway',
|
|
56
|
+
clientId: 'client-id',
|
|
57
|
+
clientSecret: 'client-secret',
|
|
58
|
+
gatewayPlatform: 'telegram',
|
|
59
|
+
gatewayCallbackUrl: callbackUrl,
|
|
60
|
+
reachableUrl,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expect(result).toBeTypeOf('string')
|
|
64
|
+
if (typeof result !== 'string') {
|
|
65
|
+
throw new Error('Expected install URL string')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const url = new URL(result)
|
|
69
|
+
expect(url.searchParams.get('ottoCallbackUrl')).toBe(callbackUrl)
|
|
70
|
+
expect(url.searchParams.get('reachableUrl')).toBe(reachableUrl)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('returns error when gateway credentials are missing', () => {
|
|
74
|
+
const result = generateDiscordInstallUrlForBot({
|
|
75
|
+
appId: 'app-id',
|
|
76
|
+
mode: 'gateway',
|
|
77
|
+
clientId: null,
|
|
78
|
+
clientSecret: null,
|
|
79
|
+
gatewayPlatform: 'telegram',
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
expect(result).toBeInstanceOf(Error)
|
|
83
|
+
if (!(result instanceof Error)) {
|
|
84
|
+
throw new Error('Expected Error result')
|
|
85
|
+
}
|
|
86
|
+
expect(result.message).toContain('Gateway credentials are missing')
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('gateway platform helpers', () => {
|
|
91
|
+
test('returns display names and install target names', () => {
|
|
92
|
+
expect(getGatewayPlatformDisplayName({ gatewayPlatform: 'discord' })).toBe('Discord')
|
|
93
|
+
expect(getGatewayPlatformDisplayName({ gatewayPlatform: 'telegram' })).toBe('Telegram')
|
|
94
|
+
expect(getGatewayInstallTargetName({ gatewayPlatform: 'discord' })).toBe('server')
|
|
95
|
+
expect(getGatewayInstallTargetName({ gatewayPlatform: 'telegram' })).toBe('chat')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('registers slash commands only for discord gateway', () => {
|
|
99
|
+
expect(shouldRegisterSlashCommands({ gatewayPlatform: 'discord' })).toBe(true)
|
|
100
|
+
expect(shouldRegisterSlashCommands({ gatewayPlatform: 'telegram' })).toBe(false)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
describe('buildOpenUrlCommand', () => {
|
|
105
|
+
test('uses cmd start on windows', () => {
|
|
106
|
+
const result = buildOpenUrlCommand({
|
|
107
|
+
platform: 'win32',
|
|
108
|
+
url: 'https://example.com/path?x=1',
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
expect(result).toMatchInlineSnapshot(`
|
|
112
|
+
{
|
|
113
|
+
"args": [
|
|
114
|
+
"/c",
|
|
115
|
+
"start",
|
|
116
|
+
"",
|
|
117
|
+
"https://example.com/path?x=1",
|
|
118
|
+
],
|
|
119
|
+
"command": "cmd",
|
|
120
|
+
}
|
|
121
|
+
`)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('uses open on macOS', () => {
|
|
125
|
+
const result = buildOpenUrlCommand({
|
|
126
|
+
platform: 'darwin',
|
|
127
|
+
url: 'https://example.com/path?x=1',
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
expect(result).toMatchInlineSnapshot(`
|
|
131
|
+
{
|
|
132
|
+
"args": [
|
|
133
|
+
"https://example.com/path?x=1",
|
|
134
|
+
],
|
|
135
|
+
"command": "open",
|
|
136
|
+
}
|
|
137
|
+
`)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test('uses xdg-open on linux', () => {
|
|
141
|
+
const result = buildOpenUrlCommand({
|
|
142
|
+
platform: 'linux',
|
|
143
|
+
url: 'https://example.com/path?x=1',
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
expect(result).toMatchInlineSnapshot(`
|
|
147
|
+
{
|
|
148
|
+
"args": [
|
|
149
|
+
"https://example.com/path?x=1",
|
|
150
|
+
],
|
|
151
|
+
"command": "xdg-open",
|
|
152
|
+
}
|
|
153
|
+
`)
|
|
154
|
+
})
|
|
155
|
+
})
|