@agentprojectcontext/apx 1.15.6 → 1.16.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/package.json +40 -5
- package/src/cli/commands/log.js +113 -0
- package/src/cli/commands/overlay.js +253 -0
- package/src/cli/commands/sys.js +88 -16
- package/src/cli/index.js +23 -1
- package/src/cli/terminal-chat/renderer.js +71 -56
- package/src/cli-ts/commands/agent.ts +173 -0
- package/src/cli-ts/commands/chat.ts +119 -0
- package/src/cli-ts/commands/daemon.ts +112 -0
- package/src/cli-ts/commands/exec.ts +109 -0
- package/src/cli-ts/commands/mcp.ts +235 -0
- package/src/cli-ts/commands/session.ts +224 -0
- package/src/cli-ts/commands/status.ts +61 -0
- package/src/cli-ts/http.ts +36 -0
- package/src/cli-ts/index.ts +73 -0
- package/src/cli-ts/ui.ts +107 -0
- package/src/core/logging.js +81 -0
- package/src/daemon/api.js +58 -0
- package/src/daemon/engines/anthropic.js +60 -1
- package/src/daemon/engines/index.js +2 -1
- package/src/daemon/engines/ollama.js +70 -3
- package/src/daemon/index.js +58 -0
- package/src/daemon/overlay-ws.js +40 -0
- package/src/daemon/plugins/index.js +2 -1
- package/src/daemon/plugins/overlay.js +177 -0
- package/src/daemon/plugins/telegram.js +15 -3
- package/src/daemon/super-agent.js +102 -19
- package/src/daemon/transcription.js +262 -59
- package/src/daemon/whisper-server.py +57 -6
- package/src/overlay/index.html +44 -0
- package/src/overlay/main.js +480 -0
- package/src/overlay/package.json +3 -0
- package/src/overlay/preload.js +34 -0
- package/src/overlay/renderer.js +371 -0
- package/src/overlay/style.css +250 -0
- package/src/tui/_shims/cli-error.ts +6 -0
- package/src/tui/_shims/cli-logo.ts +18 -0
- package/src/tui/_shims/cli-ui.ts +1 -0
- package/src/tui/_shims/config-console-state.ts +7 -0
- package/src/tui/_shims/core-any.ts +30 -0
- package/src/tui/_shims/core-binary.ts +13 -0
- package/src/tui/_shims/core-flag.ts +3 -0
- package/src/tui/_shims/core-log.ts +14 -0
- package/src/tui/_shims/lsp-language.ts +1 -0
- package/src/tui/_shims/opencode-any.ts +135 -0
- package/src/tui/_shims/opencode-sdk-v2.ts +48 -0
- package/src/tui/_shims/plugin-tui.ts +13 -0
- package/src/tui/_shims/provider-provider.ts +10 -0
- package/src/tui/_shims/session-retry.ts +1 -0
- package/src/tui/_shims/session-schema.ts +15 -0
- package/src/tui/_shims/session-session.ts +3 -0
- package/src/tui/_shims/snapshot.ts +4 -0
- package/src/tui/_shims/tool-any.ts +18 -0
- package/src/tui/_shims/util-error.ts +7 -0
- package/src/tui/_shims/util-filesystem.ts +79 -0
- package/src/tui/_shims/util-format.ts +7 -0
- package/src/tui/_shims/util-iife.ts +3 -0
- package/src/tui/_shims/util-locale.ts +10 -0
- package/src/tui/_shims/util-process.ts +38 -0
- package/src/tui/app.tsx +783 -0
- package/src/tui/asset/charge.wav +0 -0
- package/src/tui/asset/pulse-a.wav +0 -0
- package/src/tui/asset/pulse-b.wav +0 -0
- package/src/tui/asset/pulse-c.wav +0 -0
- package/src/tui/attach.ts +100 -0
- package/src/tui/component/bg-pulse-render.ts +436 -0
- package/src/tui/component/bg-pulse.tsx +99 -0
- package/src/tui/component/border.tsx +21 -0
- package/src/tui/component/dialog-agent.tsx +31 -0
- package/src/tui/component/dialog-console-org.tsx +103 -0
- package/src/tui/component/dialog-mcp.tsx +85 -0
- package/src/tui/component/dialog-model.tsx +175 -0
- package/src/tui/component/dialog-provider.tsx +456 -0
- package/src/tui/component/dialog-retry-action.tsx +160 -0
- package/src/tui/component/dialog-session-delete-failed.tsx +99 -0
- package/src/tui/component/dialog-session-list.tsx +323 -0
- package/src/tui/component/dialog-session-rename.tsx +31 -0
- package/src/tui/component/dialog-skill.tsx +36 -0
- package/src/tui/component/dialog-stash.tsx +87 -0
- package/src/tui/component/dialog-status.tsx +168 -0
- package/src/tui/component/dialog-tag.tsx +44 -0
- package/src/tui/component/dialog-theme-list.tsx +50 -0
- package/src/tui/component/dialog-variant.tsx +39 -0
- package/src/tui/component/dialog-workspace-create.tsx +302 -0
- package/src/tui/component/dialog-workspace-file-changes.tsx +138 -0
- package/src/tui/component/dialog-workspace-unavailable.tsx +69 -0
- package/src/tui/component/error-component.tsx +92 -0
- package/src/tui/component/logo.tsx +896 -0
- package/src/tui/component/plugin-route-missing.tsx +14 -0
- package/src/tui/component/prompt/autocomplete.tsx +869 -0
- package/src/tui/component/prompt/cwd.ts +0 -0
- package/src/tui/component/prompt/frecency.tsx +90 -0
- package/src/tui/component/prompt/history.tsx +108 -0
- package/src/tui/component/prompt/index.tsx +1809 -0
- package/src/tui/component/prompt/part.ts +16 -0
- package/src/tui/component/prompt/stash.tsx +101 -0
- package/src/tui/component/prompt/traits.ts +35 -0
- package/src/tui/component/spinner.tsx +24 -0
- package/src/tui/component/startup-loading.tsx +63 -0
- package/src/tui/component/todo-item.tsx +32 -0
- package/src/tui/component/use-connected.tsx +9 -0
- package/src/tui/component/workspace-label.tsx +19 -0
- package/src/tui/config/cwd.ts +5 -0
- package/src/tui/config/keybind.ts +432 -0
- package/src/tui/config/tui-migrate.ts +154 -0
- package/src/tui/config/tui-schema.ts +34 -0
- package/src/tui/config/tui.ts +46 -0
- package/src/tui/context/aggregate-failures.ts +34 -0
- package/src/tui/context/args.tsx +15 -0
- package/src/tui/context/command-palette.tsx +163 -0
- package/src/tui/context/directory.ts +15 -0
- package/src/tui/context/editor-zed.ts +283 -0
- package/src/tui/context/editor.ts +468 -0
- package/src/tui/context/event-apx.ts +22 -0
- package/src/tui/context/event.ts +6 -0
- package/src/tui/context/exit.tsx +60 -0
- package/src/tui/context/helper.tsx +25 -0
- package/src/tui/context/kv.tsx +81 -0
- package/src/tui/context/local.tsx +608 -0
- package/src/tui/context/path-format.tsx +39 -0
- package/src/tui/context/project-apx.tsx +48 -0
- package/src/tui/context/project.tsx +7 -0
- package/src/tui/context/prompt.tsx +18 -0
- package/src/tui/context/route.tsx +52 -0
- package/src/tui/context/sdk-apx.tsx +185 -0
- package/src/tui/context/sdk.tsx +6 -0
- package/src/tui/context/sync-apx.tsx +178 -0
- package/src/tui/context/sync-v2.tsx +16 -0
- package/src/tui/context/sync.tsx +118 -0
- package/src/tui/context/theme/aura.json +69 -0
- package/src/tui/context/theme/ayu.json +80 -0
- package/src/tui/context/theme/carbonfox.json +248 -0
- package/src/tui/context/theme/catppuccin-frappe.json +230 -0
- package/src/tui/context/theme/catppuccin-macchiato.json +230 -0
- package/src/tui/context/theme/catppuccin.json +112 -0
- package/src/tui/context/theme/cobalt2.json +225 -0
- package/src/tui/context/theme/cursor.json +249 -0
- package/src/tui/context/theme/dracula.json +219 -0
- package/src/tui/context/theme/everforest.json +241 -0
- package/src/tui/context/theme/flexoki.json +237 -0
- package/src/tui/context/theme/github.json +233 -0
- package/src/tui/context/theme/gruvbox.json +242 -0
- package/src/tui/context/theme/kanagawa.json +77 -0
- package/src/tui/context/theme/lucent-orng.json +234 -0
- package/src/tui/context/theme/material.json +235 -0
- package/src/tui/context/theme/matrix.json +77 -0
- package/src/tui/context/theme/mercury.json +252 -0
- package/src/tui/context/theme/monokai.json +221 -0
- package/src/tui/context/theme/nightowl.json +221 -0
- package/src/tui/context/theme/nord.json +223 -0
- package/src/tui/context/theme/one-dark.json +84 -0
- package/src/tui/context/theme/opencode.json +245 -0
- package/src/tui/context/theme/orng.json +249 -0
- package/src/tui/context/theme/osaka-jade.json +93 -0
- package/src/tui/context/theme/palenight.json +222 -0
- package/src/tui/context/theme/rosepine.json +234 -0
- package/src/tui/context/theme/solarized.json +223 -0
- package/src/tui/context/theme/synthwave84.json +226 -0
- package/src/tui/context/theme/tokyonight.json +243 -0
- package/src/tui/context/theme/vercel.json +245 -0
- package/src/tui/context/theme/vesper.json +218 -0
- package/src/tui/context/theme/zenburn.json +223 -0
- package/src/tui/context/theme.tsx +1247 -0
- package/src/tui/context/tui-config.tsx +9 -0
- package/src/tui/event.ts +16 -0
- package/src/tui/feature-plugins/home/footer.tsx +94 -0
- package/src/tui/feature-plugins/home/tips-view.tsx +166 -0
- package/src/tui/feature-plugins/home/tips.tsx +59 -0
- package/src/tui/feature-plugins/sidebar/context.tsx +65 -0
- package/src/tui/feature-plugins/sidebar/files.tsx +63 -0
- package/src/tui/feature-plugins/sidebar/footer.tsx +94 -0
- package/src/tui/feature-plugins/sidebar/lsp.tsx +65 -0
- package/src/tui/feature-plugins/sidebar/mcp.tsx +97 -0
- package/src/tui/feature-plugins/sidebar/todo.tsx +49 -0
- package/src/tui/feature-plugins/system/plugins.tsx +269 -0
- package/src/tui/feature-plugins/system/session-v2.tsx +1143 -0
- package/src/tui/feature-plugins/system/which-key.tsx +608 -0
- package/src/tui/keymap.tsx +166 -0
- package/src/tui/layer.ts +6 -0
- package/src/tui/plugin/api.tsx +381 -0
- package/src/tui/plugin/command-shim.ts +109 -0
- package/src/tui/plugin/internal.ts +33 -0
- package/src/tui/plugin/runtime.ts +1069 -0
- package/src/tui/plugin/slots.tsx +60 -0
- package/src/tui/routes/home.tsx +96 -0
- package/src/tui/routes/session/dialog-fork-from-timeline.tsx +76 -0
- package/src/tui/routes/session/dialog-message.tsx +108 -0
- package/src/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/tui/routes/session/footer.tsx +91 -0
- package/src/tui/routes/session/index.tsx +188 -0
- package/src/tui/routes/session/permission.tsx +722 -0
- package/src/tui/routes/session/question.tsx +490 -0
- package/src/tui/routes/session/sidebar.tsx +102 -0
- package/src/tui/routes/session/subagent-footer.tsx +133 -0
- package/src/tui/run.ts +84 -0
- package/src/tui/thread.ts +261 -0
- package/src/tui/tsconfig.json +40 -0
- package/src/tui/ui/dialog-alert.tsx +66 -0
- package/src/tui/ui/dialog-confirm.tsx +108 -0
- package/src/tui/ui/dialog-export-options.tsx +217 -0
- package/src/tui/ui/dialog-help.tsx +40 -0
- package/src/tui/ui/dialog-prompt.tsx +101 -0
- package/src/tui/ui/dialog-select.tsx +553 -0
- package/src/tui/ui/dialog.tsx +211 -0
- package/src/tui/ui/link.tsx +34 -0
- package/src/tui/ui/spinner.ts +368 -0
- package/src/tui/ui/toast.tsx +111 -0
- package/src/tui/util/clipboard.ts +217 -0
- package/src/tui/util/editor.ts +37 -0
- package/src/tui/util/model.ts +23 -0
- package/src/tui/util/provider-origin.ts +7 -0
- package/src/tui/util/revert-diff.ts +18 -0
- package/src/tui/util/scroll.ts +25 -0
- package/src/tui/util/selection.ts +65 -0
- package/src/tui/util/signal.ts +41 -0
- package/src/tui/util/sound.ts +156 -0
- package/src/tui/util/transcript.ts +112 -0
- package/src/tui/validate-session.ts +29 -0
- package/src/tui/win32.ts +130 -0
- package/src/tui/worker.ts +104 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentprojectcontext/apx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -10,11 +10,13 @@
|
|
|
10
10
|
"bin": {
|
|
11
11
|
"apx": "./src/cli/index.js",
|
|
12
12
|
"apx-daemon": "./src/daemon/index.js",
|
|
13
|
-
"apx-mcp": "./src/mcp/index.js"
|
|
13
|
+
"apx-mcp": "./src/mcp/index.js",
|
|
14
|
+
"apx-ng": "./dist/cli/index.js"
|
|
14
15
|
},
|
|
15
16
|
"files": [
|
|
16
17
|
"src/",
|
|
17
18
|
"skills/",
|
|
19
|
+
"dist/",
|
|
18
20
|
"README.md"
|
|
19
21
|
],
|
|
20
22
|
"engines": {
|
|
@@ -24,6 +26,9 @@
|
|
|
24
26
|
"start": "node src/daemon/index.js",
|
|
25
27
|
"smoke": "node src/daemon/smoke.js",
|
|
26
28
|
"test": "node --test --test-reporter=spec tests/*.test.js",
|
|
29
|
+
"build": "node scripts/build-cli.js",
|
|
30
|
+
"build:watch": "node scripts/build-cli.js --watch",
|
|
31
|
+
"typecheck": "tsc --noEmit -p tsconfig.cli.json",
|
|
27
32
|
"upgrade": "pnpm install && pnpm add -g .",
|
|
28
33
|
"prepack": "node scripts/sync-apc-skill.js",
|
|
29
34
|
"postinstall": "node src/cli/postinstall.js"
|
|
@@ -31,21 +36,50 @@
|
|
|
31
36
|
"packageManager": "pnpm@10.25.0",
|
|
32
37
|
"dependencies": {
|
|
33
38
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
|
+
"@opentui/core": "^0.2.8",
|
|
40
|
+
"@opentui/keymap": "^0.2.8",
|
|
41
|
+
"@opentui/solid": "^0.2.8",
|
|
42
|
+
"@solid-primitives/event-bus": "^1.1.3",
|
|
43
|
+
"@solid-primitives/keyboard": "^1.3.5",
|
|
44
|
+
"@solid-primitives/scheduled": "^1.5.3",
|
|
45
|
+
"ansi-regex": "^6.2.2",
|
|
34
46
|
"chalk": "^5.6.2",
|
|
35
47
|
"cli-cursor": "^5.0.0",
|
|
48
|
+
"cli-sound": "^1.1.3",
|
|
49
|
+
"clipboardy": "^5.3.1",
|
|
36
50
|
"cron-parser": "^5.5.0",
|
|
51
|
+
"effect": "^3.21.2",
|
|
37
52
|
"express": "^4.21.0",
|
|
38
|
-
"
|
|
53
|
+
"fuzzysort": "^3.1.0",
|
|
54
|
+
"jsonc-parser": "^3.3.1",
|
|
55
|
+
"node-fetch": "^3.3.2",
|
|
56
|
+
"open": "^11.0.0",
|
|
57
|
+
"opentui-spinner": "^0.0.6",
|
|
58
|
+
"react": "^19.2.6",
|
|
59
|
+
"remeda": "^2.34.1",
|
|
60
|
+
"semver": "^7.8.0",
|
|
61
|
+
"solid-js": "^1.9.12",
|
|
62
|
+
"strip-ansi": "^7.2.0",
|
|
63
|
+
"yargs": "^18.0.0"
|
|
39
64
|
},
|
|
40
65
|
"optionalDependencies": {
|
|
41
66
|
"fast-glob": "^3.3.2",
|
|
42
|
-
"puppeteer": "^22.0.0"
|
|
67
|
+
"puppeteer": "^22.0.0",
|
|
68
|
+
"ws": "^8.18.0"
|
|
43
69
|
},
|
|
44
70
|
"devDependencies": {
|
|
71
|
+
"@babel/core": "^7.29.0",
|
|
45
72
|
"@semantic-release/changelog": "^6.0.3",
|
|
46
73
|
"@semantic-release/git": "^10.0.1",
|
|
74
|
+
"@types/node": "^25.7.0",
|
|
75
|
+
"@types/yargs": "^17.0.35",
|
|
76
|
+
"babel-preset-solid": "^1.9.12",
|
|
47
77
|
"better-sqlite3": "^11.3.0",
|
|
48
|
-
"conventional-changelog-conventionalcommits": "^9.3.1"
|
|
78
|
+
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
79
|
+
"electron": "^33.4.11",
|
|
80
|
+
"esbuild": "^0.28.0",
|
|
81
|
+
"esbuild-plugin-solid": "^0.6.0",
|
|
82
|
+
"typescript": "^6.0.3"
|
|
49
83
|
},
|
|
50
84
|
"keywords": [
|
|
51
85
|
"apc",
|
|
@@ -59,6 +93,7 @@
|
|
|
59
93
|
"pnpm": {
|
|
60
94
|
"onlyBuiltDependencies": [
|
|
61
95
|
"better-sqlite3",
|
|
96
|
+
"electron",
|
|
62
97
|
"puppeteer"
|
|
63
98
|
]
|
|
64
99
|
},
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// apx log — read the unified daemon log (~/.apx/logs/apx.log)
|
|
2
|
+
//
|
|
3
|
+
// apx log tail last 100 lines
|
|
4
|
+
// apx log --tail N tail last N lines
|
|
5
|
+
// apx log -f follow (tail -f)
|
|
6
|
+
// apx log --follow same as -f
|
|
7
|
+
// apx log --errors only show [ERROR] lines (works with --tail / -f too)
|
|
8
|
+
//
|
|
9
|
+
// Every line written through core/logging.js or the daemon's log() lands
|
|
10
|
+
// here, regardless of which module produced it (telegram, whisper, super-agent…).
|
|
11
|
+
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import os from "node:os";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
|
|
16
|
+
const APX_LOG_PATH = path.join(os.homedir(), ".apx", "logs", "apx.log");
|
|
17
|
+
const ERROR_TRACE_PATH = path.join(os.homedir(), ".apx", "logs", "errors.jsonl");
|
|
18
|
+
|
|
19
|
+
const c = {
|
|
20
|
+
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
|
|
21
|
+
green: "\x1b[32m", red: "\x1b[31m", yellow: "\x1b[33m",
|
|
22
|
+
cyan: "\x1b[36m", gray: "\x1b[90m", white: "\x1b[97m",
|
|
23
|
+
};
|
|
24
|
+
const colorize = (line) =>
|
|
25
|
+
line
|
|
26
|
+
.replace(/^\[([\d-]+\s[\d:.]+)\]/, (_m, ts) => `[${c.gray}${ts}${c.reset}]`)
|
|
27
|
+
.replace(/\[ERROR\s*\]/, `[${c.red}ERROR${c.reset}]`)
|
|
28
|
+
.replace(/\[WARN\s*\]/, `[${c.yellow}WARN ${c.reset}]`)
|
|
29
|
+
.replace(/\[INFO\s*\]/, `[${c.cyan}INFO ${c.reset}]`)
|
|
30
|
+
.replace(/\[([a-z_-]+)\]/i, (_m, mod) => `[${c.bold}${mod}${c.reset}]`);
|
|
31
|
+
|
|
32
|
+
function readLastLines(file, n) {
|
|
33
|
+
if (!fs.existsSync(file)) return [];
|
|
34
|
+
const content = fs.readFileSync(file, "utf8");
|
|
35
|
+
return content.split("\n").filter(Boolean).slice(-n);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function shouldShow(line, opts) {
|
|
39
|
+
if (opts.errors) return /\[ERROR\s*\]/.test(line);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function cmdLog(args = {}) {
|
|
44
|
+
const flags = args.flags || {};
|
|
45
|
+
const follow = !!(flags.follow || flags.f);
|
|
46
|
+
const errorsOnly = !!flags.errors;
|
|
47
|
+
const tail = flags.tail ? parseInt(flags.tail, 10) : 100;
|
|
48
|
+
|
|
49
|
+
// --errors without an explicit log file inspects both the unified log AND
|
|
50
|
+
// the structured errors.jsonl, since some surfaces (api routes) only emit
|
|
51
|
+
// there. We surface a small banner when we hit the structured file.
|
|
52
|
+
if (!fs.existsSync(APX_LOG_PATH)) {
|
|
53
|
+
console.log(`${c.gray} (no log yet at ${APX_LOG_PATH})${c.reset}`);
|
|
54
|
+
if (errorsOnly && fs.existsSync(ERROR_TRACE_PATH)) {
|
|
55
|
+
console.log(`${c.gray} showing structured errors from ${ERROR_TRACE_PATH}:${c.reset}\n`);
|
|
56
|
+
const traces = readLastLines(ERROR_TRACE_PATH, tail);
|
|
57
|
+
for (const t of traces) {
|
|
58
|
+
try {
|
|
59
|
+
const j = JSON.parse(t);
|
|
60
|
+
console.log(`${c.gray}[${j.ts}]${c.reset} ${c.red}ERROR${c.reset} ${c.bold}${j.surface || "api"}${c.reset} ${j.route || ""} ${j.error?.message || ""}`);
|
|
61
|
+
} catch { console.log(t); }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const lines = readLastLines(APX_LOG_PATH, tail);
|
|
68
|
+
const filtered = lines.filter((l) => shouldShow(l, { errors: errorsOnly }));
|
|
69
|
+
for (const line of filtered) {
|
|
70
|
+
console.log(colorize(line));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (errorsOnly && fs.existsSync(ERROR_TRACE_PATH)) {
|
|
74
|
+
const traces = readLastLines(ERROR_TRACE_PATH, tail);
|
|
75
|
+
if (traces.length > 0) {
|
|
76
|
+
console.log(`\n${c.gray}── structured errors (${ERROR_TRACE_PATH}) ──${c.reset}`);
|
|
77
|
+
for (const t of traces) {
|
|
78
|
+
try {
|
|
79
|
+
const j = JSON.parse(t);
|
|
80
|
+
console.log(`${c.gray}[${j.ts}]${c.reset} ${c.red}ERROR${c.reset} ${c.bold}${j.surface || "api"}${c.reset} ${j.route || ""} ${j.error?.message || ""}`);
|
|
81
|
+
} catch { console.log(t); }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!follow) return;
|
|
87
|
+
|
|
88
|
+
// tail -f mode. fs.watch on macOS sometimes loses events on heavy writes,
|
|
89
|
+
// so we also poll size every 500ms as a safety net.
|
|
90
|
+
let currentSize = fs.statSync(APX_LOG_PATH).size;
|
|
91
|
+
|
|
92
|
+
const drain = () => {
|
|
93
|
+
let newSize;
|
|
94
|
+
try { newSize = fs.statSync(APX_LOG_PATH).size; }
|
|
95
|
+
catch { return; }
|
|
96
|
+
if (newSize === currentSize) return;
|
|
97
|
+
if (newSize < currentSize) { currentSize = newSize; return; } // truncated/rotated
|
|
98
|
+
const fd = fs.openSync(APX_LOG_PATH, "r");
|
|
99
|
+
const buf = Buffer.alloc(newSize - currentSize);
|
|
100
|
+
fs.readSync(fd, buf, 0, buf.length, currentSize);
|
|
101
|
+
fs.closeSync(fd);
|
|
102
|
+
currentSize = newSize;
|
|
103
|
+
const chunkLines = buf.toString("utf8").split("\n").filter(Boolean);
|
|
104
|
+
for (const l of chunkLines) {
|
|
105
|
+
if (shouldShow(l, { errors: errorsOnly })) console.log(colorize(l));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
try { fs.watch(APX_LOG_PATH, () => drain()); } catch {}
|
|
110
|
+
const poll = setInterval(drain, 500);
|
|
111
|
+
// Keep process alive
|
|
112
|
+
return new Promise(() => { void poll; });
|
|
113
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// apx overlay — launch/manage the floating voice overlay window.
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawn, execFileSync } from "node:child_process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { http } from "../http.js";
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
const OVERLAY_MAIN = path.resolve(__dirname, "../../overlay/main.js");
|
|
13
|
+
const OVERLAY_PID = path.join(os.homedir(), ".apx", "overlay.pid");
|
|
14
|
+
|
|
15
|
+
// ── ANSI ─────────────────────────────────────────────────────────────────────
|
|
16
|
+
const c = { reset:"\x1b[0m", bold:"\x1b[1m", dim:"\x1b[2m", green:"\x1b[32m",
|
|
17
|
+
red:"\x1b[31m", yellow:"\x1b[33m", cyan:"\x1b[36m", gray:"\x1b[90m" };
|
|
18
|
+
const fmt = {
|
|
19
|
+
bold:(s)=>`${c.bold}${s}${c.reset}`, dim:(s)=>`${c.dim}${s}${c.reset}`,
|
|
20
|
+
green:(s)=>`${c.green}${s}${c.reset}`, red:(s)=>`${c.red}${s}${c.reset}`,
|
|
21
|
+
cyan:(s)=>`${c.cyan}${s}${c.reset}`, gray:(s)=>`${c.gray}${s}${c.reset}`,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
function readPid() {
|
|
27
|
+
try { return parseInt(fs.readFileSync(OVERLAY_PID, "utf8").trim(), 10); } catch { return null; }
|
|
28
|
+
}
|
|
29
|
+
function writePid(pid) {
|
|
30
|
+
fs.mkdirSync(path.dirname(OVERLAY_PID), { recursive: true });
|
|
31
|
+
fs.writeFileSync(OVERLAY_PID, String(pid));
|
|
32
|
+
}
|
|
33
|
+
function clearPid() { try { fs.unlinkSync(OVERLAY_PID); } catch {} }
|
|
34
|
+
function pidAlive(pid) {
|
|
35
|
+
if (!pid) return false;
|
|
36
|
+
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function findElectron() {
|
|
40
|
+
// 1. Local node_modules (pnpm/npm install electron)
|
|
41
|
+
const candidates = [
|
|
42
|
+
path.resolve(__dirname, "../../../node_modules/.bin/electron"),
|
|
43
|
+
path.resolve(__dirname, "../../../node_modules/electron/cli.js"),
|
|
44
|
+
];
|
|
45
|
+
for (const c of candidates) {
|
|
46
|
+
if (fs.existsSync(c)) return c;
|
|
47
|
+
}
|
|
48
|
+
// 2. Global electron
|
|
49
|
+
try {
|
|
50
|
+
const which = execFileSync("which", ["electron"], { stdio: ["ignore","pipe","ignore"] }).toString().trim();
|
|
51
|
+
if (which) return which;
|
|
52
|
+
} catch {}
|
|
53
|
+
// 3. npx electron
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
export async function cmdOverlayStart(args = {}) {
|
|
60
|
+
const debug = !!(args.debug || args.d);
|
|
61
|
+
|
|
62
|
+
const pid = readPid();
|
|
63
|
+
if (pidAlive(pid)) {
|
|
64
|
+
if (debug) {
|
|
65
|
+
console.log(`\n ${fmt.cyan("●")} Overlay already running ${fmt.dim("pid " + pid)} — stop it first with: apx overlay stop\n`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
console.log(`\n ${fmt.cyan("●")} Overlay already running ${fmt.dim("pid " + pid)}\n`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
clearPid();
|
|
72
|
+
|
|
73
|
+
if (!fs.existsSync(OVERLAY_MAIN)) {
|
|
74
|
+
console.error(`\n ${fmt.red("✗")} Overlay app not found at ${fmt.dim(OVERLAY_MAIN)}\n`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const electronBin = findElectron();
|
|
79
|
+
if (!electronBin) {
|
|
80
|
+
console.error(
|
|
81
|
+
`\n ${fmt.red("✗")} Electron not found.\n` +
|
|
82
|
+
` Install it with: ${fmt.cyan("pnpm add -D electron")}\n` +
|
|
83
|
+
` or globally: ${fmt.cyan("npm install -g electron")}\n`
|
|
84
|
+
);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Get daemon port from running daemon or env
|
|
89
|
+
let daemonPort = process.env.APX_PORT || "7430";
|
|
90
|
+
try {
|
|
91
|
+
const health = await http.get("/health").catch(() => null);
|
|
92
|
+
if (health?.port) daemonPort = String(health.port);
|
|
93
|
+
} catch {}
|
|
94
|
+
|
|
95
|
+
const isScript = electronBin.endsWith(".js");
|
|
96
|
+
const cmd = isScript ? process.execPath : electronBin;
|
|
97
|
+
const argv = isScript
|
|
98
|
+
? [electronBin, OVERLAY_MAIN, "--port", daemonPort]
|
|
99
|
+
: [OVERLAY_MAIN, "--port", daemonPort];
|
|
100
|
+
|
|
101
|
+
const logFile = path.join(os.homedir(), ".apx", "overlay.log");
|
|
102
|
+
|
|
103
|
+
if (debug) {
|
|
104
|
+
// ── Debug mode: start overlay normally, then tail -f the log ─────────
|
|
105
|
+
// Truncate log so we only see fresh output
|
|
106
|
+
try { fs.writeFileSync(logFile, `--- APX Overlay debug started ${new Date().toISOString()} ---\n`); } catch {}
|
|
107
|
+
|
|
108
|
+
const logFd = fs.openSync(logFile, "a");
|
|
109
|
+
const child = spawn(cmd, argv, {
|
|
110
|
+
detached: false,
|
|
111
|
+
stdio: ["ignore", logFd, logFd],
|
|
112
|
+
env: { ...process.env, ELECTRON_ENABLE_LOGGING: "1" },
|
|
113
|
+
});
|
|
114
|
+
fs.closeSync(logFd);
|
|
115
|
+
|
|
116
|
+
if (child.pid) writePid(child.pid);
|
|
117
|
+
|
|
118
|
+
// Small pause so Electron writes its first lines before we tail
|
|
119
|
+
await new Promise(r => setTimeout(r, 600));
|
|
120
|
+
|
|
121
|
+
console.log(
|
|
122
|
+
`\n ${fmt.cyan("◉")} ${fmt.bold("APX Overlay")} ${fmt.yellow("[DEBUG]")}` +
|
|
123
|
+
` pid ${child.pid} port ${daemonPort}` +
|
|
124
|
+
`\n ${fmt.dim("Tailing:")} ${logFile}` +
|
|
125
|
+
`\n ${fmt.dim("Press Ctrl+C to stop tailing (overlay keeps running).")}\n`
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Tail the log file live — read new bytes as they arrive
|
|
129
|
+
const logStream = fs.createReadStream(logFile, { encoding: "utf8", start: 0 });
|
|
130
|
+
logStream.pipe(process.stdout);
|
|
131
|
+
|
|
132
|
+
// After initial content, watch for new data
|
|
133
|
+
const watcher = fs.watch(logFile, () => {});
|
|
134
|
+
let pos = fs.statSync(logFile).size;
|
|
135
|
+
const interval = setInterval(() => {
|
|
136
|
+
const stat = fs.statSync(logFile);
|
|
137
|
+
if (stat.size > pos) {
|
|
138
|
+
const stream = fs.createReadStream(logFile, { start: pos, end: stat.size });
|
|
139
|
+
stream.on("data", (chunk) => {
|
|
140
|
+
const lines = chunk.toString();
|
|
141
|
+
// Filter Chromium noise
|
|
142
|
+
lines.split("\n").forEach(line => {
|
|
143
|
+
if (!/^\[[\d:]+\]/.test(line.trim())) process.stdout.write(line + (line ? "\n" : ""));
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
pos = stat.size;
|
|
147
|
+
}
|
|
148
|
+
}, 300);
|
|
149
|
+
|
|
150
|
+
child.on("exit", (code) => {
|
|
151
|
+
clearInterval(interval);
|
|
152
|
+
watcher.close();
|
|
153
|
+
console.log(`\n ${code === 0 ? fmt.green("✓") : fmt.red("✗")} Overlay exited (code ${code})\n`);
|
|
154
|
+
clearPid();
|
|
155
|
+
process.exit(code || 0);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
await new Promise((resolve) => {
|
|
159
|
+
process.on("SIGINT", () => {
|
|
160
|
+
clearInterval(interval);
|
|
161
|
+
watcher.close();
|
|
162
|
+
console.log(`\n ${fmt.dim("Stopped tailing. Overlay is still running.")}\n`);
|
|
163
|
+
resolve();
|
|
164
|
+
});
|
|
165
|
+
process.on("SIGTERM", resolve);
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Normal (detached) mode ────────────────────────────────────────────
|
|
171
|
+
const logFd = fs.openSync(logFile, "a");
|
|
172
|
+
const child = spawn(cmd, argv, {
|
|
173
|
+
detached: false,
|
|
174
|
+
stdio: ["ignore", logFd, logFd],
|
|
175
|
+
env: { ...process.env, ELECTRON_ENABLE_LOGGING: "1" },
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Give the process 1.5s to fail fast, then detach and let it run
|
|
179
|
+
await new Promise((res) => {
|
|
180
|
+
let exited = false;
|
|
181
|
+
child.on("exit", (code) => {
|
|
182
|
+
exited = true;
|
|
183
|
+
if (code !== 0) {
|
|
184
|
+
console.error(
|
|
185
|
+
`\n ${fmt.red("✗")} Overlay exited with code ${code}\n` +
|
|
186
|
+
` ${fmt.dim("Check log:")} ${logFile}\n` +
|
|
187
|
+
` ${fmt.dim("Or run with:")} ${fmt.cyan("apx overlay start --debug")}\n`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
res();
|
|
191
|
+
});
|
|
192
|
+
setTimeout(() => {
|
|
193
|
+
if (!exited) {
|
|
194
|
+
child.unref();
|
|
195
|
+
res();
|
|
196
|
+
}
|
|
197
|
+
}, 1500);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (!child.exitCode && child.pid) writePid(child.pid);
|
|
201
|
+
else return;
|
|
202
|
+
|
|
203
|
+
// Read configured shortcut (if any) for display
|
|
204
|
+
let shortcutHint = "⌘G (macOS) / Ctrl+G (Win/Linux)";
|
|
205
|
+
try {
|
|
206
|
+
const cfg = JSON.parse(fs.readFileSync(path.join(os.homedir(), ".apx", "config.json"), "utf8"));
|
|
207
|
+
if (cfg?.overlay?.shortcut) shortcutHint = cfg.overlay.shortcut;
|
|
208
|
+
} catch {}
|
|
209
|
+
|
|
210
|
+
console.log(
|
|
211
|
+
`\n ${fmt.green("●")} ${fmt.bold("APX Overlay")} started` +
|
|
212
|
+
` ${fmt.dim("pid " + child.pid)}` +
|
|
213
|
+
` ${fmt.dim("port " + daemonPort)}` +
|
|
214
|
+
`\n ${fmt.dim("Shortcut:")} ${fmt.cyan(shortcutHint)}` +
|
|
215
|
+
`\n ${fmt.dim("Debug:")} ${fmt.gray("apx overlay start --debug")}` +
|
|
216
|
+
`\n ${fmt.dim("Log:")} ${fmt.gray(logFile)}\n`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function cmdOverlayStop(_args = {}) {
|
|
221
|
+
const pid = readPid();
|
|
222
|
+
if (!pidAlive(pid)) {
|
|
223
|
+
console.log(`\n ${fmt.dim("Overlay is not running.")}\n`);
|
|
224
|
+
clearPid();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
process.kill(pid, "SIGTERM");
|
|
229
|
+
clearPid();
|
|
230
|
+
console.log(`\n ${fmt.green("✓")} Overlay stopped ${fmt.dim("(pid " + pid + ")")}\n`);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
console.error(`\n ${fmt.red("✗")} Could not stop overlay: ${e.message}\n`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export async function cmdOverlayStatus(_args = {}) {
|
|
237
|
+
const pid = readPid();
|
|
238
|
+
const alive = pidAlive(pid);
|
|
239
|
+
|
|
240
|
+
let daemonClients = 0;
|
|
241
|
+
try {
|
|
242
|
+
const s = await http.get("/overlay/status").catch(() => null);
|
|
243
|
+
daemonClients = s?.connected_clients ?? 0;
|
|
244
|
+
} catch {}
|
|
245
|
+
|
|
246
|
+
const icon = alive ? fmt.green("●") : fmt.dim("○");
|
|
247
|
+
const state = alive ? fmt.green("running") : fmt.dim("stopped");
|
|
248
|
+
console.log(
|
|
249
|
+
`\n ${icon} ${fmt.bold("APX Overlay")} ${state}` +
|
|
250
|
+
(pid ? ` ${fmt.dim("pid " + pid)}` : "") +
|
|
251
|
+
`\n ${fmt.dim("daemon connections:")} ${daemonClients}\n`
|
|
252
|
+
);
|
|
253
|
+
}
|
package/src/cli/commands/sys.js
CHANGED
|
@@ -10,8 +10,15 @@ import {
|
|
|
10
10
|
renderTerminalChat,
|
|
11
11
|
titlecase,
|
|
12
12
|
} from "../terminal-chat/renderer.js";
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { dirname, resolve } from "node:path";
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
13
17
|
|
|
14
|
-
const
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const TUI_SRC = resolve(__dirname, "../../tui/run.ts");
|
|
20
|
+
|
|
21
|
+
const MAIN_PALETTE_OPTIONS = ["Switch model", "Switch agent", "Connect provider", "Open editor", "Exit"];
|
|
15
22
|
|
|
16
23
|
// Message Actions overlay options for a queued message
|
|
17
24
|
const MSG_ACTION_SEND = "Send now (interrupt current)";
|
|
@@ -24,6 +31,20 @@ export async function cmdSys(args) {
|
|
|
24
31
|
const cfg = readConfig();
|
|
25
32
|
const id = readIdentity();
|
|
26
33
|
|
|
34
|
+
// Launch new Solid.js TUI via bun (runs TS source directly — no esbuild bundle needed)
|
|
35
|
+
if (existsSync(TUI_SRC)) {
|
|
36
|
+
const bunBin = process.env.BUN_PATH || "bun";
|
|
37
|
+
spawnSync(bunBin, [
|
|
38
|
+
"--preload", "@opentui/solid/preload",
|
|
39
|
+
TUI_SRC,
|
|
40
|
+
"--pid", pid,
|
|
41
|
+
"--agent", id?.agent_name || cfg.super_agent?.name || "super-agent",
|
|
42
|
+
"--model", cfg.super_agent?.model || "claude-3-5-sonnet",
|
|
43
|
+
], { stdio: "inherit", cwd: resolve(__dirname, "../../..") });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
27
48
|
const state = {
|
|
28
49
|
currentModeIdx: 0,
|
|
29
50
|
inputText: "",
|
|
@@ -162,7 +183,7 @@ export async function cmdSys(args) {
|
|
|
162
183
|
}
|
|
163
184
|
|
|
164
185
|
if (state.inCommandPalette) {
|
|
165
|
-
await handlePaletteKey(key, cfg, state, renderScreen, close);
|
|
186
|
+
await handlePaletteKey(key, pid, cfg, state, renderScreen, close);
|
|
166
187
|
return;
|
|
167
188
|
}
|
|
168
189
|
|
|
@@ -341,7 +362,7 @@ export function handleScrollKey(key, state, renderScreen) {
|
|
|
341
362
|
return false;
|
|
342
363
|
}
|
|
343
364
|
|
|
344
|
-
async function handlePaletteKey(key, cfg, state, renderScreen, close) {
|
|
365
|
+
async function handlePaletteKey(key, pid, cfg, state, renderScreen, close) {
|
|
345
366
|
if (key.name === "up") {
|
|
346
367
|
state.paletteSelection = Math.max(0, state.paletteSelection - 1);
|
|
347
368
|
renderScreen();
|
|
@@ -362,19 +383,28 @@ async function handlePaletteKey(key, cfg, state, renderScreen, close) {
|
|
|
362
383
|
const selected = state.paletteOptions[state.paletteSelection];
|
|
363
384
|
|
|
364
385
|
if (state.paletteState === "main") {
|
|
365
|
-
if (selected === "Exit") close();
|
|
386
|
+
if (selected === "Exit") { close(); return; }
|
|
366
387
|
|
|
367
388
|
if (selected === "Switch model") {
|
|
368
389
|
state.paletteState = "switch_model";
|
|
369
390
|
state.paletteOptions = ["Loading models..."];
|
|
370
391
|
state.paletteSelection = 0;
|
|
371
392
|
renderScreen();
|
|
372
|
-
loadModelOptions(cfg, state, renderScreen);
|
|
393
|
+
loadModelOptions(pid, cfg, state, renderScreen);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (selected === "Switch agent") {
|
|
398
|
+
state.paletteState = "switch_agent";
|
|
399
|
+
state.paletteOptions = ["Loading agents..."];
|
|
400
|
+
state.paletteSelection = 0;
|
|
401
|
+
renderScreen();
|
|
402
|
+
loadAgentOptions(pid, state, renderScreen);
|
|
373
403
|
return;
|
|
374
404
|
}
|
|
375
405
|
|
|
376
406
|
state.inCommandPalette = false;
|
|
377
|
-
state.transcript.push({ type: "status", text: `
|
|
407
|
+
state.transcript.push({ type: "status", text: `Command: ${selected} (not implemented yet)` });
|
|
378
408
|
renderScreen();
|
|
379
409
|
return;
|
|
380
410
|
}
|
|
@@ -393,7 +423,20 @@ async function handlePaletteKey(key, cfg, state, renderScreen, close) {
|
|
|
393
423
|
configModule.writeConfig(currentCfg);
|
|
394
424
|
|
|
395
425
|
state.inCommandPalette = false;
|
|
396
|
-
state.transcript.push({ type: "status", text: `Model
|
|
426
|
+
state.transcript.push({ type: "status", text: `Model → ${selected}` });
|
|
427
|
+
renderScreen();
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (
|
|
432
|
+
state.paletteState === "switch_agent" &&
|
|
433
|
+
!selected.startsWith("Loading") &&
|
|
434
|
+
!selected.startsWith("Failed") &&
|
|
435
|
+
!selected.startsWith("No ")
|
|
436
|
+
) {
|
|
437
|
+
state.activeAgent = selected;
|
|
438
|
+
state.inCommandPalette = false;
|
|
439
|
+
state.transcript.push({ type: "status", text: `Agent → ${selected}` });
|
|
397
440
|
renderScreen();
|
|
398
441
|
return;
|
|
399
442
|
}
|
|
@@ -401,23 +444,52 @@ async function handlePaletteKey(key, cfg, state, renderScreen, close) {
|
|
|
401
444
|
renderScreen();
|
|
402
445
|
}
|
|
403
446
|
|
|
404
|
-
function loadModelOptions(cfg, state, renderScreen) {
|
|
405
|
-
|
|
406
|
-
|
|
447
|
+
function loadModelOptions(pid, cfg, state, renderScreen) {
|
|
448
|
+
// Load engines from APX daemon first, then fall back to Ollama tags
|
|
449
|
+
const apxEnginesPromise = pid
|
|
450
|
+
? http.get("/engines").then((d) => d?.engines || []).catch(() => [])
|
|
451
|
+
: Promise.resolve([]);
|
|
452
|
+
|
|
453
|
+
const ollamaBaseUrl = cfg.engines?.ollama?.base_url || "http://127.0.0.1:11434";
|
|
454
|
+
const ollamaPromise = fetch(`${ollamaBaseUrl}/api/tags`)
|
|
407
455
|
.then((r) => r.json())
|
|
408
|
-
.then((
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
456
|
+
.then((d) => (d.models || []).map((m) => "ollama:" + m.name))
|
|
457
|
+
.catch(() => []);
|
|
458
|
+
|
|
459
|
+
Promise.all([apxEnginesPromise, ollamaPromise])
|
|
460
|
+
.then(([apxEngines, ollamaModels]) => {
|
|
461
|
+
const all = [
|
|
462
|
+
...apxEngines.filter((e) => typeof e === "string"),
|
|
463
|
+
...ollamaModels,
|
|
464
|
+
];
|
|
465
|
+
state.paletteOptions = all.length ? all : ["No models found"];
|
|
413
466
|
if (state.paletteState === "switch_model") renderScreen();
|
|
414
467
|
})
|
|
415
468
|
.catch(() => {
|
|
416
|
-
state.paletteOptions = ["Failed to load
|
|
469
|
+
state.paletteOptions = ["Failed to load models"];
|
|
417
470
|
if (state.paletteState === "switch_model") renderScreen();
|
|
418
471
|
});
|
|
419
472
|
}
|
|
420
473
|
|
|
474
|
+
function loadAgentOptions(pid, state, renderScreen) {
|
|
475
|
+
if (!pid) {
|
|
476
|
+
state.paletteOptions = ["No project selected"];
|
|
477
|
+
renderScreen();
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
http.get(`/projects/${pid}/agents`)
|
|
481
|
+
.then((agents) => {
|
|
482
|
+
state.paletteOptions = Array.isArray(agents) && agents.length
|
|
483
|
+
? agents.map((a) => a.slug || a.name || String(a))
|
|
484
|
+
: ["No agents found"];
|
|
485
|
+
if (state.paletteState === "switch_agent") renderScreen();
|
|
486
|
+
})
|
|
487
|
+
.catch(() => {
|
|
488
|
+
state.paletteOptions = ["Failed to load agents"];
|
|
489
|
+
if (state.paletteState === "switch_agent") renderScreen();
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
421
493
|
export function handleEditingKey(str, key, state, renderScreen) {
|
|
422
494
|
if (key.name === "tab") {
|
|
423
495
|
state.currentModeIdx = (state.currentModeIdx + 1) % MODES.length;
|
package/src/cli/index.js
CHANGED
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
cmdTelegramSetup,
|
|
55
55
|
} from "./commands/telegram.js";
|
|
56
56
|
import { cmdMessagesTail, cmdMessagesSearch, cmdMessagesChat } from "./commands/messages.js";
|
|
57
|
+
import { cmdLog } from "./commands/log.js";
|
|
57
58
|
import { cmdSearch } from "./commands/search.js";
|
|
58
59
|
import { cmdExec } from "./commands/exec.js";
|
|
59
60
|
import {
|
|
@@ -71,6 +72,7 @@ import {
|
|
|
71
72
|
cmdPermission,
|
|
72
73
|
} from "./commands/config.js";
|
|
73
74
|
import { cmdPluginsList, cmdPluginStatus } from "./commands/plugins.js";
|
|
75
|
+
import { cmdOverlayStart, cmdOverlayStop, cmdOverlayStatus } from "./commands/overlay.js";
|
|
74
76
|
import { cmdSkillsAdd, cmdSkillsList, cmdSkillsStatus } from "./commands/skills.js";
|
|
75
77
|
import { cmdIdentity } from "./commands/identity.js";
|
|
76
78
|
import { cmdCommandList, cmdCommandShow } from "./commands/command.js";
|
|
@@ -1136,7 +1138,8 @@ function buildHelp(version) {
|
|
|
1136
1138
|
hCmd("apx daemon start", 36, ""),
|
|
1137
1139
|
hCmd("apx daemon stop", 36, ""),
|
|
1138
1140
|
hCmd("apx daemon status", 36, ""),
|
|
1139
|
-
hCmd("apx daemon logs", 36, "--tail N"),
|
|
1141
|
+
hCmd("apx daemon logs", 36, "--tail N legacy daemon stdout log"),
|
|
1142
|
+
hCmd("apx log", 36, "unified log (all modules) -f follow --tail N --errors only"),
|
|
1140
1143
|
|
|
1141
1144
|
hSec("Telegram"),
|
|
1142
1145
|
hCmd("apx telegram send \"text\"", 36, "--chat <id>"),
|
|
@@ -1453,6 +1456,15 @@ async function dispatch(cmd, rest) {
|
|
|
1453
1456
|
break;
|
|
1454
1457
|
}
|
|
1455
1458
|
|
|
1459
|
+
case "log":
|
|
1460
|
+
case "logs": {
|
|
1461
|
+
// `apx log` is the unified daemon log (everything: telegram, whisper,
|
|
1462
|
+
// super-agent, tools, overlay). For just the legacy stdout sink,
|
|
1463
|
+
// use `apx daemon logs`. `apx log -f` follows; `--errors` filters.
|
|
1464
|
+
await cmdLog(parseArgs(rest));
|
|
1465
|
+
break;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1456
1468
|
case "exec":
|
|
1457
1469
|
await cmdExec(parseArgs(rest));
|
|
1458
1470
|
break;
|
|
@@ -1590,6 +1602,16 @@ async function dispatch(cmd, rest) {
|
|
|
1590
1602
|
await cmdUpdate(parseArgs(rest), VERSION);
|
|
1591
1603
|
return; // skip checkForUpdate after an update
|
|
1592
1604
|
|
|
1605
|
+
case "overlay": {
|
|
1606
|
+
const [sub, ...oRest] = rest;
|
|
1607
|
+
const oArgs = parseArgs(oRest);
|
|
1608
|
+
if (!sub || sub === "start") { await cmdOverlayStart(oArgs); return; }
|
|
1609
|
+
if (sub === "stop") { await cmdOverlayStop(oArgs); return; }
|
|
1610
|
+
if (sub === "status") { await cmdOverlayStatus(oArgs);return; }
|
|
1611
|
+
die(`unknown overlay sub-command: ${sub}\nUsage: apx overlay <start|stop|status>`);
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1593
1615
|
default:
|
|
1594
1616
|
die(`unknown command: ${cmd}\nRun \`apx --help\` for usage.`);
|
|
1595
1617
|
}
|