@agentprojectcontext/apx 1.24.0 → 1.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +42 -12
- package/skills/apx/SKILL.md +50 -119
- package/skills/apx-agency-agents/SKILL.md +141 -0
- package/skills/apx-agent/SKILL.md +100 -0
- package/skills/apx-mcp/SKILL.md +114 -0
- package/skills/apx-mcp-builder/SKILL.md +186 -0
- package/skills/apx-project/SKILL.md +100 -0
- package/skills/apx-routine/SKILL.md +138 -0
- package/skills/apx-runtime/SKILL.md +115 -0
- package/skills/apx-sessions/SKILL.md +281 -0
- package/skills/apx-skill-builder/SKILL.md +149 -0
- package/skills/apx-task/SKILL.md +95 -0
- package/skills/apx-telegram/SKILL.md +115 -0
- package/skills/apx-voice/SKILL.md +135 -0
- package/src/core/agent/constants.js +3 -0
- package/src/core/agent/ghost-guard.js +24 -0
- package/src/core/agent/index.js +35 -0
- package/src/core/agent/model-router.js +257 -0
- package/src/core/agent/prompt-builder.js +313 -0
- package/src/core/agent/prompts/channels/api.md +7 -0
- package/src/core/agent/prompts/channels/cli.md +6 -0
- package/src/core/agent/prompts/channels/code.md +20 -0
- package/src/core/agent/prompts/channels/deck.md +6 -0
- package/src/core/agent/prompts/channels/desktop.md +24 -0
- package/src/core/agent/prompts/channels/routine.md +9 -0
- package/src/core/agent/prompts/channels/telegram.md +9 -0
- package/src/core/agent/prompts/channels/terminal.md +16 -0
- package/src/core/agent/prompts/channels/web.md +7 -0
- package/src/core/agent/prompts/channels/web_sidebar.md +7 -0
- package/src/core/agent/prompts/modes/voice.md +4 -0
- package/src/core/agent/prompts/super-agent-base.md +42 -0
- package/src/core/agent/pseudo-tools.js +40 -0
- package/src/core/agent/retry.js +85 -0
- package/src/core/agent/run-agent.js +423 -0
- package/src/core/agent/self-memory.js +155 -0
- package/src/{daemon → core/agent}/tool-call-parser.js +83 -10
- package/src/core/agent/tools-overlap.js +66 -0
- package/src/core/apc-skill-sync.js +97 -0
- package/src/core/code-sessions-store.js +150 -0
- package/src/core/config.js +473 -11
- package/src/core/desktop/autostart.js +162 -0
- package/src/core/engines/_health.js +63 -0
- package/src/{daemon → core}/engines/anthropic.js +16 -0
- package/src/core/engines/gemini.js +168 -0
- package/src/core/engines/groq.js +8 -0
- package/src/{daemon → core}/engines/index.js +3 -1
- package/src/{daemon → core}/engines/mock.js +22 -0
- package/src/{daemon → core}/engines/ollama.js +35 -0
- package/src/core/engines/openai-compatible.js +145 -0
- package/src/core/engines/openai.js +8 -0
- package/src/core/engines/openrouter.js +8 -0
- package/src/core/git-baseline.js +147 -0
- package/src/core/identity.js +21 -0
- package/src/core/logging.js +1 -1
- package/src/core/mcp/index.js +14 -0
- package/src/{daemon/mcp-runner.js → core/mcp/runner.js} +18 -6
- package/src/core/mcp/sources.js +246 -0
- package/src/core/memory/active-threads.js +124 -0
- package/src/core/memory/broker.js +144 -0
- package/src/core/memory/compactor.js +186 -0
- package/src/core/memory/embed-engines/gemini.js +62 -0
- package/src/core/memory/embed-engines/index.js +148 -0
- package/src/core/memory/embed-engines/ollama.js +55 -0
- package/src/core/memory/embed-engines/openai.js +64 -0
- package/src/core/memory/embed-engines/tf.js +20 -0
- package/src/core/memory/embeddings.js +132 -0
- package/src/core/memory/index.js +161 -0
- package/src/core/memory/indexer.js +257 -0
- package/src/core/memory/store.js +231 -0
- package/src/core/messages-store.js +143 -25
- package/src/core/parser.js +78 -16
- package/src/core/scaffold.js +175 -79
- package/src/core/tasks-store.js +264 -0
- package/src/core/telegram-identity.js +126 -0
- package/src/core/tools/index.js +6 -0
- package/src/core/voice/engines/elevenlabs.js +96 -0
- package/src/core/voice/engines/gemini.js +148 -0
- package/src/core/voice/engines/index.js +144 -0
- package/src/core/voice/engines/mock.js +59 -0
- package/src/core/voice/engines/openai.js +82 -0
- package/src/core/voice/engines/piper.js +93 -0
- package/src/core/voice/index.js +3 -0
- package/src/core/voice/tts.js +89 -0
- package/src/{daemon → host/daemon}/apc-runtime-context.js +1 -1
- package/src/host/daemon/api/admin-config.js +159 -0
- package/src/host/daemon/api/admin.js +72 -0
- package/src/host/daemon/api/agents.js +284 -0
- package/src/host/daemon/api/artifacts.js +52 -0
- package/src/host/daemon/api/code.js +351 -0
- package/src/host/daemon/api/config.js +104 -0
- package/src/host/daemon/api/connections.js +42 -0
- package/src/host/daemon/api/conversations.js +161 -0
- package/src/host/daemon/api/deck.js +511 -0
- package/src/host/daemon/api/desktop.js +71 -0
- package/src/host/daemon/api/embeddings.js +65 -0
- package/src/host/daemon/api/engines.js +80 -0
- package/src/host/daemon/api/exec.js +193 -0
- package/src/host/daemon/api/health.js +10 -0
- package/src/host/daemon/api/identity.js +36 -0
- package/src/host/daemon/api/mcps.js +205 -0
- package/src/host/daemon/api/messages.js +83 -0
- package/src/host/daemon/api/pairing.js +194 -0
- package/src/host/daemon/api/plugins.js +19 -0
- package/src/host/daemon/api/projects.js +35 -0
- package/src/host/daemon/api/routines.js +84 -0
- package/src/host/daemon/api/run.js +55 -0
- package/src/host/daemon/api/runtimes.js +219 -0
- package/src/host/daemon/api/sessions-search.js +177 -0
- package/src/host/daemon/api/sessions.js +115 -0
- package/src/host/daemon/api/shared.js +217 -0
- package/src/host/daemon/api/super-agent.js +208 -0
- package/src/host/daemon/api/tasks.js +118 -0
- package/src/host/daemon/api/telegram.js +325 -0
- package/src/host/daemon/api/tools.js +21 -0
- package/src/host/daemon/api/top-level.js +131 -0
- package/src/host/daemon/api/transcribe.js +35 -0
- package/src/host/daemon/api/tts.js +44 -0
- package/src/host/daemon/api/voice.js +519 -0
- package/src/host/daemon/api/web.js +123 -0
- package/src/host/daemon/api.js +152 -0
- package/src/{daemon → host/daemon}/compact.js +1 -1
- package/src/{daemon → host/daemon}/db.js +19 -5
- package/src/{daemon/overlay-ws.js → host/daemon/desktop-ws.js} +7 -7
- package/src/host/daemon/engine-sessions.js +245 -0
- package/src/{daemon → host/daemon}/index.js +27 -10
- package/src/{daemon/plugins/overlay.js → host/daemon/plugins/desktop.js} +36 -28
- package/src/{daemon → host/daemon}/plugins/index.js +2 -2
- package/src/{daemon → host/daemon}/plugins/telegram.js +165 -33
- package/src/{daemon → host/daemon}/project-config.js +31 -7
- package/src/{daemon → host/daemon}/routines.js +27 -8
- package/src/{daemon → host/daemon}/skills-loader.js +4 -2
- package/src/{daemon → host/daemon}/smoke.js +9 -5
- package/src/{daemon → host/daemon}/super-agent-tools/helpers.js +1 -1
- package/src/host/daemon/super-agent-tools/index.js +157 -0
- package/src/{daemon → host/daemon}/super-agent-tools/registry-bridge.js +1 -1
- package/src/{daemon → host/daemon}/super-agent-tools/tools/add-project.js +2 -2
- package/src/{daemon → host/daemon}/super-agent-tools/tools/ask-questions.js +4 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/call-agent.js +5 -2
- package/src/{daemon → host/daemon}/super-agent-tools/tools/call-runtime.js +117 -8
- package/src/host/daemon/super-agent-tools/tools/create-task.js +52 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/import-agent.js +2 -3
- package/src/{daemon → host/daemon}/super-agent-tools/tools/list-agents.js +1 -1
- package/src/host/daemon/super-agent-tools/tools/list-tasks.js +52 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/list-vault-agents.js +1 -1
- package/src/host/daemon/super-agent-tools/tools/read-self-memory.js +21 -0
- package/src/host/daemon/super-agent-tools/tools/remember.js +40 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/search-messages.js +1 -1
- package/src/host/daemon/super-agent-tools/tools/search-sessions.js +134 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/send-telegram.js +2 -2
- package/src/{daemon → host/daemon}/super-agent-tools/tools/set-identity.js +1 -1
- package/src/{daemon → host/daemon}/super-agent-tools/tools/set-permission-mode.js +1 -1
- package/src/{daemon → host/daemon}/super-agent-tools/tools/tail-messages.js +1 -1
- package/src/host/daemon/super-agent.js +129 -0
- package/src/host/daemon/token-store.js +118 -0
- package/src/host/daemon/tool-call-parser.js +2 -0
- package/src/{daemon → host/daemon}/transcription.js +1 -1
- package/src/{daemon → host/daemon}/wakeup.js +2 -2
- package/src/interfaces/cli/claude-permissions.js +33 -0
- package/src/{cli → interfaces/cli}/commands/agent.js +43 -8
- package/src/{cli → interfaces/cli}/commands/command.js +1 -1
- package/src/{cli → interfaces/cli}/commands/config.js +1 -1
- package/src/{cli → interfaces/cli}/commands/daemon.js +67 -0
- package/src/interfaces/cli/commands/desktop.js +335 -0
- package/src/interfaces/cli/commands/exec.js +92 -0
- package/src/{cli → interfaces/cli}/commands/identity.js +6 -63
- package/src/{cli → interfaces/cli}/commands/init.js +1 -1
- package/src/{cli → interfaces/cli}/commands/mcp.js +69 -10
- package/src/{cli → interfaces/cli}/commands/memory.js +2 -2
- package/src/interfaces/cli/commands/model.js +136 -0
- package/src/interfaces/cli/commands/pair.js +170 -0
- package/src/interfaces/cli/commands/project-config.js +131 -0
- package/src/{cli → interfaces/cli}/commands/project.js +1 -1
- package/src/{cli → interfaces/cli}/commands/search.js +1 -1
- package/src/interfaces/cli/commands/session.js +892 -0
- package/src/interfaces/cli/commands/sessions.js +997 -0
- package/src/{cli → interfaces/cli}/commands/setup.js +98 -4
- package/src/{cli → interfaces/cli}/commands/skills.js +117 -9
- package/src/{cli → interfaces/cli}/commands/status.js +9 -1
- package/src/{cli → interfaces/cli}/commands/sys.js +96 -17
- package/src/interfaces/cli/commands/task.js +179 -0
- package/src/interfaces/cli/commands/telegram.js +366 -0
- package/src/{cli → interfaces/cli}/commands/update.js +1 -1
- package/src/interfaces/cli/commands/voice.js +258 -0
- package/src/{cli → interfaces/cli}/http.js +6 -2
- package/src/{cli → interfaces/cli}/index.js +955 -63
- package/src/interfaces/cli/postinstall.js +34 -0
- package/src/interfaces/desktop/assets/app-icon-180.png +0 -0
- package/src/interfaces/desktop/assets/app-icon-32.png +0 -0
- package/src/interfaces/desktop/assets/app-icon.png +0 -0
- package/src/interfaces/desktop/assets/apx-logo.png +0 -0
- package/src/interfaces/desktop/assets/superagent.png +0 -0
- package/src/interfaces/desktop/assets/tray-icon.png +0 -0
- package/src/interfaces/desktop/index.html +18 -0
- package/src/interfaces/desktop/main.js +652 -0
- package/src/interfaces/desktop/preload.js +48 -0
- package/src/interfaces/desktop/renderer.js +1006 -0
- package/src/interfaces/desktop/style.css +400 -0
- package/src/{mcp → interfaces/mcp-server}/index.js +2 -2
- package/src/interfaces/tui/_shims/util-which.ts +53 -0
- package/src/{tui → interfaces/tui}/app.tsx +2 -2
- package/src/{tui → interfaces/tui}/component/prompt/index.tsx +4 -1
- package/src/{tui → interfaces/tui}/context/sdk-apx.tsx +84 -16
- package/src/interfaces/tui/context/sync-apx.tsx +398 -0
- package/src/interfaces/tui/routes/session/index.tsx +368 -0
- package/src/interfaces/tui/routes/session/message-actions.tsx +58 -0
- package/src/interfaces/tui/routes/session/sidebar-apx.tsx +114 -0
- package/src/{tui → interfaces/tui}/tsconfig.json +1 -0
- package/src/{tui → interfaces/tui}/util/clipboard.ts +1 -1
- package/src/interfaces/web/README.md +102 -0
- package/src/interfaces/web/coming-soon.html +65 -0
- package/src/interfaces/web/components.json +25 -0
- package/src/interfaces/web/dist/assets/index-BDUsA6L6.css +1 -0
- package/src/interfaces/web/dist/assets/index-CfWyjPBa.js +548 -0
- package/src/interfaces/web/dist/assets/index-CfWyjPBa.js.map +1 -0
- package/src/interfaces/web/dist/favicon/dark/android-chrome-192x192.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/android-chrome-512x512.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/apple-touch-icon.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon-16x16.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon-32x32.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon-48x48.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon.ico +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon.webp +0 -0
- package/src/interfaces/web/dist/favicon/dark/site.webmanifest +18 -0
- package/src/interfaces/web/dist/favicon/white/android-chrome-192x192.png +0 -0
- package/src/interfaces/web/dist/favicon/white/android-chrome-512x512.png +0 -0
- package/src/interfaces/web/dist/favicon/white/apple-touch-icon.png +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon-16x16.png +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon-32x32.png +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon-48x48.png +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon.ico +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon.webp +0 -0
- package/src/interfaces/web/dist/favicon/white/site.webmanifest +18 -0
- package/src/interfaces/web/dist/index.html +27 -0
- package/src/interfaces/web/dist/logo/logo_dark.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_only_dark.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_only_white.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_vertical_dark.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_vertical_white.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_white.webp +0 -0
- package/src/interfaces/web/dist/modules/superagent.png +0 -0
- package/src/interfaces/web/index.html +26 -0
- package/src/interfaces/web/package-lock.json +4253 -0
- package/src/interfaces/web/package.json +55 -0
- package/src/interfaces/web/playwright.config.ts +45 -0
- package/src/interfaces/web/pnpm-lock.yaml +2946 -0
- package/src/interfaces/web/public/favicon/dark/android-chrome-192x192.png +0 -0
- package/src/interfaces/web/public/favicon/dark/android-chrome-512x512.png +0 -0
- package/src/interfaces/web/public/favicon/dark/apple-touch-icon.png +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon-16x16.png +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon-32x32.png +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon-48x48.png +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon.ico +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon.webp +0 -0
- package/src/interfaces/web/public/favicon/dark/site.webmanifest +18 -0
- package/src/interfaces/web/public/favicon/white/android-chrome-192x192.png +0 -0
- package/src/interfaces/web/public/favicon/white/android-chrome-512x512.png +0 -0
- package/src/interfaces/web/public/favicon/white/apple-touch-icon.png +0 -0
- package/src/interfaces/web/public/favicon/white/favicon-16x16.png +0 -0
- package/src/interfaces/web/public/favicon/white/favicon-32x32.png +0 -0
- package/src/interfaces/web/public/favicon/white/favicon-48x48.png +0 -0
- package/src/interfaces/web/public/favicon/white/favicon.ico +0 -0
- package/src/interfaces/web/public/favicon/white/favicon.webp +0 -0
- package/src/interfaces/web/public/favicon/white/site.webmanifest +18 -0
- package/src/interfaces/web/public/logo/logo_dark.webp +0 -0
- package/src/interfaces/web/public/logo/logo_only_dark.webp +0 -0
- package/src/interfaces/web/public/logo/logo_only_white.webp +0 -0
- package/src/interfaces/web/public/logo/logo_vertical_dark.webp +0 -0
- package/src/interfaces/web/public/logo/logo_vertical_white.webp +0 -0
- package/src/interfaces/web/public/logo/logo_white.webp +0 -0
- package/src/interfaces/web/public/modules/superagent.png +0 -0
- package/src/interfaces/web/src/App.tsx +199 -0
- package/src/interfaces/web/src/components/AddProjectDialog.tsx +121 -0
- package/src/interfaces/web/src/components/ModelCombobox.tsx +96 -0
- package/src/interfaces/web/src/components/RobyBubble.tsx +213 -0
- package/src/interfaces/web/src/components/Section.tsx +44 -0
- package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +97 -0
- package/src/interfaces/web/src/components/TelegramSendDialog.tsx +48 -0
- package/src/interfaces/web/src/components/Toast.tsx +84 -0
- package/src/interfaces/web/src/components/UiSelect.tsx +74 -0
- package/src/interfaces/web/src/components/chat/Composer.tsx +43 -0
- package/src/interfaces/web/src/components/chat/ContextBar.tsx +111 -0
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +95 -0
- package/src/interfaces/web/src/components/chat/MessageList.tsx +35 -0
- package/src/interfaces/web/src/components/chat/ModelPicker.tsx +145 -0
- package/src/interfaces/web/src/components/chat/ToolCall.tsx +141 -0
- package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +87 -0
- package/src/interfaces/web/src/components/code/CodeComposer.tsx +87 -0
- package/src/interfaces/web/src/components/code/CodeContextTab.tsx +83 -0
- package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +39 -0
- package/src/interfaces/web/src/components/code/CodeSessionList.tsx +97 -0
- package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +44 -0
- package/src/interfaces/web/src/components/code/CodeToolTrail.tsx +29 -0
- package/src/interfaces/web/src/components/code/DiffView.tsx +67 -0
- package/src/interfaces/web/src/components/common/Qr.tsx +27 -0
- package/src/interfaces/web/src/components/common/TabLayout.tsx +46 -0
- package/src/interfaces/web/src/components/common/TabNav.tsx +113 -0
- package/src/interfaces/web/src/components/config/ConfigTabsEditor.tsx +202 -0
- package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +42 -0
- package/src/interfaces/web/src/components/config/global-config-sections.ts +60 -0
- package/src/interfaces/web/src/components/config/project-config-sections.ts +58 -0
- package/src/interfaces/web/src/components/deck/DaemonCard.tsx +58 -0
- package/src/interfaces/web/src/components/deck/DesktopGroup.tsx +33 -0
- package/src/interfaces/web/src/components/deck/WidgetRow.tsx +100 -0
- package/src/interfaces/web/src/components/layout/Logo.tsx +59 -0
- package/src/interfaces/web/src/components/layout/ProjectAvatar.tsx +116 -0
- package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +151 -0
- package/src/interfaces/web/src/components/settings/AdvancedPanel.tsx +45 -0
- package/src/interfaces/web/src/components/settings/AppearancePanel.tsx +72 -0
- package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +232 -0
- package/src/interfaces/web/src/components/settings/DevicesPanel.tsx +60 -0
- package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +127 -0
- package/src/interfaces/web/src/components/settings/IdentityPanel.tsx +69 -0
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +226 -0
- package/src/interfaces/web/src/components/settings/PairDeviceDialog.tsx +175 -0
- package/src/interfaces/web/src/components/settings/SuperAgentPanel.tsx +93 -0
- package/src/interfaces/web/src/components/settings/TelegramChannelsPanel.tsx +90 -0
- package/src/interfaces/web/src/components/settings/TelegramContactsPanel.tsx +101 -0
- package/src/interfaces/web/src/components/settings/TelegramGlobalPanel.tsx +100 -0
- package/src/interfaces/web/src/components/settings/TelegramRolesPanel.tsx +108 -0
- package/src/interfaces/web/src/components/settings/TelegramSettingsTabs.tsx +55 -0
- package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +95 -0
- package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +405 -0
- package/src/interfaces/web/src/components/settings/providers/typeStyles.ts +155 -0
- package/src/interfaces/web/src/components/settings/providers/types.ts +26 -0
- package/src/interfaces/web/src/components/ui/accordion.tsx +72 -0
- package/src/interfaces/web/src/components/ui/alert-dialog.tsx +187 -0
- package/src/interfaces/web/src/components/ui/alert.tsx +76 -0
- package/src/interfaces/web/src/components/ui/aspect-ratio.tsx +22 -0
- package/src/interfaces/web/src/components/ui/avatar.tsx +107 -0
- package/src/interfaces/web/src/components/ui/badge.tsx +52 -0
- package/src/interfaces/web/src/components/ui/breadcrumb.tsx +125 -0
- package/src/interfaces/web/src/components/ui/button-group.tsx +87 -0
- package/src/interfaces/web/src/components/ui/button.tsx +58 -0
- package/src/interfaces/web/src/components/ui/calendar.tsx +221 -0
- package/src/interfaces/web/src/components/ui/card.tsx +103 -0
- package/src/interfaces/web/src/components/ui/carousel.tsx +242 -0
- package/src/interfaces/web/src/components/ui/chart.tsx +371 -0
- package/src/interfaces/web/src/components/ui/chat-input.tsx +122 -0
- package/src/interfaces/web/src/components/ui/checkbox.tsx +29 -0
- package/src/interfaces/web/src/components/ui/collapsible.tsx +19 -0
- package/src/interfaces/web/src/components/ui/combobox.tsx +295 -0
- package/src/interfaces/web/src/components/ui/command.tsx +196 -0
- package/src/interfaces/web/src/components/ui/context-menu.tsx +271 -0
- package/src/interfaces/web/src/components/ui/dialog.tsx +158 -0
- package/src/interfaces/web/src/components/ui/direction.tsx +4 -0
- package/src/interfaces/web/src/components/ui/drawer.tsx +134 -0
- package/src/interfaces/web/src/components/ui/dropdown-menu.tsx +266 -0
- package/src/interfaces/web/src/components/ui/empty.tsx +104 -0
- package/src/interfaces/web/src/components/ui/field.tsx +236 -0
- package/src/interfaces/web/src/components/ui/hover-card.tsx +51 -0
- package/src/interfaces/web/src/components/ui/input-group.tsx +158 -0
- package/src/interfaces/web/src/components/ui/input-otp.tsx +85 -0
- package/src/interfaces/web/src/components/ui/input.tsx +20 -0
- package/src/interfaces/web/src/components/ui/item.tsx +201 -0
- package/src/interfaces/web/src/components/ui/kbd.tsx +26 -0
- package/src/interfaces/web/src/components/ui/label.tsx +20 -0
- package/src/interfaces/web/src/components/ui/menubar.tsx +280 -0
- package/src/interfaces/web/src/components/ui/native-select.tsx +61 -0
- package/src/interfaces/web/src/components/ui/navigation-menu.tsx +168 -0
- package/src/interfaces/web/src/components/ui/pagination.tsx +130 -0
- package/src/interfaces/web/src/components/ui/popover.tsx +88 -0
- package/src/interfaces/web/src/components/ui/progress.tsx +83 -0
- package/src/interfaces/web/src/components/ui/radio-group.tsx +36 -0
- package/src/interfaces/web/src/components/ui/resizable.tsx +50 -0
- package/src/interfaces/web/src/components/ui/scroll-area.tsx +53 -0
- package/src/interfaces/web/src/components/ui/select.tsx +201 -0
- package/src/interfaces/web/src/components/ui/separator.tsx +23 -0
- package/src/interfaces/web/src/components/ui/sheet.tsx +138 -0
- package/src/interfaces/web/src/components/ui/sidebar.tsx +723 -0
- package/src/interfaces/web/src/components/ui/skeleton.tsx +13 -0
- package/src/interfaces/web/src/components/ui/slider.tsx +52 -0
- package/src/interfaces/web/src/components/ui/sonner.tsx +49 -0
- package/src/interfaces/web/src/components/ui/spinner.tsx +10 -0
- package/src/interfaces/web/src/components/ui/switch.tsx +30 -0
- package/src/interfaces/web/src/components/ui/table.tsx +116 -0
- package/src/interfaces/web/src/components/ui/tabs.tsx +72 -0
- package/src/interfaces/web/src/components/ui/textarea.tsx +18 -0
- package/src/interfaces/web/src/components/ui/tip.tsx +21 -0
- package/src/interfaces/web/src/components/ui/toggle-group.tsx +87 -0
- package/src/interfaces/web/src/components/ui/toggle.tsx +45 -0
- package/src/interfaces/web/src/components/ui/tooltip.tsx +64 -0
- package/src/interfaces/web/src/components/ui.tsx +211 -0
- package/src/interfaces/web/src/components/voice/VoiceProviderList.tsx +197 -0
- package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +213 -0
- package/src/interfaces/web/src/components/voice/VoiceSttCard.tsx +72 -0
- package/src/interfaces/web/src/components/voice/VoiceTestCard.tsx +112 -0
- package/src/interfaces/web/src/components/voice/useTtsPlayer.ts +59 -0
- package/src/interfaces/web/src/constants/index.ts +91 -0
- package/src/interfaces/web/src/hooks/use-mobile.ts +19 -0
- package/src/interfaces/web/src/hooks/useChat.ts +276 -0
- package/src/interfaces/web/src/hooks/useDaemonStatus.ts +12 -0
- package/src/interfaces/web/src/hooks/useDevices.ts +12 -0
- package/src/interfaces/web/src/hooks/useEngines.ts +10 -0
- package/src/interfaces/web/src/hooks/useGlobalConfig.ts +24 -0
- package/src/interfaces/web/src/hooks/useIdentity.ts +16 -0
- package/src/interfaces/web/src/hooks/useProjects.ts +27 -0
- package/src/interfaces/web/src/hooks/useTelegram.ts +35 -0
- package/src/interfaces/web/src/hooks/useTheme.tsx +57 -0
- package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +122 -0
- package/src/interfaces/web/src/i18n/en.ts +767 -0
- package/src/interfaces/web/src/i18n/es.ts +770 -0
- package/src/interfaces/web/src/i18n/index.ts +86 -0
- package/src/interfaces/web/src/lib/api/admin.ts +30 -0
- package/src/interfaces/web/src/lib/api/agents.ts +46 -0
- package/src/interfaces/web/src/lib/api/code.ts +122 -0
- package/src/interfaces/web/src/lib/api/conversations.ts +16 -0
- package/src/interfaces/web/src/lib/api/deck.ts +106 -0
- package/src/interfaces/web/src/lib/api/desktop.ts +54 -0
- package/src/interfaces/web/src/lib/api/embeddings.ts +44 -0
- package/src/interfaces/web/src/lib/api/engines.ts +17 -0
- package/src/interfaces/web/src/lib/api/filesystem.ts +12 -0
- package/src/interfaces/web/src/lib/api/health.ts +6 -0
- package/src/interfaces/web/src/lib/api/identity.ts +7 -0
- package/src/interfaces/web/src/lib/api/mcps.ts +29 -0
- package/src/interfaces/web/src/lib/api/messages.ts +24 -0
- package/src/interfaces/web/src/lib/api/projects.ts +29 -0
- package/src/interfaces/web/src/lib/api/routines.ts +14 -0
- package/src/interfaces/web/src/lib/api/sessions.ts +16 -0
- package/src/interfaces/web/src/lib/api/super_agent.ts +29 -0
- package/src/interfaces/web/src/lib/api/tasks.ts +19 -0
- package/src/interfaces/web/src/lib/api/telegram.ts +57 -0
- package/src/interfaces/web/src/lib/api/tools.ts +13 -0
- package/src/interfaces/web/src/lib/api/voice.ts +169 -0
- package/src/interfaces/web/src/lib/api.ts +48 -0
- package/src/interfaces/web/src/lib/cn.ts +6 -0
- package/src/interfaces/web/src/lib/code-context.ts +83 -0
- package/src/interfaces/web/src/lib/config-values.ts +29 -0
- package/src/interfaces/web/src/lib/device.ts +10 -0
- package/src/interfaces/web/src/lib/http.ts +104 -0
- package/src/interfaces/web/src/lib/secrets.ts +15 -0
- package/src/interfaces/web/src/lib/utils.ts +6 -0
- package/src/interfaces/web/src/main.tsx +16 -0
- package/src/interfaces/web/src/screens/ApxAdminScreen.tsx +174 -0
- package/src/interfaces/web/src/screens/PairingScreen.tsx +105 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +178 -0
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +111 -0
- package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +274 -0
- package/src/interfaces/web/src/screens/base/ComingSoon.tsx +16 -0
- package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +53 -0
- package/src/interfaces/web/src/screens/base/LogsTab.tsx +188 -0
- package/src/interfaces/web/src/screens/base/ModelsTab.tsx +13 -0
- package/src/interfaces/web/src/screens/base/SessionsTab.tsx +58 -0
- package/src/interfaces/web/src/screens/base/WorkspacesTab.tsx +49 -0
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +295 -0
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +173 -0
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +304 -0
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +174 -0
- package/src/interfaces/web/src/screens/project/AgentBrainGraph.tsx +152 -0
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +455 -0
- package/src/interfaces/web/src/screens/project/AgentsTab.tsx +364 -0
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +198 -0
- package/src/interfaces/web/src/screens/project/ConfigTab.tsx +94 -0
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +149 -0
- package/src/interfaces/web/src/screens/project/MemoriesTab.tsx +134 -0
- package/src/interfaces/web/src/screens/project/Overview.tsx +37 -0
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +386 -0
- package/src/interfaces/web/src/screens/project/TasksTab.tsx +116 -0
- package/src/interfaces/web/src/screens/project/TelegramTab.tsx +126 -0
- package/src/interfaces/web/src/screens/project/ThreadsTab.tsx +100 -0
- package/src/interfaces/web/src/styles.css +128 -0
- package/src/interfaces/web/src/types/daemon.ts +289 -0
- package/src/interfaces/web/tailwind.config.js +53 -0
- package/src/interfaces/web/tsconfig.json +24 -0
- package/src/interfaces/web/vite.config.ts +50 -0
- package/src/cli/commands/exec.js +0 -56
- package/src/cli/commands/overlay.js +0 -253
- package/src/cli/commands/session.js +0 -395
- package/src/cli/commands/sessions.js +0 -517
- package/src/cli/commands/telegram.js +0 -77
- package/src/cli/postinstall.js +0 -75
- package/src/cli-ts/commands/agent.ts +0 -173
- package/src/cli-ts/commands/chat.ts +0 -119
- package/src/cli-ts/commands/daemon.ts +0 -112
- package/src/cli-ts/commands/exec.ts +0 -109
- package/src/cli-ts/commands/mcp.ts +0 -235
- package/src/cli-ts/commands/session.ts +0 -224
- package/src/cli-ts/commands/status.ts +0 -61
- package/src/cli-ts/http.ts +0 -36
- package/src/cli-ts/index.ts +0 -73
- package/src/cli-ts/ui.ts +0 -107
- package/src/daemon/api.js +0 -1558
- package/src/daemon/engines/gemini.js +0 -56
- package/src/daemon/engines/openai.js +0 -79
- package/src/daemon/mcp-sources.js +0 -114
- package/src/daemon/super-agent-tools/index.js +0 -84
- package/src/daemon/super-agent-tools.js +0 -1
- package/src/daemon/super-agent.js +0 -541
- package/src/overlay/index.html +0 -44
- package/src/overlay/main.js +0 -480
- package/src/overlay/preload.js +0 -34
- package/src/overlay/renderer.js +0 -371
- package/src/overlay/style.css +0 -250
- package/src/tui/context/sync-apx.tsx +0 -284
- package/src/tui/routes/session/index.tsx +0 -274
- package/src/tui/routes/session/sidebar-apx.tsx +0 -90
- /package/src/{daemon → core}/tools/browser.js +0 -0
- /package/src/{daemon → core}/tools/fetch.js +0 -0
- /package/src/{daemon → core}/tools/glob.js +0 -0
- /package/src/{daemon → core}/tools/grep.js +0 -0
- /package/src/{daemon → core}/tools/registry.js +0 -0
- /package/src/{daemon → core}/tools/search.js +0 -0
- /package/src/{daemon → host/daemon}/conversations.js +0 -0
- /package/src/{daemon → host/daemon}/env-detect.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/_spawn.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/aider.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/claude-code.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/codex.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/cursor-agent.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/gemini-cli.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/index.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/opencode.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/qwen-code.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/call-mcp.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/edit-file.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/list-files.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/list-mcps.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/list-projects.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/list-skills.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/load-skill.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/read-agent-memory.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/read-file.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/run-shell.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/search-files.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/transcribe-audio.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/write-file.js +0 -0
- /package/src/{daemon → host/daemon}/thinking.js +0 -0
- /package/src/{daemon → host/daemon}/whisper-server.py +0 -0
- /package/src/{daemon → host/daemon}/whisper-transcribe.py +0 -0
- /package/src/{cli → interfaces/cli}/commands/a2a.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/artifact.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/chat.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/log.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/messages.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/plugins.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/routine.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/runtime.js +0 -0
- /package/src/{cli → interfaces/cli}/terminal-chat/renderer.js +0 -0
- /package/src/{overlay → interfaces/desktop}/package.json +0 -0
- /package/src/{tui → interfaces/tui}/_shims/cli-error.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/cli-logo.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/cli-ui.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/config-console-state.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/core-any.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/core-binary.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/core-flag.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/core-log.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/lsp-language.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/opencode-any.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/opencode-sdk-v2.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/plugin-tui.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/prompt-display.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/provider-provider.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/session-retry.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/session-schema.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/session-session.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/snapshot.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/tool-any.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-error.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-filesystem.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-format.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-iife.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-locale.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-process.ts +0 -0
- /package/src/{tui → interfaces/tui}/asset/charge.wav +0 -0
- /package/src/{tui → interfaces/tui}/asset/pulse-a.wav +0 -0
- /package/src/{tui → interfaces/tui}/asset/pulse-b.wav +0 -0
- /package/src/{tui → interfaces/tui}/asset/pulse-c.wav +0 -0
- /package/src/{tui → interfaces/tui}/attach.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/bg-pulse-render.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/bg-pulse.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/border.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-agent.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-console-org.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-mcp.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-model.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-provider.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-retry-action.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-session-delete-failed.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-session-list.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-session-rename.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-skill.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-stash.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-status.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-tag.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-theme-list.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-variant.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-workspace-create.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-workspace-file-changes.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-workspace-unavailable.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/error-component.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/logo.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/plugin-route-missing.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/autocomplete.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/cwd.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/frecency.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/history.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/part.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/stash.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/traits.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/spinner.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/startup-loading.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/todo-item.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/use-connected.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/workspace-label.tsx +0 -0
- /package/src/{tui → interfaces/tui}/config/cwd.ts +0 -0
- /package/src/{tui → interfaces/tui}/config/keybind.ts +0 -0
- /package/src/{tui → interfaces/tui}/config/tui-migrate.ts +0 -0
- /package/src/{tui → interfaces/tui}/config/tui-schema.ts +0 -0
- /package/src/{tui → interfaces/tui}/config/tui.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/aggregate-failures.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/args.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/command-palette.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/directory.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/editor-zed.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/editor.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/event-apx.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/event.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/exit.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/helper.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/kv.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/local.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/path-format.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/project-apx.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/project.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/prompt.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/route.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/sdk.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/sync-v2.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/sync.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/aura.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/ayu.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/carbonfox.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/catppuccin-frappe.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/catppuccin-macchiato.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/catppuccin.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/cobalt2.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/cursor.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/dracula.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/everforest.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/flexoki.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/github.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/gruvbox.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/kanagawa.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/lucent-orng.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/material.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/matrix.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/mercury.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/monokai.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/nightowl.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/nord.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/one-dark.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/opencode.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/orng.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/osaka-jade.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/palenight.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/rosepine.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/solarized.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/synthwave84.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/tokyonight.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/vercel.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/vesper.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/zenburn.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/tui-config.tsx +0 -0
- /package/src/{tui → interfaces/tui}/event.ts +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/home/footer.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/home/tips-view.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/home/tips.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/context.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/files.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/footer.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/lsp.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/mcp.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/todo.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/system/plugins.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/system/session-v2.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/system/which-key.tsx +0 -0
- /package/src/{tui → interfaces/tui}/keymap.tsx +0 -0
- /package/src/{tui → interfaces/tui}/layer.ts +0 -0
- /package/src/{tui → interfaces/tui}/plugin/api.tsx +0 -0
- /package/src/{tui → interfaces/tui}/plugin/command-shim.ts +0 -0
- /package/src/{tui → interfaces/tui}/plugin/internal.ts +0 -0
- /package/src/{tui → interfaces/tui}/plugin/runtime.ts +0 -0
- /package/src/{tui → interfaces/tui}/plugin/slots.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/home.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/dialog-fork-from-timeline.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/dialog-message.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/dialog-subagent.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/dialog-timeline.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/footer.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/permission.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/question.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/sidebar.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/subagent-footer.tsx +0 -0
- /package/src/{tui → interfaces/tui}/run.ts +0 -0
- /package/src/{tui → interfaces/tui}/thread.ts +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-alert.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-confirm.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-export-options.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-help.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-prompt.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-select.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/link.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/spinner.ts +0 -0
- /package/src/{tui → interfaces/tui}/ui/toast.tsx +0 -0
- /package/src/{tui → interfaces/tui}/util/editor.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/model.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/provider-origin.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/revert-diff.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/scroll.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/selection.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/signal.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/sound.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/transcript.ts +0 -0
- /package/src/{tui → interfaces/tui}/validate-session.ts +0 -0
- /package/src/{tui → interfaces/tui}/win32.ts +0 -0
- /package/src/{tui → interfaces/tui}/worker.ts +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Sender identity resolution for Telegram (and, by reuse, any future channel).
|
|
2
|
+
//
|
|
3
|
+
// The unit of identity is the PERSON, keyed by their stable platform user id
|
|
4
|
+
// (Telegram `msg.from.id`) — never the chat_id (breaks in groups) nor the phone
|
|
5
|
+
// (Telegram doesn't expose it). A person is the same across every channel/chat;
|
|
6
|
+
// their role lives on the global contact entry. The per-channel `owner_user_id`
|
|
7
|
+
// only marks who owns *that* channel and overrides the role to "owner" there.
|
|
8
|
+
//
|
|
9
|
+
// resolveSender() is pure (read-only). registerSender() has the side effect of
|
|
10
|
+
// recording unknown senders and claiming the first private-chat sender as the
|
|
11
|
+
// channel owner when none is set yet.
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
readConfig,
|
|
15
|
+
findContact,
|
|
16
|
+
findTelegramChannel,
|
|
17
|
+
upsertContact,
|
|
18
|
+
upsertTelegramChannel,
|
|
19
|
+
} from "./config.js";
|
|
20
|
+
|
|
21
|
+
function telegramDisplayName(from) {
|
|
22
|
+
const full = [from?.first_name, from?.last_name].filter(Boolean).join(" ").trim();
|
|
23
|
+
return full || from?.username || "unknown";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve who the sender is, relative to a channel. Read-only.
|
|
28
|
+
* @returns {{userId, username, name, role, isOwner, isGroup, known, note}}
|
|
29
|
+
*/
|
|
30
|
+
export function resolveSender({ cfg, channelName, from, chatType }) {
|
|
31
|
+
const userId = from?.id ?? null;
|
|
32
|
+
const username = from?.username || "";
|
|
33
|
+
const channel = channelName ? findTelegramChannel(cfg, channelName) : null;
|
|
34
|
+
const contact = userId != null ? findContact(cfg, userId) : null;
|
|
35
|
+
const isGroup = chatType === "group" || chatType === "supergroup";
|
|
36
|
+
const isOwner =
|
|
37
|
+
channel?.owner_user_id != null &&
|
|
38
|
+
String(channel.owner_user_id) === String(userId);
|
|
39
|
+
const role = isOwner ? "owner" : contact?.role || "guest";
|
|
40
|
+
return {
|
|
41
|
+
userId,
|
|
42
|
+
username,
|
|
43
|
+
name: contact?.name || telegramDisplayName(from),
|
|
44
|
+
role,
|
|
45
|
+
isOwner,
|
|
46
|
+
isGroup,
|
|
47
|
+
known: !!contact || isOwner,
|
|
48
|
+
note: contact?.note || "",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the tool allowlist for a sender's role.
|
|
54
|
+
* - owner → "*" (all tools)
|
|
55
|
+
* - guest → [] (no tools; text only)
|
|
56
|
+
* - a role defined in telegram.roles → its `tools` ("*" or an array)
|
|
57
|
+
* - any other named role with no definition → "*" (an admin assigned it
|
|
58
|
+
* deliberately; default permissive rather than silently muting it)
|
|
59
|
+
* Returns "*" or an array of tool names.
|
|
60
|
+
*/
|
|
61
|
+
export function resolveAllowedTools(cfg, sender) {
|
|
62
|
+
if (sender?.isOwner) return "*";
|
|
63
|
+
const def = cfg?.telegram?.roles?.[sender?.role];
|
|
64
|
+
if (def && def.tools !== undefined) return def.tools;
|
|
65
|
+
if (sender?.role === "guest") return [];
|
|
66
|
+
return "*";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Like resolveSender(), but records the sender if unrecognized.
|
|
71
|
+
* - Private chat + channel has no owner yet → claim this sender as owner.
|
|
72
|
+
* - Otherwise an unknown sender is recorded as a role-less guest.
|
|
73
|
+
* - A known sender's last_seen is refreshed at most once per day.
|
|
74
|
+
*
|
|
75
|
+
* Persistence is done against a FRESH on-disk config (so concurrent edits from
|
|
76
|
+
* the web/CLI aren't clobbered and the file isn't ballooned with merged
|
|
77
|
+
* defaults). The in-memory `cfg.telegram` subtree is then refreshed so later
|
|
78
|
+
* messages in this daemon session see the change without a reload.
|
|
79
|
+
* Returns { sender, mutated }.
|
|
80
|
+
*/
|
|
81
|
+
export function registerSender({ cfg, channelName, from, chatType }) {
|
|
82
|
+
const userId = from?.id ?? null;
|
|
83
|
+
|
|
84
|
+
// Always refresh the in-memory telegram subtree from disk so role/owner
|
|
85
|
+
// edits made via CLI/web/API are honored on the very next message without a
|
|
86
|
+
// daemon reload. Telegram traffic is low-frequency, so a config read per
|
|
87
|
+
// inbound message is cheap. The helpers below mutate `disk` in place and
|
|
88
|
+
// persist; since cfg.telegram now references it, the session stays current.
|
|
89
|
+
const disk = readConfig();
|
|
90
|
+
cfg.telegram = disk.telegram;
|
|
91
|
+
|
|
92
|
+
const base = () => resolveSender({ cfg, channelName, from, chatType });
|
|
93
|
+
if (userId == null) return { sender: base(), mutated: false };
|
|
94
|
+
|
|
95
|
+
const channel = channelName ? findTelegramChannel(disk, channelName) : null;
|
|
96
|
+
const isPrivate = chatType === "private";
|
|
97
|
+
const existing = findContact(disk, userId);
|
|
98
|
+
const now = new Date().toISOString();
|
|
99
|
+
const username = from?.username || "";
|
|
100
|
+
const name = telegramDisplayName(from);
|
|
101
|
+
|
|
102
|
+
const ownerUnset =
|
|
103
|
+
channel && (channel.owner_user_id == null || channel.owner_user_id === "");
|
|
104
|
+
|
|
105
|
+
let kind = null; // "claim" | "guest" | "touch"
|
|
106
|
+
if (isPrivate && ownerUnset) kind = "claim";
|
|
107
|
+
else if (!existing) kind = "guest";
|
|
108
|
+
else if (existing.last_seen?.slice(0, 10) !== now.slice(0, 10)) kind = "touch";
|
|
109
|
+
|
|
110
|
+
if (!kind) return { sender: base(), mutated: false };
|
|
111
|
+
|
|
112
|
+
if (kind === "claim") {
|
|
113
|
+
upsertTelegramChannel(disk, channelName, { owner_user_id: userId });
|
|
114
|
+
upsertContact(disk, userId, {
|
|
115
|
+
name, username, role: "owner", first_seen: now, last_seen: now,
|
|
116
|
+
});
|
|
117
|
+
} else if (kind === "guest") {
|
|
118
|
+
upsertContact(disk, userId, {
|
|
119
|
+
name, username, role: "guest", first_seen: now, last_seen: now,
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
upsertContact(disk, userId, { last_seen: now });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { sender: base(), mutated: true };
|
|
126
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./registry.js";
|
|
2
|
+
export { buildBrowserRouter } from "./browser.js";
|
|
3
|
+
export { buildFetchRouter } from "./fetch.js";
|
|
4
|
+
export { buildSearchRouter } from "./search.js";
|
|
5
|
+
export { buildGlobRouter } from "./glob.js";
|
|
6
|
+
export { buildGrepRouter } from "./grep.js";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// ElevenLabs TTS adapter.
|
|
2
|
+
// API docs: https://elevenlabs.io/docs/api-reference/text-to-speech
|
|
3
|
+
//
|
|
4
|
+
// Config (~/.apx/config.json → voice.tts.elevenlabs):
|
|
5
|
+
// {
|
|
6
|
+
// "api_key": "sk_...", // or env ELEVENLABS_API_KEY
|
|
7
|
+
// "model": "eleven_multilingual_v2",
|
|
8
|
+
// "voice_id": "EXAVITQu4vr4xnSDxMaL", // default voice (Bella)
|
|
9
|
+
// "output_format": "mp3_44100_128"
|
|
10
|
+
// }
|
|
11
|
+
//
|
|
12
|
+
// Returns audio as mp3 by default.
|
|
13
|
+
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { randomUUID } from "node:crypto";
|
|
17
|
+
|
|
18
|
+
const API_BASE = "https://api.elevenlabs.io/v1/text-to-speech";
|
|
19
|
+
|
|
20
|
+
// Reasonable Spanish-friendly default voice (multilingual model handles it).
|
|
21
|
+
const DEFAULT_VOICE_ID = "EXAVITQu4vr4xnSDxMaL";
|
|
22
|
+
const DEFAULT_MODEL = "eleven_multilingual_v2";
|
|
23
|
+
const DEFAULT_FORMAT = "mp3_44100_128";
|
|
24
|
+
|
|
25
|
+
function getKey(config) {
|
|
26
|
+
return config.api_key || process.env.ELEVENLABS_API_KEY || "";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function formatToExt(fmt) {
|
|
30
|
+
if (!fmt) return "mp3";
|
|
31
|
+
if (fmt.startsWith("mp3")) return "mp3";
|
|
32
|
+
if (fmt.startsWith("pcm")) return "wav";
|
|
33
|
+
if (fmt.startsWith("ulaw")) return "ulaw";
|
|
34
|
+
if (fmt.startsWith("opus") || fmt.startsWith("ogg")) return "ogg";
|
|
35
|
+
return "mp3";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function formatToMime(fmt) {
|
|
39
|
+
const ext = formatToExt(fmt);
|
|
40
|
+
return {
|
|
41
|
+
mp3: "audio/mpeg",
|
|
42
|
+
wav: "audio/wav",
|
|
43
|
+
ogg: "audio/ogg",
|
|
44
|
+
ulaw: "audio/basic",
|
|
45
|
+
}[ext] || "audio/mpeg";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default {
|
|
49
|
+
id: "elevenlabs",
|
|
50
|
+
|
|
51
|
+
async isAvailable(config = {}) {
|
|
52
|
+
return Boolean(getKey(config));
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
async synthesize({ text, voice, outDir, config = {}, signal }) {
|
|
56
|
+
if (!text) throw new Error("elevenlabs: empty text");
|
|
57
|
+
const key = getKey(config);
|
|
58
|
+
if (!key) throw new Error("elevenlabs: no api_key (set ELEVENLABS_API_KEY or voice.tts.elevenlabs.api_key)");
|
|
59
|
+
|
|
60
|
+
const voiceId = voice || config.voice_id || DEFAULT_VOICE_ID;
|
|
61
|
+
const model = config.model || DEFAULT_MODEL;
|
|
62
|
+
const outputFormat = config.output_format || DEFAULT_FORMAT;
|
|
63
|
+
|
|
64
|
+
const url = `${API_BASE}/${encodeURIComponent(voiceId)}?output_format=${encodeURIComponent(outputFormat)}`;
|
|
65
|
+
const res = await fetch(url, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: {
|
|
68
|
+
"xi-api-key": key,
|
|
69
|
+
"content-type": "application/json",
|
|
70
|
+
accept: "audio/mpeg",
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
text,
|
|
74
|
+
model_id: model,
|
|
75
|
+
}),
|
|
76
|
+
signal,
|
|
77
|
+
});
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
const err = await res.text().catch(() => "");
|
|
80
|
+
throw new Error(`elevenlabs ${res.status}: ${err.slice(0, 300)}`);
|
|
81
|
+
}
|
|
82
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
83
|
+
|
|
84
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
85
|
+
const ext = formatToExt(outputFormat);
|
|
86
|
+
const audioPath = path.join(outDir, `elevenlabs-${randomUUID()}.${ext}`);
|
|
87
|
+
fs.writeFileSync(audioPath, buf);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
audio_path: audioPath,
|
|
91
|
+
duration_s: null, // ElevenLabs doesn't return duration in this endpoint
|
|
92
|
+
mime: formatToMime(outputFormat),
|
|
93
|
+
provider: "elevenlabs",
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Gemini TTS adapter.
|
|
2
|
+
// At the time of writing the Gemini TTS surface is gated and not stable across
|
|
3
|
+
// SDK versions: some models (gemini-2.5-flash-preview-tts) expose synthesize
|
|
4
|
+
// via the v1beta REST surface, others require Vertex. To keep APX engine-
|
|
5
|
+
// agnostic, this adapter performs a best-effort call against the documented
|
|
6
|
+
// REST shape, but flags itself as not-implemented when the response does not
|
|
7
|
+
// include inline audio data.
|
|
8
|
+
//
|
|
9
|
+
// Config (~/.apx/config.json → voice.tts.gemini):
|
|
10
|
+
// { "api_key": "...", "model": "gemini-2.5-flash-preview-tts", "voice": "Kore",
|
|
11
|
+
// "style": "habla en tono alegre y enérgico" }
|
|
12
|
+
//
|
|
13
|
+
// `style` is an optional natural-language instruction describing HOW the voice
|
|
14
|
+
// should speak. Gemini single-speaker TTS controls delivery by prefixing the
|
|
15
|
+
// text with such an instruction, so we prepend "<style>: <text>" before
|
|
16
|
+
// synthesizing. A per-call `style` arg overrides the saved config.style.
|
|
17
|
+
//
|
|
18
|
+
// If you need a guaranteed-working Gemini TTS path today, prefer ElevenLabs or
|
|
19
|
+
// OpenAI engines and revisit this once Google stabilises the API.
|
|
20
|
+
|
|
21
|
+
import fs from "node:fs";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
import { randomUUID } from "node:crypto";
|
|
24
|
+
|
|
25
|
+
const DEFAULT_MODEL = "gemini-2.5-flash-preview-tts";
|
|
26
|
+
|
|
27
|
+
function getKey(config, parentEnginesCfg) {
|
|
28
|
+
return (
|
|
29
|
+
config.api_key ||
|
|
30
|
+
parentEnginesCfg?.gemini?.api_key ||
|
|
31
|
+
process.env.GEMINI_API_KEY ||
|
|
32
|
+
""
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default {
|
|
37
|
+
id: "gemini",
|
|
38
|
+
|
|
39
|
+
async isAvailable(config = {}, parentEnginesCfg) {
|
|
40
|
+
// Marked "available" if a key exists — but synthesize() may still throw
|
|
41
|
+
// not-implemented if the SDK surface changes. Selector will fall back.
|
|
42
|
+
return Boolean(getKey(config, parentEnginesCfg));
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
async synthesize({ text, voice, style, outDir, config = {}, signal, parentEnginesCfg }) {
|
|
46
|
+
if (!text) throw new Error("gemini-tts: empty text");
|
|
47
|
+
const key = getKey(config, parentEnginesCfg);
|
|
48
|
+
if (!key) throw new Error("gemini-tts: no api_key (set GEMINI_API_KEY or engines.gemini.api_key)");
|
|
49
|
+
|
|
50
|
+
const model = config.model || DEFAULT_MODEL;
|
|
51
|
+
const voiceName = voice || config.voice || "Kore";
|
|
52
|
+
|
|
53
|
+
// Speaking-style instruction: per-call `style` wins over saved config.style.
|
|
54
|
+
const styleHint = (style ?? config.style ?? "").trim();
|
|
55
|
+
const promptText = styleHint ? `${styleHint}: ${text}` : text;
|
|
56
|
+
|
|
57
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model)}:generateContent?key=${encodeURIComponent(key)}`;
|
|
58
|
+
const body = {
|
|
59
|
+
contents: [{ parts: [{ text: promptText }] }],
|
|
60
|
+
generationConfig: {
|
|
61
|
+
responseModalities: ["AUDIO"],
|
|
62
|
+
speechConfig: {
|
|
63
|
+
voiceConfig: {
|
|
64
|
+
prebuiltVoiceConfig: { voiceName },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const res = await fetch(url, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: { "content-type": "application/json" },
|
|
73
|
+
body: JSON.stringify(body),
|
|
74
|
+
signal,
|
|
75
|
+
});
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const err = await res.text().catch(() => "");
|
|
78
|
+
throw new Error(`gemini-tts: not implemented for this account/model (status ${res.status}). Details: ${err.slice(0, 200)}`);
|
|
79
|
+
}
|
|
80
|
+
const json = await res.json().catch(() => ({}));
|
|
81
|
+
// The REST surface returns inlineData.data (base64) + mimeType.
|
|
82
|
+
const parts = json?.candidates?.[0]?.content?.parts || [];
|
|
83
|
+
const audioPart = parts.find((p) => p.inlineData?.data || p.inline_data?.data);
|
|
84
|
+
const inline = audioPart?.inlineData || audioPart?.inline_data;
|
|
85
|
+
if (!inline?.data) {
|
|
86
|
+
throw new Error("gemini-tts: not implemented (response did not include inline audio data)");
|
|
87
|
+
}
|
|
88
|
+
const mime = inline.mimeType || inline.mime_type || "audio/wav";
|
|
89
|
+
const rawBuf = Buffer.from(inline.data, "base64");
|
|
90
|
+
|
|
91
|
+
// Gemini commonly returns mime like "audio/L16;codec=pcm;rate=24000" —
|
|
92
|
+
// raw signed 16-bit little-endian PCM with no RIFF header. macOS afplay
|
|
93
|
+
// and most other players won't decode it. Wrap it in a WAV container if
|
|
94
|
+
// the mime signals PCM; pass through anything that's already a container.
|
|
95
|
+
const isRawPcm = /audio\/L16|audio\/pcm|codec=pcm/i.test(mime);
|
|
96
|
+
const isOgg = /audio\/ogg/i.test(mime);
|
|
97
|
+
const isMpeg = /audio\/mpeg/i.test(mime);
|
|
98
|
+
|
|
99
|
+
let buf = rawBuf;
|
|
100
|
+
let ext = "wav";
|
|
101
|
+
let outMime = mime;
|
|
102
|
+
if (isMpeg) { ext = "mp3"; outMime = "audio/mpeg"; }
|
|
103
|
+
else if (isOgg) { ext = "ogg"; outMime = "audio/ogg"; }
|
|
104
|
+
else if (isRawPcm || !/audio\/wav|audio\/x-wav/i.test(mime)) {
|
|
105
|
+
// Default any unknown / L16 mime to wrapped WAV.
|
|
106
|
+
const rateMatch = /rate=(\d+)/i.exec(mime || "");
|
|
107
|
+
const sampleRate = rateMatch ? parseInt(rateMatch[1], 10) : 24000;
|
|
108
|
+
buf = wrapPcmAsWav(rawBuf, { sampleRate, channels: 1, bitsPerSample: 16 });
|
|
109
|
+
ext = "wav";
|
|
110
|
+
outMime = "audio/wav";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
114
|
+
const audioPath = path.join(outDir, `gemini-${randomUUID()}.${ext}`);
|
|
115
|
+
fs.writeFileSync(audioPath, buf);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
audio_path: audioPath,
|
|
119
|
+
duration_s: null,
|
|
120
|
+
mime: outMime,
|
|
121
|
+
provider: "gemini",
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Build a minimal 44-byte RIFF/WAVE header for signed PCM and prepend it to
|
|
127
|
+
// `pcm`. Used when an engine (today: Gemini) returns raw L16 PCM bytes that
|
|
128
|
+
// players can't decode without a container.
|
|
129
|
+
function wrapPcmAsWav(pcm, { sampleRate = 24000, channels = 1, bitsPerSample = 16 } = {}) {
|
|
130
|
+
const byteRate = sampleRate * channels * (bitsPerSample / 8);
|
|
131
|
+
const blockAlign = channels * (bitsPerSample / 8);
|
|
132
|
+
const dataSize = pcm.length;
|
|
133
|
+
const header = Buffer.alloc(44);
|
|
134
|
+
header.write("RIFF", 0, "ascii");
|
|
135
|
+
header.writeUInt32LE(36 + dataSize, 4);
|
|
136
|
+
header.write("WAVE", 8, "ascii");
|
|
137
|
+
header.write("fmt ", 12, "ascii");
|
|
138
|
+
header.writeUInt32LE(16, 16); // PCM fmt chunk size
|
|
139
|
+
header.writeUInt16LE(1, 20); // audio format = PCM
|
|
140
|
+
header.writeUInt16LE(channels, 22);
|
|
141
|
+
header.writeUInt32LE(sampleRate, 24);
|
|
142
|
+
header.writeUInt32LE(byteRate, 28);
|
|
143
|
+
header.writeUInt16LE(blockAlign, 32);
|
|
144
|
+
header.writeUInt16LE(bitsPerSample, 34);
|
|
145
|
+
header.write("data", 36, "ascii");
|
|
146
|
+
header.writeUInt32LE(dataSize, 40);
|
|
147
|
+
return Buffer.concat([header, pcm]);
|
|
148
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// TTS engine registry. Mirrors the LLM engine selector at src/core/engines/.
|
|
2
|
+
//
|
|
3
|
+
// Two selection modes (config.voice.tts.mode):
|
|
4
|
+
// "chain" — ordered fallback router. Walk the engine order (config.voice.tts
|
|
5
|
+
// .order, falling back to AUTO_PREFERENCE) skipping engines turned
|
|
6
|
+
// off (config.voice.tts.<id>.enabled === false) and pick the first
|
|
7
|
+
// one whose isAvailable() returns true. "mock" is always kept as
|
|
8
|
+
// the final guaranteed fallback.
|
|
9
|
+
// "single" — use exactly config.voice.tts.provider, no fallback. Lets you keep
|
|
10
|
+
// several engines configured but only ever use the chosen one by
|
|
11
|
+
// default (the others stay available for explicit overrides).
|
|
12
|
+
//
|
|
13
|
+
// An explicit `provider` argument to synthesize()/selectTtsEngine() always wins
|
|
14
|
+
// (used by the "Probar voz" tester to force a specific engine).
|
|
15
|
+
//
|
|
16
|
+
// Backward compat: when `mode` is absent it's derived from `provider`
|
|
17
|
+
// (provider set & not "auto" → single; otherwise chain), so existing configs
|
|
18
|
+
// keep working unchanged.
|
|
19
|
+
|
|
20
|
+
import piper from "./piper.js";
|
|
21
|
+
import elevenlabs from "./elevenlabs.js";
|
|
22
|
+
import openai from "./openai.js";
|
|
23
|
+
import gemini from "./gemini.js";
|
|
24
|
+
import mock from "./mock.js";
|
|
25
|
+
|
|
26
|
+
const ADAPTERS = { piper, elevenlabs, openai, gemini, mock };
|
|
27
|
+
export const TTS_ENGINE_IDS = Object.keys(ADAPTERS);
|
|
28
|
+
|
|
29
|
+
export const AUTO_PREFERENCE = ["piper", "elevenlabs", "openai", "gemini", "mock"];
|
|
30
|
+
|
|
31
|
+
export function getTtsAdapter(provider) {
|
|
32
|
+
const a = ADAPTERS[provider];
|
|
33
|
+
if (!a) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`unknown TTS provider "${provider}". Known: ${TTS_ENGINE_IDS.join(", ")}`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return a;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ttsConfig(globalConfig) {
|
|
42
|
+
return globalConfig?.voice?.tts || {};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function providerConfig(globalConfig, provider) {
|
|
46
|
+
return ttsConfig(globalConfig)?.[provider] || {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isEnabled(ttsCfg, id) {
|
|
50
|
+
return ttsCfg?.[id]?.enabled !== false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Effective routing mode for the chain/single decision. */
|
|
54
|
+
export function resolveMode(ttsCfg) {
|
|
55
|
+
if (ttsCfg?.mode === "chain" || ttsCfg?.mode === "single") return ttsCfg.mode;
|
|
56
|
+
const p = ttsCfg?.provider;
|
|
57
|
+
return p && p !== "auto" ? "single" : "chain";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Full engine order for chain mode: the user's custom order first (only known
|
|
62
|
+
* ids), then any remaining AUTO_PREFERENCE engines. Includes disabled engines
|
|
63
|
+
* so the UI can render + reorder every row; filtering happens at selection time.
|
|
64
|
+
*/
|
|
65
|
+
export function resolveChainOrder(ttsCfg) {
|
|
66
|
+
const custom = Array.isArray(ttsCfg?.order)
|
|
67
|
+
? ttsCfg.order.filter((id) => TTS_ENGINE_IDS.includes(id))
|
|
68
|
+
: [];
|
|
69
|
+
const rest = AUTO_PREFERENCE.filter((id) => !custom.includes(id));
|
|
70
|
+
const full = [...custom, ...rest];
|
|
71
|
+
// Guarantee mock is present as the ultimate fallback.
|
|
72
|
+
if (!full.includes("mock")) full.push("mock");
|
|
73
|
+
return full;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolve which engine should handle a synthesize() call.
|
|
78
|
+
* Returns { provider, adapter, engineConfig }.
|
|
79
|
+
*/
|
|
80
|
+
export async function selectTtsEngine({ globalConfig, provider }) {
|
|
81
|
+
const ttsCfg = ttsConfig(globalConfig);
|
|
82
|
+
|
|
83
|
+
// 1. Explicit override (tester / API caller) always wins.
|
|
84
|
+
if (provider && provider !== "auto") {
|
|
85
|
+
const adapter = getTtsAdapter(provider);
|
|
86
|
+
return { provider, adapter, engineConfig: providerConfig(globalConfig, provider) };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const mode = resolveMode(ttsCfg);
|
|
90
|
+
|
|
91
|
+
// 2. Single mode: use the configured engine verbatim, no fallback.
|
|
92
|
+
if (mode === "single") {
|
|
93
|
+
const id = ttsCfg?.provider;
|
|
94
|
+
if (id && id !== "auto") {
|
|
95
|
+
const adapter = getTtsAdapter(id);
|
|
96
|
+
return { provider: id, adapter, engineConfig: providerConfig(globalConfig, id) };
|
|
97
|
+
}
|
|
98
|
+
// Misconfigured single mode (no concrete provider) → fall through to chain.
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 3. Chain mode: probe the (enabled) order, first available wins.
|
|
102
|
+
for (const id of resolveChainOrder(ttsCfg)) {
|
|
103
|
+
if (id !== "mock" && !isEnabled(ttsCfg, id)) continue;
|
|
104
|
+
const adapter = ADAPTERS[id];
|
|
105
|
+
const cfg = providerConfig(globalConfig, id);
|
|
106
|
+
try {
|
|
107
|
+
if (await adapter.isAvailable(cfg, globalConfig?.engines)) {
|
|
108
|
+
return { provider: id, adapter, engineConfig: cfg };
|
|
109
|
+
}
|
|
110
|
+
} catch { /* probe failures fall through */ }
|
|
111
|
+
}
|
|
112
|
+
// mock is always available, but guard anyway.
|
|
113
|
+
return {
|
|
114
|
+
provider: "mock",
|
|
115
|
+
adapter: mock,
|
|
116
|
+
engineConfig: providerConfig(globalConfig, "mock"),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Discover which engines are configured/available right now. Pure read of
|
|
122
|
+
* config + cheap probes; safe to call frequently.
|
|
123
|
+
*/
|
|
124
|
+
export async function listAvailableTtsEngines(globalConfig) {
|
|
125
|
+
const ttsCfg = ttsConfig(globalConfig);
|
|
126
|
+
const out = [];
|
|
127
|
+
for (const id of TTS_ENGINE_IDS) {
|
|
128
|
+
const adapter = ADAPTERS[id];
|
|
129
|
+
const cfg = providerConfig(globalConfig, id);
|
|
130
|
+
let available = false;
|
|
131
|
+
try {
|
|
132
|
+
available = await adapter.isAvailable(cfg, globalConfig?.engines);
|
|
133
|
+
} catch { available = false; }
|
|
134
|
+
out.push({
|
|
135
|
+
id,
|
|
136
|
+
available,
|
|
137
|
+
// `enabled` is a routing flag, not real config — exclude it from the
|
|
138
|
+
// "configured" heuristic so toggling on/off doesn't fake-mark an engine.
|
|
139
|
+
configured: Object.keys(cfg).filter((k) => k !== "enabled").length > 0,
|
|
140
|
+
enabled: isEnabled(ttsCfg, id),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Mock TTS engine. Writes a tiny placeholder WAV (silent) so callers get a real
|
|
2
|
+
// playable file without external dependencies. Used in tests and as a fallback
|
|
3
|
+
// when no real engine is configured.
|
|
4
|
+
//
|
|
5
|
+
// The "audio" produced is a minimally valid 8 kHz mono PCM-16 WAV with `n_ms`
|
|
6
|
+
// milliseconds of silence so duration_s is meaningful. Total size grows with
|
|
7
|
+
// text length so different inputs produce different files (handy in tests).
|
|
8
|
+
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
|
|
13
|
+
function buildSilentWav(ms) {
|
|
14
|
+
const sampleRate = 8000;
|
|
15
|
+
const numSamples = Math.max(1, Math.floor((sampleRate * ms) / 1000));
|
|
16
|
+
const byteRate = sampleRate * 2; // 16-bit mono
|
|
17
|
+
const dataSize = numSamples * 2;
|
|
18
|
+
const buf = Buffer.alloc(44 + dataSize);
|
|
19
|
+
buf.write("RIFF", 0);
|
|
20
|
+
buf.writeUInt32LE(36 + dataSize, 4);
|
|
21
|
+
buf.write("WAVE", 8);
|
|
22
|
+
buf.write("fmt ", 12);
|
|
23
|
+
buf.writeUInt32LE(16, 16); // PCM chunk size
|
|
24
|
+
buf.writeUInt16LE(1, 20); // PCM format
|
|
25
|
+
buf.writeUInt16LE(1, 22); // mono
|
|
26
|
+
buf.writeUInt32LE(sampleRate, 24);
|
|
27
|
+
buf.writeUInt32LE(byteRate, 28);
|
|
28
|
+
buf.writeUInt16LE(2, 32); // block align
|
|
29
|
+
buf.writeUInt16LE(16, 34); // bits per sample
|
|
30
|
+
buf.write("data", 36);
|
|
31
|
+
buf.writeUInt32LE(dataSize, 40);
|
|
32
|
+
// samples already zero-filled by Buffer.alloc → silence
|
|
33
|
+
return buf;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default {
|
|
37
|
+
id: "mock",
|
|
38
|
+
|
|
39
|
+
async isAvailable() {
|
|
40
|
+
return true;
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async synthesize({ text, outDir, format = "wav" }) {
|
|
44
|
+
if (!text) throw new Error("mock-tts: empty text");
|
|
45
|
+
// Roughly 60 ms per character — gives test assertions something to check.
|
|
46
|
+
const durationMs = Math.max(100, Math.min(60_000, text.length * 60));
|
|
47
|
+
const wav = buildSilentWav(durationMs);
|
|
48
|
+
const filename = `mock-${randomUUID()}.${format === "wav" ? "wav" : "wav"}`;
|
|
49
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
50
|
+
const audioPath = path.join(outDir, filename);
|
|
51
|
+
fs.writeFileSync(audioPath, wav);
|
|
52
|
+
return {
|
|
53
|
+
audio_path: audioPath,
|
|
54
|
+
duration_s: durationMs / 1000,
|
|
55
|
+
mime: "audio/wav",
|
|
56
|
+
provider: "mock",
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// OpenAI TTS adapter (tts-1 / tts-1-hd).
|
|
2
|
+
// Docs: https://platform.openai.com/docs/api-reference/audio/createSpeech
|
|
3
|
+
//
|
|
4
|
+
// Reuses engines.openai.api_key from ~/.apx/config.json. Per-engine voice
|
|
5
|
+
// config (~/.apx/config.json → voice.tts.openai) can override model/voice.
|
|
6
|
+
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
|
|
11
|
+
const API_URL = "https://api.openai.com/v1/audio/speech";
|
|
12
|
+
const DEFAULT_MODEL = "tts-1";
|
|
13
|
+
const DEFAULT_VOICE = "alloy"; // alloy|echo|fable|onyx|nova|shimmer
|
|
14
|
+
|
|
15
|
+
function getKey(config, parentEnginesCfg) {
|
|
16
|
+
return (
|
|
17
|
+
config.api_key ||
|
|
18
|
+
parentEnginesCfg?.openai?.api_key ||
|
|
19
|
+
process.env.OPENAI_API_KEY ||
|
|
20
|
+
""
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function mimeFor(format) {
|
|
25
|
+
return {
|
|
26
|
+
mp3: "audio/mpeg",
|
|
27
|
+
opus: "audio/ogg",
|
|
28
|
+
aac: "audio/aac",
|
|
29
|
+
flac: "audio/flac",
|
|
30
|
+
wav: "audio/wav",
|
|
31
|
+
pcm: "audio/L16",
|
|
32
|
+
}[format] || "audio/mpeg";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default {
|
|
36
|
+
id: "openai",
|
|
37
|
+
|
|
38
|
+
async isAvailable(config = {}, parentEnginesCfg) {
|
|
39
|
+
return Boolean(getKey(config, parentEnginesCfg));
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
async synthesize({ text, voice, outDir, config = {}, format, signal, parentEnginesCfg }) {
|
|
43
|
+
if (!text) throw new Error("openai-tts: empty text");
|
|
44
|
+
const key = getKey(config, parentEnginesCfg);
|
|
45
|
+
if (!key) throw new Error("openai-tts: no api_key (set OPENAI_API_KEY or engines.openai.api_key)");
|
|
46
|
+
|
|
47
|
+
const model = config.model || DEFAULT_MODEL;
|
|
48
|
+
const chosenVoice = voice || config.voice || DEFAULT_VOICE;
|
|
49
|
+
const responseFormat = format || config.format || "mp3";
|
|
50
|
+
|
|
51
|
+
const res = await fetch(API_URL, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
authorization: `Bearer ${key}`,
|
|
55
|
+
"content-type": "application/json",
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
model,
|
|
59
|
+
voice: chosenVoice,
|
|
60
|
+
input: text,
|
|
61
|
+
response_format: responseFormat,
|
|
62
|
+
}),
|
|
63
|
+
signal,
|
|
64
|
+
});
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
const err = await res.text().catch(() => "");
|
|
67
|
+
throw new Error(`openai-tts ${res.status}: ${err.slice(0, 300)}`);
|
|
68
|
+
}
|
|
69
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
70
|
+
|
|
71
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
72
|
+
const audioPath = path.join(outDir, `openai-${randomUUID()}.${responseFormat}`);
|
|
73
|
+
fs.writeFileSync(audioPath, buf);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
audio_path: audioPath,
|
|
77
|
+
duration_s: null,
|
|
78
|
+
mime: mimeFor(responseFormat),
|
|
79
|
+
provider: "openai",
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|