@agentprojectcontext/apx 1.77.3 → 1.78.0
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/README.md +23 -9
- package/package.json +12 -12
- package/src/core/agent/build-agent-system.js +3 -2
- package/src/core/agent/constants.js +3 -2
- package/src/core/agent/loop/greeting-guard.js +55 -0
- package/src/core/agent/loop/side-effects.js +52 -0
- package/src/core/agent/prompt-builder.js +49 -8
- package/src/core/agent/run-agent.js +13 -48
- package/src/core/agent/security.js +1 -1
- package/src/core/agent/self-memory.js +4 -2
- package/src/core/agent/skills/index-store.js +2 -2
- package/src/core/agent/skills/loader.js +3 -22
- package/src/core/agent/super-agent.js +0 -2
- package/src/core/agent/tools/handlers/call-runtime.js +4 -4
- package/src/core/agent/tools/handlers/search-sessions.js +1 -1
- package/src/core/agent/tools/handlers/set-permission-mode.js +11 -4
- package/src/core/agent/tools/names.js +58 -0
- package/src/core/apc/context-copy.js +1 -1
- package/src/core/apc/frontmatter.js +85 -0
- package/src/core/apc/parser.js +9 -21
- package/src/core/apc/paths.js +8 -8
- package/src/core/apc/scaffold.js +5 -4
- package/src/core/channels/telegram/config.js +201 -0
- package/src/core/config/index.js +25 -183
- package/src/core/config/paths.js +54 -1
- package/src/core/confirmation/adapters/web.js +1 -1
- package/src/core/deck/manifest.js +12 -12
- package/src/core/desktop/autostart.js +3 -2
- package/src/core/desktop/process.js +4 -4
- package/src/core/engines/presets.js +2 -2
- package/src/core/http-tools/browser.js +1 -1
- package/src/core/http-tools/catalog.js +551 -0
- package/src/core/http-tools/fetch.js +1 -1
- package/src/core/http-tools/glob.js +3 -3
- package/src/core/http-tools/grep.js +2 -9
- package/src/core/http-tools/inline-handlers.js +59 -0
- package/src/core/http-tools/registry.js +31 -615
- package/src/core/http-tools/search.js +2 -2
- package/src/core/identity/self.js +6 -7
- package/src/core/identity/telegram.js +3 -3
- package/src/core/logging.js +6 -5
- package/src/core/mcp/sources.js +12 -16
- package/src/core/memory/indexer.js +2 -5
- package/src/core/profiles/lifecycle.js +0 -2
- package/src/core/profiles/store.js +2 -7
- package/src/core/routines/runner.js +1 -1
- package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
- package/src/core/runtime-skills/apx-profile/SKILL.md +3 -3
- package/src/core/runtime-skills/apx-project/SKILL.md +1 -1
- package/src/core/runtime-skills/apx-task/SKILL.md +8 -8
- package/src/core/runtime-skills/apx-telegram/SKILL.md +4 -4
- package/src/core/runtime-skills/apx-voice/SKILL.md +4 -4
- package/src/core/runtimes/detect.js +2 -2
- package/src/core/sessions/index.js +800 -0
- package/src/core/stores/code-sessions.js +3 -14
- package/src/core/stores/engine-sessions.js +0 -4
- package/src/core/stores/messages.js +0 -5
- package/src/core/stores/organization.js +9 -11
- package/src/core/stores/routines.js +6 -11
- package/src/core/stores/runtime-callbacks.js +2 -1
- package/src/core/util/json-file.js +128 -0
- package/src/core/vars/sources.js +14 -21
- package/src/core/voice/stt-models.js +1 -1
- package/src/core/voice/tts.js +5 -4
- package/src/host/daemon/api/admin-config.js +5 -5
- package/src/host/daemon/api/admin.js +8 -7
- package/src/host/daemon/api/agents.js +16 -16
- package/src/host/daemon/api/artifact-preview.js +8 -8
- package/src/host/daemon/api/artifacts.js +7 -7
- package/src/host/daemon/api/code.js +8 -8
- package/src/host/daemon/api/config.js +6 -6
- package/src/host/daemon/api/confirm.js +2 -2
- package/src/host/daemon/api/connections.js +2 -2
- package/src/host/daemon/api/conversations.js +12 -12
- package/src/host/daemon/api/deck.js +6 -6
- package/src/host/daemon/api/desktop.js +8 -8
- package/src/host/daemon/api/embeddings.js +4 -4
- package/src/host/daemon/api/engines.js +8 -7
- package/src/host/daemon/api/exec.js +5 -4
- package/src/host/daemon/api/files-project.js +6 -6
- package/src/host/daemon/api/health.js +2 -2
- package/src/host/daemon/api/identity.js +3 -3
- package/src/host/daemon/api/inbox.js +2 -2
- package/src/host/daemon/api/integrations.js +23 -19
- package/src/host/daemon/api/mcps.js +16 -12
- package/src/host/daemon/api/messages.js +5 -5
- package/src/host/daemon/api/organization.js +8 -8
- package/src/host/daemon/api/pairing.js +6 -6
- package/src/host/daemon/api/plugins.js +3 -3
- package/src/host/daemon/api/prefix.js +20 -0
- package/src/host/daemon/api/profiles.js +9 -9
- package/src/host/daemon/api/projects.js +5 -5
- package/src/host/daemon/api/routines.js +8 -8
- package/src/host/daemon/api/run.js +2 -2
- package/src/host/daemon/api/runtimes.js +6 -6
- package/src/host/daemon/api/sessions-search.js +18 -11
- package/src/host/daemon/api/sessions.js +6 -6
- package/src/host/daemon/api/shared.js +59 -13
- package/src/host/daemon/api/skills.js +12 -12
- package/src/host/daemon/api/super-agent.js +4 -4
- package/src/host/daemon/api/tasks.js +11 -11
- package/src/host/daemon/api/telegram.js +19 -20
- package/src/host/daemon/api/tools.js +7 -7
- package/src/host/daemon/api/top-level.js +7 -7
- package/src/host/daemon/api/transcribe.js +6 -6
- package/src/host/daemon/api/tts.js +3 -3
- package/src/host/daemon/api/vars.js +11 -8
- package/src/host/daemon/api/voice.js +6 -6
- package/src/host/daemon/api/web.js +28 -32
- package/src/host/daemon/api.js +77 -48
- package/src/host/daemon/db.js +1 -1
- package/src/host/daemon/desktop-ws.js +7 -3
- package/src/host/daemon/index.js +11 -5
- package/src/host/daemon/plugins/desktop/index.js +1 -1
- package/src/host/daemon/plugins/telegram/index.js +1 -1
- package/src/host/daemon/stt-venv.js +20 -81
- package/src/host/daemon/wakeup.js +1 -1
- package/src/interfaces/acp/index.js +3 -3
- package/src/interfaces/acp/session.js +2 -2
- package/src/interfaces/cli/commands/a2a.js +2 -2
- package/src/interfaces/cli/commands/agent.js +3 -3
- package/src/interfaces/cli/commands/artifact.js +13 -13
- package/src/interfaces/cli/commands/chat.js +3 -3
- package/src/interfaces/cli/commands/command.js +2 -2
- package/src/interfaces/cli/commands/config.js +4 -4
- package/src/interfaces/cli/commands/daemon.js +17 -18
- package/src/interfaces/cli/commands/desktop.js +7 -14
- package/src/interfaces/cli/commands/exec.js +2 -2
- package/src/interfaces/cli/commands/log.js +3 -4
- package/src/interfaces/cli/commands/mcp.js +9 -9
- package/src/interfaces/cli/commands/memory.js +2 -2
- package/src/interfaces/cli/commands/messages.js +5 -5
- package/src/interfaces/cli/commands/model.js +0 -18
- package/src/interfaces/cli/commands/obsidian.js +5 -5
- package/src/interfaces/cli/commands/org.js +5 -5
- package/src/interfaces/cli/commands/pair.js +6 -6
- package/src/interfaces/cli/commands/plugins.js +2 -2
- package/src/interfaces/cli/commands/profile.js +10 -10
- package/src/interfaces/cli/commands/project-config.js +6 -6
- package/src/interfaces/cli/commands/project.js +10 -10
- package/src/interfaces/cli/commands/routine.js +8 -9
- package/src/interfaces/cli/commands/runtime.js +2 -2
- package/src/interfaces/cli/commands/search.js +0 -1
- package/src/interfaces/cli/commands/session.js +12 -40
- package/src/interfaces/cli/commands/sessions.js +21 -798
- package/src/interfaces/cli/commands/setup.js +16 -6
- package/src/interfaces/cli/commands/skills.js +1 -2
- package/src/interfaces/cli/commands/status.js +3 -3
- package/src/interfaces/cli/commands/task.js +8 -8
- package/src/interfaces/cli/commands/telegram.js +21 -21
- package/src/interfaces/cli/commands/voice.js +6 -6
- package/src/interfaces/cli/help/index.js +2245 -0
- package/src/interfaces/cli/http.js +9 -4
- package/src/interfaces/cli/index.js +14 -2857
- package/src/interfaces/cli/routes/acp.js +13 -0
- package/src/interfaces/cli/routes/agent.js +27 -0
- package/src/interfaces/cli/routes/artifact.js +28 -0
- package/src/interfaces/cli/routes/chat.js +11 -0
- package/src/interfaces/cli/routes/code.js +11 -0
- package/src/interfaces/cli/routes/command.js +21 -0
- package/src/interfaces/cli/routes/config.js +16 -0
- package/src/interfaces/cli/routes/connections.js +11 -0
- package/src/interfaces/cli/routes/conversations.js +21 -0
- package/src/interfaces/cli/routes/daemon.js +19 -0
- package/src/interfaces/cli/routes/desktop.js +20 -0
- package/src/interfaces/cli/routes/env.js +13 -0
- package/src/interfaces/cli/routes/exec.js +11 -0
- package/src/interfaces/cli/routes/identity.js +11 -0
- package/src/interfaces/cli/routes/index.js +71 -0
- package/src/interfaces/cli/routes/init.js +11 -0
- package/src/interfaces/cli/routes/log.js +20 -0
- package/src/interfaces/cli/routes/mcp.js +22 -0
- package/src/interfaces/cli/routes/memory.js +11 -0
- package/src/interfaces/cli/routes/messages.js +16 -0
- package/src/interfaces/cli/routes/model.js +11 -0
- package/src/interfaces/cli/routes/obsidian.js +17 -0
- package/src/interfaces/cli/routes/org.js +34 -0
- package/src/interfaces/cli/routes/overlay.js +10 -0
- package/src/interfaces/cli/routes/pair.js +17 -0
- package/src/interfaces/cli/routes/panel.js +16 -0
- package/src/interfaces/cli/routes/permission.js +11 -0
- package/src/interfaces/cli/routes/plugins.js +21 -0
- package/src/interfaces/cli/routes/profile.js +27 -0
- package/src/interfaces/cli/routes/project.js +41 -0
- package/src/interfaces/cli/routes/restart.js +15 -0
- package/src/interfaces/cli/routes/routine.js +28 -0
- package/src/interfaces/cli/routes/run.js +11 -0
- package/src/interfaces/cli/routes/search.js +11 -0
- package/src/interfaces/cli/routes/send.js +11 -0
- package/src/interfaces/cli/routes/session.js +26 -0
- package/src/interfaces/cli/routes/sessions.js +15 -0
- package/src/interfaces/cli/routes/setup.js +18 -0
- package/src/interfaces/cli/routes/skills.js +20 -0
- package/src/interfaces/cli/routes/status.js +12 -0
- package/src/interfaces/cli/routes/task.js +26 -0
- package/src/interfaces/cli/routes/telegram.js +39 -0
- package/src/interfaces/cli/routes/update.js +24 -0
- package/src/interfaces/cli/routes/voice.js +17 -0
- package/src/interfaces/desktop/main.js +6 -7
- package/src/interfaces/desktop/renderer.js +0 -5
- package/src/interfaces/mcp-server/index.js +15 -14
- package/src/interfaces/tui/_shims/globals.d.ts +8 -0
- package/src/interfaces/tui/tsconfig.json +4 -1
- package/src/interfaces/web/README.md +6 -6
- package/src/interfaces/web/dist/assets/{index-BptlbKjU.js → index-CBR_-QyA.js} +52 -52
- package/src/interfaces/web/dist/assets/index-CBR_-QyA.js.map +1 -0
- package/src/interfaces/web/dist/index.html +1 -1
- package/src/interfaces/web/src/components/AddProjectDialog.tsx +1 -1
- package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +1 -1
- package/src/interfaces/web/src/components/chat/ChatList.tsx +2 -2
- package/src/interfaces/web/src/components/chat/SkillPicker.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +1 -1
- package/src/interfaces/web/src/components/desktop/DesktopStatusCard.tsx +1 -1
- package/src/interfaces/web/src/components/files/FileBrowser.tsx +2 -2
- package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +2 -2
- package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +1 -1
- package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +2 -2
- package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/SkillsManager.tsx +2 -2
- package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +1 -1
- package/src/interfaces/web/src/hooks/useDaemonStatus.ts +1 -1
- package/src/interfaces/web/src/hooks/useDevices.ts +1 -1
- package/src/interfaces/web/src/hooks/useEngines.ts +1 -1
- package/src/interfaces/web/src/hooks/useGlobalConfig.ts +2 -2
- package/src/interfaces/web/src/hooks/useIdentity.ts +1 -1
- package/src/interfaces/web/src/hooks/useInbox.ts +1 -1
- package/src/interfaces/web/src/hooks/useProfiles.ts +3 -3
- package/src/interfaces/web/src/hooks/useProjects.ts +1 -1
- package/src/interfaces/web/src/hooks/useTelegram.ts +3 -3
- package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +3 -3
- package/src/interfaces/web/src/lib/api/admin.ts +11 -11
- package/src/interfaces/web/src/lib/api/agents.ts +14 -14
- package/src/interfaces/web/src/lib/api/artifacts.ts +11 -11
- package/src/interfaces/web/src/lib/api/code.ts +1 -1
- package/src/interfaces/web/src/lib/api/conversations.ts +8 -8
- package/src/interfaces/web/src/lib/api/deck.ts +3 -3
- package/src/interfaces/web/src/lib/api/desktop.ts +8 -8
- package/src/interfaces/web/src/lib/api/embeddings.ts +3 -3
- package/src/interfaces/web/src/lib/api/engines.ts +3 -3
- package/src/interfaces/web/src/lib/api/filesystem.ts +2 -2
- package/src/interfaces/web/src/lib/api/health.ts +1 -1
- package/src/interfaces/web/src/lib/api/identity.ts +2 -2
- package/src/interfaces/web/src/lib/api/inbox.ts +1 -1
- package/src/interfaces/web/src/lib/api/integrations.ts +8 -8
- package/src/interfaces/web/src/lib/api/mcps.ts +6 -6
- package/src/interfaces/web/src/lib/api/messages.ts +3 -3
- package/src/interfaces/web/src/lib/api/organization.ts +7 -7
- package/src/interfaces/web/src/lib/api/profiles.ts +8 -8
- package/src/interfaces/web/src/lib/api/projectFiles.ts +5 -5
- package/src/interfaces/web/src/lib/api/projects.ts +12 -12
- package/src/interfaces/web/src/lib/api/routines.ts +7 -7
- package/src/interfaces/web/src/lib/api/sessions.ts +2 -2
- package/src/interfaces/web/src/lib/api/skills.ts +11 -11
- package/src/interfaces/web/src/lib/api/super_agent.ts +3 -3
- package/src/interfaces/web/src/lib/api/tasks.ts +12 -12
- package/src/interfaces/web/src/lib/api/telegram.ts +14 -14
- package/src/interfaces/web/src/lib/api/tools.ts +1 -1
- package/src/interfaces/web/src/lib/api/vars.ts +4 -4
- package/src/interfaces/web/src/lib/api/voice.ts +6 -6
- package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +1 -1
- package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +1 -1
- package/src/interfaces/web/src/screens/base/LogsTab.tsx +3 -3
- package/src/interfaces/web/src/screens/base/SessionsTab.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +8 -8
- package/src/interfaces/web/src/screens/project/AgentsTab.tsx +2 -2
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +3 -3
- package/src/interfaces/web/src/screens/project/ConfigTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +3 -3
- package/src/interfaces/web/src/screens/project/Overview.tsx +6 -6
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +2 -2
- package/src/interfaces/web/src/screens/project/StructureTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/TasksTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/VarsTab.tsx +1 -1
- package/src/interfaces/web/vite.config.ts +13 -18
- package/src/interfaces/web/dist/assets/index-BptlbKjU.js.map +0 -1
- package/src/skills/apc-context/SKILL.md +0 -159
- /package/src/{host/daemon → core}/runtimes/_spawn.js +0 -0
- /package/src/{host/daemon → core}/runtimes/aider.js +0 -0
- /package/src/{host/daemon → core}/runtimes/antigravity.js +0 -0
- /package/src/{host/daemon → core}/runtimes/claude-code.js +0 -0
- /package/src/{host/daemon → core}/runtimes/codex.js +0 -0
- /package/src/{host/daemon → core}/runtimes/cursor-agent.js +0 -0
- /package/src/{host/daemon → core}/runtimes/gemini-cli.js +0 -0
- /package/src/{host/daemon → core}/runtimes/index.js +0 -0
- /package/src/{host/daemon → core}/runtimes/opencode.js +0 -0
- /package/src/{host/daemon → core}/runtimes/qwen-code.js +0 -0
|
@@ -12,9 +12,9 @@ import fs from "node:fs";
|
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { resolveMemoryPath } from "./shared.js";
|
|
14
14
|
|
|
15
|
-
export function register(
|
|
15
|
+
export function register(api, { projects, registries, resolveTopProject }) {
|
|
16
16
|
// ---- /memory ----
|
|
17
|
-
|
|
17
|
+
api.get("/memory", (req, res) => {
|
|
18
18
|
const p = resolveTopProject(req.query);
|
|
19
19
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
20
20
|
const memPath = resolveMemoryPath(p);
|
|
@@ -22,7 +22,7 @@ export function register(app, { projects, registries, resolveTopProject }) {
|
|
|
22
22
|
res.json({ project_id: p.id, path: memPath, body });
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
api.post("/memory", (req, res) => {
|
|
26
26
|
const p = resolveTopProject(req.query);
|
|
27
27
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
28
28
|
const { body } = req.body || {};
|
|
@@ -42,7 +42,7 @@ export function register(app, { projects, registries, resolveTopProject }) {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
// ---- /files ----
|
|
45
|
-
|
|
45
|
+
api.get("/files", (req, res) => {
|
|
46
46
|
const p = resolveTopProject(req.query);
|
|
47
47
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
48
48
|
const rel = req.query.path;
|
|
@@ -90,7 +90,7 @@ export function register(app, { projects, registries, resolveTopProject }) {
|
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
api.post("/files", (req, res) => {
|
|
94
94
|
const p = resolveTopProject(req.query);
|
|
95
95
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
96
96
|
const { path: rel, content } = req.body || {};
|
|
@@ -111,13 +111,13 @@ export function register(app, { projects, registries, resolveTopProject }) {
|
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
// ---- /mcp ----
|
|
114
|
-
|
|
114
|
+
api.get("/mcp", (req, res) => {
|
|
115
115
|
const p = resolveTopProject(req.query);
|
|
116
116
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
117
117
|
res.json(registries.for(p).list());
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
api.post("/mcp/run", async (req, res) => {
|
|
121
121
|
const { project: projectRef, name, tool, params } = req.body || {};
|
|
122
122
|
if (!name) return res.status(400).json({ error: "name required" });
|
|
123
123
|
if (!tool) return res.status(400).json({ error: "tool required" });
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
// X-Provider auto | local | openai | custom (overrides config)
|
|
6
6
|
//
|
|
7
7
|
// Shared by overlay, telegram voice messages, and any external caller.
|
|
8
|
-
export function register(
|
|
8
|
+
export function register(api) {
|
|
9
9
|
// GET /transcribe/providers — STT engine list + availability for the web
|
|
10
10
|
// admin (mirror of /tts/providers). local = embedded faster-whisper;
|
|
11
11
|
// openai = cloud Whisper; custom = any OpenAI-compatible server (mlx-audio
|
|
12
12
|
// on Metal, a Radeon/NVIDIA box on the LAN, a remote endpoint).
|
|
13
|
-
|
|
13
|
+
api.get("/transcribe/providers", async (_req, res) => {
|
|
14
14
|
try {
|
|
15
15
|
const { readConfig } = await import("#core/config/index.js");
|
|
16
16
|
const { listSttProviders } = await import("#core/voice/transcription.js");
|
|
@@ -23,7 +23,7 @@ export function register(app) {
|
|
|
23
23
|
// GET /transcribe/hardware — detected machine + the recommended local backend
|
|
24
24
|
// (mlx on Apple Silicon, faster-whisper cuda on NVIDIA, else CPU). Drives the
|
|
25
25
|
// "engine adapts itself" UX in the web admin.
|
|
26
|
-
|
|
26
|
+
api.get("/transcribe/hardware", async (_req, res) => {
|
|
27
27
|
try {
|
|
28
28
|
const { detectHardware, recommendStt } = await import("#core/voice/stt-hardware.js");
|
|
29
29
|
const hw = detectHardware();
|
|
@@ -35,7 +35,7 @@ export function register(app) {
|
|
|
35
35
|
|
|
36
36
|
// GET /transcribe/models?backend=faster|mlx — model catalog with on-disk
|
|
37
37
|
// status (downloaded? size) for the model-manager UI.
|
|
38
|
-
|
|
38
|
+
api.get("/transcribe/models", async (req, res) => {
|
|
39
39
|
try {
|
|
40
40
|
const backend = String(req.query.backend || "faster");
|
|
41
41
|
const { listSttModels } = await import("#core/voice/stt-models.js");
|
|
@@ -48,7 +48,7 @@ export function register(app) {
|
|
|
48
48
|
// GET /transcribe/warmup — load the local whisper model (if needed) and reset
|
|
49
49
|
// its idle watchdog. Callers (e.g. the desktop window) ping this while open so
|
|
50
50
|
// the first real utterance doesn't pay the cold-load cost.
|
|
51
|
-
|
|
51
|
+
api.get("/transcribe/warmup", async (_req, res) => {
|
|
52
52
|
try {
|
|
53
53
|
const { warmupWhisper } = await import("../whisper-server.js");
|
|
54
54
|
res.json(await warmupWhisper());
|
|
@@ -57,7 +57,7 @@ export function register(app) {
|
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
api.post("/transcribe/chunk", async (req, res) => {
|
|
61
61
|
const chunks = [];
|
|
62
62
|
req.on("data", (c) => chunks.push(c));
|
|
63
63
|
req.on("end", async () => {
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
import { synthesize, listProviders } from "#core/voice/tts.js";
|
|
12
12
|
import { readConfig } from "#core/config/index.js";
|
|
13
13
|
|
|
14
|
-
export function register(
|
|
15
|
-
|
|
14
|
+
export function register(api) {
|
|
15
|
+
api.post("/tts/say", async (req, res) => {
|
|
16
16
|
try {
|
|
17
17
|
const { text, voice, language, provider, format, style } = req.body || {};
|
|
18
18
|
if (typeof text !== "string" || !text.trim()) {
|
|
@@ -33,7 +33,7 @@ export function register(app) {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
api.get("/tts/providers", async (_req, res) => {
|
|
37
37
|
try {
|
|
38
38
|
const info = await listProviders(readConfig());
|
|
39
39
|
res.json(info);
|
|
@@ -24,7 +24,10 @@ import {
|
|
|
24
24
|
maskValue,
|
|
25
25
|
} from "#core/vars/index.js";
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
// Var scope vocabulary: project | global. The default depends on whether the
|
|
28
|
+
// request targets the base store. See the note in api/mcps.js: these scope
|
|
29
|
+
// helpers share a shape but not a vocabulary.
|
|
30
|
+
function normalizeVarScope(raw, { isBase }) {
|
|
28
31
|
if (!raw) return isBase ? "global" : "project";
|
|
29
32
|
const s = String(raw).toLowerCase();
|
|
30
33
|
if (s === "project" || s === "global") return s;
|
|
@@ -37,8 +40,8 @@ function maskAll(obj) {
|
|
|
37
40
|
return out;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
export function register(
|
|
41
|
-
|
|
43
|
+
export function register(api, { project, registries }) {
|
|
44
|
+
api.get("/projects/:pid/vars", (req, res) => {
|
|
42
45
|
const p = project(req, res);
|
|
43
46
|
if (!p) return;
|
|
44
47
|
const reveal = req.query?.reveal === "1" || req.query?.reveal === "true";
|
|
@@ -54,7 +57,7 @@ export function register(app, { project, registries }) {
|
|
|
54
57
|
});
|
|
55
58
|
});
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
api.get("/projects/:pid/vars/:name", (req, res) => {
|
|
58
61
|
const p = project(req, res);
|
|
59
62
|
if (!p) return;
|
|
60
63
|
const name = req.params.name;
|
|
@@ -80,7 +83,7 @@ export function register(app, { project, registries }) {
|
|
|
80
83
|
});
|
|
81
84
|
});
|
|
82
85
|
|
|
83
|
-
|
|
86
|
+
api.post("/projects/:pid/vars", (req, res) => {
|
|
84
87
|
const p = project(req, res);
|
|
85
88
|
if (!p) return;
|
|
86
89
|
const { name, value } = req.body || {};
|
|
@@ -91,7 +94,7 @@ export function register(app, { project, registries }) {
|
|
|
91
94
|
return res.status(400).json({ error: "value required" });
|
|
92
95
|
}
|
|
93
96
|
const isBase = String(p.id) === "0";
|
|
94
|
-
const scope =
|
|
97
|
+
const scope = normalizeVarScope(req.body?.scope, { isBase });
|
|
95
98
|
if (scope === null) {
|
|
96
99
|
return res
|
|
97
100
|
.status(400)
|
|
@@ -111,11 +114,11 @@ export function register(app, { project, registries }) {
|
|
|
111
114
|
res.status(201).json({ ok: true, name, scope });
|
|
112
115
|
});
|
|
113
116
|
|
|
114
|
-
|
|
117
|
+
api.delete("/projects/:pid/vars/:name", (req, res) => {
|
|
115
118
|
const p = project(req, res);
|
|
116
119
|
if (!p) return;
|
|
117
120
|
const isBase = String(p.id) === "0";
|
|
118
|
-
const scope =
|
|
121
|
+
const scope = normalizeVarScope(req.query?.scope, { isBase });
|
|
119
122
|
if (scope === null) {
|
|
120
123
|
return res
|
|
121
124
|
.status(400)
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
// core/. This file is just glue: parse request → call core → format response.
|
|
21
21
|
import fs from "node:fs";
|
|
22
22
|
import path from "node:path";
|
|
23
|
+
import { TTS_TMP_DIR } from "#core/config/paths.js";
|
|
23
24
|
import { readConfig } from "#core/config/index.js";
|
|
24
25
|
import { synthesize } from "#core/voice/tts.js";
|
|
25
26
|
import { stripEmotionTags } from "#core/voice/emotions.js";
|
|
@@ -31,18 +32,17 @@ import { extractSuggestions } from "#core/agent/suggestions.js";
|
|
|
31
32
|
import { appendGlobalMessage } from "#core/stores/messages.js";
|
|
32
33
|
import { appendErrorTrace, previewText } from "#core/logging.js";
|
|
33
34
|
|
|
34
|
-
export function register(
|
|
35
|
+
export function register(api, { projects, plugins, registries }) {
|
|
35
36
|
// GET /voice/tts?path=<abs>
|
|
36
37
|
//
|
|
37
38
|
// Streams a TTS audio file back to the caller. Sandboxed to the
|
|
38
39
|
// ~/.apx/tmp/tts directory so a client can't request arbitrary
|
|
39
40
|
// filesystem paths through a manifest-leaked reply_audio_path.
|
|
40
|
-
|
|
41
|
+
api.get("/voice/tts", async (req, res) => {
|
|
41
42
|
const rawPath = String(req.query.path || "");
|
|
42
43
|
if (!rawPath) return res.status(400).json({ error: "path required" });
|
|
43
44
|
try {
|
|
44
|
-
const
|
|
45
|
-
const ttsRoot = path.resolve(os.homedir(), ".apx", "tmp", "tts");
|
|
45
|
+
const ttsRoot = path.resolve(TTS_TMP_DIR);
|
|
46
46
|
const resolved = path.resolve(rawPath);
|
|
47
47
|
if (!resolved.startsWith(ttsRoot + path.sep)) {
|
|
48
48
|
return res.status(403).json({ error: "path outside tts dir" });
|
|
@@ -65,7 +65,7 @@ export function register(app, { projects, plugins, registries }) {
|
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
api.post("/voice/turn", async (req, res) => {
|
|
69
69
|
const body = req.body || {};
|
|
70
70
|
const cfg = readConfig();
|
|
71
71
|
let userText = (body.text || "").trim();
|
|
@@ -103,7 +103,7 @@ export function register(app, { projects, plugins, registries }) {
|
|
|
103
103
|
: [];
|
|
104
104
|
const channel = body.channel || "voice";
|
|
105
105
|
let suggestions = [];
|
|
106
|
-
|
|
106
|
+
const toolsUsed = [];
|
|
107
107
|
// Attribution for the persisted turn (see the appendGlobalMessage below).
|
|
108
108
|
let replyModel = null;
|
|
109
109
|
let replyUsage = null;
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
//
|
|
3
3
|
// The web bundle is built into src/interfaces/web/dist. When that folder
|
|
4
4
|
// exists, this module mounts it at `/` so users can open
|
|
5
|
-
// http://127.0.0.1:7430 and get the UI.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// http://127.0.0.1:7430 and get the UI. The root namespace belongs to the
|
|
6
|
+
// panel alone — every data route lives under /api (see ./prefix.js) — so the
|
|
7
|
+
// SPA fallback only has to step aside for that single prefix instead of a
|
|
8
|
+
// hand-maintained list that drifts away from the route registry.
|
|
8
9
|
//
|
|
9
10
|
// During development the user runs `vite dev` on :7431 with proxy → :7430,
|
|
10
11
|
// so this mount is a no-op (dist/ doesn't exist yet).
|
|
@@ -12,29 +13,17 @@ import fs from "node:fs";
|
|
|
12
13
|
import path from "node:path";
|
|
13
14
|
import { fileURLToPath } from "node:url";
|
|
14
15
|
|
|
16
|
+
import { isApiPath } from "./prefix.js";
|
|
17
|
+
|
|
18
|
+
// Re-exported so the SPA/API seam keeps a single import site for callers that
|
|
19
|
+
// reason about "which side of the seam is this path on?" (tests, auth).
|
|
20
|
+
export { isApiPath };
|
|
21
|
+
|
|
15
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
23
|
const __dirname = path.dirname(__filename);
|
|
17
24
|
// host/daemon/api/web.js → ../../.. = src/ then interfaces/web/dist
|
|
18
25
|
const WEB_DIST = path.resolve(__dirname, "..", "..", "..", "interfaces", "web", "dist");
|
|
19
26
|
|
|
20
|
-
// Paths the panel is NOT allowed to swallow with its SPA fallback. These are
|
|
21
|
-
// the real API surfaces; we keep the list flat so it doesn't drift away from
|
|
22
|
-
// the actual route registry.
|
|
23
|
-
const API_PREFIXES = [
|
|
24
|
-
"/health", "/admin", "/projects", "/telegram", "/engines", "/runtimes",
|
|
25
|
-
"/messages", "/sessions", "/tools", "/mcp", "/voice", "/tts", "/desktop", "/overlay",
|
|
26
|
-
"/transcribe", "/run", "/files", "/memory", "/env", "/pair", "/deck",
|
|
27
|
-
"/super-agent", "/identity", "/skills", "/profiles", "/inbox",
|
|
28
|
-
"/tasks", "/agents", "/plugins", "/previews", "/embeddings",
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
export function isApiPath(p) {
|
|
32
|
-
for (const prefix of API_PREFIXES) {
|
|
33
|
-
if (p === prefix || p.startsWith(prefix + "/")) return true;
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
27
|
// Client-side routes the SPA actually knows how to render. Must mirror the
|
|
39
28
|
// <Routes> registry in src/interfaces/web/src/App.tsx. Anything else still
|
|
40
29
|
// gets the SPA shell (so React Router shows the styled 404 page) but with an
|
|
@@ -51,15 +40,18 @@ export function isKnownSpaRoute(p) {
|
|
|
51
40
|
return SPA_ROUTES.some((re) => re.test(p));
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
43
|
+
// /api/admin/web-token: localhost-only endpoint that returns the daemon token
|
|
44
|
+
// so the same-origin admin panel can authenticate every subsequent call.
|
|
45
|
+
// Refuses if the request didn't come from loopback. Also refuses if the
|
|
46
|
+
// request was tunneled in via Cloudflare/ngrok/etc. — those connect from
|
|
47
|
+
// a local agent so the socket IP IS loopback, but tunnel-specific headers
|
|
48
|
+
// give them away. When tunneled, the SPA must instead receive the token
|
|
49
|
+
// via URL fragment (#token=…) that the operator shares out-of-band.
|
|
50
|
+
//
|
|
51
|
+
// Registered on the API router (not the static mount below) so the panel's
|
|
52
|
+
// bootstrap call is a normal /api/* request like every other.
|
|
53
|
+
export function registerWebToken(api, { token }) {
|
|
54
|
+
api.get("/admin/web-token", (req, res) => {
|
|
63
55
|
const ra = req.ip || req.socket?.remoteAddress || "";
|
|
64
56
|
const isLocal =
|
|
65
57
|
ra === "127.0.0.1" ||
|
|
@@ -86,7 +78,11 @@ export function register(app, { express, token }) {
|
|
|
86
78
|
}
|
|
87
79
|
res.json({ token });
|
|
88
80
|
});
|
|
81
|
+
}
|
|
89
82
|
|
|
83
|
+
// Static admin panel at the root. Mounted after `app.use("/api", api)` in
|
|
84
|
+
// api.js, so a stray /api/* request can never reach the SPA fallback.
|
|
85
|
+
export function register(app, { express }) {
|
|
90
86
|
if (!fs.existsSync(WEB_DIST)) {
|
|
91
87
|
// No build present. Expose a hint endpoint so users hitting the daemon
|
|
92
88
|
// root know what to do.
|
|
@@ -112,8 +108,8 @@ export function register(app, { express, token }) {
|
|
|
112
108
|
return;
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
// Serve static assets. NB: we mount AFTER the
|
|
116
|
-
//
|
|
111
|
+
// Serve static assets. NB: we mount AFTER the /api router is mounted in
|
|
112
|
+
// api.js so data routes always win over this catch-all.
|
|
117
113
|
app.use(
|
|
118
114
|
express.static(WEB_DIST, {
|
|
119
115
|
index: false,
|
package/src/host/daemon/api.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
// Express REST API for APX. See APC docs reference/apx-daemon.
|
|
2
2
|
//
|
|
3
3
|
// Routes are split by domain under ./api/*.js — each module exports
|
|
4
|
-
// `register(
|
|
4
|
+
// `register(router, ctx)`. This file is purely orchestration: middlewares,
|
|
5
5
|
// context construction, mount order, 404 catch-all.
|
|
6
|
+
//
|
|
7
|
+
// EVERY data route lives under /api. Domain modules still declare their paths
|
|
8
|
+
// root-relative (`/projects/:pid/tasks`); they register on an express.Router()
|
|
9
|
+
// that is mounted at API_PREFIX, so the effective URL is /api/projects/:pid/
|
|
10
|
+
// tasks. The root namespace belongs exclusively to the admin panel (static
|
|
11
|
+
// bundle + client-side routes), which is why the "is this an API path?" test
|
|
12
|
+
// is now a single startsWith instead of a hand-maintained prefix list.
|
|
6
13
|
import express from "express";
|
|
7
14
|
|
|
15
|
+
import { API_PREFIX } from "./api/prefix.js";
|
|
16
|
+
import { logError } from "#core/logging.js";
|
|
17
|
+
|
|
8
18
|
import {
|
|
9
19
|
traceIdMiddleware,
|
|
10
20
|
buildAuthMiddleware,
|
|
11
21
|
makeProjectResolver,
|
|
12
22
|
makeTopProjectResolver,
|
|
23
|
+
errorMiddleware,
|
|
13
24
|
} from "./api/shared.js";
|
|
14
25
|
|
|
15
26
|
import { register as registerTools } from "./api/tools.js";
|
|
@@ -53,7 +64,7 @@ import { register as registerAdminConfig } from "./api/admin-config.js";
|
|
|
53
64
|
import { register as registerIdentity } from "./api/identity.js";
|
|
54
65
|
import { register as registerProfiles } from "./api/profiles.js";
|
|
55
66
|
import { register as registerInbox } from "./api/inbox.js";
|
|
56
|
-
import { register as registerWeb } from "./api/web.js";
|
|
67
|
+
import { register as registerWeb, registerWebToken } from "./api/web.js";
|
|
57
68
|
import { register as registerConfirm } from "./api/confirm.js";
|
|
58
69
|
|
|
59
70
|
export function buildApi({
|
|
@@ -70,6 +81,9 @@ export function buildApi({
|
|
|
70
81
|
}) {
|
|
71
82
|
const telegram = plugins?.get("telegram");
|
|
72
83
|
const app = express();
|
|
84
|
+
// Every domain module registers on this router; it is mounted at /api at the
|
|
85
|
+
// bottom of this function. Modules keep declaring root-relative paths.
|
|
86
|
+
const api = express.Router();
|
|
73
87
|
|
|
74
88
|
// ---- Global middleware -------------------------------------------
|
|
75
89
|
app.use(express.json({ limit: "2mb" }));
|
|
@@ -103,62 +117,71 @@ export function buildApi({
|
|
|
103
117
|
};
|
|
104
118
|
|
|
105
119
|
// ---- Tool routers — must mount BEFORE wildcard registry below ----
|
|
106
|
-
registerTools(
|
|
120
|
+
registerTools(api, ctx);
|
|
107
121
|
|
|
108
122
|
// ---- Health (unauthenticated) ------------------------------------
|
|
109
|
-
registerHealth(
|
|
123
|
+
registerHealth(api, ctx);
|
|
110
124
|
|
|
111
125
|
// ---- Projects + per-project surfaces -----------------------------
|
|
112
|
-
registerProjects(
|
|
113
|
-
registerAgents(
|
|
114
|
-
registerSessions(
|
|
115
|
-
registerMcps(
|
|
116
|
-
registerIntegrations(
|
|
117
|
-
registerVars(
|
|
118
|
-
registerMessages(
|
|
119
|
-
registerEngines(
|
|
120
|
-
registerSkills(
|
|
121
|
-
registerExec(
|
|
122
|
-
registerSuperAgent(
|
|
123
|
-
registerConfirm(
|
|
124
|
-
registerCode(
|
|
125
|
-
registerConversations(
|
|
126
|
-
registerConnections(
|
|
127
|
-
registerRuntimes(
|
|
128
|
-
registerRoutines(
|
|
129
|
-
registerArtifacts(
|
|
130
|
-
registerArtifactPreview(
|
|
131
|
-
registerTasks(
|
|
132
|
-
registerOrganization(
|
|
133
|
-
registerProjectFiles(
|
|
134
|
-
registerConfig(
|
|
126
|
+
registerProjects(api, ctx);
|
|
127
|
+
registerAgents(api, ctx);
|
|
128
|
+
registerSessions(api, ctx);
|
|
129
|
+
registerMcps(api, ctx);
|
|
130
|
+
registerIntegrations(api, ctx);
|
|
131
|
+
registerVars(api, ctx);
|
|
132
|
+
registerMessages(api, ctx);
|
|
133
|
+
registerEngines(api, ctx);
|
|
134
|
+
registerSkills(api, ctx);
|
|
135
|
+
registerExec(api, ctx);
|
|
136
|
+
registerSuperAgent(api, ctx);
|
|
137
|
+
registerConfirm(api, ctx);
|
|
138
|
+
registerCode(api, ctx);
|
|
139
|
+
registerConversations(api, ctx);
|
|
140
|
+
registerConnections(api, ctx);
|
|
141
|
+
registerRuntimes(api, ctx);
|
|
142
|
+
registerRoutines(api, ctx);
|
|
143
|
+
registerArtifacts(api, ctx);
|
|
144
|
+
registerArtifactPreview(api, ctx);
|
|
145
|
+
registerTasks(api, ctx);
|
|
146
|
+
registerOrganization(api, ctx);
|
|
147
|
+
registerProjectFiles(api, ctx);
|
|
148
|
+
registerConfig(api, ctx);
|
|
135
149
|
|
|
136
150
|
// ---- Top-level shortcuts (MCP server clients) --------------------
|
|
137
|
-
registerRun(
|
|
138
|
-
registerTopLevel(
|
|
139
|
-
registerSessionsSearch(
|
|
151
|
+
registerRun(api, ctx);
|
|
152
|
+
registerTopLevel(api, ctx);
|
|
153
|
+
registerSessionsSearch(api, ctx);
|
|
140
154
|
|
|
141
155
|
// ---- Channels & plugin surfaces ----------------------------------
|
|
142
|
-
registerTelegram(
|
|
143
|
-
registerPlugins(
|
|
144
|
-
registerTranscribe(
|
|
145
|
-
registerTts(
|
|
146
|
-
registerEmbeddings(
|
|
147
|
-
registerVoice(
|
|
148
|
-
registerDesktop(
|
|
149
|
-
registerDeck(
|
|
150
|
-
registerPairing(
|
|
156
|
+
registerTelegram(api, ctx);
|
|
157
|
+
registerPlugins(api, ctx);
|
|
158
|
+
registerTranscribe(api, ctx);
|
|
159
|
+
registerTts(api, ctx);
|
|
160
|
+
registerEmbeddings(api, ctx);
|
|
161
|
+
registerVoice(api, ctx);
|
|
162
|
+
registerDesktop(api, ctx);
|
|
163
|
+
registerDeck(api, ctx);
|
|
164
|
+
registerPairing(api, ctx);
|
|
151
165
|
|
|
152
166
|
// ---- Admin -------------------------------------------------------
|
|
153
|
-
registerAdmin(
|
|
154
|
-
registerAdminConfig(
|
|
155
|
-
registerIdentity(
|
|
156
|
-
registerProfiles(
|
|
157
|
-
registerInbox(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
//
|
|
161
|
-
//
|
|
167
|
+
registerAdmin(api, ctx);
|
|
168
|
+
registerAdminConfig(api, ctx);
|
|
169
|
+
registerIdentity(api, ctx);
|
|
170
|
+
registerProfiles(api, ctx);
|
|
171
|
+
registerInbox(api, ctx);
|
|
172
|
+
registerWebToken(api, ctx);
|
|
173
|
+
|
|
174
|
+
// ---- API 404 (MUST be last on the router) ------------------------
|
|
175
|
+
// Terminal for the /api namespace: an unknown /api/* path answers JSON here
|
|
176
|
+
// instead of falling through to the SPA shell below.
|
|
177
|
+
api.use((req, res) =>
|
|
178
|
+
res.status(404).json({ error: `no route ${req.method} ${req.baseUrl}${req.path}` })
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
app.use(API_PREFIX, api);
|
|
182
|
+
|
|
183
|
+
// ---- Web admin panel (static SPA, must mount after /api) ---------
|
|
184
|
+
// Serves src/interfaces/web/dist when present. No-op until the panel is built.
|
|
162
185
|
registerWeb(app, ctx);
|
|
163
186
|
|
|
164
187
|
// ---- 404 catch-all (MUST be last) --------------------------------
|
|
@@ -166,5 +189,11 @@ export function buildApi({
|
|
|
166
189
|
res.status(404).json({ error: `no route ${req.method} ${req.path}` })
|
|
167
190
|
);
|
|
168
191
|
|
|
192
|
+
// ---- Error handler (MUST be after everything) --------------------
|
|
193
|
+
// Express only recognises the 4-argument shape, and only when it is mounted
|
|
194
|
+
// last. Anything an asyncRoute() rejects lands here as JSON instead of an
|
|
195
|
+
// unhandled rejection that kills the daemon.
|
|
196
|
+
app.use(errorMiddleware(logError));
|
|
197
|
+
|
|
169
198
|
return app;
|
|
170
199
|
}
|
package/src/host/daemon/db.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { appendMessageToFs } from "#core/stores/messages.js";
|
|
6
6
|
import { effectiveConfig } from "./project-config.js";
|
|
7
7
|
import { readAgents } from "#core/apc/parser.js";
|
|
8
|
-
import {
|
|
8
|
+
import { apcProjectFile, apcAgentsDir, apcCommandsDir } from "#core/apc/paths.js";
|
|
9
9
|
import { getOrCreateApxId } from "#core/apc/scaffold.js";
|
|
10
10
|
import {
|
|
11
11
|
ensureProjectStorage,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Singleton WebSocket hub for the desktop channel.
|
|
2
2
|
// Imported by api.js (to register connections) and by plugins/desktop/index.js (to broadcast).
|
|
3
|
+
import { apiPath } from "./api/prefix.js";
|
|
3
4
|
|
|
4
5
|
const _clients = new Set(); // Set<WebSocket>
|
|
5
6
|
let _messageHandler = null; // (ws, data) => void — set by desktop plugin
|
|
@@ -12,7 +13,7 @@ export function setDesktopMessageHandler(fn) {
|
|
|
12
13
|
|
|
13
14
|
// --- WS upgrade auth helpers (shared by the daemon upgrade handler + tests) ---
|
|
14
15
|
//
|
|
15
|
-
// The desktop WS channel must authenticate the same way the HTTP /desktop/*
|
|
16
|
+
// The desktop WS channel must authenticate the same way the HTTP /api/desktop/*
|
|
16
17
|
// routes do: a bearer token (master or paired client) carried on the upgrade
|
|
17
18
|
// request. The legitimate desktop window sends `Authorization: Bearer <token>`
|
|
18
19
|
// (src/interfaces/desktop/main.js); browser clients can pass `?token=`. Without
|
|
@@ -20,11 +21,14 @@ export function setDesktopMessageHandler(fn) {
|
|
|
20
21
|
// when host is set to 0.0.0.0) could open the channel and drive the
|
|
21
22
|
// super-agent. See QA BUG-WS-AUTH.
|
|
22
23
|
|
|
23
|
-
/**
|
|
24
|
+
/** The desktop channel's upgrade path. Lives under /api like every other route. */
|
|
25
|
+
export const DESKTOP_WS_PATH = apiPath("/desktop/ws");
|
|
26
|
+
|
|
27
|
+
/** Path-gate: is this upgrade for the desktop WS channel? */
|
|
24
28
|
export function isDesktopUpgradePath(url) {
|
|
25
29
|
let pathname = url || "";
|
|
26
30
|
try { pathname = new URL(url, "http://localhost").pathname; } catch { /* keep raw */ }
|
|
27
|
-
return pathname ===
|
|
31
|
+
return pathname === DESKTOP_WS_PATH;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/** Extract the bearer token from the upgrade request (header first, ?token= fallback). */
|
package/src/host/daemon/index.js
CHANGED
|
@@ -12,12 +12,10 @@ import { randomBytes } from "node:crypto";
|
|
|
12
12
|
const LOOPBACK_HOST = "127.0.0.1";
|
|
13
13
|
import {
|
|
14
14
|
readConfig,
|
|
15
|
-
writeConfig,
|
|
16
15
|
effectiveHost,
|
|
17
16
|
effectivePort,
|
|
18
17
|
addProject as addProjectInConfig,
|
|
19
18
|
PID_PATH,
|
|
20
|
-
LOG_PATH,
|
|
21
19
|
APX_HOME,
|
|
22
20
|
TOKEN_PATH,
|
|
23
21
|
} from "#core/config/index.js";
|
|
@@ -297,12 +295,11 @@ async function main() {
|
|
|
297
295
|
}).catch(() => {});
|
|
298
296
|
});
|
|
299
297
|
|
|
300
|
-
// Attach WebSocket upgrade for the desktop channel on /desktop/ws
|
|
301
|
-
// (legacy /overlay/ws still accepted for one release).
|
|
298
|
+
// Attach WebSocket upgrade for the desktop channel on /api/desktop/ws.
|
|
302
299
|
server.on("upgrade", async (req, socket, head) => {
|
|
303
300
|
if (!isDesktopUpgradePath(req.url)) { socket.destroy(); return; }
|
|
304
301
|
// Auth: the WS upgrade must carry a valid token (master or paired client),
|
|
305
|
-
// matching the HTTP /desktop/* routes. Without this, any client that can
|
|
302
|
+
// matching the HTTP /api/desktop/* routes. Without this, any client that can
|
|
306
303
|
// reach the daemon (host binds 0.0.0.0 → the LAN) could open the desktop
|
|
307
304
|
// channel and drive the super-agent (permission_mode "total"). The
|
|
308
305
|
// legitimate desktop window already sends the bearer token. See QA BUG-WS-AUTH.
|
|
@@ -358,6 +355,15 @@ async function main() {
|
|
|
358
355
|
process.on("uncaughtException", (e) => {
|
|
359
356
|
log(`uncaughtException: ${e.stack || e.message}`);
|
|
360
357
|
});
|
|
358
|
+
// Node >= 15 terminates the process on an unhandled rejection. The CLI has
|
|
359
|
+
// always registered this; the daemon had not, so one un-awaited failure in a
|
|
360
|
+
// route or a plugin took down the process serving the SPA, Telegram polling,
|
|
361
|
+
// voice and the deck at once. Log and keep running — the request-level
|
|
362
|
+
// errorMiddleware already answers the caller.
|
|
363
|
+
process.on("unhandledRejection", (reason) => {
|
|
364
|
+
const e = reason instanceof Error ? reason : new Error(String(reason));
|
|
365
|
+
log(`unhandledRejection: ${e.stack || e.message}`);
|
|
366
|
+
});
|
|
361
367
|
}
|
|
362
368
|
|
|
363
369
|
main().catch((e) => {
|
|
@@ -103,7 +103,7 @@ async function _handleMessage({ ws, text, previousMessages }, { projects, config
|
|
|
103
103
|
await appendGlobalMessage({ channel: CHANNEL, direction: "in", type: "user", author: "user", body: text });
|
|
104
104
|
} catch {}
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
const toolsExecuted = [];
|
|
107
107
|
|
|
108
108
|
// Per-segment streaming: instead of merging the whole turn into one blob, we
|
|
109
109
|
// emit each assistant text piece as its own `segment` (an intro before a tool,
|
|
@@ -311,7 +311,7 @@ export default {
|
|
|
311
311
|
// that bot; otherwise first available bot-tokened channel. Always logs
|
|
312
312
|
// the outbound on `messages` of the channel's target project so audit
|
|
313
313
|
// trails are complete.
|
|
314
|
-
async send({ channel: channelName, chat_id, text, author = resolveAgentName(config)
|
|
314
|
+
async send({ channel: channelName, chat_id, text, author = resolveAgentName(config) }) {
|
|
315
315
|
const p = pickPoller(pollers, channelName);
|
|
316
316
|
const result = await p._send({ chat_id, text });
|
|
317
317
|
appendGlobalMessage({
|