@piagent/platform 1.3.1 → 1.4.1
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/CHANGELOG.md +132 -0
- package/README.md +10 -4
- package/README.vi.md +27 -0
- package/architecture/layers.json +96 -1
- package/docs/README.md +1 -0
- package/docs/architecture.md +1 -0
- package/docs/authority-manifest.md +1 -0
- package/docs/auto-delegation-policy.md +1 -0
- package/docs/capability-packs.md +1 -0
- package/docs/command-reference-vietnamese.md +5 -4
- package/docs/context-engine.md +1 -0
- package/docs/context-window-policy.md +1 -0
- package/docs/distribution-standard.md +8 -7
- package/docs/en/architecture.md +13 -0
- package/docs/herdr-workflow.md +1 -0
- package/docs/languages.json +20 -4
- package/docs/mcp-and-tools.md +125 -9
- package/docs/memory-policy.md +1 -0
- package/docs/model-options.md +1 -0
- package/docs/oauth-providers.md +1 -0
- package/docs/operator-manual-vietnamese.md +15 -12
- package/docs/package-architecture-notes.md +1 -0
- package/docs/project-adapters.md +1 -0
- package/docs/project-onboarding.md +1 -0
- package/docs/publishing-for-teams.md +37 -0
- package/docs/quality-benchmark.md +1 -0
- package/docs/quickstart-vietnamese.md +3 -2
- package/docs/release-install-policy.md +22 -8
- package/docs/runtime-harness-standard.md +2 -1
- package/docs/runtime-policy-design.md +1 -0
- package/docs/runtime-quality-baseline.md +1 -0
- package/docs/security-redaction-benchmark.md +1 -0
- package/docs/security-threat-model.md +2 -1
- package/docs/subagent-orchestration-capabilities.md +1 -0
- package/docs/subagents-and-multiagent.md +1 -0
- package/docs/task-implementation-contract.md +12 -4
- package/docs/task-lifecycle-vietnamese.md +1 -0
- package/docs/team-onboarding.md +3 -2
- package/docs/third-party-neutrality.md +26 -0
- package/docs/usage-observability.md +71 -0
- package/docs/vercel-docs-site.md +1 -0
- package/docs/vi/architecture.md +13 -0
- package/docs/workflow-recipes.md +1 -0
- package/docs-site/architecture.html +5 -1
- package/docs-site/benchmark.html +5 -1
- package/docs-site/commands.html +5 -1
- package/docs-site/content/en/index.html +49 -0
- package/docs-site/content/en/runtime.html +13 -0
- package/docs-site/content/index.html +49 -0
- package/docs-site/content/runtime.html +28 -0
- package/docs-site/context-engine.html +5 -1
- package/docs-site/en/architecture.html +5 -1
- package/docs-site/en/benchmark.html +5 -1
- package/docs-site/en/commands.html +5 -1
- package/docs-site/en/context-engine.html +5 -1
- package/docs-site/en/index.html +54 -1
- package/docs-site/en/mcp-approval.html +5 -1
- package/docs-site/en/mcp-auth.html +5 -1
- package/docs-site/en/mcp-servers.html +5 -1
- package/docs-site/en/mcp.html +5 -1
- package/docs-site/en/permissions.html +5 -1
- package/docs-site/en/profiles.html +5 -1
- package/docs-site/en/quickstart.html +5 -1
- package/docs-site/en/runtime.html +18 -1
- package/docs-site/en/scope.html +5 -1
- package/docs-site/en/security.html +5 -1
- package/docs-site/en/team.html +5 -1
- package/docs-site/en/whats-new.html +5 -1
- package/docs-site/en/workflows.html +5 -1
- package/docs-site/index.html +54 -1
- package/docs-site/mcp-approval.html +5 -1
- package/docs-site/mcp-auth.html +5 -1
- package/docs-site/mcp-servers.html +5 -1
- package/docs-site/mcp.html +5 -1
- package/docs-site/permissions.html +5 -1
- package/docs-site/profiles.html +5 -1
- package/docs-site/quickstart.html +5 -1
- package/docs-site/runtime.html +33 -1
- package/docs-site/scope.html +5 -1
- package/docs-site/security.html +5 -1
- package/docs-site/team.html +5 -1
- package/docs-site/whats-new.html +5 -1
- package/docs-site/workflows.html +5 -1
- package/evals/fixtures/mutation-provenance-record.invalid.json +17 -0
- package/evals/fixtures/mutation-provenance-record.valid.json +27 -0
- package/evals/fixtures/piagent-webui/approval-v1.invalid.json +54 -0
- package/evals/fixtures/piagent-webui/approval-v1.valid.json +58 -0
- package/evals/fixtures/piagent-webui/attachment-v1.invalid.json +25 -0
- package/evals/fixtures/piagent-webui/attachment-v1.valid.json +25 -0
- package/evals/fixtures/piagent-webui/capabilities-v1.invalid.json +51 -0
- package/evals/fixtures/piagent-webui/capabilities-v1.valid.json +122 -0
- package/evals/fixtures/piagent-webui/commit-summary-v1.invalid.json +10 -0
- package/evals/fixtures/piagent-webui/commit-summary-v1.valid.json +25 -0
- package/evals/fixtures/piagent-webui/control-command-v1.invalid.json +37 -0
- package/evals/fixtures/piagent-webui/control-command-v1.valid.json +35 -0
- package/evals/fixtures/piagent-webui/diff-v1.invalid.json +13 -0
- package/evals/fixtures/piagent-webui/diff-v1.valid.json +141 -0
- package/evals/fixtures/piagent-webui/gateway-capabilities-v1.invalid.json +35 -0
- package/evals/fixtures/piagent-webui/gateway-capabilities-v1.valid.json +35 -0
- package/evals/fixtures/piagent-webui/gateway-protocol-v1.invalid.json +14 -0
- package/evals/fixtures/piagent-webui/gateway-protocol-v1.valid.json +14 -0
- package/evals/fixtures/piagent-webui/handoff-history-v1.invalid.json +16 -0
- package/evals/fixtures/piagent-webui/handoff-history-v1.valid.json +16 -0
- package/evals/fixtures/piagent-webui/model-catalog-v1.invalid.json +31 -0
- package/evals/fixtures/piagent-webui/model-catalog-v1.valid.json +43 -0
- package/evals/fixtures/piagent-webui/queue-v1.invalid.json +29 -0
- package/evals/fixtures/piagent-webui/queue-v1.valid.json +44 -0
- package/evals/fixtures/piagent-webui/recovery-history-v1.invalid.json +17 -0
- package/evals/fixtures/piagent-webui/recovery-history-v1.valid.json +20 -0
- package/evals/fixtures/piagent-webui/release-monitor-v1.invalid.json +12 -0
- package/evals/fixtures/piagent-webui/release-monitor-v1.valid.json +29 -0
- package/evals/fixtures/piagent-webui/review-state-v1.invalid.json +25 -0
- package/evals/fixtures/piagent-webui/review-state-v1.valid.json +38 -0
- package/evals/fixtures/piagent-webui/runtime-event-v2.invalid.json +49 -0
- package/evals/fixtures/piagent-webui/runtime-event-v2.valid.json +49 -0
- package/evals/fixtures/piagent-webui/session-catalog-v1.invalid.json +17 -0
- package/evals/fixtures/piagent-webui/session-catalog-v1.valid.json +58 -0
- package/evals/fixtures/piagent-webui/session-command-v1.invalid.json +19 -0
- package/evals/fixtures/piagent-webui/session-command-v1.valid.json +19 -0
- package/evals/fixtures/piagent-webui/snapshot-v1.invalid.json +5 -0
- package/evals/fixtures/piagent-webui/snapshot-v1.valid.json +191 -0
- package/evals/fixtures/piagent-webui/source-change-v1.invalid.json +57 -0
- package/evals/fixtures/piagent-webui/source-change-v1.valid.json +98 -0
- package/evals/fixtures/piagent-webui/source-mutation-v1.invalid.json +19 -0
- package/evals/fixtures/piagent-webui/source-mutation-v1.valid.json +38 -0
- package/evals/fixtures/piagent-webui/source-revert-v1.invalid.json +20 -0
- package/evals/fixtures/piagent-webui/source-revert-v1.valid.json +62 -0
- package/evals/fixtures/piagent-webui/subagent-tree-v1.invalid.json +26 -0
- package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +39 -0
- package/evals/fixtures/piagent-webui/task-index-v1.invalid.json +15 -0
- package/evals/fixtures/piagent-webui/task-index-v1.valid.json +15 -0
- package/evals/fixtures/piagent-webui/task-timeline-v1.invalid.json +14 -0
- package/evals/fixtures/piagent-webui/task-timeline-v1.valid.json +16 -0
- package/evals/fixtures/piagent-webui/transcript-v1.invalid.json +35 -0
- package/evals/fixtures/piagent-webui/transcript-v1.valid.json +55 -0
- package/evals/fixtures/task-baseline-manifest.invalid.json +16 -0
- package/evals/fixtures/task-baseline-manifest.valid.json +41 -0
- package/evals/fixtures/verifier-file-snapshot.invalid.json +21 -0
- package/evals/fixtures/verifier-file-snapshot.valid.json +21 -0
- package/package.json +43 -4
- package/packages/piagent-core/README.md +3 -2
- package/packages/piagent-core/benchmark/benchmark-candidate.d.ts +1 -0
- package/packages/piagent-core/benchmark/benchmark-forensics.d.ts +1 -0
- package/packages/piagent-core/benchmark/benchmark-ledger.d.ts +4 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.d.ts +1 -0
- package/packages/piagent-core/benchmark/benchmark-runtime.d.ts +1 -0
- package/packages/piagent-core/capabilities/capability-core.js +2 -2
- package/packages/piagent-core/capabilities/capability-verification-cache.js +17 -7
- package/packages/piagent-core/capabilities/runtime-integrity.js +190 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +80 -272
- package/packages/piagent-core/extensions/redaction-core.d.ts +2 -0
- package/packages/piagent-core/extensions/runtime-evidence.js +12 -1
- package/packages/piagent-core/extensions/shell-reach.ts +250 -0
- package/packages/piagent-core/extensions/task-journal.js +30 -1
- package/packages/piagent-core/extensions/task-state.js +33 -36
- package/packages/piagent-core/mcp/mcp-command-actions.js +1 -1
- package/packages/piagent-core/mcp/mcp-server-catalog.js +139 -9
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +2 -2
- package/packages/piagent-core/runtime/hooks/tool-call-hook.ts +12 -2
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +20 -19
- package/packages/piagent-core/runtime/hooks/tool-result-metadata.ts +13 -0
- package/packages/piagent-core/runtime/input/chat-images.ts +2 -2
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +94 -0
- package/packages/piagent-core/runtime/inspection/approval-broker.ts +318 -0
- package/packages/piagent-core/runtime/inspection/commit-summary-projection.ts +59 -0
- package/packages/piagent-core/runtime/inspection/context-telemetry-inspection.ts +37 -0
- package/packages/piagent-core/runtime/inspection/criteria-links.ts +295 -0
- package/packages/piagent-core/runtime/inspection/diff-projection.ts +499 -0
- package/packages/piagent-core/runtime/inspection/git-filter-safety.ts +38 -0
- package/packages/piagent-core/runtime/inspection/git-numstat-adapter.ts +71 -0
- package/packages/piagent-core/runtime/inspection/git-status-adapter.ts +493 -0
- package/packages/piagent-core/runtime/inspection/git-tree-adapter.ts +108 -0
- package/packages/piagent-core/runtime/inspection/mcp-control.ts +10 -0
- package/packages/piagent-core/runtime/inspection/mutation-provenance-contract.ts +131 -0
- package/packages/piagent-core/runtime/inspection/mutation-provenance-recorder.ts +48 -0
- package/packages/piagent-core/runtime/inspection/mutation-provenance-store.ts +161 -0
- package/packages/piagent-core/runtime/inspection/project-runtime-inspection.ts +36 -0
- package/packages/piagent-core/runtime/inspection/review-state-contract.ts +108 -0
- package/packages/piagent-core/runtime/inspection/review-state-projection.ts +93 -0
- package/packages/piagent-core/runtime/inspection/review-state-store.ts +112 -0
- package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +262 -0
- package/packages/piagent-core/runtime/inspection/same-process-bridge-proof.ts +103 -0
- package/packages/piagent-core/runtime/inspection/source-change-projection.ts +471 -0
- package/packages/piagent-core/runtime/inspection/source-evidence-contract.ts +139 -0
- package/packages/piagent-core/runtime/inspection/source-evidence-store.ts +315 -0
- package/packages/piagent-core/runtime/inspection/source-handoff-store.ts +70 -0
- package/packages/piagent-core/runtime/inspection/source-inspection-plan.ts +34 -0
- package/packages/piagent-core/runtime/inspection/source-mutation-projection.ts +164 -0
- package/packages/piagent-core/runtime/inspection/source-mutation-store.ts +168 -0
- package/packages/piagent-core/runtime/inspection/source-open-target.ts +38 -0
- package/packages/piagent-core/runtime/inspection/source-revert-projection.ts +147 -0
- package/packages/piagent-core/runtime/inspection/task-baseline-start-capture.ts +22 -0
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +160 -0
- package/packages/piagent-core/runtime/inspection/task-control-journal.ts +131 -0
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +95 -0
- package/packages/piagent-core/runtime/inspection/task-provenance-projection.ts +60 -0
- package/packages/piagent-core/runtime/inspection/task-recovery-timeline.ts +88 -0
- package/packages/piagent-core/runtime/inspection/task-run-index.ts +87 -0
- package/packages/piagent-core/runtime/inspection/task-source-projection.ts +401 -0
- package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +101 -0
- package/packages/piagent-core/runtime/inspection/text-diff.ts +60 -0
- package/packages/piagent-core/runtime/inspection/verifier-snapshot-contract.ts +114 -0
- package/packages/piagent-core/runtime/inspection/verifier-snapshot-store.ts +189 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +320 -0
- package/packages/piagent-core/runtime/inspection/workspace-file-reader.ts +132 -0
- package/packages/piagent-core/runtime/inspection/workspace-source-projection.ts +96 -0
- package/packages/piagent-core/runtime/inspection/zero-turn-conformance.ts +175 -0
- package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +93 -6
- package/packages/piagent-core/runtime/policy/block-remedy.ts +139 -0
- package/packages/piagent-core/runtime/policy/source-index-transaction.ts +173 -0
- package/packages/piagent-core/runtime/policy/source-mutation-guard-binding.ts +42 -0
- package/packages/piagent-core/runtime/policy/source-mutation-guard.ts +184 -0
- package/packages/piagent-core/runtime/policy/source-worktree-transaction.ts +105 -0
- package/packages/piagent-core/runtime/product/activity-inspector-footer.ts +5 -3
- package/packages/piagent-core/runtime/product/activity-inspector.ts +85 -343
- package/packages/piagent-core/runtime/recovery/resume-state.ts +38 -1
- package/packages/piagent-core/runtime/registration/activity-inspector-command.ts +68 -14
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +22 -27
- package/packages/piagent-core/runtime/session/model-authorship-state.ts +31 -3
- package/packages/piagent-core/runtime/session/runtime-state.ts +11 -1
- package/packages/piagent-core/runtime/session/token-economics.js +34 -0
- package/packages/piagent-core/runtime/tools/phase-tool-runtime.ts +2 -1
- package/packages/piagent-core/runtime/workflows/task-intake.ts +5 -0
- package/packages/piagent-core/security/sensitive-data.js +7 -0
- package/packages/piagent-webui/dist/client/assets/index-DwKCWbwd.css +1 -0
- package/packages/piagent-webui/dist/client/assets/index-bu7XpD9Q.js +172 -0
- package/packages/piagent-webui/dist/client/index.html +16 -0
- package/packages/piagent-webui/extension/attachment-store.ts +323 -0
- package/packages/piagent-webui/extension/held-message-queue.ts +311 -0
- package/packages/piagent-webui/extension/lifecycle-controller.ts +423 -0
- package/packages/piagent-webui/extension/lifecycle-event-adapter.ts +23 -0
- package/packages/piagent-webui/extension/piagent-webui.ts +470 -0
- package/packages/piagent-webui/extension/review-controller.ts +160 -0
- package/packages/piagent-webui/extension/same-session-bridge.ts +500 -0
- package/packages/piagent-webui/extension/session-options-controller.ts +281 -0
- package/packages/piagent-webui/extension/session-stream-adapter.ts +249 -0
- package/packages/piagent-webui/extension/source-mutation-controller.ts +215 -0
- package/packages/piagent-webui/extension/source-open-controller.ts +136 -0
- package/packages/piagent-webui/extension/source-revert-controller.ts +189 -0
- package/packages/piagent-webui/extension/terminal-session-adapter.ts +73 -0
- package/packages/piagent-webui/extension/vscode-handoff.ts +51 -0
- package/packages/piagent-webui/gateway/control-socket.ts +92 -0
- package/packages/piagent-webui/gateway/extension-authority.ts +47 -0
- package/packages/piagent-webui/gateway/gateway-events.ts +60 -0
- package/packages/piagent-webui/gateway/gateway-protocol-service.ts +64 -0
- package/packages/piagent-webui/gateway/gateway-service.ts +215 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +110 -0
- package/packages/piagent-webui/gateway/mcp-auth-broker.ts +158 -0
- package/packages/piagent-webui/gateway/native-project-picker.ts +29 -0
- package/packages/piagent-webui/gateway/pi-host.ts +41 -0
- package/packages/piagent-webui/gateway/profile-state.ts +1 -0
- package/packages/piagent-webui/gateway/project-registry.ts +108 -0
- package/packages/piagent-webui/gateway/provider-auth-broker.ts +172 -0
- package/packages/piagent-webui/gateway/session-catalog.ts +128 -0
- package/packages/piagent-webui/gateway/session-command-controller.ts +237 -0
- package/packages/piagent-webui/gateway/session-command-store.ts +224 -0
- package/packages/piagent-webui/gateway/session-inspection-registry.ts +239 -0
- package/packages/piagent-webui/gateway/session-lease-store.ts +1 -0
- package/packages/piagent-webui/gateway/session-metadata-store.ts +152 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +489 -0
- package/packages/piagent-webui/ownership/gateway-runtime-context.ts +8 -0
- package/packages/piagent-webui/ownership/profile-state.ts +117 -0
- package/packages/piagent-webui/ownership/session-lease-store.ts +329 -0
- package/packages/piagent-webui/ownership/session-refs.ts +8 -0
- package/packages/piagent-webui/package.json +33 -0
- package/packages/piagent-webui/server/README.md +4 -0
- package/packages/piagent-webui/server/benchmark-release-monitor.ts +230 -0
- package/packages/piagent-webui/server/core-inspection-provider.ts +403 -0
- package/packages/piagent-webui/server/gateway-websocket.ts +143 -0
- package/packages/piagent-webui/server/http-security.ts +27 -0
- package/packages/piagent-webui/server/ipc-read-model-client.ts +89 -0
- package/packages/piagent-webui/server/loopback-server.ts +368 -0
- package/packages/piagent-webui/server/read-model-provider.ts +37 -0
- package/packages/piagent-webui/server/read-only-router.ts +105 -0
- package/packages/piagent-webui/server/session-auth.ts +88 -0
- package/packages/piagent-webui/server/sidecar-main.ts +86 -0
- package/packages/piagent-webui/server/sse-hub.ts +76 -0
- package/packages/piagent-webui/server/static-bundle.ts +40 -0
- package/packages/piagent-webui/server/transcript-projection.ts +125 -0
- package/schemas/mutation-provenance-record.schema.json +100 -0
- package/schemas/piagent-webui/approval-v1.schema.json +654 -0
- package/schemas/piagent-webui/attachment-v1.schema.json +267 -0
- package/schemas/piagent-webui/capabilities-v1.schema.json +729 -0
- package/schemas/piagent-webui/catalog-v1.json +240 -0
- package/schemas/piagent-webui/commit-summary-v1.schema.json +63 -0
- package/schemas/piagent-webui/common-v1.schema.json +479 -0
- package/schemas/piagent-webui/control-command-v1.schema.json +1780 -0
- package/schemas/piagent-webui/diff-v1.schema.json +826 -0
- package/schemas/piagent-webui/gateway-capabilities-v1.schema.json +136 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +456 -0
- package/schemas/piagent-webui/handoff-history-v1.schema.json +92 -0
- package/schemas/piagent-webui/model-catalog-v1.schema.json +95 -0
- package/schemas/piagent-webui/queue-v1.schema.json +99 -0
- package/schemas/piagent-webui/recovery-history-v1.schema.json +144 -0
- package/schemas/piagent-webui/release-monitor-v1.schema.json +173 -0
- package/schemas/piagent-webui/review-state-v1.schema.json +206 -0
- package/schemas/piagent-webui/runtime-event-v2.schema.json +5206 -0
- package/schemas/piagent-webui/session-catalog-v1.schema.json +222 -0
- package/schemas/piagent-webui/session-command-v1.schema.json +390 -0
- package/schemas/piagent-webui/snapshot-v1.schema.json +2622 -0
- package/schemas/piagent-webui/source-change-v1.schema.json +763 -0
- package/schemas/piagent-webui/source-mutation-v1.schema.json +96 -0
- package/schemas/piagent-webui/source-revert-v1.schema.json +130 -0
- package/schemas/piagent-webui/subagent-tree-v1.schema.json +88 -0
- package/schemas/piagent-webui/task-index-v1.schema.json +74 -0
- package/schemas/piagent-webui/task-timeline-v1.schema.json +49 -0
- package/schemas/piagent-webui/transcript-v1.schema.json +243 -0
- package/schemas/task-baseline-manifest.schema.json +122 -0
- package/schemas/verifier-file-snapshot.schema.json +81 -0
- package/scripts/build-docs-site.mjs +21 -1
- package/scripts/check-architecture.mjs +1 -0
- package/scripts/check-doc-languages.mjs +19 -2
- package/scripts/check-third-party-neutrality.mjs +43 -0
- package/scripts/explain-command.mjs +240 -0
- package/scripts/install-global.sh +105 -4
- package/scripts/mcp-manage.mjs +17 -4
- package/scripts/pi-session-stats.sh +10 -2
- package/scripts/piagent-cli.mjs +30 -3
- package/scripts/piagent-dashboard.mjs +261 -0
- package/scripts/piagent-webui-launcher.mjs +121 -0
- package/scripts/setup.sh +69 -33
- package/scripts/traffic-snapshot.mjs +214 -0
- package/scripts/typescript-loader.mjs +42 -1
- package/scripts/uninstall-global.sh +53 -2
- package/scripts/update-global.mjs +33 -1
- package/scripts/verify-local.sh +257 -0
- package/scripts/verify-release-identity.mjs +4 -1
- package/scripts/verify-translation-pairing.mjs +121 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
import { requestGatewayControl } from "../packages/piagent-webui/gateway/control-socket.ts";
|
|
9
|
+
import { installedPiHostRoot } from "../packages/piagent-webui/gateway/pi-host.ts";
|
|
10
|
+
import { gatewayProfileState, profileRef, readGatewayDescriptor, readOrCreateCatalogKey,
|
|
11
|
+
removeGatewayDescriptor } from "../packages/piagent-webui/gateway/profile-state.ts";
|
|
12
|
+
import { startPiagentGateway } from "../packages/piagent-webui/gateway/gateway-service.ts";
|
|
13
|
+
|
|
14
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
15
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
16
|
+
const expectedPiVersion = packageJson.peerDependencies["@earendil-works/pi-coding-agent"];
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
const command = ["--help", "-h"].includes(args[0]) ? args.shift()
|
|
19
|
+
: args[0] && !args[0].startsWith("-") ? args.shift() : "open";
|
|
20
|
+
const noOpen = args.includes("--no-open");
|
|
21
|
+
const json = args.includes("--json");
|
|
22
|
+
const repair = args.includes("--repair");
|
|
23
|
+
const agentDirIndex = args.indexOf("--agent-dir");
|
|
24
|
+
const agentDir = agentDirIndex >= 0 ? args[agentDirIndex + 1] : undefined;
|
|
25
|
+
const GATEWAY_ERROR_LOG = "gateway-stderr.log";
|
|
26
|
+
const GATEWAY_ERROR_MAX_BYTES = 4_000;
|
|
27
|
+
|
|
28
|
+
function output(value) {
|
|
29
|
+
process.stdout.write(`${json ? JSON.stringify(value) : value}\n`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function openBrowser(url) {
|
|
33
|
+
if (noOpen) return;
|
|
34
|
+
const opener = process.platform === "darwin" ? ["open", [url]]
|
|
35
|
+
: process.platform === "win32" ? ["cmd", ["/c", "start", "", url]]
|
|
36
|
+
: ["xdg-open", [url]];
|
|
37
|
+
const result = spawnSync(opener[0], opener[1], { stdio: "ignore" });
|
|
38
|
+
if (result.status !== 0) process.stderr.write(`Dashboard is ready at ${url}\n`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function processAlive(pid) {
|
|
42
|
+
try { process.kill(pid, 0); return true; }
|
|
43
|
+
catch (error) { return !(error && typeof error === "object" && "code" in error && error.code === "ESRCH"); }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ownerOnlyDirectory(target) {
|
|
47
|
+
try {
|
|
48
|
+
const stat = fs.lstatSync(target);
|
|
49
|
+
return stat.isDirectory() && !stat.isSymbolicLink() && (stat.mode & 0o077) === 0
|
|
50
|
+
&& (typeof process.getuid !== "function" || stat.uid === process.getuid());
|
|
51
|
+
} catch { return false; }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function removeOwnedRegularFile(target) {
|
|
55
|
+
try {
|
|
56
|
+
const stat = fs.lstatSync(target);
|
|
57
|
+
if (!stat.isFile() || stat.isSymbolicLink()
|
|
58
|
+
|| typeof process.getuid === "function" && stat.uid !== process.getuid()) return false;
|
|
59
|
+
fs.unlinkSync(target); return true;
|
|
60
|
+
} catch { return false; }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function removeOwnedSocket(target) {
|
|
64
|
+
if (process.platform === "win32") return false;
|
|
65
|
+
try {
|
|
66
|
+
const stat = fs.lstatSync(target);
|
|
67
|
+
if (!stat.isSocket() || stat.isSymbolicLink()
|
|
68
|
+
|| typeof process.getuid === "function" && stat.uid !== process.getuid()) return false;
|
|
69
|
+
fs.unlinkSync(target); return true;
|
|
70
|
+
} catch { return false; }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function gatewayErrorLog(state) {
|
|
74
|
+
return path.join(state.root, GATEWAY_ERROR_LOG);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function writeGatewayErrorLog(state, message = "") {
|
|
78
|
+
const target = gatewayErrorLog(state);
|
|
79
|
+
const descriptor = fs.openSync(target, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_TRUNC
|
|
80
|
+
| (fs.constants.O_NOFOLLOW ?? 0), 0o600);
|
|
81
|
+
try {
|
|
82
|
+
const stat = fs.fstatSync(descriptor);
|
|
83
|
+
if (!stat.isFile() || typeof process.getuid === "function" && stat.uid !== process.getuid()) {
|
|
84
|
+
throw new Error("gateway-error-log-invalid");
|
|
85
|
+
}
|
|
86
|
+
fs.fchmodSync(descriptor, 0o600);
|
|
87
|
+
if (message) fs.writeFileSync(descriptor, message.slice(-GATEWAY_ERROR_MAX_BYTES));
|
|
88
|
+
} finally { fs.closeSync(descriptor); }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readGatewayErrorLog(state) {
|
|
92
|
+
const target = gatewayErrorLog(state);
|
|
93
|
+
try {
|
|
94
|
+
const before = fs.lstatSync(target);
|
|
95
|
+
if (!before.isFile() || before.isSymbolicLink()
|
|
96
|
+
|| typeof process.getuid === "function" && before.uid !== process.getuid()) return "";
|
|
97
|
+
const descriptor = fs.openSync(target, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
98
|
+
try {
|
|
99
|
+
const stat = fs.fstatSync(descriptor);
|
|
100
|
+
if (!stat.isFile() || stat.dev !== before.dev || stat.ino !== before.ino) return "";
|
|
101
|
+
const length = Math.min(stat.size, GATEWAY_ERROR_MAX_BYTES);
|
|
102
|
+
const body = Buffer.alloc(length);
|
|
103
|
+
fs.readSync(descriptor, body, 0, length, Math.max(0, stat.size - length));
|
|
104
|
+
return body.toString("utf8");
|
|
105
|
+
} finally { fs.closeSync(descriptor); }
|
|
106
|
+
} catch { return ""; }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function control(action, timeoutMs = 1_500) {
|
|
110
|
+
const state = gatewayProfileState(agentDir);
|
|
111
|
+
return await requestGatewayControl(state.controlSocket, { action }, timeoutMs);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function currentLaunchUrl() {
|
|
115
|
+
try {
|
|
116
|
+
const response = await control("issue-launch-url");
|
|
117
|
+
if (response.ok && response.value && typeof response.value === "object"
|
|
118
|
+
&& typeof response.value.launchUrl === "string") return response.value.launchUrl;
|
|
119
|
+
} catch {
|
|
120
|
+
// Gateway is not ready yet.
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function ensureStarted() {
|
|
126
|
+
const existing = await currentLaunchUrl();
|
|
127
|
+
if (existing) return existing;
|
|
128
|
+
const effectiveAgentDir = path.resolve(agentDir ?? process.env.PI_CODING_AGENT_DIR ?? path.join(os.homedir(), ".pi", "agent"));
|
|
129
|
+
const state = gatewayProfileState(effectiveAgentDir);
|
|
130
|
+
writeGatewayErrorLog(state);
|
|
131
|
+
const child = spawn(process.execPath, [
|
|
132
|
+
"--disable-warning=ExperimentalWarning",
|
|
133
|
+
"--import", path.join(packageRoot, "scripts", "register-typescript-loader.mjs"),
|
|
134
|
+
fileURLToPath(import.meta.url),
|
|
135
|
+
"serve",
|
|
136
|
+
...(agentDir ? ["--agent-dir", agentDir] : [])
|
|
137
|
+
], { cwd: process.cwd(), env: { ...process.env,
|
|
138
|
+
PIAGENT_PINNED_TS_TRANSFORM_ROOT: path.join(effectiveAgentDir, "npm", "node_modules", "pi-mcp-adapter") },
|
|
139
|
+
detached: true, stdio: "ignore" });
|
|
140
|
+
let exit = null;
|
|
141
|
+
child.on("exit", (code, signal) => { exit = signal ? `signal ${signal}` : `exit code ${code}`; });
|
|
142
|
+
child.unref();
|
|
143
|
+
const deadline = Date.now() + 12_000;
|
|
144
|
+
while (Date.now() < deadline) {
|
|
145
|
+
const url = await currentLaunchUrl();
|
|
146
|
+
if (url) return url;
|
|
147
|
+
// A child that has already exited is never going to publish a URL, so
|
|
148
|
+
// waiting out the rest of the deadline only delays the report.
|
|
149
|
+
if (exit !== null) break;
|
|
150
|
+
await new Promise((resolve) => setTimeout(resolve, 80));
|
|
151
|
+
}
|
|
152
|
+
const detail = [exit && `The gateway process ended with ${exit}.`, readGatewayErrorLog(state).trim()]
|
|
153
|
+
.filter(Boolean).join("\n");
|
|
154
|
+
throw new Error(detail
|
|
155
|
+
? `gateway-start-failed\n${detail}`
|
|
156
|
+
: "gateway-start-timeout\nThe gateway did not publish a URL within 12s and wrote nothing to stderr.");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function serve() {
|
|
160
|
+
const gateway = await startPiagentGateway({ packageRoot, expectedPiVersion, agentDir });
|
|
161
|
+
const shutdown = () => { void gateway.close(); };
|
|
162
|
+
process.once("SIGINT", shutdown);
|
|
163
|
+
process.once("SIGTERM", shutdown);
|
|
164
|
+
await gateway.wait();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function main() {
|
|
168
|
+
if (command === "serve") return await serve();
|
|
169
|
+
if (["help", "--help", "-h"].includes(command)) {
|
|
170
|
+
output("Usage: piagent dashboard [open|status|stop|restart|doctor] [--no-open] [--json] [--repair]");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (command === "open" || command === "start") {
|
|
174
|
+
const launchUrl = await ensureStarted();
|
|
175
|
+
output(json ? { state: "running", launchUrl } : launchUrl);
|
|
176
|
+
openBrowser(launchUrl);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (command === "status") {
|
|
180
|
+
try {
|
|
181
|
+
const response = await control("health");
|
|
182
|
+
if (!response.ok) throw new Error(response.error);
|
|
183
|
+
output(json ? { state: "running", gateway: response.value } : "Piagent Gateway is running.");
|
|
184
|
+
} catch {
|
|
185
|
+
const descriptor = readGatewayDescriptor(gatewayProfileState(agentDir));
|
|
186
|
+
output(json ? { state: "stopped", staleDescriptor: Boolean(descriptor) } : "Piagent Gateway is stopped.");
|
|
187
|
+
process.exitCode = 1;
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (command === "stop") {
|
|
192
|
+
try {
|
|
193
|
+
const response = await control("stop");
|
|
194
|
+
if (!response.ok) throw new Error(response.error);
|
|
195
|
+
output(json ? { state: "stopping" } : "Piagent Gateway is stopping.");
|
|
196
|
+
} catch {
|
|
197
|
+
output(json ? { state: "stopped" } : "Piagent Gateway is already stopped.");
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (command === "restart") {
|
|
202
|
+
try { await control("stop"); } catch { /* already stopped */ }
|
|
203
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
204
|
+
const launchUrl = await ensureStarted();
|
|
205
|
+
output(json ? { state: "running", launchUrl } : launchUrl);
|
|
206
|
+
openBrowser(launchUrl);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (command === "doctor") {
|
|
210
|
+
const state = gatewayProfileState(agentDir);
|
|
211
|
+
const key = readOrCreateCatalogKey(state), descriptor = readGatewayDescriptor(state);
|
|
212
|
+
let actual = null;
|
|
213
|
+
try {
|
|
214
|
+
const hostRoot = installedPiHostRoot();
|
|
215
|
+
actual = JSON.parse(fs.readFileSync(path.join(hostRoot, "package.json"), "utf8")).version;
|
|
216
|
+
} catch { /* reported as a failed check below */ }
|
|
217
|
+
let health = null;
|
|
218
|
+
try {
|
|
219
|
+
const response = await control("health", 1_000);
|
|
220
|
+
health = response.ok ? "ready" : "unavailable";
|
|
221
|
+
} catch { health = "unavailable"; }
|
|
222
|
+
const staleDescriptor = Boolean(descriptor && !processAlive(descriptor.pid));
|
|
223
|
+
const invalidDescriptor = fs.existsSync(state.descriptorFile) && !descriptor;
|
|
224
|
+
const repairs = [];
|
|
225
|
+
if (repair && staleDescriptor && descriptor) {
|
|
226
|
+
removeGatewayDescriptor(state, descriptor.gatewayInstanceRef); repairs.push("stale-descriptor-removed");
|
|
227
|
+
} else if (repair && invalidDescriptor && removeOwnedRegularFile(state.descriptorFile)) repairs.push("invalid-descriptor-removed");
|
|
228
|
+
if (repair && health !== "ready" && removeOwnedSocket(state.controlSocket)) repairs.push("stale-control-socket-removed");
|
|
229
|
+
const checks = {
|
|
230
|
+
piHostVersion: actual === expectedPiVersion,
|
|
231
|
+
clientBuild: fs.existsSync(path.join(packageRoot, "packages", "piagent-webui", "dist", "client", "index.html")),
|
|
232
|
+
profileStateOwnerOnly: ownerOnlyDirectory(state.root),
|
|
233
|
+
descriptor: !invalidDescriptor && !staleDescriptor,
|
|
234
|
+
gatewayHealth: descriptor ? health === "ready" : true
|
|
235
|
+
};
|
|
236
|
+
const result = {
|
|
237
|
+
ok: Object.values(checks).every(Boolean),
|
|
238
|
+
expectedPiVersion,
|
|
239
|
+
actualPiVersion: actual,
|
|
240
|
+
profileRef: profileRef(state, key),
|
|
241
|
+
gateway: descriptor ? health === "ready" ? "ready" : staleDescriptor ? "stale" : "unavailable" : "not-registered",
|
|
242
|
+
checks,
|
|
243
|
+
repaired: repairs
|
|
244
|
+
};
|
|
245
|
+
output(json ? result : result.ok ? "Piagent Gateway doctor passed." : repair && repairs.length
|
|
246
|
+
? "Piagent Gateway doctor repaired stale local state; run doctor again."
|
|
247
|
+
: "Piagent Gateway doctor found a version, build, ownership, or lifecycle mismatch.");
|
|
248
|
+
if (!result.ok) process.exitCode = 1;
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
throw new Error(`unknown-dashboard-command:${command}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
main().catch((error) => {
|
|
255
|
+
const message = `Piagent dashboard failed: ${error instanceof Error ? error.message : String(error)}\n`;
|
|
256
|
+
if (command === "serve") {
|
|
257
|
+
try { writeGatewayErrorLog(gatewayProfileState(agentDir), message); } catch { /* stderr still reports the failure. */ }
|
|
258
|
+
}
|
|
259
|
+
process.stderr.write(message);
|
|
260
|
+
process.exitCode = 1;
|
|
261
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
const REF = /^[A-Za-z0-9][A-Za-z0-9._~-]{0,159}$/;
|
|
9
|
+
|
|
10
|
+
function help() {
|
|
11
|
+
return [
|
|
12
|
+
"Usage: piagent-webui [--session <opaque-session-ref>] [--no-open]",
|
|
13
|
+
"",
|
|
14
|
+
"Opens a WebUI already owned by the current Pi process.",
|
|
15
|
+
"Start it first with /piagent-webui inside the Pi terminal.",
|
|
16
|
+
"This command never creates a second Pi runtime."
|
|
17
|
+
].join("\n");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parse(argv) {
|
|
21
|
+
let session = null, open = true;
|
|
22
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
23
|
+
const value = argv[index];
|
|
24
|
+
if (value === "--help" || value === "-h") return { help: true, session, open };
|
|
25
|
+
if (value === "--no-open") { open = false; continue; }
|
|
26
|
+
if (value === "--session" && typeof argv[index + 1] === "string" && REF.test(argv[index + 1])) { session = argv[index += 1]; continue; }
|
|
27
|
+
throw new Error(`Unknown or invalid option: ${value}`);
|
|
28
|
+
}
|
|
29
|
+
return { help: false, session, open };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function launcherDirectory(start) {
|
|
33
|
+
let current = path.resolve(start);
|
|
34
|
+
while (true) {
|
|
35
|
+
const candidate = path.join(current, ".pi", "piagent-state", "webui-launcher");
|
|
36
|
+
try { if (fs.lstatSync(candidate).isDirectory()) return candidate; } catch { /* keep walking */ }
|
|
37
|
+
const parent = path.dirname(current);
|
|
38
|
+
if (parent === current) return null;
|
|
39
|
+
current = parent;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readDescriptor(file) {
|
|
44
|
+
const stat = fs.lstatSync(file);
|
|
45
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 8_192 || process.platform !== "win32" && (stat.mode & 0o077) !== 0) {
|
|
46
|
+
throw new Error("WebUI launcher descriptor is unsafe");
|
|
47
|
+
}
|
|
48
|
+
const value = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
49
|
+
const temporary = fs.realpathSync(os.tmpdir());
|
|
50
|
+
const socket = path.resolve(String(value.controlSocket ?? ""));
|
|
51
|
+
const canonicalSocket = fs.realpathSync(socket);
|
|
52
|
+
if (value.schemaVersion !== 1 || !REF.test(String(value.sessionRef ?? "")) || !REF.test(String(value.projectRef ?? ""))
|
|
53
|
+
|| typeof value.controlToken !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(value.controlToken)
|
|
54
|
+
|| path.relative(temporary, canonicalSocket).startsWith("..") || path.isAbsolute(path.relative(temporary, canonicalSocket))) {
|
|
55
|
+
throw new Error("WebUI launcher descriptor is invalid");
|
|
56
|
+
}
|
|
57
|
+
return { ...value, controlSocket: socket };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function requestLaunch(descriptor) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const socket = net.createConnection(descriptor.controlSocket);
|
|
63
|
+
let bytes = Buffer.alloc(0), settled = false;
|
|
64
|
+
const fail = (error) => { if (!settled) { settled = true; socket.destroy(); reject(error); } };
|
|
65
|
+
socket.setTimeout(2_000, () => fail(new Error("WebUI sidecar did not respond")));
|
|
66
|
+
socket.once("error", () => fail(new Error("WebUI sidecar is not running; use /piagent-webui in Pi")));
|
|
67
|
+
socket.once("connect", () => socket.write(`${JSON.stringify({ action: "launch", token: descriptor.controlToken })}\n`));
|
|
68
|
+
socket.on("data", (chunk) => {
|
|
69
|
+
if (settled) return;
|
|
70
|
+
bytes = Buffer.concat([bytes, chunk]);
|
|
71
|
+
if (bytes.length > 8_192) { fail(new Error("WebUI sidecar response exceeded its limit")); return; }
|
|
72
|
+
const newline = bytes.indexOf(10);
|
|
73
|
+
if (newline < 0) return;
|
|
74
|
+
try {
|
|
75
|
+
const response = JSON.parse(bytes.subarray(0, newline).toString("utf8"));
|
|
76
|
+
if (!response.ok || typeof response.launchUrl !== "string" || !/^http:\/\/127\.0\.0\.1:\d+\/#bootstrap=/.test(response.launchUrl)) {
|
|
77
|
+
fail(new Error("WebUI sidecar rejected the launch request")); return;
|
|
78
|
+
}
|
|
79
|
+
settled = true; socket.end(); resolve(response.launchUrl);
|
|
80
|
+
} catch { fail(new Error("WebUI sidecar returned an invalid response")); }
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function openUrl(url) {
|
|
86
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "linux" ? "xdg-open" : null;
|
|
87
|
+
if (!command) return false;
|
|
88
|
+
const child = spawn(command, [url], { detached: true, stdio: "ignore" });
|
|
89
|
+
child.unref(); return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function main(argv = process.argv.slice(2)) {
|
|
93
|
+
let options;
|
|
94
|
+
try { options = parse(argv); }
|
|
95
|
+
catch (error) { process.stderr.write(`${error.message}\n${help()}\n`); return 2; }
|
|
96
|
+
if (options.help) { process.stdout.write(`${help()}\n`); return 0; }
|
|
97
|
+
const directory = launcherDirectory(process.cwd());
|
|
98
|
+
if (!directory) { process.stderr.write("No running Piagent WebUI was found. Run /piagent-webui inside the Pi terminal first.\n"); return 1; }
|
|
99
|
+
let descriptors;
|
|
100
|
+
try {
|
|
101
|
+
descriptors = fs.readdirSync(directory).filter((name) => /^[a-f0-9]{64}\.json$/.test(name))
|
|
102
|
+
.map((name) => readDescriptor(path.join(directory, name)))
|
|
103
|
+
.filter((value) => !options.session || value.sessionRef === options.session);
|
|
104
|
+
} catch (error) { process.stderr.write(`${error instanceof Error ? error.message : "WebUI launcher state is unavailable"}\n`); return 1; }
|
|
105
|
+
if (descriptors.length !== 1) {
|
|
106
|
+
process.stderr.write(descriptors.length === 0
|
|
107
|
+
? "No matching running WebUI session was found.\n"
|
|
108
|
+
: "More than one WebUI session is running; pass --session <opaque-session-ref>.\n");
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const url = await requestLaunch(descriptors[0]);
|
|
113
|
+
if (!options.open || !openUrl(url)) process.stdout.write(`${url}\n`);
|
|
114
|
+
else process.stdout.write(`Opened Piagent WebUI for ${descriptors[0].sessionRef}.\n`);
|
|
115
|
+
return 0;
|
|
116
|
+
} catch (error) { process.stderr.write(`${error instanceof Error ? error.message : "WebUI could not be opened"}\n`); return 1; }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (process.argv[1] && fs.realpathSync(path.resolve(process.argv[1])) === fs.realpathSync(new URL(import.meta.url))) process.exit(await main());
|
|
120
|
+
|
|
121
|
+
export { help, main, parse, readDescriptor, requestLaunch };
|
package/scripts/setup.sh
CHANGED
|
@@ -22,7 +22,7 @@ Options:
|
|
|
22
22
|
--project-only, --no-global Only initialize project, skip global install
|
|
23
23
|
--no-project Skip project initialization
|
|
24
24
|
--with-mcp / --no-mcp Install/skip Pi MCP adapter during global install (default: install)
|
|
25
|
-
--mcp-preset <minimal|core|popular|all|docs|browser|github|design|design-local|web>
|
|
25
|
+
--mcp-preset <minimal|core|popular|all|docs|browser|github|design|design-local|web|documents|google|google-mail|google-all>
|
|
26
26
|
Configure shared global MCP baseline when --with-mcp is enabled (default: core)
|
|
27
27
|
--with-subagents / --no-subagents
|
|
28
28
|
Install/skip pi-subagents during global install (default: install)
|
|
@@ -244,15 +244,11 @@ if [[ "$WITH_MCP" == false && "$MCP_PRESET_EXPLICIT" == true ]]; then
|
|
|
244
244
|
exit 2
|
|
245
245
|
fi
|
|
246
246
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if [[ "$DO_PROJECT" == true && -n "$PROJECT_PATH" && -f "$PROJECT_PATH/.pi/settings.json" ]]; then
|
|
254
|
-
local existing_source
|
|
255
|
-
existing_source="$(node --input-type=module - "$PROJECT_PATH/.pi/settings.json" <<'NODE'
|
|
247
|
+
# The pin a project has committed, or empty. It is authority over that project
|
|
248
|
+
# and nothing else -- one project may not decide what the machine runs.
|
|
249
|
+
project_pinned_source() {
|
|
250
|
+
[[ "$DO_PROJECT" == true && -n "$PROJECT_PATH" && -f "$PROJECT_PATH/.pi/settings.json" ]] || return 0
|
|
251
|
+
node --input-type=module - "$PROJECT_PATH/.pi/settings.json" <<'NODE'
|
|
256
252
|
import fs from "node:fs";
|
|
257
253
|
const settings = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
258
254
|
const source = Array.isArray(settings.packages)
|
|
@@ -260,30 +256,51 @@ const source = Array.isArray(settings.packages)
|
|
|
260
256
|
: undefined;
|
|
261
257
|
if (source) process.stdout.write(source);
|
|
262
258
|
NODE
|
|
263
|
-
|
|
264
|
-
if [[ -n "$existing_source" ]]; then
|
|
265
|
-
printf '%s\n' "$existing_source"
|
|
266
|
-
return
|
|
267
|
-
fi
|
|
268
|
-
fi
|
|
259
|
+
}
|
|
269
260
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
fi
|
|
261
|
+
# Running from an installed package rather than a checkout. The name and version
|
|
262
|
+
# on disk are exactly what a teammate resolves from the registry, so they are a
|
|
263
|
+
# portable source; the install path is not, and project settings are meant to be
|
|
264
|
+
# committed.
|
|
265
|
+
running_helper_source() {
|
|
266
|
+
[[ "$PLATFORM_ROOT" == */node_modules/* ]] || return 0
|
|
267
|
+
local installed_name installed_version
|
|
268
|
+
installed_name="$(node -e 'const fs=require("node:fs"); process.stdout.write(JSON.parse(fs.readFileSync(process.argv[1], "utf8")).name ?? "");' "$PLATFORM_ROOT/package.json")"
|
|
269
|
+
installed_version="$(node -e 'const fs=require("node:fs"); process.stdout.write(JSON.parse(fs.readFileSync(process.argv[1], "utf8")).version ?? "");' "$PLATFORM_ROOT/package.json")"
|
|
270
|
+
if [[ -n "$installed_name" && "$installed_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?$ ]]; then
|
|
271
|
+
printf 'npm:%s@%s' "$installed_name" "$installed_version"
|
|
282
272
|
fi
|
|
273
|
+
}
|
|
283
274
|
|
|
275
|
+
local_path_source() {
|
|
284
276
|
echo "WARN: No exact package source provided." >&2
|
|
285
277
|
echo "WARN: using local platform path; do not commit project .pi/settings.json with this value for team rollout." >&2
|
|
286
|
-
printf '%s
|
|
278
|
+
printf '%s' "$PLATFORM_ROOT"
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
# One resolved value used to serve both consumers, and the two do not want the
|
|
282
|
+
# same answer. A project pin is what that project's team agreed to run; the
|
|
283
|
+
# global install is what every project on this machine gets. Reading the pin
|
|
284
|
+
# first meant the documented two-command install silently downgraded the
|
|
285
|
+
# machine-wide package to whatever one project happened to pin -- including
|
|
286
|
+
# projects the operator only meant to open once.
|
|
287
|
+
resolve_global_package_source() {
|
|
288
|
+
if [[ -n "$PACKAGE_SOURCE" ]]; then printf '%s\n' "$PACKAGE_SOURCE"; return; fi
|
|
289
|
+
local helper
|
|
290
|
+
helper="$(running_helper_source)"
|
|
291
|
+
if [[ -n "$helper" ]]; then printf '%s\n' "$helper"; return; fi
|
|
292
|
+
printf '%s\n' "$(local_path_source)"
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
resolve_project_package_source() {
|
|
296
|
+
if [[ -n "$PACKAGE_SOURCE" ]]; then printf '%s\n' "$PACKAGE_SOURCE"; return; fi
|
|
297
|
+
local pinned
|
|
298
|
+
pinned="$(project_pinned_source)"
|
|
299
|
+
if [[ -n "$pinned" ]]; then printf '%s\n' "$pinned"; return; fi
|
|
300
|
+
local helper
|
|
301
|
+
helper="$(running_helper_source)"
|
|
302
|
+
if [[ -n "$helper" ]]; then printf '%s\n' "$helper"; return; fi
|
|
303
|
+
printf '%s\n' "$(local_path_source)"
|
|
287
304
|
}
|
|
288
305
|
|
|
289
306
|
print_cmd() {
|
|
@@ -385,22 +402,41 @@ if [[ "$DO_PROJECT" == true ]]; then
|
|
|
385
402
|
PROJECT_PATH="$(cd "$PROJECT_PATH" && pwd)"
|
|
386
403
|
fi
|
|
387
404
|
|
|
388
|
-
|
|
405
|
+
GLOBAL_PACKAGE_SOURCE="$(resolve_global_package_source)"
|
|
406
|
+
PROJECT_PACKAGE_SOURCE="$(resolve_project_package_source)"
|
|
389
407
|
|
|
390
408
|
echo "Pi Agent Platform setup"
|
|
391
409
|
echo " platform: $PLATFORM_ROOT"
|
|
392
|
-
|
|
410
|
+
if [[ "$DO_GLOBAL" == true ]]; then
|
|
411
|
+
echo " globalPackageSource: $GLOBAL_PACKAGE_SOURCE"
|
|
412
|
+
fi
|
|
413
|
+
if [[ "$DO_PROJECT" == true ]]; then
|
|
414
|
+
echo " projectPackageSource: $PROJECT_PACKAGE_SOURCE"
|
|
415
|
+
fi
|
|
393
416
|
echo " globalInstall: $DO_GLOBAL"
|
|
394
417
|
echo " projectInit: $DO_PROJECT"
|
|
395
418
|
if [[ "$DO_PROJECT" == true ]]; then
|
|
396
419
|
echo " project: $PROJECT_PATH"
|
|
397
420
|
echo " profile: $PROFILE_INPUT"
|
|
398
421
|
fi
|
|
422
|
+
|
|
423
|
+
# The operator asked for both scopes and the project wants a different build
|
|
424
|
+
# than the one about to be installed machine-wide. Neither choice is wrong, so
|
|
425
|
+
# neither is overridden -- but a divergence they cannot see is how the old
|
|
426
|
+
# silent downgrade went unnoticed.
|
|
427
|
+
if [[ "$DO_GLOBAL" == true && "$DO_PROJECT" == true && "$GLOBAL_PACKAGE_SOURCE" != "$PROJECT_PACKAGE_SOURCE" ]]; then
|
|
428
|
+
echo
|
|
429
|
+
echo "NOTE: this project pins a different package source than the global install." >&2
|
|
430
|
+
echo "NOTE: global: $GLOBAL_PACKAGE_SOURCE" >&2
|
|
431
|
+
echo "NOTE: project: $PROJECT_PACKAGE_SOURCE" >&2
|
|
432
|
+
echo "NOTE: the project keeps its pin and the machine keeps the running helper." >&2
|
|
433
|
+
echo "NOTE: pass --package-source <source> to make both the same." >&2
|
|
434
|
+
fi
|
|
399
435
|
echo
|
|
400
436
|
|
|
401
437
|
if [[ "$DO_GLOBAL" == true ]]; then
|
|
402
438
|
ensure_pi_cli
|
|
403
|
-
install_args=("$PLATFORM_ROOT/scripts/install-global.sh" "--package-source" "$
|
|
439
|
+
install_args=("$PLATFORM_ROOT/scripts/install-global.sh" "--package-source" "$GLOBAL_PACKAGE_SOURCE")
|
|
404
440
|
# The opt-out has to be passed explicitly. The installer defaults MCP on, so
|
|
405
441
|
# saying nothing here would install it against the operator's choice.
|
|
406
442
|
# Subagents also needs an explicit opt-out because the installer now preserves
|
|
@@ -432,7 +468,7 @@ if [[ "$DO_GLOBAL" == true ]]; then
|
|
|
432
468
|
fi
|
|
433
469
|
|
|
434
470
|
if [[ "$DO_PROJECT" == true ]]; then
|
|
435
|
-
init_args=("$PLATFORM_ROOT/scripts/init-project.sh" "$PROJECT_PATH" "--profile" "$PROFILE_INPUT" "--package-source" "$
|
|
471
|
+
init_args=("$PLATFORM_ROOT/scripts/init-project.sh" "$PROJECT_PATH" "--profile" "$PROFILE_INPUT" "--package-source" "$PROJECT_PACKAGE_SOURCE")
|
|
436
472
|
if [[ "$FORCE_PROFILE" == true ]]; then
|
|
437
473
|
init_args+=("--force-profile")
|
|
438
474
|
fi
|