p-spore 0.1.4 → 0.1.43
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/LICENSE +21 -0
- package/README.md +242 -67
- package/dist/agents/agent-manager.d.ts +3 -3
- package/dist/agents/agent-manager.d.ts.map +1 -1
- package/dist/agents/agent-manager.js +3 -2
- package/dist/agents/agent-manager.js.map +1 -1
- package/dist/application/agent-application.d.ts +29 -5
- package/dist/application/agent-application.d.ts.map +1 -1
- package/dist/application/agent-application.js +444 -14
- package/dist/application/agent-application.js.map +1 -1
- package/dist/articles/diff.d.ts +70 -0
- package/dist/articles/diff.d.ts.map +1 -0
- package/dist/articles/diff.js +330 -0
- package/dist/articles/diff.js.map +1 -0
- package/dist/articles/docx.d.ts +10 -0
- package/dist/articles/docx.d.ts.map +1 -0
- package/dist/articles/docx.js +219 -0
- package/dist/articles/docx.js.map +1 -0
- package/dist/articles/draft.d.ts +18 -0
- package/dist/articles/draft.d.ts.map +1 -0
- package/dist/articles/draft.js +88 -0
- package/dist/articles/draft.js.map +1 -0
- package/dist/articles/edits.d.ts +24 -0
- package/dist/articles/edits.d.ts.map +1 -0
- package/dist/articles/edits.js +54 -0
- package/dist/articles/edits.js.map +1 -0
- package/dist/articles/html.d.ts +10 -0
- package/dist/articles/html.d.ts.map +1 -0
- package/dist/articles/html.js +122 -0
- package/dist/articles/html.js.map +1 -0
- package/dist/articles/plain.d.ts +8 -0
- package/dist/articles/plain.d.ts.map +1 -0
- package/dist/articles/plain.js +69 -0
- package/dist/articles/plain.js.map +1 -0
- package/dist/articles/preview.d.ts +43 -0
- package/dist/articles/preview.d.ts.map +1 -0
- package/dist/articles/preview.js +90 -0
- package/dist/articles/preview.js.map +1 -0
- package/dist/articles/publish.d.ts +10 -0
- package/dist/articles/publish.d.ts.map +1 -0
- package/dist/articles/publish.js +54 -0
- package/dist/articles/publish.js.map +1 -0
- package/dist/articles/receipt.d.ts +16 -0
- package/dist/articles/receipt.d.ts.map +1 -0
- package/dist/articles/receipt.js +27 -0
- package/dist/articles/receipt.js.map +1 -0
- package/dist/articles/store.d.ts +78 -0
- package/dist/articles/store.d.ts.map +1 -0
- package/dist/articles/store.js +308 -0
- package/dist/articles/store.js.map +1 -0
- package/dist/articles/tool.d.ts +14 -0
- package/dist/articles/tool.d.ts.map +1 -0
- package/dist/articles/tool.js +175 -0
- package/dist/articles/tool.js.map +1 -0
- package/dist/articles/writer-context.d.ts +5 -0
- package/dist/articles/writer-context.d.ts.map +1 -0
- package/dist/articles/writer-context.js +56 -0
- package/dist/articles/writer-context.js.map +1 -0
- package/dist/articles/writer.d.ts +18 -0
- package/dist/articles/writer.d.ts.map +1 -0
- package/dist/articles/writer.js +79 -0
- package/dist/articles/writer.js.map +1 -0
- package/dist/articles/zip-write.d.ts +11 -0
- package/dist/articles/zip-write.d.ts.map +1 -0
- package/dist/articles/zip-write.js +63 -0
- package/dist/articles/zip-write.js.map +1 -0
- package/dist/bundled-skills/README.md +17 -0
- package/dist/bundled-skills/aegis-search/SKILL.md +59 -0
- package/dist/bundled-skills/aegis-search/bin/.gitkeep +0 -0
- package/dist/bundled-skills/article-canvas/SKILL.md +28 -0
- package/dist/bundled-skills/binaries.json +23 -0
- package/dist/bundled-skills/doc-extract/SKILL.md +56 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +407 -54
- package/dist/cli/main.js.map +1 -1
- package/dist/config/config.d.ts +43 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +150 -6
- package/dist/config/config.js.map +1 -1
- package/dist/daemon/daemon.d.ts.map +1 -1
- package/dist/daemon/daemon.js +80 -11
- package/dist/daemon/daemon.js.map +1 -1
- package/dist/desktop/application.d.ts +4 -0
- package/dist/desktop/application.d.ts.map +1 -0
- package/dist/desktop/application.js +1688 -0
- package/dist/desktop/application.js.map +1 -0
- package/dist/desktop/article-export.d.ts +12 -0
- package/dist/desktop/article-export.d.ts.map +1 -0
- package/dist/desktop/article-export.js +87 -0
- package/dist/desktop/article-export.js.map +1 -0
- package/dist/desktop/assets/pspore.png +0 -0
- package/dist/desktop/assets/pspore.svg +24 -0
- package/dist/desktop/conversation-smoke.d.ts +5 -0
- package/dist/desktop/conversation-smoke.d.ts.map +1 -0
- package/dist/desktop/conversation-smoke.js +107 -0
- package/dist/desktop/conversation-smoke.js.map +1 -0
- package/dist/desktop/daemon-bridge.d.ts.map +1 -1
- package/dist/desktop/daemon-bridge.js +62 -17
- package/dist/desktop/daemon-bridge.js.map +1 -1
- package/dist/desktop/ipc-contract.d.ts +124 -0
- package/dist/desktop/ipc-contract.d.ts.map +1 -1
- package/dist/desktop/ipc-contract.js +191 -12
- package/dist/desktop/ipc-contract.js.map +1 -1
- package/dist/desktop/local-path.d.ts +15 -0
- package/dist/desktop/local-path.d.ts.map +1 -0
- package/dist/desktop/local-path.js +59 -0
- package/dist/desktop/local-path.js.map +1 -0
- package/dist/desktop/main.d.ts +10 -1
- package/dist/desktop/main.d.ts.map +1 -1
- package/dist/desktop/main.js +122 -530
- package/dist/desktop/main.js.map +1 -1
- package/dist/desktop/native-startup.d.ts +4 -0
- package/dist/desktop/native-startup.d.ts.map +1 -0
- package/dist/desktop/native-startup.js +57 -0
- package/dist/desktop/native-startup.js.map +1 -0
- package/dist/desktop/preload.cjs +28 -1
- package/dist/desktop/renderer/article-panel.d.ts +45 -0
- package/dist/desktop/renderer/article-panel.d.ts.map +1 -0
- package/dist/desktop/renderer/article-panel.js +1126 -0
- package/dist/desktop/renderer/article-panel.js.map +1 -0
- package/dist/desktop/renderer/article-split.d.ts +32 -0
- package/dist/desktop/renderer/article-split.d.ts.map +1 -0
- package/dist/desktop/renderer/article-split.js +48 -0
- package/dist/desktop/renderer/article-split.js.map +1 -0
- package/dist/desktop/renderer/composer-file-references.d.ts +23 -0
- package/dist/desktop/renderer/composer-file-references.d.ts.map +1 -0
- package/dist/desktop/renderer/composer-file-references.js +51 -0
- package/dist/desktop/renderer/composer-file-references.js.map +1 -0
- package/dist/desktop/renderer/conversation.d.ts +23 -0
- package/dist/desktop/renderer/conversation.d.ts.map +1 -0
- package/dist/desktop/renderer/conversation.js +66 -0
- package/dist/desktop/renderer/conversation.js.map +1 -0
- package/dist/desktop/renderer/dom-reconcile.d.ts +15 -0
- package/dist/desktop/renderer/dom-reconcile.d.ts.map +1 -0
- package/dist/desktop/renderer/dom-reconcile.js +33 -0
- package/dist/desktop/renderer/dom-reconcile.js.map +1 -0
- package/dist/desktop/renderer/index.html +211 -34
- package/dist/desktop/renderer/indexing-view.d.ts +22 -0
- package/dist/desktop/renderer/indexing-view.d.ts.map +1 -0
- package/dist/desktop/renderer/indexing-view.js +115 -0
- package/dist/desktop/renderer/indexing-view.js.map +1 -0
- package/dist/desktop/renderer/markdown-view.d.ts +23 -0
- package/dist/desktop/renderer/markdown-view.d.ts.map +1 -0
- package/dist/desktop/renderer/markdown-view.js +436 -0
- package/dist/desktop/renderer/markdown-view.js.map +1 -0
- package/dist/desktop/renderer/motion.d.ts +67 -0
- package/dist/desktop/renderer/motion.d.ts.map +1 -0
- package/dist/desktop/renderer/motion.js +296 -0
- package/dist/desktop/renderer/motion.js.map +1 -0
- package/dist/desktop/renderer/renderer.js +1799 -152
- package/dist/desktop/renderer/renderer.js.map +1 -1
- package/dist/desktop/renderer/session-find.d.ts +18 -0
- package/dist/desktop/renderer/session-find.d.ts.map +1 -0
- package/dist/desktop/renderer/session-find.js +29 -0
- package/dist/desktop/renderer/session-find.js.map +1 -0
- package/dist/desktop/renderer/session-search.d.ts +30 -0
- package/dist/desktop/renderer/session-search.d.ts.map +1 -0
- package/dist/desktop/renderer/session-search.js +296 -0
- package/dist/desktop/renderer/session-search.js.map +1 -0
- package/dist/desktop/renderer/store.d.ts +33 -0
- package/dist/desktop/renderer/store.d.ts.map +1 -1
- package/dist/desktop/renderer/store.js +92 -19
- package/dist/desktop/renderer/store.js.map +1 -1
- package/dist/desktop/renderer/styles.css +928 -268
- package/dist/desktop/renderer/vendor/gsap.min.js +11 -0
- package/dist/desktop/renderer-diagnostics.d.ts +5 -0
- package/dist/desktop/renderer-diagnostics.d.ts.map +1 -0
- package/dist/desktop/renderer-diagnostics.js +47 -0
- package/dist/desktop/renderer-diagnostics.js.map +1 -0
- package/dist/desktop/security.d.ts +26 -1
- package/dist/desktop/security.d.ts.map +1 -1
- package/dist/desktop/security.js +26 -6
- package/dist/desktop/security.js.map +1 -1
- package/dist/desktop/smoke-fixture.d.ts.map +1 -1
- package/dist/desktop/smoke-fixture.js +77 -3
- package/dist/desktop/smoke-fixture.js.map +1 -1
- package/dist/desktop/splash/index.html +25 -0
- package/dist/desktop/splash/splash.d.ts +2 -0
- package/dist/desktop/splash/splash.d.ts.map +1 -0
- package/dist/desktop/splash/splash.js +8 -0
- package/dist/desktop/splash/splash.js.map +1 -0
- package/dist/desktop/splash/styles.css +24 -0
- package/dist/desktop/system-open.d.ts +16 -0
- package/dist/desktop/system-open.d.ts.map +1 -0
- package/dist/desktop/system-open.js +90 -0
- package/dist/desktop/system-open.js.map +1 -0
- package/dist/desktop/validation.d.ts.map +1 -1
- package/dist/desktop/validation.js +99 -21
- package/dist/desktop/validation.js.map +1 -1
- package/dist/desktop/window-security.d.ts +4 -0
- package/dist/desktop/window-security.d.ts.map +1 -0
- package/dist/desktop/window-security.js +26 -0
- package/dist/desktop/window-security.js.map +1 -0
- package/dist/documents/errors.d.ts +12 -0
- package/dist/documents/errors.d.ts.map +1 -0
- package/dist/documents/errors.js +12 -0
- package/dist/documents/errors.js.map +1 -0
- package/dist/documents/extract.d.ts +37 -0
- package/dist/documents/extract.d.ts.map +1 -0
- package/dist/documents/extract.js +90 -0
- package/dist/documents/extract.js.map +1 -0
- package/dist/documents/legacy-office.d.ts +23 -0
- package/dist/documents/legacy-office.d.ts.map +1 -0
- package/dist/documents/legacy-office.js +439 -0
- package/dist/documents/legacy-office.js.map +1 -0
- package/dist/documents/ole.d.ts +20 -0
- package/dist/documents/ole.d.ts.map +1 -0
- package/dist/documents/ole.js +189 -0
- package/dist/documents/ole.js.map +1 -0
- package/dist/documents/ooxml.d.ts +23 -0
- package/dist/documents/ooxml.d.ts.map +1 -0
- package/dist/documents/ooxml.js +197 -0
- package/dist/documents/ooxml.js.map +1 -0
- package/dist/documents/pdf.d.ts +20 -0
- package/dist/documents/pdf.d.ts.map +1 -0
- package/dist/documents/pdf.js +95 -0
- package/dist/documents/pdf.js.map +1 -0
- package/dist/documents/plain-text.d.ts +22 -0
- package/dist/documents/plain-text.d.ts.map +1 -0
- package/dist/documents/plain-text.js +57 -0
- package/dist/documents/plain-text.js.map +1 -0
- package/dist/documents/zip.d.ts +15 -0
- package/dist/documents/zip.d.ts.map +1 -0
- package/dist/documents/zip.js +137 -0
- package/dist/documents/zip.js.map +1 -0
- package/dist/domain/indexing.d.ts +60 -0
- package/dist/domain/indexing.d.ts.map +1 -0
- package/dist/domain/indexing.js +13 -0
- package/dist/domain/indexing.js.map +1 -0
- package/dist/domain/runtime.d.ts +133 -5
- package/dist/domain/runtime.d.ts.map +1 -1
- package/dist/gateway/local-gateway.d.ts +8 -0
- package/dist/gateway/local-gateway.d.ts.map +1 -1
- package/dist/gateway/local-gateway.js +105 -21
- package/dist/gateway/local-gateway.js.map +1 -1
- package/dist/indexing/index-service.d.ts +56 -0
- package/dist/indexing/index-service.d.ts.map +1 -0
- package/dist/indexing/index-service.js +262 -0
- package/dist/indexing/index-service.js.map +1 -0
- package/dist/kernel/loop.d.ts +10 -0
- package/dist/kernel/loop.d.ts.map +1 -1
- package/dist/kernel/loop.js +6 -2
- package/dist/kernel/loop.js.map +1 -1
- package/dist/kernel/reducer.d.ts.map +1 -1
- package/dist/kernel/reducer.js +17 -1
- package/dist/kernel/reducer.js.map +1 -1
- package/dist/kernel/state.d.ts +17 -4
- package/dist/kernel/state.d.ts.map +1 -1
- package/dist/kernel/state.js +2 -0
- package/dist/kernel/state.js.map +1 -1
- package/dist/pi/context-budget.d.ts +8 -0
- package/dist/pi/context-budget.d.ts.map +1 -0
- package/dist/pi/context-budget.js +62 -0
- package/dist/pi/context-budget.js.map +1 -0
- package/dist/pi/pi-runtime-adapter.d.ts +38 -3
- package/dist/pi/pi-runtime-adapter.d.ts.map +1 -1
- package/dist/pi/pi-runtime-adapter.js +551 -56
- package/dist/pi/pi-runtime-adapter.js.map +1 -1
- package/dist/plugins/bundled-skills.d.ts +56 -0
- package/dist/plugins/bundled-skills.d.ts.map +1 -0
- package/dist/plugins/bundled-skills.js +243 -0
- package/dist/plugins/bundled-skills.js.map +1 -0
- package/dist/plugins/extension-manager.d.ts +23 -1
- package/dist/plugins/extension-manager.d.ts.map +1 -1
- package/dist/plugins/extension-manager.js +117 -0
- package/dist/plugins/extension-manager.js.map +1 -1
- package/dist/plugins/types.d.ts +29 -0
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/plugins/workspace-skills.d.ts +36 -0
- package/dist/plugins/workspace-skills.d.ts.map +1 -0
- package/dist/plugins/workspace-skills.js +309 -0
- package/dist/plugins/workspace-skills.js.map +1 -0
- package/dist/policy/policy.d.ts.map +1 -1
- package/dist/policy/policy.js +22 -3
- package/dist/policy/policy.js.map +1 -1
- package/dist/protocol/client.d.ts +1 -0
- package/dist/protocol/client.d.ts.map +1 -1
- package/dist/protocol/client.js +49 -24
- package/dist/protocol/client.js.map +1 -1
- package/dist/protocol/limits.d.ts +69 -0
- package/dist/protocol/limits.d.ts.map +1 -0
- package/dist/protocol/limits.js +166 -0
- package/dist/protocol/limits.js.map +1 -0
- package/dist/protocol/schema.d.ts +6 -0
- package/dist/protocol/schema.d.ts.map +1 -1
- package/dist/protocol/schema.js.map +1 -1
- package/dist/protocol/server-frame-codec.d.ts +18 -0
- package/dist/protocol/server-frame-codec.d.ts.map +1 -0
- package/dist/protocol/server-frame-codec.js +211 -0
- package/dist/protocol/server-frame-codec.js.map +1 -0
- package/dist/service/legacy-daemon.d.ts +6 -0
- package/dist/service/legacy-daemon.d.ts.map +1 -0
- package/dist/service/legacy-daemon.js +42 -0
- package/dist/service/legacy-daemon.js.map +1 -0
- package/dist/service/service-launch.d.ts +10 -0
- package/dist/service/service-launch.d.ts.map +1 -0
- package/dist/service/service-launch.js +56 -0
- package/dist/service/service-launch.js.map +1 -0
- package/dist/service/system-service.d.ts +57 -0
- package/dist/service/system-service.d.ts.map +1 -0
- package/dist/service/system-service.js +263 -0
- package/dist/service/system-service.js.map +1 -0
- package/dist/sessions/session-catalog.d.ts +45 -0
- package/dist/sessions/session-catalog.d.ts.map +1 -0
- package/dist/sessions/session-catalog.js +270 -0
- package/dist/sessions/session-catalog.js.map +1 -0
- package/dist/sessions/session-file.d.ts +12 -0
- package/dist/sessions/session-file.d.ts.map +1 -0
- package/dist/sessions/session-file.js +47 -0
- package/dist/sessions/session-file.js.map +1 -0
- package/dist/settings/compaction-settings.d.ts +10 -0
- package/dist/settings/compaction-settings.d.ts.map +1 -0
- package/dist/settings/compaction-settings.js +8 -0
- package/dist/settings/compaction-settings.js.map +1 -0
- package/dist/settings/indexing-settings-service.d.ts +16 -0
- package/dist/settings/indexing-settings-service.d.ts.map +1 -0
- package/dist/settings/indexing-settings-service.js +57 -0
- package/dist/settings/indexing-settings-service.js.map +1 -0
- package/dist/settings/indexing-settings.d.ts +30 -0
- package/dist/settings/indexing-settings.d.ts.map +1 -0
- package/dist/settings/indexing-settings.js +58 -0
- package/dist/settings/indexing-settings.js.map +1 -0
- package/dist/settings/llm-settings-service.d.ts +6 -0
- package/dist/settings/llm-settings-service.d.ts.map +1 -1
- package/dist/settings/llm-settings-service.js +137 -12
- package/dist/settings/llm-settings-service.js.map +1 -1
- package/dist/settings/llm-settings.d.ts +68 -2
- package/dist/settings/llm-settings.d.ts.map +1 -1
- package/dist/settings/llm-settings.js +119 -17
- package/dist/settings/llm-settings.js.map +1 -1
- package/dist/settings/model-api.d.ts +10 -0
- package/dist/settings/model-api.d.ts.map +1 -0
- package/dist/settings/model-api.js +10 -0
- package/dist/settings/model-api.js.map +1 -0
- package/dist/storage/home.d.ts +1 -0
- package/dist/storage/home.d.ts.map +1 -1
- package/dist/storage/home.js +37 -2
- package/dist/storage/home.js.map +1 -1
- package/dist/tools/core-tools.d.ts +2 -0
- package/dist/tools/core-tools.d.ts.map +1 -1
- package/dist/tools/core-tools.js +91 -1
- package/dist/tools/core-tools.js.map +1 -1
- package/dist/ui/shared/activity.d.ts +2 -0
- package/dist/ui/shared/activity.d.ts.map +1 -1
- package/dist/ui/shared/activity.js +18 -4
- package/dist/ui/shared/activity.js.map +1 -1
- package/dist/ui/shared/full-message.d.ts +22 -0
- package/dist/ui/shared/full-message.d.ts.map +1 -0
- package/dist/ui/shared/full-message.js +40 -0
- package/dist/ui/shared/full-message.js.map +1 -0
- package/dist/ui/shared/local-links.d.ts +7 -0
- package/dist/ui/shared/local-links.d.ts.map +1 -0
- package/dist/ui/shared/local-links.js +24 -0
- package/dist/ui/shared/local-links.js.map +1 -0
- package/dist/ui/shared/markdown-stream.d.ts +25 -0
- package/dist/ui/shared/markdown-stream.d.ts.map +1 -0
- package/dist/ui/shared/markdown-stream.js +35 -0
- package/dist/ui/shared/markdown-stream.js.map +1 -0
- package/dist/ui/shared/markdown.d.ts +70 -0
- package/dist/ui/shared/markdown.d.ts.map +1 -0
- package/dist/ui/shared/markdown.js +434 -0
- package/dist/ui/shared/markdown.js.map +1 -0
- package/dist/ui/shared/presentation.d.ts +10 -2
- package/dist/ui/shared/presentation.d.ts.map +1 -1
- package/dist/ui/shared/presentation.js +83 -22
- package/dist/ui/shared/presentation.js.map +1 -1
- package/dist/ui/shared/protocol-state.d.ts +19 -0
- package/dist/ui/shared/protocol-state.d.ts.map +1 -1
- package/dist/ui/shared/protocol-state.js +188 -26
- package/dist/ui/shared/protocol-state.js.map +1 -1
- package/dist/ui/shared/virtual-list.d.ts +34 -0
- package/dist/ui/shared/virtual-list.d.ts.map +1 -0
- package/dist/ui/shared/virtual-list.js +143 -0
- package/dist/ui/shared/virtual-list.js.map +1 -0
- package/dist/ui/tui/commands.d.ts +1 -1
- package/dist/ui/tui/commands.d.ts.map +1 -1
- package/dist/ui/tui/commands.js +8 -3
- package/dist/ui/tui/commands.js.map +1 -1
- package/dist/ui/tui/store.d.ts +22 -1
- package/dist/ui/tui/store.d.ts.map +1 -1
- package/dist/ui/tui/store.js +50 -3
- package/dist/ui/tui/store.js.map +1 -1
- package/dist/ui/tui/tui-app.d.ts.map +1 -1
- package/dist/ui/tui/tui-app.js +231 -31
- package/dist/ui/tui/tui-app.js.map +1 -1
- package/dist/ui/tui/view.d.ts +7 -0
- package/dist/ui/tui/view.d.ts.map +1 -1
- package/dist/ui/tui/view.js +161 -57
- package/dist/ui/tui/view.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +13 -2
package/dist/cli/main.js
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFile, spawn } from "node:child_process";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
|
-
import { access, readFile } from "node:fs/promises";
|
|
4
|
+
import { access, readFile, stat } from "node:fs/promises";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
|
-
import { basename } from "node:path";
|
|
6
|
+
import { basename, isAbsolute, normalize, resolve } from "node:path";
|
|
7
7
|
import { createInterface } from "node:readline/promises";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { promisify } from "node:util";
|
|
10
10
|
import { PsporeDaemon } from "../daemon/daemon.js";
|
|
11
|
-
import {
|
|
11
|
+
import { parseConfig } from "../config/config.js";
|
|
12
12
|
import { ProtocolClient } from "../protocol/client.js";
|
|
13
13
|
import { readResourceTrustSummary } from "../plugins/extension-manager.js";
|
|
14
14
|
import { resolvePsporeHome } from "../config/config.js";
|
|
15
15
|
import { ShellService } from "../shell/shell-service.js";
|
|
16
|
+
import { stopLegacyDaemon } from "../service/legacy-daemon.js";
|
|
17
|
+
import { serviceLaunchSpec } from "../service/service-launch.js";
|
|
18
|
+
import { SystemServiceManager } from "../service/system-service.js";
|
|
16
19
|
import { LlmSettingsService } from "../settings/llm-settings-service.js";
|
|
17
|
-
import {
|
|
20
|
+
import {} from "../config/config.js";
|
|
21
|
+
import { defaultModelFor, MODEL_APIS, PERMISSION_MODES, permissionModeSummary, PROVIDER_OPTIONS, THINKING_LEVELS, } from "../settings/llm-settings.js";
|
|
22
|
+
import { atomicWriteJson } from "../storage/atomic.js";
|
|
18
23
|
import { initializePsporeHome, loadConfig, psporePaths } from "../storage/home.js";
|
|
19
24
|
import { acceptsModelSetupOffer, PsporeTuiApp } from "../ui/tui/tui-app.js";
|
|
20
25
|
import { PI_VERSION, PSPORE_VERSION } from "../version.js";
|
|
@@ -55,6 +60,8 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
55
60
|
case "daemon":
|
|
56
61
|
requireNoArgs(args, "daemon");
|
|
57
62
|
return runDaemon(home);
|
|
63
|
+
case "service":
|
|
64
|
+
return runServiceCommand(home, args);
|
|
58
65
|
case "status":
|
|
59
66
|
requireNoArgs(args, "status");
|
|
60
67
|
return printStatus(home);
|
|
@@ -81,6 +88,11 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
81
88
|
case "agents":
|
|
82
89
|
requireNoArgs(args, "agents");
|
|
83
90
|
return printProtocolList(home, "agents.list");
|
|
91
|
+
case "sessions":
|
|
92
|
+
requireNoArgs(args, "sessions");
|
|
93
|
+
return printSessions(home);
|
|
94
|
+
case "session":
|
|
95
|
+
return runSessionCommand(home, args);
|
|
84
96
|
case "agent":
|
|
85
97
|
return runAgentCommand(home, args);
|
|
86
98
|
case "mcp":
|
|
@@ -90,7 +102,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
90
102
|
return printResourceList(home, "resources.list");
|
|
91
103
|
case "skills":
|
|
92
104
|
requireNoArgs(args, "skills");
|
|
93
|
-
return printResourceList(home, "skills.
|
|
105
|
+
return printResourceList(home, "skills.catalog");
|
|
94
106
|
case "plugins":
|
|
95
107
|
requireNoArgs(args, "plugins");
|
|
96
108
|
return printResourceList(home, "plugins.list");
|
|
@@ -109,16 +121,14 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
109
121
|
throw new CliError("cancel requires exactly one run ID");
|
|
110
122
|
return cancelRun(home, args[0]);
|
|
111
123
|
case "prompt":
|
|
112
|
-
|
|
113
|
-
throw new CliError("prompt requires text");
|
|
114
|
-
return runPrompt(home, args.join(" "));
|
|
124
|
+
return runPromptCommand(home, args);
|
|
115
125
|
default:
|
|
116
126
|
throw new CliError(`Unknown command: ${command}\n\n${helpText()}`);
|
|
117
127
|
}
|
|
118
128
|
}
|
|
119
129
|
async function runDaemon(home) {
|
|
120
130
|
const daemon = await PsporeDaemon.start({ home });
|
|
121
|
-
process.stdout.write(`pSpore
|
|
131
|
+
process.stdout.write(`pSpore service process ${daemon.kernel.snapshot().daemon.phase} (PID ${process.pid})\nSocket: ${daemon.paths.socket}\n`);
|
|
122
132
|
let stopping = false;
|
|
123
133
|
const stop = () => {
|
|
124
134
|
if (stopping)
|
|
@@ -126,6 +136,13 @@ async function runDaemon(home) {
|
|
|
126
136
|
stopping = true;
|
|
127
137
|
void daemon.stop().catch(() => undefined);
|
|
128
138
|
};
|
|
139
|
+
// The service holds every live session. Ending the process over a stray
|
|
140
|
+
// rejection loses them all and, once systemd's start limit is reached, leaves
|
|
141
|
+
// clients unable to reconnect at all; a journal entry is the better trade.
|
|
142
|
+
const reportRejection = (reason) => {
|
|
143
|
+
process.stderr.write(`pSpore daemon: unhandled rejection: ${describeUnknownError(reason)}\n`);
|
|
144
|
+
};
|
|
145
|
+
process.on("unhandledRejection", reportRejection);
|
|
129
146
|
process.once("SIGINT", stop);
|
|
130
147
|
process.once("SIGTERM", stop);
|
|
131
148
|
try {
|
|
@@ -133,10 +150,16 @@ async function runDaemon(home) {
|
|
|
133
150
|
return 0;
|
|
134
151
|
}
|
|
135
152
|
finally {
|
|
153
|
+
process.off("unhandledRejection", reportRejection);
|
|
136
154
|
process.off("SIGINT", stop);
|
|
137
155
|
process.off("SIGTERM", stop);
|
|
138
156
|
}
|
|
139
157
|
}
|
|
158
|
+
function describeUnknownError(reason) {
|
|
159
|
+
if (reason instanceof Error)
|
|
160
|
+
return reason.stack ?? reason.message;
|
|
161
|
+
return String(reason);
|
|
162
|
+
}
|
|
140
163
|
async function printStatus(home) {
|
|
141
164
|
const client = await connectFromEndpoint(home, "status");
|
|
142
165
|
try {
|
|
@@ -218,31 +241,89 @@ async function runTui(home) {
|
|
|
218
241
|
throw new CliError(`tui requires an interactive TTY; use \`${resolveInvokedName(process.argv[1])} status\` for a non-interactive check`);
|
|
219
242
|
}
|
|
220
243
|
const { paths } = await initializePsporeHome(home);
|
|
221
|
-
const
|
|
222
|
-
await
|
|
244
|
+
const service = await cliSystemService(home, paths.workspace);
|
|
245
|
+
const ensured = await service.manager.ensureRunning(service.spec);
|
|
246
|
+
if (ensured.lingerWarning)
|
|
247
|
+
process.stderr.write(`pSpore service warning: ${ensured.lingerWarning}\n`);
|
|
248
|
+
if (ensured.recoveredFromFailure) {
|
|
249
|
+
process.stderr.write("pSpore service: cleared a failed unit state before starting the daemon\n");
|
|
250
|
+
}
|
|
223
251
|
const settings = new LlmSettingsService(home, paths);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
252
|
+
await new PsporeTuiApp({
|
|
253
|
+
socketPath: paths.socket,
|
|
254
|
+
settings,
|
|
255
|
+
onSettingsSaved: async () => {
|
|
256
|
+
await service.manager.restart();
|
|
257
|
+
return { applied: true, restartRequired: false, message: "Settings saved; the system service is restarting" };
|
|
258
|
+
},
|
|
259
|
+
}).run();
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
async function runServiceCommand(home, args) {
|
|
263
|
+
const action = args[0] ?? "status";
|
|
264
|
+
const { paths } = await initializePsporeHome(home);
|
|
265
|
+
const service = await cliSystemService(home, paths.workspace);
|
|
266
|
+
if (action === "install" || action === "start") {
|
|
267
|
+
requireNoArgs(args.slice(1), `service ${action}`);
|
|
268
|
+
const status = await service.manager.ensureRunning(service.spec);
|
|
269
|
+
process.stdout.write(serviceStatusText(status));
|
|
270
|
+
if (status.lingerWarning)
|
|
271
|
+
process.stderr.write(`Warning: ${status.lingerWarning}\n`);
|
|
241
272
|
return 0;
|
|
242
273
|
}
|
|
243
|
-
|
|
244
|
-
|
|
274
|
+
if (action === "restart") {
|
|
275
|
+
requireNoArgs(args.slice(1), "service restart");
|
|
276
|
+
if (!(await service.manager.status()).installed)
|
|
277
|
+
await service.manager.ensureRunning(service.spec);
|
|
278
|
+
else
|
|
279
|
+
await service.manager.restart();
|
|
280
|
+
process.stdout.write(serviceStatusText(await service.manager.status()));
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
if (action === "stop") {
|
|
284
|
+
requireNoArgs(args.slice(1), "service stop");
|
|
285
|
+
await service.manager.stop();
|
|
286
|
+
process.stdout.write(serviceStatusText(await service.manager.status()));
|
|
287
|
+
return 0;
|
|
245
288
|
}
|
|
289
|
+
if (action === "uninstall") {
|
|
290
|
+
requireNoArgs(args.slice(1), "service uninstall");
|
|
291
|
+
await service.manager.uninstall();
|
|
292
|
+
process.stdout.write(`Removed ${service.manager.unitName}\n`);
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
if (action === "logs") {
|
|
296
|
+
if (args.length > 2)
|
|
297
|
+
throw new CliError("service logs accepts an optional line count");
|
|
298
|
+
const lines = args[1] === undefined ? 100 : Number(args[1]);
|
|
299
|
+
process.stdout.write(await service.manager.logs(lines));
|
|
300
|
+
return 0;
|
|
301
|
+
}
|
|
302
|
+
if (action === "status") {
|
|
303
|
+
requireNoArgs(args.slice(1), "service status");
|
|
304
|
+
process.stdout.write(serviceStatusText(await service.manager.status()));
|
|
305
|
+
return 0;
|
|
306
|
+
}
|
|
307
|
+
throw new CliError("service usage: service install|start|restart|stop|status|logs [LINES]|uninstall");
|
|
308
|
+
}
|
|
309
|
+
async function cliSystemService(home, workspace) {
|
|
310
|
+
const manager = new SystemServiceManager({ home, beforeStart: async () => stopLegacyDaemon(home) });
|
|
311
|
+
const spec = await serviceLaunchSpec(home, workspace, {
|
|
312
|
+
execPath: process.execPath,
|
|
313
|
+
...(process.argv[1] === undefined ? {} : { argvEntry: process.argv[1] }),
|
|
314
|
+
moduleUrl: import.meta.url,
|
|
315
|
+
});
|
|
316
|
+
return { manager, spec };
|
|
317
|
+
}
|
|
318
|
+
function serviceStatusText(status) {
|
|
319
|
+
return [
|
|
320
|
+
`Service: ${status.unitName}`,
|
|
321
|
+
`Unit: ${status.unitPath}`,
|
|
322
|
+
`Installed: ${status.installed ? "yes" : "no"}`,
|
|
323
|
+
`Enabled: ${status.enabled ? "yes" : "no"}`,
|
|
324
|
+
`Active: ${status.active ? "yes" : "no"}`,
|
|
325
|
+
`Boot without login: ${status.linger === undefined ? "unknown" : status.linger ? "yes" : "no"}`,
|
|
326
|
+
].join("\n") + "\n";
|
|
246
327
|
}
|
|
247
328
|
async function runDesktop(home) {
|
|
248
329
|
const entry = fileURLToPath(new URL("../desktop/main.js", import.meta.url));
|
|
@@ -258,7 +339,7 @@ async function runDesktop(home) {
|
|
|
258
339
|
electron = require("electron");
|
|
259
340
|
}
|
|
260
341
|
catch {
|
|
261
|
-
throw new CliError("Electron is not included in the npm CLI package; use the pSpore
|
|
342
|
+
throw new CliError("Electron is not included in the npm CLI package; use the pSpore desktop zip from GitHub Releases");
|
|
262
343
|
}
|
|
263
344
|
if (typeof electron !== "string")
|
|
264
345
|
throw new CliError("The local Electron executable could not be resolved");
|
|
@@ -314,35 +395,46 @@ async function runSetupWizard(home) {
|
|
|
314
395
|
const service = new LlmSettingsService(home, paths);
|
|
315
396
|
const current = await service.get();
|
|
316
397
|
process.stdout.write("\nModel setup — press Enter to accept each default, Ctrl+C to abort.\n\n");
|
|
317
|
-
const providerAnswer = await readLineInput(`1/
|
|
398
|
+
const providerAnswer = await readLineInput(`1/6 provider [${current.provider}] (deepseek · openai · custom-openai): `);
|
|
318
399
|
const provider = providerAnswer.trim() ? parseConfigProvider(providerAnswer.trim()) : current.provider;
|
|
319
400
|
const suggestedModel = provider === current.provider ? current.model : defaultModelFor(provider);
|
|
320
|
-
const modelAnswer = await readLineInput(`2/
|
|
401
|
+
const modelAnswer = await readLineInput(`2/6 model [${suggestedModel}]: `);
|
|
321
402
|
const model = modelAnswer.trim() || suggestedModel;
|
|
322
403
|
let baseUrl = "";
|
|
323
404
|
let api = "provider-default";
|
|
324
405
|
if (provider === "custom-openai") {
|
|
325
406
|
const suggestedUrl = current.provider === provider ? current.baseUrl : "";
|
|
326
|
-
const urlAnswer = await readLineInput(`3/
|
|
407
|
+
const urlAnswer = await readLineInput(`3/6 Base URL${suggestedUrl ? ` [${suggestedUrl}]` : " (required)"}: `);
|
|
327
408
|
baseUrl = urlAnswer.trim() || suggestedUrl;
|
|
328
409
|
if (!baseUrl)
|
|
329
410
|
throw new CliError("A Base URL is required for custom OpenAI");
|
|
330
|
-
const apiAnswer = await readLineInput("3/
|
|
411
|
+
const apiAnswer = await readLineInput("3/6 API [chat] (chat · responses): ");
|
|
331
412
|
api = parseConfigApi(apiAnswer.trim() || "chat");
|
|
332
413
|
}
|
|
333
|
-
const thinkingAnswer = await readLineInput(`4/
|
|
414
|
+
const thinkingAnswer = await readLineInput(`4/6 thinking [${current.thinking}] (${THINKING_LEVELS.join(" · ")}): `);
|
|
334
415
|
const thinking = parseConfigThinking(thinkingAnswer.trim() || current.thinking);
|
|
335
416
|
const keyDefault = current.hasApiKey ? "keep" : "set";
|
|
336
|
-
const keyAnswer = await readLineInput(`5/
|
|
417
|
+
const keyAnswer = await readLineInput(`5/6 API key [${keyDefault}] (set · keep · clear): `);
|
|
337
418
|
const keyAction = (keyAnswer.trim().toLowerCase() || keyDefault);
|
|
338
419
|
if (!["set", "keep", "clear"].includes(keyAction))
|
|
339
420
|
throw new CliError("Key action must be set, keep, or clear");
|
|
340
421
|
let apiKey;
|
|
341
422
|
if (keyAction === "set") {
|
|
342
|
-
apiKey = await readSecretInput();
|
|
423
|
+
apiKey = await readSecretInput("API key");
|
|
343
424
|
if (!apiKey)
|
|
344
425
|
throw new CliError("API key cannot be empty");
|
|
345
426
|
}
|
|
427
|
+
const cookieDefault = current.hasCookie ? "keep" : "skip";
|
|
428
|
+
const cookieAnswer = await readLineInput(`6/6 Cookie [${cookieDefault}] (set · keep · clear · skip): `);
|
|
429
|
+
const cookieAction = (cookieAnswer.trim().toLowerCase() || cookieDefault);
|
|
430
|
+
if (!["set", "keep", "clear", "skip"].includes(cookieAction))
|
|
431
|
+
throw new CliError("Cookie action must be set, keep, clear, or skip");
|
|
432
|
+
let cookie;
|
|
433
|
+
if (cookieAction === "set") {
|
|
434
|
+
cookie = await readSecretInput("Cookie");
|
|
435
|
+
if (!cookie)
|
|
436
|
+
throw new CliError("Cookie cannot be empty");
|
|
437
|
+
}
|
|
346
438
|
const saved = await service.save(settingsUpdate(current, {
|
|
347
439
|
provider,
|
|
348
440
|
model,
|
|
@@ -351,7 +443,9 @@ async function runSetupWizard(home) {
|
|
|
351
443
|
thinking,
|
|
352
444
|
customModel: provider === "custom-openai",
|
|
353
445
|
...(apiKey ? { apiKey } : {}),
|
|
446
|
+
...(cookie ? { cookie } : {}),
|
|
354
447
|
clearApiKey: keyAction === "clear",
|
|
448
|
+
clearCookie: cookieAction === "clear",
|
|
355
449
|
}));
|
|
356
450
|
process.stdout.write("\n");
|
|
357
451
|
printModelSettings(saved);
|
|
@@ -375,6 +469,10 @@ async function runConfigCommand(home, args) {
|
|
|
375
469
|
printModelSettings(await service.get());
|
|
376
470
|
return 0;
|
|
377
471
|
}
|
|
472
|
+
// Indexing is its own config section rather than an LLM setting, so it is
|
|
473
|
+
// written directly instead of going through the settings service.
|
|
474
|
+
if (action === "indexing")
|
|
475
|
+
return runIndexingConfig(home, paths, args.slice(1));
|
|
378
476
|
const current = await service.get();
|
|
379
477
|
let update;
|
|
380
478
|
if (action === "set") {
|
|
@@ -444,10 +542,33 @@ async function runConfigCommand(home, args) {
|
|
|
444
542
|
throw new CliError(`config thinking usage: config thinking ${THINKING_LEVELS.join("|")}`);
|
|
445
543
|
update = settingsUpdate(current, { thinking: parseConfigThinking(args[1]) });
|
|
446
544
|
}
|
|
545
|
+
else if (action === "permissions") {
|
|
546
|
+
if (args.length !== 2) {
|
|
547
|
+
throw new CliError(`config permissions usage: config permissions ${PERMISSION_MODES.join("|")}|bypass`);
|
|
548
|
+
}
|
|
549
|
+
update = settingsUpdate(current, { permissionMode: parsePermissionMode(args[1]) });
|
|
550
|
+
}
|
|
551
|
+
else if (action === "compact") {
|
|
552
|
+
if (args.length < 2 || args.length > 5) {
|
|
553
|
+
throw new CliError("config compact usage: config compact on|off [RESERVE_TOKENS] [KEEP_RECENT_TOKENS] [TRIGGER_TOKENS]");
|
|
554
|
+
}
|
|
555
|
+
update = settingsUpdate(current, {
|
|
556
|
+
autoCompactionEnabled: parseConfigOnOff(args[1]),
|
|
557
|
+
autoCompactionReserveTokens: args[2] === undefined
|
|
558
|
+
? current.autoCompactionReserveTokens
|
|
559
|
+
: parseConfigCompactionTokens(args[2], "reserve tokens"),
|
|
560
|
+
autoCompactionKeepRecentTokens: args[3] === undefined
|
|
561
|
+
? current.autoCompactionKeepRecentTokens
|
|
562
|
+
: parseConfigCompactionTokens(args[3], "keep recent tokens"),
|
|
563
|
+
autoCompactionTriggerTokens: args[4] === undefined
|
|
564
|
+
? current.autoCompactionTriggerTokens
|
|
565
|
+
: parseConfigCompactionTokens(args[4], "trigger tokens"),
|
|
566
|
+
});
|
|
567
|
+
}
|
|
447
568
|
else if (action === "key") {
|
|
448
569
|
if (args.length !== 1)
|
|
449
570
|
throw new CliError("API keys are never accepted as command arguments; use `config key` and enter it securely");
|
|
450
|
-
const apiKey = await readSecretInput();
|
|
571
|
+
const apiKey = await readSecretInput("API key");
|
|
451
572
|
if (!apiKey)
|
|
452
573
|
throw new CliError("API key cannot be empty");
|
|
453
574
|
update = settingsUpdate(current, { apiKey });
|
|
@@ -457,14 +578,131 @@ async function runConfigCommand(home, args) {
|
|
|
457
578
|
throw new CliError("config clear-key does not accept arguments");
|
|
458
579
|
update = settingsUpdate(current, { clearApiKey: true });
|
|
459
580
|
}
|
|
581
|
+
else if (action === "cookie") {
|
|
582
|
+
if (args.length !== 1)
|
|
583
|
+
throw new CliError("Cookies are never accepted as command arguments; use `config cookie` and enter it securely");
|
|
584
|
+
const cookie = await readSecretInput("Cookie");
|
|
585
|
+
if (!cookie)
|
|
586
|
+
throw new CliError("Cookie cannot be empty");
|
|
587
|
+
update = settingsUpdate(current, { cookie });
|
|
588
|
+
}
|
|
589
|
+
else if (action === "clear-cookie") {
|
|
590
|
+
if (args.length !== 1)
|
|
591
|
+
throw new CliError("config clear-cookie does not accept arguments");
|
|
592
|
+
update = settingsUpdate(current, { clearCookie: true });
|
|
593
|
+
}
|
|
594
|
+
else if (action === "header") {
|
|
595
|
+
const name = args[1];
|
|
596
|
+
if (!name || args.length !== 2)
|
|
597
|
+
throw new CliError("config header usage: config header NAME (the value is read securely)");
|
|
598
|
+
const value = await readSecretInput(`${name} value`);
|
|
599
|
+
if (!value)
|
|
600
|
+
throw new CliError("Header value cannot be empty");
|
|
601
|
+
const others = current.customHeaders.filter((header) => header.name.toLowerCase() !== name.toLowerCase());
|
|
602
|
+
update = settingsUpdate(current, {
|
|
603
|
+
customHeaders: [...others.map((header) => ({ name: header.name })), { name, value }],
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
else if (action === "clear-header") {
|
|
607
|
+
const name = args[1];
|
|
608
|
+
if (!name || args.length !== 2)
|
|
609
|
+
throw new CliError("config clear-header usage: config clear-header NAME");
|
|
610
|
+
if (!current.customHeaders.some((header) => header.name.toLowerCase() === name.toLowerCase())) {
|
|
611
|
+
throw new CliError(`No custom header named ${name}`);
|
|
612
|
+
}
|
|
613
|
+
update = settingsUpdate(current, {
|
|
614
|
+
customHeaders: current.customHeaders
|
|
615
|
+
.filter((header) => header.name.toLowerCase() !== name.toLowerCase())
|
|
616
|
+
.map((header) => ({ name: header.name })),
|
|
617
|
+
});
|
|
618
|
+
}
|
|
460
619
|
else {
|
|
461
|
-
throw new CliError("config usage: show | set | provider | model | endpoint | thinking | key | clear-key");
|
|
620
|
+
throw new CliError("config usage: show | set | provider | model | endpoint | thinking | permissions | compact | key | clear-key | cookie | clear-cookie | header | clear-header");
|
|
462
621
|
}
|
|
463
622
|
const saved = await service.save(update);
|
|
464
623
|
printModelSettings(saved);
|
|
465
624
|
process.stdout.write("Saved. Restart an existing daemon to apply the new model settings.\n");
|
|
466
625
|
return 0;
|
|
467
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Reads and edits `config.indexing`. Directories are resolved to absolute paths
|
|
629
|
+
* and checked to exist, because a typo here silently indexes nothing.
|
|
630
|
+
*/
|
|
631
|
+
async function runIndexingConfig(home, paths, args) {
|
|
632
|
+
const config = await loadConfig(home);
|
|
633
|
+
const indexing = config.indexing;
|
|
634
|
+
const action = args[0] ?? "show";
|
|
635
|
+
if (action === "show") {
|
|
636
|
+
requireNoArgs(args.slice(1), "config indexing show");
|
|
637
|
+
printIndexingSettings(indexing, paths.config);
|
|
638
|
+
return 0;
|
|
639
|
+
}
|
|
640
|
+
let next = { ...indexing, directories: [...indexing.directories], extensions: [...indexing.extensions] };
|
|
641
|
+
if (action === "on" || action === "off") {
|
|
642
|
+
requireNoArgs(args.slice(1), `config indexing ${action}`);
|
|
643
|
+
next.enabled = action === "on";
|
|
644
|
+
}
|
|
645
|
+
else if (action === "add" || action === "remove") {
|
|
646
|
+
if (args.length < 2)
|
|
647
|
+
throw new CliError(`config indexing ${action} usage: config indexing ${action} DIR [DIR...]`);
|
|
648
|
+
const targets = await Promise.all(args.slice(1).map(async (value) => resolveIndexDirectory(value, action === "add")));
|
|
649
|
+
next.directories = action === "add"
|
|
650
|
+
? [...new Set([...next.directories, ...targets])].sort()
|
|
651
|
+
: next.directories.filter((existing) => !targets.includes(existing));
|
|
652
|
+
}
|
|
653
|
+
else if (action === "interval") {
|
|
654
|
+
if (args.length !== 2)
|
|
655
|
+
throw new CliError("config indexing interval usage: config indexing interval MINUTES");
|
|
656
|
+
const minutes = Number(args[1]);
|
|
657
|
+
if (!Number.isSafeInteger(minutes) || minutes < 1 || minutes > 10_080) {
|
|
658
|
+
throw new CliError("Indexing interval must be a whole number of minutes between 1 and 10080");
|
|
659
|
+
}
|
|
660
|
+
next.intervalMinutes = minutes;
|
|
661
|
+
}
|
|
662
|
+
else if (action === "ext") {
|
|
663
|
+
if (args.length !== 2)
|
|
664
|
+
throw new CliError("config indexing ext usage: config indexing ext txt,md,pdf");
|
|
665
|
+
const extensions = (args[1] ?? "").split(",").map((value) => value.trim()).filter(Boolean);
|
|
666
|
+
if (extensions.length === 0)
|
|
667
|
+
throw new CliError("At least one extension is required");
|
|
668
|
+
next.extensions = extensions;
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
throw new CliError("config indexing usage: show | on | off | add DIR | remove DIR | interval MINUTES | ext a,b,c");
|
|
672
|
+
}
|
|
673
|
+
// Re-parsing validates the edit exactly as the daemon will read it.
|
|
674
|
+
const updated = parseConfig({ ...config, indexing: next }, home);
|
|
675
|
+
await atomicWriteJson(paths.config, updated, 0o600);
|
|
676
|
+
printIndexingSettings(updated.indexing, paths.config);
|
|
677
|
+
process.stdout.write("\nSaved. Restart the service to apply: `pspore service restart`.\n");
|
|
678
|
+
return 0;
|
|
679
|
+
}
|
|
680
|
+
async function resolveIndexDirectory(value, mustExist) {
|
|
681
|
+
const resolved = isAbsolute(value) ? normalize(value) : resolve(process.cwd(), value);
|
|
682
|
+
if (!mustExist)
|
|
683
|
+
return resolved;
|
|
684
|
+
try {
|
|
685
|
+
if (!(await stat(resolved)).isDirectory())
|
|
686
|
+
throw new CliError(`Not a directory: ${resolved}`);
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
if (error instanceof CliError)
|
|
690
|
+
throw error;
|
|
691
|
+
throw new CliError(`Directory does not exist: ${resolved}`);
|
|
692
|
+
}
|
|
693
|
+
return resolved;
|
|
694
|
+
}
|
|
695
|
+
function printIndexingSettings(indexing, configPath) {
|
|
696
|
+
process.stdout.write([
|
|
697
|
+
`Indexing: ${indexing.enabled ? "on" : "off"}`,
|
|
698
|
+
`Interval: every ${indexing.intervalMinutes} minutes`,
|
|
699
|
+
`Extensions: ${indexing.extensions.join(", ")}`,
|
|
700
|
+
indexing.directories.length > 0
|
|
701
|
+
? `Directories:\n ${indexing.directories.join("\n ")}`
|
|
702
|
+
: "Directories: none configured (nothing will be indexed)",
|
|
703
|
+
`Config file: ${configPath}`,
|
|
704
|
+
].join("\n") + "\n");
|
|
705
|
+
}
|
|
468
706
|
function printModelSettings(settings) {
|
|
469
707
|
process.stdout.write([
|
|
470
708
|
`Provider: ${settings.provider}`,
|
|
@@ -472,7 +710,18 @@ function printModelSettings(settings) {
|
|
|
472
710
|
`Base URL: ${settings.baseUrl || "provider default"}`,
|
|
473
711
|
`API: ${settings.api}`,
|
|
474
712
|
`Thinking: ${settings.thinking}`,
|
|
475
|
-
`
|
|
713
|
+
`Permissions: ${settings.permissionMode} — ${permissionModeSummary(settings.permissionMode)}`,
|
|
714
|
+
`Auto compact: ${settings.autoCompactionEnabled ? "on" : "off"}`,
|
|
715
|
+
`Compact trigger tokens: ${settings.autoCompactionTriggerTokens}`,
|
|
716
|
+
`Compact reserve tokens: ${settings.autoCompactionReserveTokens}`,
|
|
717
|
+
`Compact keep recent tokens: ${settings.autoCompactionKeepRecentTokens}`,
|
|
718
|
+
`API key: ${settings.hasApiKey ? "configured (hidden)" : "not configured"} — ${settings.apiKeyPath}`,
|
|
719
|
+
`Cookie: ${settings.hasCookie ? "configured (hidden)" : "not configured"} — ${settings.cookiePath}`,
|
|
720
|
+
settings.customHeaders.length === 0
|
|
721
|
+
? "Custom headers: none"
|
|
722
|
+
: `Custom headers: ${settings.customHeaders.map((header) => `${header.name} (${header.hasValue ? "configured" : "missing value"})`).join(", ")}`,
|
|
723
|
+
`Config file: ${settings.configPath}`,
|
|
724
|
+
`Derived model catalog: ${settings.modelsPath}`,
|
|
476
725
|
].join("\n") + "\n");
|
|
477
726
|
}
|
|
478
727
|
/** Visible single-line prompt; secrets must go through readSecretInput instead. */
|
|
@@ -485,17 +734,17 @@ async function readLineInput(prompt) {
|
|
|
485
734
|
rl.close();
|
|
486
735
|
}
|
|
487
736
|
}
|
|
488
|
-
async function readSecretInput() {
|
|
737
|
+
async function readSecretInput(label = "API key") {
|
|
489
738
|
if (!process.stdin.isTTY) {
|
|
490
739
|
let value = "";
|
|
491
740
|
for await (const chunk of process.stdin) {
|
|
492
741
|
value += String(chunk);
|
|
493
742
|
if (value.length > 16_386)
|
|
494
|
-
throw new CliError(
|
|
743
|
+
throw new CliError(`${label} input exceeds 16384 characters`);
|
|
495
744
|
}
|
|
496
745
|
return value.replace(/\r?\n$/, "");
|
|
497
746
|
}
|
|
498
|
-
process.stdout.write(
|
|
747
|
+
process.stdout.write(`${label} (hidden): `);
|
|
499
748
|
process.stdin.setRawMode(true);
|
|
500
749
|
process.stdin.resume();
|
|
501
750
|
return new Promise((resolve, reject) => {
|
|
@@ -513,7 +762,7 @@ async function readSecretInput() {
|
|
|
513
762
|
const onData = (chunk) => {
|
|
514
763
|
const data = String(chunk);
|
|
515
764
|
if (data.includes("\u0003"))
|
|
516
|
-
return finish(new CliError(
|
|
765
|
+
return finish(new CliError(`${label} input cancelled`));
|
|
517
766
|
if (data.includes("\r") || data.includes("\n"))
|
|
518
767
|
return finish();
|
|
519
768
|
if (data === "\u007f" || data === "\b") {
|
|
@@ -525,7 +774,7 @@ async function readSecretInput() {
|
|
|
525
774
|
: data;
|
|
526
775
|
const printable = [...pasted].filter((character) => character >= " " && character <= "~").join("");
|
|
527
776
|
if (value.length + printable.length > 16_384)
|
|
528
|
-
return finish(new CliError(
|
|
777
|
+
return finish(new CliError(`${label} input exceeds 16384 characters`));
|
|
529
778
|
value += printable;
|
|
530
779
|
};
|
|
531
780
|
process.stdin.on("data", onData);
|
|
@@ -545,6 +794,29 @@ function parseConfigThinking(value) {
|
|
|
545
794
|
}
|
|
546
795
|
return normalized;
|
|
547
796
|
}
|
|
797
|
+
function parsePermissionMode(value) {
|
|
798
|
+
const normalized = value?.trim().toLowerCase();
|
|
799
|
+
// `bypass` is the name users reach for; it maps onto the existing allow_all policy.
|
|
800
|
+
const mode = normalized === "bypass" ? "allow_all" : normalized;
|
|
801
|
+
if (!mode || !PERMISSION_MODES.includes(mode)) {
|
|
802
|
+
throw new CliError(`Permission mode must be one of: ${PERMISSION_MODES.join(", ")} (or bypass)`);
|
|
803
|
+
}
|
|
804
|
+
return mode;
|
|
805
|
+
}
|
|
806
|
+
function parseConfigOnOff(value) {
|
|
807
|
+
if (value === "on")
|
|
808
|
+
return true;
|
|
809
|
+
if (value === "off")
|
|
810
|
+
return false;
|
|
811
|
+
throw new CliError("Auto compact must be on or off");
|
|
812
|
+
}
|
|
813
|
+
function parseConfigCompactionTokens(value, label) {
|
|
814
|
+
const parsed = Number(value);
|
|
815
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0 || parsed > 1_000_000) {
|
|
816
|
+
throw new CliError(`${label} must be an integer from 1 to 1000000`);
|
|
817
|
+
}
|
|
818
|
+
return parsed;
|
|
819
|
+
}
|
|
548
820
|
function parseConfigApi(value) {
|
|
549
821
|
const normalized = value.trim().toLowerCase();
|
|
550
822
|
const api = normalized === "chat" ? "openai-completions" : normalized === "responses" ? "openai-responses" : normalized;
|
|
@@ -561,11 +833,34 @@ function settingsUpdate(settings, changes) {
|
|
|
561
833
|
api: changes.api ?? settings.api,
|
|
562
834
|
thinking: changes.thinking ?? settings.thinking,
|
|
563
835
|
customModel: changes.customModel ?? settings.customModel,
|
|
836
|
+
permissionMode: changes.permissionMode ?? settings.permissionMode,
|
|
837
|
+
autoCompactionEnabled: changes.autoCompactionEnabled ?? settings.autoCompactionEnabled,
|
|
838
|
+
autoCompactionTriggerTokens: changes.autoCompactionTriggerTokens ?? settings.autoCompactionTriggerTokens,
|
|
839
|
+
autoCompactionReserveTokens: changes.autoCompactionReserveTokens ?? settings.autoCompactionReserveTokens,
|
|
840
|
+
autoCompactionKeepRecentTokens: changes.autoCompactionKeepRecentTokens ?? settings.autoCompactionKeepRecentTokens,
|
|
564
841
|
...(changes.apiKey ? { apiKey: changes.apiKey } : {}),
|
|
842
|
+
...(changes.cookie ? { cookie: changes.cookie } : {}),
|
|
843
|
+
...(changes.customHeaders ? { customHeaders: changes.customHeaders } : {}),
|
|
565
844
|
clearApiKey: changes.clearApiKey ?? false,
|
|
845
|
+
clearCookie: changes.clearCookie ?? false,
|
|
566
846
|
};
|
|
567
847
|
}
|
|
568
|
-
async function
|
|
848
|
+
async function runPromptCommand(home, args) {
|
|
849
|
+
let sessionId = "main";
|
|
850
|
+
let textArgs = args;
|
|
851
|
+
if (args[0] === "--session") {
|
|
852
|
+
const selected = args[1];
|
|
853
|
+
if (!selected)
|
|
854
|
+
throw new CliError("prompt --session requires a session ID");
|
|
855
|
+
sessionId = selected;
|
|
856
|
+
textArgs = args.slice(2);
|
|
857
|
+
}
|
|
858
|
+
const text = textArgs.join(" ").trim();
|
|
859
|
+
if (!text)
|
|
860
|
+
throw new CliError("prompt requires text");
|
|
861
|
+
return runPrompt(home, text, sessionId);
|
|
862
|
+
}
|
|
863
|
+
async function runPrompt(home, text, sessionId) {
|
|
569
864
|
const client = await connectFromEndpoint(home, "prompt");
|
|
570
865
|
let resolveSettled;
|
|
571
866
|
let printedDelta = false;
|
|
@@ -573,19 +868,22 @@ async function runPrompt(home, text) {
|
|
|
573
868
|
resolveSettled = resolve;
|
|
574
869
|
});
|
|
575
870
|
const unsubscribe = client.subscribe((event) => {
|
|
576
|
-
|
|
871
|
+
const payload = event.payload;
|
|
872
|
+
if (payload["sessionId"] !== sessionId)
|
|
873
|
+
return;
|
|
874
|
+
if (event.type === "transcript.delta" && payload["kind"] !== "thinking" && typeof payload["delta"] === "string") {
|
|
577
875
|
printedDelta = true;
|
|
578
|
-
process.stdout.write(
|
|
876
|
+
process.stdout.write(payload["delta"]);
|
|
579
877
|
}
|
|
580
878
|
if (event.type === "run.settled")
|
|
581
879
|
resolveSettled();
|
|
582
880
|
});
|
|
583
|
-
const abort = () => void client.command("session.abort", { reason: "CLI interrupted" }, { sessionId
|
|
881
|
+
const abort = () => void client.command("session.abort", { reason: "CLI interrupted" }, { sessionId }).catch(() => undefined);
|
|
584
882
|
process.once("SIGINT", abort);
|
|
585
883
|
try {
|
|
586
|
-
await client.command("session.prompt", { text, mode: "
|
|
884
|
+
await client.command("session.prompt", { text, mode: "auto" }, { sessionId });
|
|
587
885
|
await settled;
|
|
588
|
-
const snapshot = await client.command("session.snapshot", {}, { sessionId
|
|
886
|
+
const snapshot = await client.command("session.snapshot", {}, { sessionId });
|
|
589
887
|
if (!snapshot)
|
|
590
888
|
throw new CliError("Session disappeared before completion");
|
|
591
889
|
const finalText = snapshot.messages.at(-1)?.text;
|
|
@@ -601,6 +899,61 @@ async function runPrompt(home, text) {
|
|
|
601
899
|
await client.close();
|
|
602
900
|
}
|
|
603
901
|
}
|
|
902
|
+
async function printSessions(home) {
|
|
903
|
+
const client = await connectFromEndpoint(home, "sessions");
|
|
904
|
+
try {
|
|
905
|
+
const status = await client.command("status.get", {});
|
|
906
|
+
const roots = Object.values(status.sessions)
|
|
907
|
+
.filter((session) => session.rootSession)
|
|
908
|
+
.map((session) => ({
|
|
909
|
+
id: session.sessionId,
|
|
910
|
+
title: session.rootSession?.title,
|
|
911
|
+
status: session.status,
|
|
912
|
+
messages: session.messageCount,
|
|
913
|
+
updatedAt: session.rootSession?.updatedAt,
|
|
914
|
+
}));
|
|
915
|
+
process.stdout.write(`${JSON.stringify(roots, null, 2)}\n`);
|
|
916
|
+
return 0;
|
|
917
|
+
}
|
|
918
|
+
finally {
|
|
919
|
+
await client.close();
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const SESSION_USAGE = "session usage: session new [TITLE] | session delete ID";
|
|
923
|
+
async function runSessionCommand(home, args) {
|
|
924
|
+
if (args[0] === "new")
|
|
925
|
+
return createSession(home, args.slice(1).join(" ").trim());
|
|
926
|
+
if (args[0] === "delete") {
|
|
927
|
+
const sessionId = args[1];
|
|
928
|
+
if (!sessionId || args.length !== 2)
|
|
929
|
+
throw new CliError("session usage: session delete ID");
|
|
930
|
+
return deleteSession(home, sessionId);
|
|
931
|
+
}
|
|
932
|
+
throw new CliError(SESSION_USAGE);
|
|
933
|
+
}
|
|
934
|
+
async function createSession(home, title) {
|
|
935
|
+
const client = await connectFromEndpoint(home, "session-new");
|
|
936
|
+
try {
|
|
937
|
+
const created = await client.command("session.create", title ? { title } : {});
|
|
938
|
+
process.stdout.write(`${created.sessionId}\t${created.rootSession?.title ?? "New session"}\n`);
|
|
939
|
+
return 0;
|
|
940
|
+
}
|
|
941
|
+
finally {
|
|
942
|
+
await client.close();
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
/** Deletes a root session together with the transcript file it owns. */
|
|
946
|
+
async function deleteSession(home, sessionId) {
|
|
947
|
+
const client = await connectFromEndpoint(home, "session-delete");
|
|
948
|
+
try {
|
|
949
|
+
const deleted = await client.command("session.delete", {}, { sessionId });
|
|
950
|
+
process.stdout.write(`${deleted.sessionId}\t${deleted.title ?? ""}\t${deleted.transcriptRemoved ? "transcript removed" : "transcript kept"}\n`);
|
|
951
|
+
return 0;
|
|
952
|
+
}
|
|
953
|
+
finally {
|
|
954
|
+
await client.close();
|
|
955
|
+
}
|
|
956
|
+
}
|
|
604
957
|
async function printProtocolList(home, command) {
|
|
605
958
|
const client = await connectFromEndpoint(home, command.split(".")[0] ?? "list");
|
|
606
959
|
try {
|
|
@@ -812,7 +1165,7 @@ async function endpointFor(home) {
|
|
|
812
1165
|
value = JSON.parse(await readFile(path, "utf8"));
|
|
813
1166
|
}
|
|
814
1167
|
catch (error) {
|
|
815
|
-
throw new CliError(`No
|
|
1168
|
+
throw new CliError(`No service endpoint at ${path}; start it with \`${resolveInvokedName(process.argv[1])} service start\``);
|
|
816
1169
|
}
|
|
817
1170
|
if (!isRecord(value) || value["version"] !== 1 || typeof value["socket"] !== "string" || typeof value["state"] !== "string") {
|
|
818
1171
|
throw new CliError(`Invalid daemon endpoint file: ${path}`);
|
|
@@ -846,7 +1199,7 @@ function requireNoArgs(args, command) {
|
|
|
846
1199
|
throw new CliError(`${command} does not accept positional arguments`);
|
|
847
1200
|
}
|
|
848
1201
|
function helpText() {
|
|
849
|
-
return `pSpore ${PSPORE_VERSION} — Linux local agent core\n\nUsage: ${resolveInvokedName(process.argv[1])} [--home PATH] <command>\n\nCommands:\n setup Guided model setup (provider, model, thinking, API key)\n init Initialize private config/workspace/state directories\n daemon Run the foreground
|
|
1202
|
+
return `pSpore ${PSPORE_VERSION} — Linux local agent core\n\nUsage: ${resolveInvokedName(process.argv[1])} [--home PATH] <command>\n\nCommands:\n setup Guided model setup (provider, model, thinking, API key, cookie)\n init Initialize private config/workspace/state directories\n service install Install, enable, and start the systemd user service\n service status Show system service, autostart, and linger state\n service logs Show recent system service logs\n service restart Restart the full background service\n service stop Stop it; service uninstall also removes the unit\n daemon Run the service process in the foreground (diagnostics/systemd)\n tui Open the reconnecting terminal shell; service stays running after exit\n desktop Open the secure Electron 43 shell\n config wizard Same guided setup as the setup command\n config show Show model settings without exposing the API key or cookie\n config set ... Configure DeepSeek, OpenAI, or a custom OpenAI endpoint\n config permissions MODE\n Set the approval policy: ask_risky | ask_all | allow_all (bypass) | deny\n config compact on|off [RESERVE] [KEEP] [TRIGGER]\n Configure automatic context compaction thresholds\n config indexing ...\n show | on | off | add DIR | remove DIR | interval MINUTES | ext a,b,c\n config key Read an API key from hidden TTY input or stdin\n config clear-key Remove the current provider's managed API key\n config cookie Read an LLM proxy Cookie header from hidden TTY input or stdin\n config clear-cookie Remove the current provider's managed Cookie header\n config header NAME\n Add or replace one custom request header; the value is read securely\n config clear-header NAME\n Remove one custom request header and its stored value\n sessions List persistent root sessions\n session new [TITLE]\n Create a persistent root session\n session delete ID\n Delete a root session and its stored transcript\n prompt [--session ID] TEXT\n Send normally; if busy, continue queuing and wait\n jobs List loaded cron jobs\n runs List durable automation runs\n notifications List durable local notifications\n agents List child agents and their current status\n agent spawn TEXT Spawn a bounded shared-read child agent\n agent send ID TEXT\n Send a queued message to a child agent\n agent join ID... Wait for one or more child agents\n agent cancel ID Cancel a child agent and its descendants\n mcp list List configured MCP health without connecting\n mcp tools List schemas activated in this service process\n mcp discover ID Lazily connect and activate one trusted server\n mcp ping ID Check one trusted MCP server connection\n resources List trusted skills and host plugins\n skills List installed, bundled, and workspace skills, and why any is not loaded\n plugins List trusted host plugins and activation state\n resource inspect PATH\n Inspect one directory under workspace/staging without executing it\n resource install PATH SHA256\n Trust and install only the exact inspected content digest\n resource disable KIND NAME SHA256\n Revoke trust without deleting installed content\n schedule put FILE\n Atomically create/update a cron job from JSON\n run JOB_ID Trigger one job now and wait for completion\n cancel RUN_ID Cancel a queued or active run\n status Show service and session status\n doctor Check Node, config, model, shell, ripgrep, resource trust, MCP, and service\n version Print pinned runtime versions\n help Show this help\n`;
|
|
850
1203
|
}
|
|
851
1204
|
class CliError extends Error {
|
|
852
1205
|
constructor(message) {
|