@pi-harness/pi-harness 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/apps/web/dist/assets/index-BcLXLKqO.js +24 -0
- package/apps/web/dist/assets/index-xF647pQJ.css +2 -0
- package/apps/web/dist/index.html +2 -2
- package/apps/web/package.json +6 -6
- package/apps/web/profile/cordis.yml +1 -0
- package/apps/web/server-dist/bin.js +45 -7
- package/apps/web/server-dist/bin.js.map +1 -1
- package/apps/web/src/server/bin.ts +45 -7
- package/apps/web/test/bin.test.ts +313 -0
- package/node_modules/@pi-harness/api-gateway/dist/index.d.ts.map +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/index.js +295 -86
- package/node_modules/@pi-harness/api-gateway/dist/index.js.map +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/agent-teams.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/anchored-standard.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/annotation.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/archify.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/at-file.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/auto-mode.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/better-sidebar.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/browser-fetch.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/browser-session.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/canvas-draw.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/change-verifier.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cleaner.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cli-notifier.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/code2skill.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/colleague-skill.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/context-doctor.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/context.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cost-meter.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/dependency-checker.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/docker-sandbox.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/fail-logger.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/genui.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/git-time-capsule.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/graph-memory.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/history-compressor.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/hol-guard.json +3 -3
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/i18n-pair.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/image-compressor.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/llm-verifier.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mcp-client.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mcp-panel.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/memory.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mirage-bridge.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mock-server.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/modlens.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/module-search.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/obsidian-sync.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/openpets.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plan-execute.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-check.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-dev.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-finder.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-radar.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-stars.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/prompt-guard.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/prompt-library.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/readme-gen.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/recall-unread.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/reverse-skill.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/reviewer-bot.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/runtime-doctor.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/secure-audit.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-bookmarks.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-bridge.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-compare.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-export.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-insights.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-search.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/skill-catalog.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/skill-guard.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/sql-lens.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/synapse.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/tab-manager.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/taskboard.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/telemetry-blocker.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/test-harness.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/theme-studio.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/token-guard.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/turn-rewind.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/undo-savepoint.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/vision-toolkit.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/web-research.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/workspace-navigator.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/workspace-search.json +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/yaml-validator.json +1 -1
- package/node_modules/@pi-harness/api-gateway/package.json +1 -1
- package/node_modules/@pi-harness/cli/dist/main.d.ts +1 -0
- package/node_modules/@pi-harness/cli/dist/main.d.ts.map +1 -1
- package/node_modules/@pi-harness/cli/dist/main.js +11 -0
- package/node_modules/@pi-harness/cli/dist/main.js.map +1 -1
- package/node_modules/@pi-harness/cli/dist/node-stdio.d.ts.map +1 -1
- package/node_modules/@pi-harness/cli/dist/node-stdio.js +3 -1
- package/node_modules/@pi-harness/cli/dist/node-stdio.js.map +1 -1
- package/node_modules/@pi-harness/cli/dist/relaunch.d.ts.map +1 -1
- package/node_modules/@pi-harness/cli/dist/relaunch.js +4 -1
- package/node_modules/@pi-harness/cli/dist/relaunch.js.map +1 -1
- package/node_modules/@pi-harness/cli/package.json +1 -1
- package/node_modules/@pi-harness/host-webserver/dist/index.d.ts +1 -0
- package/node_modules/@pi-harness/host-webserver/dist/index.d.ts.map +1 -1
- package/node_modules/@pi-harness/host-webserver/dist/index.js +86 -3
- package/node_modules/@pi-harness/host-webserver/dist/index.js.map +1 -1
- package/node_modules/@pi-harness/host-webserver/package.json +1 -1
- package/node_modules/@pi-harness/web-app/dist/index.d.ts +3 -0
- package/node_modules/@pi-harness/web-app/dist/index.d.ts.map +1 -1
- package/node_modules/@pi-harness/web-app/dist/index.js +21 -8
- package/node_modules/@pi-harness/web-app/dist/index.js.map +1 -1
- package/node_modules/@pi-harness/web-app/package.json +1 -1
- package/package.json +8 -8
- package/packages/api-gateway/dist/index.d.ts.map +1 -1
- package/packages/api-gateway/dist/index.js +295 -86
- package/packages/api-gateway/dist/index.js.map +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/agent-teams.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/anchored-standard.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/annotation.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/archify.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/at-file.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/auto-mode.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/better-sidebar.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/browser-fetch.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/browser-session.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/canvas-draw.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/change-verifier.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/cleaner.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/cli-notifier.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/code2skill.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/colleague-skill.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/context-doctor.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/context.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/cost-meter.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/dependency-checker.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/docker-sandbox.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/fail-logger.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/genui.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/git-time-capsule.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/graph-memory.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/history-compressor.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/hol-guard.json +3 -3
- package/packages/api-gateway/dist/marketplace-entries/official/i18n-pair.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/image-compressor.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/llm-verifier.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/mcp-client.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/mcp-panel.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/memory.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/mirage-bridge.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/mock-server.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/modlens.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/module-search.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/obsidian-sync.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/openpets.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/plan-execute.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/plugin-check.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/plugin-dev.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/plugin-finder.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/plugin-radar.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/plugin-stars.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/prompt-guard.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/prompt-library.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/readme-gen.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/recall-unread.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/reverse-skill.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/reviewer-bot.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/runtime-doctor.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/secure-audit.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/session-bookmarks.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/session-bridge.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/session-compare.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/session-export.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/session-insights.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/session-search.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/skill-catalog.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/skill-guard.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/sql-lens.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/synapse.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/tab-manager.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/taskboard.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/telemetry-blocker.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/test-harness.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/theme-studio.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/token-guard.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/turn-rewind.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/undo-savepoint.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/vision-toolkit.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/web-research.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/workspace-navigator.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/workspace-search.json +1 -1
- package/packages/api-gateway/dist/marketplace-entries/official/yaml-validator.json +1 -1
- package/packages/api-gateway/package.json +1 -1
- package/packages/api-gateway/src/index.ts +310 -87
- package/packages/api-gateway/src/marketplace-entries/official/agent-teams.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/anchored-standard.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/annotation.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/archify.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/at-file.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/auto-mode.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/better-sidebar.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/browser-fetch.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/browser-session.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/canvas-draw.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/change-verifier.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/cleaner.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/cli-notifier.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/code2skill.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/colleague-skill.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/context-doctor.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/context.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/cost-meter.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/dependency-checker.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/docker-sandbox.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/fail-logger.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/genui.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/git-time-capsule.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/graph-memory.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/history-compressor.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/hol-guard.json +3 -3
- package/packages/api-gateway/src/marketplace-entries/official/i18n-pair.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/image-compressor.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/llm-verifier.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/mcp-client.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/mcp-panel.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/memory.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/mirage-bridge.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/mock-server.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/modlens.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/module-search.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/obsidian-sync.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/openpets.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/plan-execute.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/plugin-check.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/plugin-dev.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/plugin-finder.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/plugin-radar.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/plugin-stars.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/prompt-guard.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/prompt-library.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/readme-gen.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/recall-unread.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/reverse-skill.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/reviewer-bot.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/runtime-doctor.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/secure-audit.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/session-bookmarks.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/session-bridge.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/session-compare.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/session-export.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/session-insights.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/session-search.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/skill-catalog.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/skill-guard.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/sql-lens.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/synapse.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/tab-manager.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/taskboard.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/telemetry-blocker.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/test-harness.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/theme-studio.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/token-guard.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/turn-rewind.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/undo-savepoint.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/vision-toolkit.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/web-research.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/workspace-navigator.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/workspace-search.json +1 -1
- package/packages/api-gateway/src/marketplace-entries/official/yaml-validator.json +1 -1
- package/packages/api-gateway/test/index.test.ts +799 -3
- package/packages/api-gateway/test/marketplace-schema.test.ts +139 -0
- package/packages/api-gateway/test/packed-package.test.ts +70 -0
- package/packages/api-gateway/test/reference-docs.test.ts +50 -0
- package/packages/api-gateway/test/release-contract.test.ts +77 -0
- package/packages/api-gateway/test/release-package.test.ts +33 -2
- package/packages/api-gateway/test/release-version.test.ts +46 -4
- package/packages/bundle-web-app/dist/index.d.ts +3 -0
- package/packages/bundle-web-app/dist/index.d.ts.map +1 -1
- package/packages/bundle-web-app/dist/index.js +21 -8
- package/packages/bundle-web-app/dist/index.js.map +1 -1
- package/packages/bundle-web-app/package.json +1 -1
- package/packages/bundle-web-app/src/index.ts +25 -8
- package/packages/bundle-web-app/test/index.test.ts +116 -4
- package/packages/cli/dist/main.d.ts +1 -0
- package/packages/cli/dist/main.d.ts.map +1 -1
- package/packages/cli/dist/main.js +11 -0
- package/packages/cli/dist/main.js.map +1 -1
- package/packages/cli/dist/node-stdio.d.ts.map +1 -1
- package/packages/cli/dist/node-stdio.js +3 -1
- package/packages/cli/dist/node-stdio.js.map +1 -1
- package/packages/cli/dist/relaunch.d.ts.map +1 -1
- package/packages/cli/dist/relaunch.js +4 -1
- package/packages/cli/dist/relaunch.js.map +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/main.ts +11 -0
- package/packages/cli/src/node-stdio.ts +6 -3
- package/packages/cli/src/relaunch.ts +4 -1
- package/packages/cli/test/cli.test.ts +3 -1
- package/packages/cli/test/main.test.ts +134 -0
- package/packages/cli/test/node-stdio.test.ts +43 -0
- package/packages/cli/test/relaunch.test.ts +110 -0
- package/packages/host-webserver/dist/index.d.ts +1 -0
- package/packages/host-webserver/dist/index.d.ts.map +1 -1
- package/packages/host-webserver/dist/index.js +86 -3
- package/packages/host-webserver/dist/index.js.map +1 -1
- package/packages/host-webserver/package.json +1 -1
- package/packages/host-webserver/src/index.ts +95 -3
- package/packages/host-webserver/test/index.test.ts +160 -1
- package/apps/web/dist/assets/index-BVI_IS-R.css +0 -2
- package/apps/web/dist/assets/index-w0KQgv8x.js +0 -22
|
@@ -1,28 +1,43 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { mkdir, mkdtemp, readFile, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
-
import { basename, isAbsolute, join, relative, resolve } from "node:path";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import {
|
|
6
|
+
import { atomicWriteFile } from "@pi-harness/core";
|
|
7
|
+
import { MARKETPLACE_CAPABILITIES, MARKETPLACE_CATEGORIES, MARKETPLACE_PLUGINS, attachMarketplaceStatistics, createMarketplaceStatisticsLoader, paginateMarketplace, searchMarketplace, needsMarketplacePackageInstall, marketplaceNpmPackageName, sortMarketplaceByRecommendation, } from "./marketplace.js";
|
|
7
8
|
import { parseGitWorktrees } from "./workspaces.js";
|
|
8
9
|
const loadMarketplaceStatistics = createMarketplaceStatisticsLoader();
|
|
10
|
+
const DEFAULT_BODY_LIMIT_BYTES = 64 * 1024;
|
|
11
|
+
const IMPORT_CONTENT_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
12
|
+
// The import body carries the JSONL content as a JSON string, so quotes and newlines are escaped; leave headroom above the content limit for that overhead.
|
|
13
|
+
const IMPORT_BODY_LIMIT_BYTES = 16 * 1024 * 1024;
|
|
14
|
+
// Retained trajectory events are replayed to every SSE client and returned by /api/session, so the array must stay bounded.
|
|
15
|
+
const MAX_RETAINED_EVENTS = 2000;
|
|
16
|
+
const GIT_TIMEOUT_MS = 15_000;
|
|
17
|
+
class PayloadTooLargeError extends Error {
|
|
18
|
+
constructor() {
|
|
19
|
+
super("Request body is too large");
|
|
20
|
+
this.name = "PayloadTooLargeError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
9
23
|
function sendJson(response, status, payload) {
|
|
10
24
|
const body = JSON.stringify(payload);
|
|
11
25
|
response.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store", "content-length": Buffer.byteLength(body) });
|
|
12
26
|
response.end(body);
|
|
13
27
|
}
|
|
14
|
-
|
|
28
|
+
// A multi-byte UTF-8 sequence can straddle a chunk boundary, so the raw bytes are collected and decoded once; decoding each chunk on its own would replace the split sequence with U+FFFD.
|
|
29
|
+
async function bodyText(request, maxBytes = DEFAULT_BODY_LIMIT_BYTES) {
|
|
15
30
|
const chunks = [];
|
|
16
31
|
let length = 0;
|
|
17
32
|
for await (const chunk of request) {
|
|
18
33
|
const input = chunk;
|
|
19
|
-
const
|
|
20
|
-
length +=
|
|
21
|
-
if (length >
|
|
22
|
-
throw new
|
|
23
|
-
chunks.push(
|
|
34
|
+
const buffer = typeof input === "string" ? Buffer.from(input, "utf8") : Buffer.isBuffer(input) ? input : Buffer.from(input);
|
|
35
|
+
length += buffer.length;
|
|
36
|
+
if (length > maxBytes)
|
|
37
|
+
throw new PayloadTooLargeError();
|
|
38
|
+
chunks.push(buffer);
|
|
24
39
|
}
|
|
25
|
-
return chunks.
|
|
40
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
26
41
|
}
|
|
27
42
|
function errorText(error) {
|
|
28
43
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -42,10 +57,16 @@ function jsonSafe(value, seen = new WeakSet()) {
|
|
|
42
57
|
return Object.prototype.toString.call(value);
|
|
43
58
|
if (seen.has(value))
|
|
44
59
|
return "[Circular]";
|
|
60
|
+
// Only the ancestors of the current node stay marked: a sibling that repeats the same object (Pi pushes one AgentMessage instance into both the message list and its events) is legitimate data, not a cycle.
|
|
45
61
|
seen.add(value);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
try {
|
|
63
|
+
if (Array.isArray(value))
|
|
64
|
+
return value.map((item) => jsonSafe(item, seen));
|
|
65
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, jsonSafe(item, seen)]));
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
seen.delete(value);
|
|
69
|
+
}
|
|
49
70
|
}
|
|
50
71
|
function createStatus(services, events) {
|
|
51
72
|
const activeModel = services.runtime.session.model ?? services.models.model;
|
|
@@ -106,26 +127,63 @@ function piConfig(services) {
|
|
|
106
127
|
function sessionMetadataFile(manager) {
|
|
107
128
|
return resolve(manager.getSessionDir(), ".pi-harness-session-meta.json");
|
|
108
129
|
}
|
|
109
|
-
|
|
130
|
+
// A missing file is the normal first-run state. Any other read or parse failure means the file exists but is unusable; it is renamed aside so the wreckage survives for recovery and the next write cannot silently replace the only copy.
|
|
131
|
+
async function readSessionMetadata(manager, logger) {
|
|
132
|
+
const file = sessionMetadataFile(manager);
|
|
133
|
+
let raw;
|
|
110
134
|
try {
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
raw = await readFile(file, "utf8");
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
if (error.code === "ENOENT")
|
|
113
139
|
return {};
|
|
140
|
+
await quarantineSessionMetadata(file, logger, error);
|
|
141
|
+
return {};
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const parsed = JSON.parse(raw);
|
|
145
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
146
|
+
throw new Error("session metadata must be a JSON object");
|
|
114
147
|
return parsed;
|
|
115
148
|
}
|
|
116
|
-
catch {
|
|
149
|
+
catch (error) {
|
|
150
|
+
await quarantineSessionMetadata(file, logger, error);
|
|
117
151
|
return {};
|
|
118
152
|
}
|
|
119
153
|
}
|
|
154
|
+
async function quarantineSessionMetadata(file, logger, error) {
|
|
155
|
+
const quarantined = `${file}.corrupt-${Date.now()}`;
|
|
156
|
+
try {
|
|
157
|
+
await rename(file, quarantined);
|
|
158
|
+
logger.warn(`Session metadata at ${file} is unreadable (${errorText(error)}); moved it to ${quarantined} and starting from an empty map`);
|
|
159
|
+
}
|
|
160
|
+
catch (renameError) {
|
|
161
|
+
logger.warn(`Session metadata at ${file} is unreadable (${errorText(error)}) and could not be quarantined: ${errorText(renameError)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
120
164
|
async function writeSessionMetadata(manager, metadata) {
|
|
121
165
|
await mkdir(manager.getSessionDir(), { recursive: true });
|
|
122
|
-
await
|
|
166
|
+
await atomicWriteFile(sessionMetadataFile(manager), JSON.stringify(metadata, null, 2) + "\n", { encoding: "utf8" });
|
|
167
|
+
}
|
|
168
|
+
// Every read-modify-write of the metadata file runs through this chain so concurrent requests cannot interleave and drop each other's updates.
|
|
169
|
+
let sessionMetadataQueue = Promise.resolve();
|
|
170
|
+
function mutateSessionMetadata(manager, logger, mutate) {
|
|
171
|
+
const operation = sessionMetadataQueue
|
|
172
|
+
.catch(() => undefined)
|
|
173
|
+
.then(async () => {
|
|
174
|
+
const metadata = await readSessionMetadata(manager, logger);
|
|
175
|
+
const result = await mutate(metadata);
|
|
176
|
+
await writeSessionMetadata(manager, metadata);
|
|
177
|
+
return result;
|
|
178
|
+
});
|
|
179
|
+
sessionMetadataQueue = operation;
|
|
180
|
+
return operation;
|
|
123
181
|
}
|
|
124
182
|
function sessionPathInDirectory(path, manager) {
|
|
125
183
|
const root = resolve(manager.getSessionDir());
|
|
126
184
|
const target = resolve(path);
|
|
127
185
|
const relativePath = relative(root, target);
|
|
128
|
-
return relativePath !== "" && !isAbsolute(relativePath) && relativePath !== ".." && !relativePath.startsWith(".." +
|
|
186
|
+
return relativePath !== "" && !isAbsolute(relativePath) && relativePath !== ".." && !relativePath.startsWith(".." + sep) && target.endsWith(".jsonl");
|
|
129
187
|
}
|
|
130
188
|
const webSessionUi = {
|
|
131
189
|
select: (_title, options) => Promise.resolve(options[0]),
|
|
@@ -255,6 +313,7 @@ function sanitizeLogValue(value, state, depth = 0) {
|
|
|
255
313
|
return "[Max Depth]";
|
|
256
314
|
if (state.seen.has(value))
|
|
257
315
|
return "[Circular]";
|
|
316
|
+
// Unmarked once its subtree is done, so an object logged twice as a sibling is rendered twice instead of being mistaken for a cycle.
|
|
258
317
|
state.seen.add(value);
|
|
259
318
|
try {
|
|
260
319
|
if (value instanceof Date)
|
|
@@ -288,6 +347,9 @@ function sanitizeLogValue(value, state, depth = 0) {
|
|
|
288
347
|
catch {
|
|
289
348
|
return "[Unavailable]";
|
|
290
349
|
}
|
|
350
|
+
finally {
|
|
351
|
+
state.seen.delete(value);
|
|
352
|
+
}
|
|
291
353
|
}
|
|
292
354
|
function sanitizeLogArguments(args) {
|
|
293
355
|
const sanitized = sanitizeLogValue(args, { remaining: loggerPanelNodeLimit, seen: new WeakSet() });
|
|
@@ -412,21 +474,53 @@ async function updateMarketplaceProfile(configPath, entryId, update) {
|
|
|
412
474
|
function writeSse(response, payload) {
|
|
413
475
|
response.write(`data: ${JSON.stringify(jsonSafe(payload))}\n\n`);
|
|
414
476
|
}
|
|
477
|
+
// Node reports a maxBuffer overflow through a string error code and a timeout through `killed`; neither is a git exit status, so they must not be folded into an ordinary non-zero exit.
|
|
478
|
+
function gitTermination(error) {
|
|
479
|
+
if (error === null)
|
|
480
|
+
return undefined;
|
|
481
|
+
if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER")
|
|
482
|
+
return "output-limit";
|
|
483
|
+
if (error.killed === true)
|
|
484
|
+
return "timeout";
|
|
485
|
+
return undefined;
|
|
486
|
+
}
|
|
487
|
+
function gitTerminationMessage(command, termination) {
|
|
488
|
+
return termination === "timeout"
|
|
489
|
+
? `git ${command} timed out after ${GIT_TIMEOUT_MS / 1000} seconds`
|
|
490
|
+
: `git ${command} produced more output than the buffer limit allows`;
|
|
491
|
+
}
|
|
492
|
+
// `-z` is what makes the paths usable: without it git wraps any path holding a space, a quote or a non-ASCII byte in C-style quoting, and the quoted form is not a pathspec git will accept back on diff, add or ls-files.
|
|
415
493
|
function gitStatus(cwd) {
|
|
416
|
-
return new Promise((
|
|
417
|
-
execFile("git", ["status", "--short", "--untracked-files=all"], { cwd, maxBuffer: 512 * 1024 }, (error, stdout) =>
|
|
494
|
+
return new Promise((resolveStatus, rejectStatus) => {
|
|
495
|
+
execFile("git", ["status", "--short", "-z", "--untracked-files=all"], { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: 512 * 1024 }, (error, stdout, stderr) => {
|
|
496
|
+
if (error === null) {
|
|
497
|
+
resolveStatus({ output: stdout, truncated: false });
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const termination = gitTermination(error);
|
|
501
|
+
if (termination === "output-limit") {
|
|
502
|
+
// Keep the complete records that arrived before the kill; the final one may have been cut mid-path.
|
|
503
|
+
resolveStatus({ output: stdout.slice(0, stdout.lastIndexOf("\0") + 1), truncated: true });
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (termination === undefined && error.code === 128 && /not a git repository/i.test(stderr)) {
|
|
507
|
+
resolveStatus({ output: "", truncated: false });
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
rejectStatus(new Error(termination ? gitTerminationMessage("status", termination) : stderr.trim() || error.message, { cause: error }));
|
|
511
|
+
});
|
|
418
512
|
});
|
|
419
513
|
}
|
|
420
514
|
function gitDiff(cwd, path) {
|
|
421
515
|
return new Promise((resolveOutput) => {
|
|
422
|
-
execFile("git", ["diff", "--no-ext-diff", "--", path], { cwd, maxBuffer: 1024 * 1024 }, (error, stdout) => resolveOutput(error && stdout.length === 0 ? "" : stdout));
|
|
516
|
+
execFile("git", ["diff", "--no-ext-diff", "--", path], { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: 1024 * 1024 }, (error, stdout) => resolveOutput(error && stdout.length === 0 ? "" : stdout));
|
|
423
517
|
});
|
|
424
518
|
}
|
|
425
519
|
function gitCommand(cwd, args) {
|
|
426
520
|
return new Promise((resolveResult) => {
|
|
427
|
-
execFile("git", [...args], { cwd, maxBuffer: 2 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
521
|
+
execFile("git", [...args], { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: 2 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
428
522
|
const code = error && typeof error.code === "number" ? error.code : error ? 1 : 0;
|
|
429
|
-
resolveResult({ stdout, stderr, code });
|
|
523
|
+
resolveResult({ stdout, stderr, code, terminated: gitTermination(error) });
|
|
430
524
|
});
|
|
431
525
|
});
|
|
432
526
|
}
|
|
@@ -457,6 +551,26 @@ async function isDirectory(path) {
|
|
|
457
551
|
return false;
|
|
458
552
|
}
|
|
459
553
|
}
|
|
554
|
+
async function isFile(path) {
|
|
555
|
+
try {
|
|
556
|
+
return (await stat(path)).isFile();
|
|
557
|
+
}
|
|
558
|
+
catch {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
// The loader resolves a bare plugin specifier from the profile file upwards (`createRequire(profilePath).resolve(name)`), never from the shell the harness was started in, so a marketplace package has to land in the package that owns the profile or it installs successfully and then fails to load.
|
|
563
|
+
async function marketplaceInstallDirectory(configPath, fallback) {
|
|
564
|
+
let directory = dirname(resolve(configPath));
|
|
565
|
+
let parent = dirname(directory);
|
|
566
|
+
while (parent !== directory) {
|
|
567
|
+
if (await isFile(join(directory, "package.json")))
|
|
568
|
+
return directory;
|
|
569
|
+
directory = parent;
|
|
570
|
+
parent = dirname(directory);
|
|
571
|
+
}
|
|
572
|
+
return fallback;
|
|
573
|
+
}
|
|
460
574
|
function pickDirectory() {
|
|
461
575
|
return new Promise((resolvePath, reject) => {
|
|
462
576
|
execFile("osascript", ["-e", 'POSIX path of (choose folder with prompt "选择 Pi 工作区")'], { timeout: 120_000, maxBuffer: 64 * 1024 }, (error, stdout, stderr) => {
|
|
@@ -503,7 +617,12 @@ export default {
|
|
|
503
617
|
const events = [];
|
|
504
618
|
const eventClients = new Set();
|
|
505
619
|
const handleEvent = (event) => {
|
|
506
|
-
events.
|
|
620
|
+
// Streaming deltas reach clients live over SSE and each one carries the whole partial message, so only durable events are retained for snapshots.
|
|
621
|
+
if (event.type !== "message_update") {
|
|
622
|
+
events.push(event);
|
|
623
|
+
if (events.length > MAX_RETAINED_EVENTS)
|
|
624
|
+
events.splice(0, events.length - MAX_RETAINED_EVENTS);
|
|
625
|
+
}
|
|
507
626
|
for (const response of eventClients) {
|
|
508
627
|
if (response.writableEnded || response.destroyed) {
|
|
509
628
|
eventClients.delete(response);
|
|
@@ -639,7 +758,9 @@ export default {
|
|
|
639
758
|
return;
|
|
640
759
|
}
|
|
641
760
|
const path = join(services.launch.agentDir, "settings.json");
|
|
642
|
-
|
|
761
|
+
// Replace the file atomically so an interrupted write never leaves a truncated settings.json behind; keep the existing permission bits.
|
|
762
|
+
const mode = await stat(path).then((info) => info.mode & 0o777, () => 0o644);
|
|
763
|
+
await atomicWriteFile(path, JSON.stringify(parsed, null, 2) + "\n", { encoding: "utf8", mode });
|
|
643
764
|
await services.runtime.session.settingsManager.reload();
|
|
644
765
|
sendJson(response, 200, piConfig(services));
|
|
645
766
|
}
|
|
@@ -900,15 +1021,17 @@ export default {
|
|
|
900
1021
|
sendJson(response, 409, { error: "Plugin is already installed", plugin });
|
|
901
1022
|
return;
|
|
902
1023
|
}
|
|
903
|
-
const
|
|
904
|
-
const
|
|
1024
|
+
const installDirectory = await marketplaceInstallDirectory(configPath, services.launch.cwd);
|
|
1025
|
+
const packageJsonPath = join(installDirectory, "package.json");
|
|
1026
|
+
const packageLockPath = join(installDirectory, "package-lock.json");
|
|
905
1027
|
const packageJsonBefore = await readFile(packageJsonPath, "utf8").catch(() => undefined);
|
|
906
1028
|
const packageLockBefore = await readFile(packageLockPath, "utf8").catch(() => undefined);
|
|
907
1029
|
let profileBefore;
|
|
908
1030
|
let entryId;
|
|
909
1031
|
try {
|
|
910
1032
|
if (needsMarketplacePackageInstall(plugin)) {
|
|
911
|
-
|
|
1033
|
+
const specifier = `${marketplaceNpmPackageName(plugin.packageName)}@${plugin.version}`;
|
|
1034
|
+
await runProcess("npm", ["install", "--save-exact", "--package-lock=false", specifier], installDirectory);
|
|
912
1035
|
}
|
|
913
1036
|
profileBefore = await appendMarketplaceProfile(configPath, plugin);
|
|
914
1037
|
if (!needsMarketplacePackageInstall(plugin)) {
|
|
@@ -932,9 +1055,14 @@ export default {
|
|
|
932
1055
|
await loader.remove(entryId).catch(() => { });
|
|
933
1056
|
if (profileBefore !== undefined)
|
|
934
1057
|
await writeFile(configPath, profileBefore, "utf8").catch(() => { });
|
|
935
|
-
|
|
1058
|
+
// A manifest npm created for this install is removed rather than left behind; restoring is only possible when one existed before.
|
|
1059
|
+
if (packageJsonBefore === undefined)
|
|
1060
|
+
await unlink(packageJsonPath).catch(() => { });
|
|
1061
|
+
else
|
|
936
1062
|
await writeFile(packageJsonPath, packageJsonBefore, "utf8").catch(() => { });
|
|
937
|
-
if (packageLockBefore
|
|
1063
|
+
if (packageLockBefore === undefined)
|
|
1064
|
+
await unlink(packageLockPath).catch(() => { });
|
|
1065
|
+
else
|
|
938
1066
|
await writeFile(packageLockPath, packageLockBefore, "utf8").catch(() => { });
|
|
939
1067
|
sendJson(response, 502, { error: errorText(error) });
|
|
940
1068
|
}
|
|
@@ -1036,8 +1164,9 @@ export default {
|
|
|
1036
1164
|
}
|
|
1037
1165
|
const profileEntryId = entry.options.id;
|
|
1038
1166
|
const profileBefore = await readFile(configPath, "utf8");
|
|
1039
|
-
const
|
|
1040
|
-
const
|
|
1167
|
+
const installDirectory = await marketplaceInstallDirectory(configPath, services.launch.cwd);
|
|
1168
|
+
const packageJsonPath = join(installDirectory, "package.json");
|
|
1169
|
+
const packageLockPath = join(installDirectory, "package-lock.json");
|
|
1041
1170
|
const packageJsonBefore = await readFile(packageJsonPath, "utf8").catch(() => undefined);
|
|
1042
1171
|
const packageLockBefore = await readFile(packageLockPath, "utf8").catch(() => undefined);
|
|
1043
1172
|
await entry.parent.remove(entry.options.id);
|
|
@@ -1045,7 +1174,7 @@ export default {
|
|
|
1045
1174
|
try {
|
|
1046
1175
|
await updateMarketplaceProfile(configPath, profileEntryId, { remove: true });
|
|
1047
1176
|
if (needsMarketplacePackageInstall(plugin))
|
|
1048
|
-
await runProcess("npm", ["uninstall", "--package-lock=false", plugin.packageName],
|
|
1177
|
+
await runProcess("npm", ["uninstall", "--package-lock=false", marketplaceNpmPackageName(plugin.packageName)], installDirectory);
|
|
1049
1178
|
sendJson(response, 200, { uninstalled: true, id: payload.id });
|
|
1050
1179
|
}
|
|
1051
1180
|
catch (error) {
|
|
@@ -1149,20 +1278,31 @@ export default {
|
|
|
1149
1278
|
const disposeFiles = services.webServer.register({
|
|
1150
1279
|
path: "/api/files",
|
|
1151
1280
|
async handler(_request, response) {
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1281
|
+
let status;
|
|
1282
|
+
try {
|
|
1283
|
+
status = await gitStatus(activeCwd(services));
|
|
1284
|
+
}
|
|
1285
|
+
catch (error) {
|
|
1286
|
+
sendJson(response, 500, { error: errorText(error) });
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
const records = status.output.split("\0");
|
|
1290
|
+
const items = [];
|
|
1291
|
+
for (let index = 0; index < records.length; index += 1) {
|
|
1292
|
+
const record = records[index] ?? "";
|
|
1293
|
+
if (record.length < 4)
|
|
1294
|
+
continue;
|
|
1295
|
+
const code = record.slice(0, 2).trim() || "??";
|
|
1296
|
+
// Rename and copy records carry the original path in a second NUL-terminated field, which is not a workspace change of its own.
|
|
1297
|
+
if (code.startsWith("R") || code.startsWith("C"))
|
|
1298
|
+
index += 1;
|
|
1299
|
+
items.push({
|
|
1300
|
+
path: record.slice(3),
|
|
1301
|
+
status: code,
|
|
1302
|
+
label: code === "??" ? "untracked" : code.includes("D") ? "deleted" : code.includes("A") ? "added" : "modified",
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
sendJson(response, 200, { items, ...(status.truncated ? { truncated: true } : {}) });
|
|
1166
1306
|
},
|
|
1167
1307
|
});
|
|
1168
1308
|
const disposeFileDiff = services.webServer.register({
|
|
@@ -1181,7 +1321,7 @@ export default {
|
|
|
1181
1321
|
const root = resolve(activeCwd(services));
|
|
1182
1322
|
const absolute = resolve(root, requested);
|
|
1183
1323
|
const relativePath = relative(root, absolute);
|
|
1184
|
-
if (isAbsolute(relativePath) || relativePath === ".." || relativePath.startsWith(".." +
|
|
1324
|
+
if (isAbsolute(relativePath) || relativePath === ".." || relativePath.startsWith(".." + sep)) {
|
|
1185
1325
|
sendJson(response, 400, { error: "path must stay inside the workspace" });
|
|
1186
1326
|
return;
|
|
1187
1327
|
}
|
|
@@ -1208,11 +1348,19 @@ export default {
|
|
|
1208
1348
|
}
|
|
1209
1349
|
const root = resolve(activeCwd(services));
|
|
1210
1350
|
const add = await gitCommand(root, ["add", "-A", "--", ...paths]);
|
|
1351
|
+
if (add.terminated) {
|
|
1352
|
+
sendJson(response, 500, { error: gitTerminationMessage("add", add.terminated) });
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1211
1355
|
if (add.code !== 0) {
|
|
1212
1356
|
sendJson(response, 409, { error: add.stderr.trim() || "Unable to stage workspace files" });
|
|
1213
1357
|
return;
|
|
1214
1358
|
}
|
|
1215
1359
|
const commit = await gitCommand(root, ["commit", "-m", payload.message.trim(), "--", ...paths]);
|
|
1360
|
+
if (commit.terminated) {
|
|
1361
|
+
sendJson(response, 500, { error: gitTerminationMessage("commit", commit.terminated) });
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1216
1364
|
if (commit.code !== 0) {
|
|
1217
1365
|
sendJson(response, 409, { error: commit.stdout.trim() || commit.stderr.trim() || "Nothing to commit" });
|
|
1218
1366
|
return;
|
|
@@ -1244,16 +1392,39 @@ export default {
|
|
|
1244
1392
|
sendJson(response, 400, { error: paths.message });
|
|
1245
1393
|
return;
|
|
1246
1394
|
}
|
|
1247
|
-
const untracked = await gitCommand(root, ["ls-files", "--others", "--exclude-standard", "--", ...paths]);
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1395
|
+
const untracked = await gitCommand(root, ["ls-files", "-z", "--others", "--exclude-standard", "--", ...paths]);
|
|
1396
|
+
// `git restore` refuses the whole request when any pathspec is unknown to it, so restore only the paths git tracks. `--with-tree=HEAD` keeps staged deletions in the list; it fails on an unborn HEAD, where the plain index listing is all there is.
|
|
1397
|
+
let tracked = await gitCommand(root, ["ls-files", "-z", "--with-tree=HEAD", "--", ...paths]);
|
|
1398
|
+
if (tracked.code !== 0 && tracked.terminated === undefined)
|
|
1399
|
+
tracked = await gitCommand(root, ["ls-files", "-z", "--", ...paths]);
|
|
1400
|
+
const listing = [untracked, tracked].find((result) => result.terminated !== undefined || result.code !== 0);
|
|
1401
|
+
if (listing) {
|
|
1402
|
+
if (listing.terminated)
|
|
1403
|
+
sendJson(response, 500, { error: gitTerminationMessage("ls-files", listing.terminated) });
|
|
1404
|
+
else
|
|
1405
|
+
sendJson(response, 409, { error: listing.stderr.trim() || "Unable to inspect workspace files" });
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
const untrackedPaths = untracked.stdout.split("\0").filter(Boolean);
|
|
1409
|
+
const trackedPaths = tracked.stdout.split("\0").filter(Boolean);
|
|
1410
|
+
const known = [...untrackedPaths, ...trackedPaths];
|
|
1411
|
+
const unknown = paths.filter((path) => !known.some((candidate) => candidate === path || candidate.startsWith(path + "/")));
|
|
1412
|
+
if (unknown.length > 0) {
|
|
1413
|
+
sendJson(response, 409, { error: `Paths did not match any file known to git: ${unknown.join(", ")}` });
|
|
1251
1414
|
return;
|
|
1252
1415
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
.
|
|
1256
|
-
|
|
1416
|
+
if (trackedPaths.length > 0) {
|
|
1417
|
+
const restore = await gitCommand(root, ["restore", "--worktree", "--staged", "--", ...trackedPaths]);
|
|
1418
|
+
if (restore.terminated) {
|
|
1419
|
+
sendJson(response, 500, { error: gitTerminationMessage("restore", restore.terminated) });
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
if (restore.code !== 0) {
|
|
1423
|
+
sendJson(response, 409, { error: restore.stderr.trim() || "Unable to restore workspace files" });
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
for (const path of untrackedPaths) {
|
|
1257
1428
|
await rm(resolve(root, path), { recursive: true, force: true });
|
|
1258
1429
|
}
|
|
1259
1430
|
sendJson(response, 200, { reverted: true, paths });
|
|
@@ -1291,6 +1462,10 @@ export default {
|
|
|
1291
1462
|
const disposePrompt = services.webServer.register({
|
|
1292
1463
|
path: "/api/prompt",
|
|
1293
1464
|
async handler(request, response) {
|
|
1465
|
+
if (request.method !== "POST") {
|
|
1466
|
+
sendJson(response, 405, { error: "Method not allowed" });
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1294
1469
|
if (busy) {
|
|
1295
1470
|
sendJson(response, 409, { error: "Another prompt is already running" });
|
|
1296
1471
|
return;
|
|
@@ -1541,9 +1716,9 @@ export default {
|
|
|
1541
1716
|
events.length = 0;
|
|
1542
1717
|
}
|
|
1543
1718
|
await unlink(path);
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1719
|
+
await mutateSessionMetadata(manager, context.logger, (metadata) => {
|
|
1720
|
+
delete metadata[path];
|
|
1721
|
+
});
|
|
1547
1722
|
sendJson(response, 200, { deleted: true, path, sessionFile: services.runtime.session.sessionFile });
|
|
1548
1723
|
}
|
|
1549
1724
|
catch (error) {
|
|
@@ -1566,16 +1741,17 @@ export default {
|
|
|
1566
1741
|
sendJson(response, 400, { error: "Invalid session path" });
|
|
1567
1742
|
return;
|
|
1568
1743
|
}
|
|
1569
|
-
const
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1744
|
+
const nextMetadata = await mutateSessionMetadata(manager, context.logger, (metadata) => {
|
|
1745
|
+
const currentMetadata = metadata[path] ?? {};
|
|
1746
|
+
const updated = {
|
|
1747
|
+
...currentMetadata,
|
|
1748
|
+
...(payload.archived === undefined ? {} : { archived: payload.archived === true }),
|
|
1749
|
+
...(payload.pinned === undefined ? {} : { pinned: payload.pinned === true }),
|
|
1750
|
+
};
|
|
1751
|
+
metadata[path] = updated;
|
|
1752
|
+
return updated;
|
|
1753
|
+
});
|
|
1754
|
+
sendJson(response, 200, { path, metadata: nextMetadata });
|
|
1579
1755
|
}
|
|
1580
1756
|
catch (error) {
|
|
1581
1757
|
sendJson(response, 400, { error: errorText(error) });
|
|
@@ -1612,13 +1788,41 @@ export default {
|
|
|
1612
1788
|
sendJson(response, 409, { error: "Cannot batch-delete the active session" });
|
|
1613
1789
|
return;
|
|
1614
1790
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1791
|
+
if (action === "delete") {
|
|
1792
|
+
// The unlinks run outside the metadata mutation: a failure halfway through must not discard the key removals for the files that are already gone from disk.
|
|
1793
|
+
const removed = [];
|
|
1794
|
+
const failed = [];
|
|
1795
|
+
for (const path of paths) {
|
|
1796
|
+
try {
|
|
1797
|
+
await unlink(path);
|
|
1798
|
+
removed.push(path);
|
|
1799
|
+
}
|
|
1800
|
+
catch (error) {
|
|
1801
|
+
// A session another tab already deleted leaves nothing to unlink, but its metadata entry still has to go.
|
|
1802
|
+
if (error.code === "ENOENT")
|
|
1803
|
+
removed.push(path);
|
|
1804
|
+
else
|
|
1805
|
+
failed.push({ path, error: errorText(error) });
|
|
1806
|
+
}
|
|
1620
1807
|
}
|
|
1621
|
-
|
|
1808
|
+
await mutateSessionMetadata(manager, context.logger, (metadata) => {
|
|
1809
|
+
for (const path of removed)
|
|
1810
|
+
delete metadata[path];
|
|
1811
|
+
});
|
|
1812
|
+
if (failed.length > 0) {
|
|
1813
|
+
sendJson(response, 409, {
|
|
1814
|
+
error: `Some sessions could not be deleted: ${failed.map((item) => item.path).join(", ")}`,
|
|
1815
|
+
action,
|
|
1816
|
+
count: removed.length,
|
|
1817
|
+
failed,
|
|
1818
|
+
});
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
sendJson(response, 200, { action, count: removed.length });
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
await mutateSessionMetadata(manager, context.logger, (metadata) => {
|
|
1825
|
+
for (const path of paths) {
|
|
1622
1826
|
const current = metadata[path] ?? {};
|
|
1623
1827
|
metadata[path] = {
|
|
1624
1828
|
...current,
|
|
@@ -1626,8 +1830,7 @@ export default {
|
|
|
1626
1830
|
...(action === "pin" || action === "unpin" ? { pinned: action === "pin" } : {}),
|
|
1627
1831
|
};
|
|
1628
1832
|
}
|
|
1629
|
-
}
|
|
1630
|
-
await writeSessionMetadata(manager, metadata);
|
|
1833
|
+
});
|
|
1631
1834
|
sendJson(response, 200, { action, count: paths.length });
|
|
1632
1835
|
}
|
|
1633
1836
|
catch (error) {
|
|
@@ -1677,11 +1880,16 @@ export default {
|
|
|
1677
1880
|
return;
|
|
1678
1881
|
}
|
|
1679
1882
|
try {
|
|
1680
|
-
const payload = JSON.parse(await bodyText(request));
|
|
1883
|
+
const payload = JSON.parse(await bodyText(request, IMPORT_BODY_LIMIT_BYTES));
|
|
1681
1884
|
const suppliedPath = typeof payload.path === "string" ? payload.path : "";
|
|
1682
1885
|
const content = typeof payload.content === "string" ? payload.content : undefined;
|
|
1683
|
-
|
|
1684
|
-
|
|
1886
|
+
// `path` reads an existing file and `content` is written to a private temporary file; accepting both would let the request choose where the content lands.
|
|
1887
|
+
if (suppliedPath && content !== undefined) {
|
|
1888
|
+
sendJson(response, 400, { error: "Provide either a JSONL file path or file content, not both" });
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
if (content === undefined && (!suppliedPath || !isAbsolute(suppliedPath) || !suppliedPath.endsWith(".jsonl"))) {
|
|
1892
|
+
sendJson(response, 400, { error: "An absolute .jsonl file path or file content is required" });
|
|
1685
1893
|
return;
|
|
1686
1894
|
}
|
|
1687
1895
|
const manager = services.runtime.session.sessionManager;
|
|
@@ -1691,11 +1899,12 @@ export default {
|
|
|
1691
1899
|
}
|
|
1692
1900
|
const targetCwd = typeof payload.cwd === "string" && payload.cwd.trim() ? resolve(payload.cwd) : activeCwd(services);
|
|
1693
1901
|
const temporaryDirectory = content === undefined ? undefined : await mkdtemp(join(tmpdir(), "pi-harness-import-"));
|
|
1694
|
-
const
|
|
1695
|
-
|
|
1902
|
+
const requestedName = basename(typeof payload.filename === "string" && payload.filename.trim() ? payload.filename : "import.jsonl");
|
|
1903
|
+
const importName = requestedName === "" || requestedName === "." || requestedName === ".." ? "import.jsonl" : requestedName;
|
|
1904
|
+
const importPath = temporaryDirectory === undefined ? suppliedPath : join(temporaryDirectory, importName);
|
|
1696
1905
|
try {
|
|
1697
1906
|
if (content !== undefined) {
|
|
1698
|
-
if (Buffer.byteLength(content, "utf8") >
|
|
1907
|
+
if (Buffer.byteLength(content, "utf8") > IMPORT_CONTENT_LIMIT_BYTES) {
|
|
1699
1908
|
sendJson(response, 413, { error: "Imported session must be at most 10 MiB" });
|
|
1700
1909
|
return;
|
|
1701
1910
|
}
|
|
@@ -1723,7 +1932,7 @@ export default {
|
|
|
1723
1932
|
}
|
|
1724
1933
|
}
|
|
1725
1934
|
catch (error) {
|
|
1726
|
-
sendJson(response, 400, { error: errorText(error) });
|
|
1935
|
+
sendJson(response, error instanceof PayloadTooLargeError ? 413 : 400, { error: errorText(error) });
|
|
1727
1936
|
}
|
|
1728
1937
|
},
|
|
1729
1938
|
});
|
|
@@ -1761,7 +1970,7 @@ export default {
|
|
|
1761
1970
|
const page = Math.max(0, Number.parseInt(url.searchParams.get("page") ?? "0", 10) || 0);
|
|
1762
1971
|
const pageSize = Math.min(100, Math.max(1, Number.parseInt(url.searchParams.get("pageSize") ?? "50", 10) || 50));
|
|
1763
1972
|
const includeArchived = url.searchParams.get("includeArchived") === "true";
|
|
1764
|
-
const metadata = await readSessionMetadata(manager);
|
|
1973
|
+
const metadata = await readSessionMetadata(manager, context.logger);
|
|
1765
1974
|
const items = typeof manager.isPersisted === "function" && manager.isPersisted() ? await SessionManager.list(services.launch.cwd, manager.getSessionDir()) : [];
|
|
1766
1975
|
const filtered = items.filter((item) => includeArchived || metadata[item.path]?.archived !== true);
|
|
1767
1976
|
const sorted = filtered.sort((a, b) => Number(metadata[b.path]?.pinned === true) - Number(metadata[a.path]?.pinned === true) || b.modified.getTime() - a.modified.getTime());
|