@pi-harness/pi-harness 0.1.23 → 0.1.25
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 +14 -9
- package/apps/web/dist/assets/index-Deo3hxmM.js +25 -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 +27 -2
- package/apps/web/server-dist/bin.js.map +1 -1
- package/apps/web/src/server/bin.ts +27 -2
- package/apps/web/test/bin.test.ts +228 -5
- package/node_modules/@pi-harness/api-gateway/dist/index.d.ts.map +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/index.js +160 -69
- 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 +1 -1
- 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/relaunch.d.ts.map +1 -1
- package/node_modules/@pi-harness/cli/dist/relaunch.js +2 -4
- 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 +43 -7
- 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.map +1 -1
- package/node_modules/@pi-harness/web-app/dist/index.js +12 -3
- 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 +7 -7
- package/packages/api-gateway/dist/index.d.ts.map +1 -1
- package/packages/api-gateway/dist/index.js +160 -69
- 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 +1 -1
- 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 +186 -88
- 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 +1 -1
- 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 +656 -4
- 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-package.test.ts +32 -1
- package/packages/api-gateway/test/release-version.test.ts +40 -0
- package/packages/bundle-web-app/dist/index.d.ts.map +1 -1
- package/packages/bundle-web-app/dist/index.js +12 -3
- 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 +13 -3
- package/packages/bundle-web-app/test/index.test.ts +83 -2
- 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/relaunch.d.ts.map +1 -1
- package/packages/cli/dist/relaunch.js +2 -4
- 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/relaunch.ts +2 -3
- package/packages/cli/test/cli.test.ts +3 -1
- package/packages/cli/test/main.test.ts +134 -0
- package/packages/cli/test/relaunch.test.ts +86 -11
- 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 +43 -7
- 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 +48 -8
- package/packages/host-webserver/test/index.test.ts +58 -5
- package/apps/web/dist/assets/index-BVI_IS-R.css +0 -2
- package/apps/web/dist/assets/index-Bo9CNI_U.js +0 -22
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const fixtures: string[] = [];
|
|
10
|
+
const repositoryRoot = resolve(import.meta.dirname, "../../..");
|
|
11
|
+
|
|
12
|
+
// Stands in for npm so the smoke test can be exercised without packing and globally installing the real workspace: `pack` reports tarball names and `install` lays out
|
|
13
|
+
// the tree the script inspects, optionally without the files the root bin map points at.
|
|
14
|
+
const fakeNpmSource = `import { mkdirSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import process from "node:process";
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
if (args[0] === "pack") {
|
|
20
|
+
process.stdout.write(args.includes("--workspace") ? "core.tgz\\n" : "harness.tgz\\n");
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
if (args[0] !== "install") process.exit(1);
|
|
24
|
+
const prefix = args[args.indexOf("--prefix") + 1];
|
|
25
|
+
const harnessRoot = join(prefix, "lib", "node_modules", "@pi-harness", "pi-harness");
|
|
26
|
+
const agentRoot = join(harnessRoot, "node_modules", "@earendil-works", "pi-coding-agent");
|
|
27
|
+
const write = (path, content) => {
|
|
28
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
29
|
+
writeFileSync(path, content);
|
|
30
|
+
};
|
|
31
|
+
const bin = { "pi-harness": "./apps/web/server-dist/bin.js", pih: "./packages/cli/dist/bin.js" };
|
|
32
|
+
write(join(harnessRoot, "package.json"), JSON.stringify({ name: "@pi-harness/pi-harness", version: "9.9.9", bin, dependencies: { "@earendil-works/pi-coding-agent": "0.84.4" } }));
|
|
33
|
+
write(join(agentRoot, "package.json"), JSON.stringify({ name: "@earendil-works/pi-coding-agent", version: "0.84.4", dependencies: {} }));
|
|
34
|
+
write(join(agentRoot, "dist", "cli", "args.js"), "");
|
|
35
|
+
if (process.env.PACKED_PACKAGE_FIXTURE_BINS === "1") for (const target of Object.values(bin)) write(join(harnessRoot, target), "");
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const createFakeNpm = async (): Promise<string> => {
|
|
39
|
+
const fixture = await mkdtemp(resolve(tmpdir(), "pi-harness-packed-package-fixture-"));
|
|
40
|
+
fixtures.push(fixture);
|
|
41
|
+
const fakeNpm = resolve(fixture, "npm.mjs");
|
|
42
|
+
await writeFile(fakeNpm, fakeNpmSource);
|
|
43
|
+
return fakeNpm;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const runSmokeTest = async (fakeNpm: string, bins: boolean): ReturnType<typeof execFileAsync> =>
|
|
47
|
+
execFileAsync(process.execPath, [resolve(repositoryRoot, "scripts/test-packed-package.mjs")], {
|
|
48
|
+
env: { ...process.env, npm_execpath: fakeNpm, PACKED_PACKAGE_FIXTURE_BINS: bins ? "1" : "0" },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(async () => {
|
|
52
|
+
await Promise.all(fixtures.splice(0).map((fixture) => rm(fixture, { recursive: true, force: true })));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("packed package smoke test", () => {
|
|
56
|
+
it("accepts a tarball whose bin entrypoints are present", async () => {
|
|
57
|
+
const fakeNpm = await createFakeNpm();
|
|
58
|
+
|
|
59
|
+
const { stdout } = await runSmokeTest(fakeNpm, true);
|
|
60
|
+
|
|
61
|
+
expect(stdout).toContain("Packed package smoke test passed");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("rejects a tarball that ships no runnable bin entrypoint", async () => {
|
|
65
|
+
// `npm pack --ignore-scripts` never runs prepack, so an unbuilt workspace produces a tarball with a bin map pointing at files the tarball does not contain.
|
|
66
|
+
const fakeNpm = await createFakeNpm();
|
|
67
|
+
|
|
68
|
+
await expect(runSmokeTest(fakeNpm, false)).rejects.toThrow(/missing the pi-harness entrypoint \.\/apps\/web\/server-dist\/bin\.js/u);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
const repositoryRoot = resolve(import.meta.dirname, "../../..");
|
|
6
|
+
|
|
7
|
+
const readText = async (path: string): Promise<string> => readFile(resolve(repositoryRoot, path), "utf8");
|
|
8
|
+
|
|
9
|
+
// The number of built-in plugins that `docs/README.reference.md` still has no `###` section for. It may only ever go down.
|
|
10
|
+
const undocumentedBudget = 53;
|
|
11
|
+
|
|
12
|
+
describe("reference documentation", () => {
|
|
13
|
+
it("documents every registered API route", async () => {
|
|
14
|
+
// The reference is the inventory a reader audits the unauthenticated local port with, so an undocumented route is an invisible attack surface.
|
|
15
|
+
const gateway = await readText("packages/api-gateway/src/index.ts");
|
|
16
|
+
const reference = await readText("docs/README.reference.md");
|
|
17
|
+
const routes = [...gateway.matchAll(/path: "(\/api[^"]*)"/gu)].map((match) => match[1] ?? "");
|
|
18
|
+
|
|
19
|
+
expect(routes.length).toBeGreaterThan(0);
|
|
20
|
+
for (const route of routes) expect(reference, `${route} is not documented`).toContain(`\`${route}\``);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("claims documentation coverage only for the plugins it actually describes", async () => {
|
|
24
|
+
const readme = await readText("README.md");
|
|
25
|
+
const reference = await readText("docs/README.reference.md");
|
|
26
|
+
const sections = new Set([...reference.matchAll(/^### (.+)$/gmu)].map((match) => (match[1] ?? "").toLowerCase().replaceAll(/[^a-z]/gu, "")));
|
|
27
|
+
const plugins = (await readdir(resolve(repositoryRoot, "packages/core/src/plugins"), { withFileTypes: true }))
|
|
28
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".ts"))
|
|
29
|
+
.map((entry) => entry.name.replace(/\.ts$/u, "").replaceAll("-", ""));
|
|
30
|
+
const undocumented = plugins.filter((plugin) => !sections.has(plugin));
|
|
31
|
+
|
|
32
|
+
// A ratchet, not a floor: documenting more plugins only lowers the count, while a plugin that arrives without a reference section pushes it over the budget. Lower the budget whenever it drops.
|
|
33
|
+
expect(undocumented.length, `undocumented built-in plugins: ${undocumented.join(", ")}`).toBeLessThanOrEqual(undocumentedBudget);
|
|
34
|
+
expect(readme).not.toMatch(/documents every built-in plugin/u);
|
|
35
|
+
expect(reference).not.toMatch(/canonical reference for the complete plugin catalog/u);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("documents the model the shipped web profile actually selects", async () => {
|
|
39
|
+
const profile = await readText("apps/web/profile/cordis.yml");
|
|
40
|
+
const reference = await readText("docs/README.reference.md");
|
|
41
|
+
const readme = await readText("README.md");
|
|
42
|
+
const provider = /provider: .*\?\? '([^']+)'/u.exec(profile)?.[1];
|
|
43
|
+
const model = /model: .*\?\? '([^']+)'/u.exec(profile)?.[1];
|
|
44
|
+
|
|
45
|
+
expect(provider).toBeDefined();
|
|
46
|
+
expect(model).toBeDefined();
|
|
47
|
+
expect(reference).toContain(`\`${provider ?? ""}/${model ?? ""}\``);
|
|
48
|
+
for (const variable of ["PI_HARNESS_PROVIDER", "PI_HARNESS_MODEL"]) expect(readme, `${variable} is undocumented`).toContain(variable);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
|
|
@@ -35,4 +35,35 @@ describe("release package", () => {
|
|
|
35
35
|
expect(workflow).toContain("Verify package availability");
|
|
36
36
|
expect(workflow).not.toContain("RELEASE_TAG_EXISTS");
|
|
37
37
|
});
|
|
38
|
+
|
|
39
|
+
it("cleans every compiled workspace output", async () => {
|
|
40
|
+
// `tsc` never removes stale emit, so an output directory that no clean script touches keeps publishing files whose sources are gone.
|
|
41
|
+
const rootManifest = await readJson("package.json");
|
|
42
|
+
const rootClean = (rootManifest.scripts as Record<string, string>).clean ?? "";
|
|
43
|
+
const outputs: string[] = [];
|
|
44
|
+
|
|
45
|
+
for (const root of ["packages", "apps", "examples"]) {
|
|
46
|
+
for (const entry of await readdir(resolve(repositoryRoot, root), { withFileTypes: true })) {
|
|
47
|
+
if (!entry.isDirectory()) continue;
|
|
48
|
+
const workspace = `${root}/${entry.name}`;
|
|
49
|
+
const buildConfigPath = resolve(repositoryRoot, workspace, "tsconfig.build.json");
|
|
50
|
+
const hasBuildConfig = await stat(buildConfigPath).then(
|
|
51
|
+
() => true,
|
|
52
|
+
() => false,
|
|
53
|
+
);
|
|
54
|
+
if (!hasBuildConfig) continue;
|
|
55
|
+
const buildConfig = JSON.parse(await readFile(buildConfigPath, "utf8")) as { compilerOptions?: { outDir?: string } };
|
|
56
|
+
const outDir = buildConfig.compilerOptions?.outDir;
|
|
57
|
+
expect(outDir, `${workspace}/tsconfig.build.json declares no outDir`).toBeDefined();
|
|
58
|
+
const workspaceManifest = await readJson(`${workspace}/package.json`);
|
|
59
|
+
const workspaceClean = (workspaceManifest.scripts as Record<string, string> | undefined)?.clean;
|
|
60
|
+
outputs.push(`${workspace}/${outDir ?? ""}`);
|
|
61
|
+
expect(workspaceClean !== undefined || rootClean.includes(`${workspace}/${outDir ?? ""}`), `${workspace}/${outDir ?? ""} survives npm run clean`).toBe(
|
|
62
|
+
true,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
expect(outputs.length).toBeGreaterThan(0);
|
|
68
|
+
});
|
|
38
69
|
});
|
|
@@ -78,4 +78,44 @@ describe("release version preparation", () => {
|
|
|
78
78
|
expect(packedInternalEntry.version).toBe("0.1.3");
|
|
79
79
|
expect(packedExternalEntry.version).toBe("4.5.6");
|
|
80
80
|
});
|
|
81
|
+
|
|
82
|
+
it("re-runs against a checkout that already carries the release version", async () => {
|
|
83
|
+
// A re-dispatched release run checks out the release commit, so every manifest and marketplace entry already holds the requested version.
|
|
84
|
+
const fixture = await mkdtemp(resolve(tmpdir(), "pi-harness-release-version-"));
|
|
85
|
+
fixtures.push(fixture);
|
|
86
|
+
await Promise.all([mkdir(resolve(fixture, "apps")), mkdir(resolve(fixture, "examples"))]);
|
|
87
|
+
await writeJson(fixture, "package.json", { name: "@pi-harness/pi-harness", version: "0.1.3", dependencies: { "@pi-harness/core": "^0.1.3" } });
|
|
88
|
+
await writeJson(fixture, "packages/core/package.json", { name: "@pi-harness/core", version: "0.1.3" });
|
|
89
|
+
await writeJson(fixture, "packages/api-gateway/package.json", { name: "@pi-harness/api-gateway", version: "0.1.3" });
|
|
90
|
+
await writeJson(fixture, "package-lock.json", {
|
|
91
|
+
name: "@pi-harness/pi-harness",
|
|
92
|
+
version: "0.1.3",
|
|
93
|
+
lockfileVersion: 3,
|
|
94
|
+
packages: { "": { name: "@pi-harness/pi-harness", version: "0.1.3", dependencies: { "@pi-harness/core": "^0.1.3" } } },
|
|
95
|
+
});
|
|
96
|
+
const formattedInternalEntry = `{\n "packageName": "@pi-harness/core/plugins/example",\n "version": "0.1.3",\n "hooks": ["tool", "panel"]\n}\n`;
|
|
97
|
+
await mkdir(resolve(fixture, "packages/api-gateway/src/marketplace-entries/official"), { recursive: true });
|
|
98
|
+
await writeFile(resolve(fixture, "packages/api-gateway/src/marketplace-entries/official/internal.json"), formattedInternalEntry);
|
|
99
|
+
|
|
100
|
+
await execFileAsync(process.execPath, [resolve(repositoryRoot, "scripts/set-release-version.mjs"), "0.1.3"], { cwd: fixture });
|
|
101
|
+
|
|
102
|
+
expect(await readFile(resolve(fixture, "packages/api-gateway/src/marketplace-entries/official/internal.json"), "utf8")).toBe(formattedInternalEntry);
|
|
103
|
+
expect(await readFile(resolve(fixture, "packages/api-gateway/dist/marketplace-entries/official/internal.json"), "utf8")).toBe(formattedInternalEntry);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("rejects an internal marketplace entry without a top-level version field", async () => {
|
|
107
|
+
const fixture = await mkdtemp(resolve(tmpdir(), "pi-harness-release-version-"));
|
|
108
|
+
fixtures.push(fixture);
|
|
109
|
+
await Promise.all([mkdir(resolve(fixture, "apps")), mkdir(resolve(fixture, "examples"))]);
|
|
110
|
+
await writeJson(fixture, "package.json", { name: "@pi-harness/pi-harness", version: "0.1.2" });
|
|
111
|
+
await writeJson(fixture, "packages/core/package.json", { name: "@pi-harness/core", version: "0.1.2" });
|
|
112
|
+
await writeJson(fixture, "packages/api-gateway/package.json", { name: "@pi-harness/api-gateway", version: "0.1.2" });
|
|
113
|
+
await writeJson(fixture, "package-lock.json", { name: "@pi-harness/pi-harness", version: "0.1.2", lockfileVersion: 3, packages: {} });
|
|
114
|
+
await mkdir(resolve(fixture, "packages/api-gateway/src/marketplace-entries/official"), { recursive: true });
|
|
115
|
+
await writeJson(fixture, "packages/api-gateway/src/marketplace-entries/official/internal.json", { packageName: "@pi-harness/core/plugins/example" });
|
|
116
|
+
|
|
117
|
+
await expect(execFileAsync(process.execPath, [resolve(repositoryRoot, "scripts/set-release-version.mjs"), "0.1.3"], { cwd: fixture })).rejects.toThrow(
|
|
118
|
+
/has no top-level version field to update/u,
|
|
119
|
+
);
|
|
120
|
+
});
|
|
81
121
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4C,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAExF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,4BAA4B,CAAC;AAEpC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4C,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAExF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,4BAA4B,CAAC;AAEpC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAwBD,KAAK,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,YAAY,GAAG,UAAU,GAAG,KAAK,CAAC,CAAC;AAI7E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,eAA8B,GAAG,OAAO,CAGpH;;;;mBAwBgB,OAAO,UAAU,YAAY;;AAH9C,wBAuBE"}
|
|
@@ -8,7 +8,14 @@ const MIME_TYPES = {
|
|
|
8
8
|
".html": "text/html; charset=utf-8",
|
|
9
9
|
".js": "text/javascript; charset=utf-8",
|
|
10
10
|
".json": "application/json; charset=utf-8",
|
|
11
|
+
".png": "image/png",
|
|
11
12
|
".svg": "image/svg+xml",
|
|
13
|
+
".webmanifest": "application/manifest+json",
|
|
14
|
+
};
|
|
15
|
+
// The console renders model- and tool-produced markdown through dangerouslySetInnerHTML, so a CSP is the second line of defence behind the sanitizer: a bypass still cannot load a remote script or exfiltrate to another origin. frame-ancestors keeps any page the operator visits from framing the unauthenticated console, which the Host/Origin guard cannot block because browsers send no Origin on a frame navigation. Inline styles stay allowed because React writes element style attributes; nosniff is safe because every extension the bundle ships has an entry in MIME_TYPES.
|
|
16
|
+
const SECURITY_HEADERS = {
|
|
17
|
+
"content-security-policy": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; worker-src 'self' blob:; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
|
|
18
|
+
"x-content-type-options": "nosniff",
|
|
12
19
|
};
|
|
13
20
|
function contentType(path) {
|
|
14
21
|
const extension = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
@@ -30,7 +37,7 @@ async function sendFile(path, response) {
|
|
|
30
37
|
const info = await stat(path);
|
|
31
38
|
if (!info.isFile())
|
|
32
39
|
throw new Error("Not a file");
|
|
33
|
-
response.writeHead(200, { "content-type": contentType(path), "content-length": info.size, "cache-control": "no-cache" });
|
|
40
|
+
response.writeHead(200, { "content-type": contentType(path), "content-length": info.size, "cache-control": "no-cache", ...SECURITY_HEADERS });
|
|
34
41
|
await pipeline(createReadStream(path), response);
|
|
35
42
|
}
|
|
36
43
|
async function sendSafeFile(root, path, response) {
|
|
@@ -55,8 +62,10 @@ export default {
|
|
|
55
62
|
await sendSafeFile(root, requested, response);
|
|
56
63
|
return;
|
|
57
64
|
}
|
|
58
|
-
catch {
|
|
59
|
-
// SPA fallback below.
|
|
65
|
+
catch (error) {
|
|
66
|
+
// SPA fallback below, but only while nothing has reached the wire: once sendFile has written the 200 header the request is committed to that file, so a second writeHead would throw ERR_HTTP_HEADERS_SENT over the real failure.
|
|
67
|
+
if (response.headersSent)
|
|
68
|
+
throw error;
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
await sendSafeFile(root, index, response);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAqB,MAAM,WAAW,CAAC;AACxF,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,4BAA4B,CAAC;AAMpC,MAAM,UAAU,GAA2B;IACzC,MAAM,EAAE,yBAAyB;IACjC,OAAO,EAAE,0BAA0B;IACnC,KAAK,EAAE,gCAAgC;IACvC,OAAO,EAAE,iCAAiC;IAC1C,MAAM,EAAE,eAAe;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,9 +15,18 @@ const MIME_TYPES: Record<string, string> = {
|
|
|
15
15
|
".html": "text/html; charset=utf-8",
|
|
16
16
|
".js": "text/javascript; charset=utf-8",
|
|
17
17
|
".json": "application/json; charset=utf-8",
|
|
18
|
+
".png": "image/png",
|
|
18
19
|
".svg": "image/svg+xml",
|
|
20
|
+
".webmanifest": "application/manifest+json",
|
|
19
21
|
};
|
|
20
22
|
|
|
23
|
+
// The console renders model- and tool-produced markdown through dangerouslySetInnerHTML, so a CSP is the second line of defence behind the sanitizer: a bypass still cannot load a remote script or exfiltrate to another origin. frame-ancestors keeps any page the operator visits from framing the unauthenticated console, which the Host/Origin guard cannot block because browsers send no Origin on a frame navigation. Inline styles stay allowed because React writes element style attributes; nosniff is safe because every extension the bundle ships has an entry in MIME_TYPES.
|
|
24
|
+
const SECURITY_HEADERS = {
|
|
25
|
+
"content-security-policy":
|
|
26
|
+
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; worker-src 'self' blob:; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
|
|
27
|
+
"x-content-type-options": "nosniff",
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
21
30
|
function contentType(path: string): string {
|
|
22
31
|
const extension = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
23
32
|
return MIME_TYPES[extension] ?? "application/octet-stream";
|
|
@@ -41,7 +50,7 @@ function safePath(root: string, pathname: string): string | undefined {
|
|
|
41
50
|
async function sendFile(path: string, response: ServerResponse): Promise<void> {
|
|
42
51
|
const info = await stat(path);
|
|
43
52
|
if (!info.isFile()) throw new Error("Not a file");
|
|
44
|
-
response.writeHead(200, { "content-type": contentType(path), "content-length": info.size, "cache-control": "no-cache" });
|
|
53
|
+
response.writeHead(200, { "content-type": contentType(path), "content-length": info.size, "cache-control": "no-cache", ...SECURITY_HEADERS });
|
|
45
54
|
await pipeline(createReadStream(path), response);
|
|
46
55
|
}
|
|
47
56
|
|
|
@@ -65,8 +74,9 @@ export default {
|
|
|
65
74
|
try {
|
|
66
75
|
await sendSafeFile(root, requested, response);
|
|
67
76
|
return;
|
|
68
|
-
} catch {
|
|
69
|
-
// SPA fallback below.
|
|
77
|
+
} catch (error) {
|
|
78
|
+
// SPA fallback below, but only while nothing has reached the wire: once sendFile has written the 200 header the request is committed to that file, so a second writeHead would throw ERR_HTTP_HEADERS_SENT over the real failure.
|
|
79
|
+
if (response.headersSent) throw error;
|
|
70
80
|
}
|
|
71
81
|
}
|
|
72
82
|
await sendSafeFile(root, index, response);
|
|
@@ -1,13 +1,52 @@
|
|
|
1
|
-
import { mkdtemp, symlink, writeFile } from "node:fs/promises";
|
|
1
|
+
import { chmod, mkdtemp, symlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join, posix, win32 } from "node:path";
|
|
4
|
+
import { PassThrough } from "node:stream";
|
|
4
5
|
import { Context } from "@deepseek-ai/cordis";
|
|
5
6
|
import { afterEach, describe, expect, test } from "vitest";
|
|
6
|
-
import webServerPlugin from "@pi-harness/host-webserver";
|
|
7
|
+
import webServerPlugin, { type WebRoute, type WebServer } from "@pi-harness/host-webserver";
|
|
7
8
|
import webAppPlugin, { isInsideStaticRoot } from "../src/index.js";
|
|
9
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
8
10
|
|
|
9
11
|
const contexts: Context[] = [];
|
|
10
12
|
|
|
13
|
+
// A response that records every writeHead and rejects a second one exactly like node:http, so a failure raised after the headers are on the wire is observable without a socket.
|
|
14
|
+
type MockResponse = PassThrough & { headersSent: boolean; writeHead: (status: number, headers?: unknown) => MockResponse };
|
|
15
|
+
|
|
16
|
+
function mockResponse(): { response: ServerResponse; statuses: number[] } {
|
|
17
|
+
const stream = new PassThrough() as MockResponse;
|
|
18
|
+
stream.resume();
|
|
19
|
+
const statuses: number[] = [];
|
|
20
|
+
stream.headersSent = false;
|
|
21
|
+
stream.writeHead = (status) => {
|
|
22
|
+
if (stream.headersSent) throw Object.assign(new Error("Cannot write headers after they are sent to the client"), { code: "ERR_HTTP_HEADERS_SENT" });
|
|
23
|
+
stream.headersSent = true;
|
|
24
|
+
statuses.push(status);
|
|
25
|
+
return stream;
|
|
26
|
+
};
|
|
27
|
+
return { response: stream as unknown as ServerResponse, statuses };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Captures the fallback the web app registers so it can be driven with a mock response instead of a real socket.
|
|
31
|
+
function provideFakeWebServer(context: Context): { current: WebRoute["handler"] | undefined } {
|
|
32
|
+
const captured: { current: WebRoute["handler"] | undefined } = { current: undefined };
|
|
33
|
+
const service: WebServer = {
|
|
34
|
+
host: "127.0.0.1",
|
|
35
|
+
port: 3141,
|
|
36
|
+
url: "http://127.0.0.1:3141",
|
|
37
|
+
register: () => () => {},
|
|
38
|
+
registerFallback(handler) {
|
|
39
|
+
captured.current = handler;
|
|
40
|
+
return () => {
|
|
41
|
+
captured.current = undefined;
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
close: async () => {},
|
|
45
|
+
};
|
|
46
|
+
context.provide("webServer", service);
|
|
47
|
+
return captured;
|
|
48
|
+
}
|
|
49
|
+
|
|
11
50
|
afterEach(async () => {
|
|
12
51
|
await Promise.all(contexts.splice(0).map(async (context) => context.fiber.dispose()));
|
|
13
52
|
});
|
|
@@ -26,6 +65,48 @@ describe("web app plugin", () => {
|
|
|
26
65
|
await expect(fetch(context.webServer.url + "/missing-route").then((response) => response.text())).resolves.toBe("<main>console</main>");
|
|
27
66
|
await expect(fetch(context.webServer.url + "/leak").then((response) => response.text())).resolves.toBe("<main>console</main>");
|
|
28
67
|
});
|
|
68
|
+
|
|
69
|
+
test("sends a content security policy and a declared content type with every static response", async () => {
|
|
70
|
+
const root = await mkdtemp(join(tmpdir(), "pi-harness-web-root-"));
|
|
71
|
+
await writeFile(join(root, "index.html"), "<main>console</main>", "utf8");
|
|
72
|
+
await writeFile(join(root, "icon.png"), Buffer.from("89504e470d0a1a0a", "hex"));
|
|
73
|
+
const context = new Context();
|
|
74
|
+
contexts.push(context);
|
|
75
|
+
await context.plugin(webServerPlugin, { host: "127.0.0.1", port: 0 });
|
|
76
|
+
await context.plugin(webAppPlugin, { staticDir: root });
|
|
77
|
+
|
|
78
|
+
const page = await fetch(context.webServer.url + "/");
|
|
79
|
+
expect(page.headers.get("x-content-type-options")).toBe("nosniff");
|
|
80
|
+
const policy = page.headers.get("content-security-policy") ?? "";
|
|
81
|
+
expect(policy).toContain("default-src 'self'");
|
|
82
|
+
expect(policy).toContain("script-src 'self'");
|
|
83
|
+
expect(policy).toContain("object-src 'none'");
|
|
84
|
+
expect(policy).toContain("frame-ancestors 'none'");
|
|
85
|
+
|
|
86
|
+
const icon = await fetch(context.webServer.url + "/icon.png");
|
|
87
|
+
expect(icon.headers.get("content-type")).toBe("image/png");
|
|
88
|
+
expect(icon.headers.get("x-content-type-options")).toBe("nosniff");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("reports a read failure raised after the headers are sent instead of writing the SPA fallback over it", async () => {
|
|
92
|
+
const root = await mkdtemp(join(tmpdir(), "pi-harness-web-root-"));
|
|
93
|
+
await writeFile(join(root, "index.html"), "<main>console</main>", "utf8");
|
|
94
|
+
await writeFile(join(root, "locked.js"), "export const value = 1;\n", "utf8");
|
|
95
|
+
await chmod(join(root, "locked.js"), 0o000);
|
|
96
|
+
const context = new Context();
|
|
97
|
+
contexts.push(context);
|
|
98
|
+
const fallback = provideFakeWebServer(context);
|
|
99
|
+
await context.plugin(webAppPlugin, { staticDir: root });
|
|
100
|
+
expect(fallback.current).toBeDefined();
|
|
101
|
+
|
|
102
|
+
const locked = mockResponse();
|
|
103
|
+
await expect(fallback.current?.({ url: "/locked.js" } as IncomingMessage, locked.response)).rejects.toThrow(/EACCES/);
|
|
104
|
+
expect(locked.statuses).toEqual([200]);
|
|
105
|
+
|
|
106
|
+
const missing = mockResponse();
|
|
107
|
+
await expect(fallback.current?.({ url: "/missing-route" } as IncomingMessage, missing.response)).resolves.toBeUndefined();
|
|
108
|
+
expect(missing.statuses).toEqual([200]);
|
|
109
|
+
});
|
|
29
110
|
});
|
|
30
111
|
|
|
31
112
|
describe("static root containment", () => {
|
|
@@ -12,5 +12,6 @@ export interface CliEnvironment {
|
|
|
12
12
|
forceExit(code: number): void;
|
|
13
13
|
onSignal(listener: (signal: NodeJS.Signals) => void): () => void;
|
|
14
14
|
}
|
|
15
|
+
export declare const DUPLICATE_SIGNAL_WINDOW_MS = 50;
|
|
15
16
|
export declare function runCli(_args: readonly string[], _environment: CliEnvironment): Promise<number>;
|
|
16
17
|
//# sourceMappingURL=main.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAMtD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAClE;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAMtD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAClE;AAgCD,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAM7C,wBAAsB,MAAM,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,YAAY,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmLpG"}
|
|
@@ -28,6 +28,8 @@ application plugin. The bundled stdio application reads its prompt from --prompt
|
|
|
28
28
|
with a dash.
|
|
29
29
|
`;
|
|
30
30
|
const FLUSH_TIMEOUT_MS = 2_000;
|
|
31
|
+
// A terminal broadcasts Ctrl-C to the whole foreground process group, so under `pih --profile development` this process receives SIGINT twice: once from the terminal and once relayed by the supervisor, measured under a millisecond apart. 50ms is far above that relay latency and far below the hundreds of milliseconds between two deliberate key presses, so a repeat inside the window is one keypress delivered twice and a repeat after it is still a force-quit request.
|
|
32
|
+
export const DUPLICATE_SIGNAL_WINDOW_MS = 50;
|
|
31
33
|
function signalExitCode(signal) {
|
|
32
34
|
return signal === "SIGINT" ? 130 : signal === "SIGHUP" ? 129 : 143;
|
|
33
35
|
}
|
|
@@ -98,8 +100,17 @@ export async function runCli(_args, _environment) {
|
|
|
98
100
|
const startupAbort = new AbortController();
|
|
99
101
|
const stdio = new NodeStdio(environment.stdin, environment.stdout, environment.stderr);
|
|
100
102
|
let signalCount = 0;
|
|
103
|
+
let lastSignal;
|
|
104
|
+
let lastSignalAt = 0;
|
|
101
105
|
const removeSignals = environment.onSignal((signal) => {
|
|
102
106
|
const code = signalExitCode(signal);
|
|
107
|
+
// performance.now() is monotonic, so a clock adjustment cannot turn a duplicate into a force-quit or the reverse.
|
|
108
|
+
const receivedAt = performance.now();
|
|
109
|
+
// The window is measured from the first delivery, not the previous one, so a burst cannot postpone the force-quit indefinitely.
|
|
110
|
+
if (signal === lastSignal && receivedAt - lastSignalAt < DUPLICATE_SIGNAL_WINDOW_MS)
|
|
111
|
+
return;
|
|
112
|
+
lastSignal = signal;
|
|
113
|
+
lastSignalAt = receivedAt;
|
|
103
114
|
signalCount += 1;
|
|
104
115
|
if (signalCount > 1) {
|
|
105
116
|
environment.stderr.write(`Received ${signal} again; exiting immediately\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,oBAAoB,EAAsB,MAAM,kBAAkB,CAAC;AACtJ,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAkB7D,KAAK,UAAU,YAAY,CAAI,OAAmB,EAAE,SAAiB;IACnE,IAAI,KAAiC,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,EAAE;QAC1D,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAa,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3G,IAAI,KAAK,KAAK,SAAS;QAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,SAAS,GAAG;;;wDAGsC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;CAUlF,CAAC;AAEF,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,qdAAqd;AACrd,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C,SAAS,cAAc,CAAC,MAAsB;IAC5C,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAwB,EAAE,YAA4B;IACjF,MAAM,IAAI,GAAG,KAAK,CAAC;IACnB,MAAM,WAAW,GAAG,YAAY,CAAC;IACjC,IAAI,UAAgD,CAAC;IACrD,IAAI,CAAC;QACH,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxF,OAAO,KAAK,YAAY,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC/B,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpC,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAClC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,OAAO,IAAI,CAAC,CAAC;QACrD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,oBAAoB,CAAC;YACtC,GAAG,CAAC,UAAU,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,CAAC;YAC/H,GAAG,EAAE,WAAW,CAAC,GAAG;SACrB,CAAC,CAAC;QACH,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;YAC1B,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;YAC7D,OAAO,CAAC,CAAC;QACX,CAAC;QACD,IAAI,WAAW,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YAC9C,KAAK,OAAO,CAAC,OAAO,EAAE;iBACnB,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,eAAe,EAAE,EAAE,CAAC;iBAC3C,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBACf,IAAI,MAAM,KAAK,SAAS;oBAAE,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC7D,CAAC,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,OAAkC,CAAC;IACvC,IAAI,UAA8B,CAAC;IACnC,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,MAAM,SAAS,GAAG,KAAK,EAAE,IAAY,EAAE,KAAK,GAAG,IAAI,EAAE,EAAE;QACrD,IAAI,UAAU;YAAE,OAAO;QACvB,UAAU,GAAG,IAAI,CAAC;QAClB,+FAA+F;QAC/F,kFAAkF;QAClF,IAAI,KAAK;YAAE,MAAM,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAC/D,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC;IACF,IAAI,aAAmD,CAAC;IACxD,MAAM,oBAAoB,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QAC3D,aAAa,GAAG,OAAO,CAAC;IAC1B,CAAC,CAAC,CAAC;IACH,IAAI,aAAmD,CAAC;IACxD,MAAM,aAAa,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACpD,aAAa,GAAG,OAAO,CAAC;IAC1B,CAAC,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,IAAI,eAAe,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACvF,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,UAAsC,CAAC;IAC3C,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,MAAM,aAAa,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,EAAE;QACpD,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACpC,kHAAkH;QAClH,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QACrC,gIAAgI;QAChI,IAAI,MAAM,KAAK,UAAU,IAAI,UAAU,GAAG,YAAY,GAAG,0BAA0B;YAAE,OAAO;QAC5F,UAAU,GAAG,MAAM,CAAC;QACpB,YAAY,GAAG,UAAU,CAAC;QAC1B,WAAW,IAAI,CAAC,CAAC;QACjB,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACpB,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,MAAM,+BAA+B,CAAC,CAAC;YAC5E,KAAK,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC;QACtB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,YAAY,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,WAAW,GAAyB,WAAW,CAAC;YACpD,UAAU;YACV,MAAM,EAAE,YAAY,CAAC,MAAM;YAC3B,YAAY;gBACV,IAAI,WAAW,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;oBACpC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,0GAA0G,CAAC,CAAC;oBACrI,OAAO;gBACT,CAAC;gBACD,aAAa,EAAE,CAAC,4BAA4B,CAAC,CAAC;YAChD,CAAC;YACD,OAAO,CAAC,OAAO;gBACb,oBAAoB,CAAC,OAAO,EAAE;oBAC5B,GAAG,EAAE,WAAW,CAAC,GAAG;oBACpB,QAAQ,EAAE,WAAW,CAAC,QAAQ;oBAC9B,IAAI,EAAE,UAAU,CAAC,IAAI;oBACrB,WAAW,CAAC,IAAI;wBACd,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC;oBACxB,CAAC;iBACF,CAAC,CAAC;gBACH,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACtC,CAAC;SACF,CAAC,CAAC,IAAI,CACL,CAAC,MAAM,EAAe,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAC7D,CAAC,KAAc,EAAe,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAC5D,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACrH,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;YAC1B,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAC;YAC/E,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC9B,OAAO,OAAO,CAAC,IAAI,CAAC;YACtB,CAAC;YACD,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO;gBAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;YACpE,OAAO,OAAO,CAAC,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO;YAAE,MAAM,OAAO,CAAC,KAAK,CAAC;QAClD,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC1B,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACzD,IAAI,WAAW,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QACzG,MAAM,aAAa,GAAG,IAAI,eAAe,EAAE,CAAC;QAC5C,MAAM,cAAc,GAAG,WAAW,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,aAAsB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACxH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAChC,cAAc;YACd,oBAAoB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3E,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;SACpE,CAAC,CAAC;QACH,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;YACpC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACjD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,OAAO,GAAG,MAAM,YAAY,CAChC,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,CAClB,GAAG,EAAE,CAAC,SAAS,EACf,CAAC,KAAc,EAAE,EAAE;oBACjB,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC3G,CAAC,CACF,EACD,WAAW,CAAC,iBAAiB,CAC9B,CAAC;gBACF,IAAI,CAAC,OAAO,CAAC,OAAO;oBAAE,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACrD,CAAC;YACD,uFAAuF;YACvF,qFAAqF;YACrF,MAAM,QAAQ,GAAG,MAAM,YAAY,CACjC,cAAc,CAAC,IAAI,CACjB,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CAChB,EACD,WAAW,CAAC,iBAAiB,CAC9B,CAAC;YACF,IAAI,CAAC,QAAQ,CAAC,OAAO;gBAAE,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzG,UAAU,GAAG,CAAC,CAAC;QACf,OAAO,CAAC,CAAC;IACX,CAAC;YAAS,CAAC;QACT,aAAa,EAAE,CAAC;QAChB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,MAAM,YAAY,CACjC,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CACpB,GAAG,EAAE,CAAC,SAAS,EACf,CAAC,KAAc,EAAE,EAAE;gBACjB,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3G,CAAC,CACF,EACD,WAAW,CAAC,iBAAiB,CAC9B,CAAC;YACF,IAAI,CAAC,QAAQ,CAAC,OAAO;gBAAE,MAAM,SAAS,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relaunch.d.ts","sourceRoot":"","sources":["../src/relaunch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAK9D,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAW/C,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"relaunch.d.ts","sourceRoot":"","sources":["../src/relaunch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAK9D,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAW/C,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCvI;AAED,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAQjH"}
|
|
@@ -18,12 +18,10 @@ export async function superviseDevelopmentProcess(command, args, options = {}) {
|
|
|
18
18
|
const result = await new Promise((resolve, reject) => {
|
|
19
19
|
const child = spawn(command, [...args], options);
|
|
20
20
|
const signals = process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
21
|
-
//
|
|
22
|
-
const forwarded = new Set(process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGTERM"]);
|
|
21
|
+
// Every signal this supervisor receives is relayed to the child, which is what a signal addressed to this pid alone (`kill -INT`, a process manager, a `timeout` wrapper) needs: it reaches no other process, so without the relay the child keeps running while the supervisor waits for an exit that never comes. Registering a listener also keeps the supervisor alive past the default disposition so it can report the child's exit code. When a terminal broadcasts the signal to the shared foreground process group instead, the child receives it twice - once from the terminal, once from this relay - because nothing observable here separates a group broadcast from a pid-directed signal; a child that treats a repeated signal as a force-quit request has to collapse that pair itself, which `runCli` does with DUPLICATE_SIGNAL_WINDOW_MS in main.ts.
|
|
23
22
|
const handlers = signals.map((signal) => {
|
|
24
23
|
const handler = () => {
|
|
25
|
-
|
|
26
|
-
child.kill(signal);
|
|
24
|
+
child.kill(signal);
|
|
27
25
|
};
|
|
28
26
|
process.on(signal, handler);
|
|
29
27
|
return { signal, handler };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relaunch.js","sourceRoot":"","sources":["../src/relaunch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAC/C,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACjC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAC/B,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B,SAAS,cAAc,CAAC,MAA6B;IACnD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,OAAe,EAAE,IAAuB,EAAE,UAAwB,EAAE;IACpH,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAyD,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3G,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,OAAO,GAAqB,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;YACzH,
|
|
1
|
+
{"version":3,"file":"relaunch.js","sourceRoot":"","sources":["../src/relaunch.ts"],"names":[],"mappings":"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"}
|
package/packages/cli/src/main.ts
CHANGED
|
@@ -48,6 +48,9 @@ with a dash.
|
|
|
48
48
|
|
|
49
49
|
const FLUSH_TIMEOUT_MS = 2_000;
|
|
50
50
|
|
|
51
|
+
// A terminal broadcasts Ctrl-C to the whole foreground process group, so under `pih --profile development` this process receives SIGINT twice: once from the terminal and once relayed by the supervisor, measured under a millisecond apart. 50ms is far above that relay latency and far below the hundreds of milliseconds between two deliberate key presses, so a repeat inside the window is one keypress delivered twice and a repeat after it is still a force-quit request.
|
|
52
|
+
export const DUPLICATE_SIGNAL_WINDOW_MS = 50;
|
|
53
|
+
|
|
51
54
|
function signalExitCode(signal: NodeJS.Signals): number {
|
|
52
55
|
return signal === "SIGINT" ? 130 : signal === "SIGHUP" ? 129 : 143;
|
|
53
56
|
}
|
|
@@ -115,8 +118,16 @@ export async function runCli(_args: readonly string[], _environment: CliEnvironm
|
|
|
115
118
|
const startupAbort = new AbortController();
|
|
116
119
|
const stdio = new NodeStdio(environment.stdin, environment.stdout, environment.stderr);
|
|
117
120
|
let signalCount = 0;
|
|
121
|
+
let lastSignal: NodeJS.Signals | undefined;
|
|
122
|
+
let lastSignalAt = 0;
|
|
118
123
|
const removeSignals = environment.onSignal((signal) => {
|
|
119
124
|
const code = signalExitCode(signal);
|
|
125
|
+
// performance.now() is monotonic, so a clock adjustment cannot turn a duplicate into a force-quit or the reverse.
|
|
126
|
+
const receivedAt = performance.now();
|
|
127
|
+
// The window is measured from the first delivery, not the previous one, so a burst cannot postpone the force-quit indefinitely.
|
|
128
|
+
if (signal === lastSignal && receivedAt - lastSignalAt < DUPLICATE_SIGNAL_WINDOW_MS) return;
|
|
129
|
+
lastSignal = signal;
|
|
130
|
+
lastSignalAt = receivedAt;
|
|
120
131
|
signalCount += 1;
|
|
121
132
|
if (signalCount > 1) {
|
|
122
133
|
environment.stderr.write(`Received ${signal} again; exiting immediately\n`);
|
|
@@ -20,11 +20,10 @@ export async function superviseDevelopmentProcess(command: string, args: readonl
|
|
|
20
20
|
const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {
|
|
21
21
|
const child = spawn(command, [...args], options);
|
|
22
22
|
const signals: NodeJS.Signals[] = process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
23
|
-
//
|
|
24
|
-
const forwarded = new Set<NodeJS.Signals>(process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGTERM"]);
|
|
23
|
+
// Every signal this supervisor receives is relayed to the child, which is what a signal addressed to this pid alone (`kill -INT`, a process manager, a `timeout` wrapper) needs: it reaches no other process, so without the relay the child keeps running while the supervisor waits for an exit that never comes. Registering a listener also keeps the supervisor alive past the default disposition so it can report the child's exit code. When a terminal broadcasts the signal to the shared foreground process group instead, the child receives it twice - once from the terminal, once from this relay - because nothing observable here separates a group broadcast from a pid-directed signal; a child that treats a repeated signal as a force-quit request has to collapse that pair itself, which `runCli` does with DUPLICATE_SIGNAL_WINDOW_MS in main.ts.
|
|
25
24
|
const handlers = signals.map((signal) => {
|
|
26
25
|
const handler = () => {
|
|
27
|
-
|
|
26
|
+
child.kill(signal);
|
|
28
27
|
};
|
|
29
28
|
process.on(signal, handler);
|
|
30
29
|
return { signal, handler };
|