@opengsd/gsd-pi 1.6.0 → 1.7.0-dev.30c4ec2f
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 +6 -10
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +19 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +20 -0
- package/dist/resources/extensions/gsd/auto.js +12 -0
- package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +68 -2
- package/dist/resources/extensions/gsd/doctor-history.js +65 -0
- package/dist/resources/extensions/gsd/doctor-state-checks.js +509 -0
- package/dist/resources/extensions/gsd/doctor-workspace-checks.js +91 -0
- package/dist/resources/extensions/gsd/doctor.js +14 -653
- package/dist/resources/extensions/gsd/md-importer.js +7 -2
- package/dist/resources/extensions/gsd/migrate/planning-writer.js +8 -2
- package/dist/resources/extensions/gsd/migration-auto-check.js +4 -0
- package/dist/resources/extensions/gsd/reopen-reason.js +98 -0
- package/dist/resources/extensions/gsd/session-status-io.js +31 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +54 -14
- package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +48 -5
- package/dist/resources/extensions/gsd/tools/reopen-task.js +15 -0
- package/dist/resources/extensions/shared/gsd-browser-cli.js +22 -5
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
- package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +3 -3
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/{layout-b35cbfff38aaf4cf.js → layout-4ae2d68984392bbf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-590a74400e35f685.js → main-app-90d1d8d5e5d2dc6b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +1 -0
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +6 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/cloud-cli.d.ts +1 -1
- package/packages/daemon/dist/cloud-cli.d.ts.map +1 -1
- package/packages/daemon/dist/cloud-cli.js +42 -2
- package/packages/daemon/dist/cloud-cli.js.map +1 -1
- package/packages/daemon/dist/cloud-config.d.ts +1 -0
- package/packages/daemon/dist/cloud-config.d.ts.map +1 -1
- package/packages/daemon/dist/cloud-config.js +26 -8
- package/packages/daemon/dist/cloud-config.js.map +1 -1
- package/packages/daemon/dist/cloud-config.test.js +19 -1
- package/packages/daemon/dist/cloud-config.test.js.map +1 -1
- package/packages/daemon/dist/device-flow.d.ts +24 -0
- package/packages/daemon/dist/device-flow.d.ts.map +1 -0
- package/packages/daemon/dist/device-flow.js +128 -0
- package/packages/daemon/dist/device-flow.js.map +1 -0
- package/packages/daemon/dist/device-flow.test.d.ts +2 -0
- package/packages/daemon/dist/device-flow.test.d.ts.map +1 -0
- package/packages/daemon/dist/device-flow.test.js +172 -0
- package/packages/daemon/dist/device-flow.test.js.map +1 -0
- package/packages/daemon/package.json +5 -5
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/README.md +48 -0
- package/packages/gsd-cloud/bin/gsd-cloud.js +28 -0
- package/packages/gsd-cloud/package.json +40 -0
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +19 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +22 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +20 -0
- package/src/resources/extensions/gsd/auto.ts +12 -0
- package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +77 -1
- package/src/resources/extensions/gsd/doctor-history.ts +81 -0
- package/src/resources/extensions/gsd/doctor-state-checks.ts +527 -0
- package/src/resources/extensions/gsd/doctor-workspace-checks.ts +97 -0
- package/src/resources/extensions/gsd/doctor.ts +14 -673
- package/src/resources/extensions/gsd/md-importer.ts +7 -2
- package/src/resources/extensions/gsd/migrate/planning-writer.ts +9 -3
- package/src/resources/extensions/gsd/migration-auto-check.ts +3 -0
- package/src/resources/extensions/gsd/reopen-reason.ts +109 -0
- package/src/resources/extensions/gsd/session-status-io.ts +34 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +67 -20
- package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +47 -5
- package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +76 -1
- package/src/resources/extensions/gsd/tests/doctor-history-public-api.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/planning-writer.test.ts +18 -1
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +18 -3
- package/src/resources/extensions/gsd/tests/reopen-reason.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +177 -25
- package/src/resources/extensions/gsd/tests/worker-resume-wait.test.ts +73 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +15 -0
- package/src/resources/extensions/shared/gsd-browser-cli.ts +35 -7
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
- /package/dist/web/standalone/.next/static/{6xYMOzxVhsQgryneJ5KRc → ue2JDvA1wboxPBPeblD5N}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{6xYMOzxVhsQgryneJ5KRc → ue2JDvA1wboxPBPeblD5N}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsd/agent-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-dev.30c4ec2f",
|
|
4
4
|
"description": "GSD session orchestration layer on top of @gsd/pi-coding-agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"gsd": {
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"test": "node --import ../../src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --test src/*.test.ts src/**/*.test.ts"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@gsd/native": "^1.
|
|
29
|
-
"@gsd/pi-agent-core": "^1.
|
|
30
|
-
"@gsd/pi-ai": "^1.
|
|
31
|
-
"@gsd/pi-coding-agent": "^1.
|
|
28
|
+
"@gsd/native": "^1.7.0-dev.30c4ec2f",
|
|
29
|
+
"@gsd/pi-agent-core": "^1.7.0-dev.30c4ec2f",
|
|
30
|
+
"@gsd/pi-ai": "^1.7.0-dev.30c4ec2f",
|
|
31
|
+
"@gsd/pi-coding-agent": "^1.7.0-dev.30c4ec2f"
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsd/agent-modes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-dev.30c4ec2f",
|
|
4
4
|
"description": "GSD run modes and CLI layer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"gsd": {
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"test": "node --import ../../src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --test src/**/*.test.ts"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@gsd/agent-core": "^1.
|
|
29
|
-
"@gsd/pi-agent-core": "^1.
|
|
30
|
-
"@gsd/pi-ai": "^1.
|
|
31
|
-
"@gsd/pi-coding-agent": "^1.
|
|
32
|
-
"@gsd/pi-tui": "^1.
|
|
33
|
-
"@opengsd/contracts": "^1.
|
|
28
|
+
"@gsd/agent-core": "^1.7.0-dev.30c4ec2f",
|
|
29
|
+
"@gsd/pi-agent-core": "^1.7.0-dev.30c4ec2f",
|
|
30
|
+
"@gsd/pi-ai": "^1.7.0-dev.30c4ec2f",
|
|
31
|
+
"@gsd/pi-coding-agent": "^1.7.0-dev.30c4ec2f",
|
|
32
|
+
"@gsd/pi-tui": "^1.7.0-dev.30c4ec2f",
|
|
33
|
+
"@opengsd/contracts": "^1.7.0-dev.30c4ec2f",
|
|
34
34
|
"chalk": "^5.5.0"
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @opengsd/gsd-cloud
|
|
2
|
+
|
|
3
|
+
Connect a local GSD runtime to [GSD Cloud](https://cloud.opengsd.net) so you can
|
|
4
|
+
monitor and control your GSD projects from any browser.
|
|
5
|
+
|
|
6
|
+
This is a **self-contained** agent. It depends only on `ws` and `yaml` — no
|
|
7
|
+
`@opengsd/daemon`, no `@opengsd/mcp-server`, no `@opengsd/gsd-pi`. It runs the
|
|
8
|
+
RFC 8628 device-flow login, opens a persistent WebSocket to the cloud gateway,
|
|
9
|
+
and forwards each requested GSD workflow tool to your locally-installed `gsd`
|
|
10
|
+
CLI (via `gsd --mode mcp`). The gateway default of `https://cloud.opengsd.net`
|
|
11
|
+
is injected for `login`/`pair` so you never have to type `--gateway`.
|
|
12
|
+
|
|
13
|
+
Requires the `gsd` CLI (from `@opengsd/gsd-pi`) to be installed and on your
|
|
14
|
+
`PATH` (or set `GSD_CLI_PATH`).
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Browser-based pairing against GSD Cloud (recommended). Opens an approval URL,
|
|
20
|
+
# polls for authorization, then auto-connects. No --gateway needed.
|
|
21
|
+
npx @opengsd/gsd-cloud login
|
|
22
|
+
|
|
23
|
+
# Show current cloud runtime configuration and connection status.
|
|
24
|
+
npx @opengsd/gsd-cloud status
|
|
25
|
+
|
|
26
|
+
# Start the daemon using a previously paired device token.
|
|
27
|
+
npx @opengsd/gsd-cloud connect
|
|
28
|
+
|
|
29
|
+
# Remove cloud runtime configuration from the local config file.
|
|
30
|
+
npx @opengsd/gsd-cloud disconnect
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`login` (and `pair`) default to `https://cloud.opengsd.net`. To target a
|
|
34
|
+
different gateway, pass `--gateway <url>` explicitly — the explicit flag always
|
|
35
|
+
wins. The `status`, `connect`, and `disconnect` commands do not use a gateway.
|
|
36
|
+
|
|
37
|
+
## Environment
|
|
38
|
+
|
|
39
|
+
- `GSD_CLOUD_PROJECTS` — path-delimiter separated list of project directories to
|
|
40
|
+
advertise to the cloud (default: the current working directory).
|
|
41
|
+
- `GSD_CLI_PATH` — path to the `gsd` binary (default: `gsd` on `PATH`).
|
|
42
|
+
- `GSD_CLOUD_EXECUTOR` — backend adapter: `gsd-pi` (default). `codex` and
|
|
43
|
+
`claude` adapters are stubbed for future use.
|
|
44
|
+
|
|
45
|
+
## Requirements
|
|
46
|
+
|
|
47
|
+
- Node.js >= 22
|
|
48
|
+
- The `gsd` CLI (`@opengsd/gsd-pi`) installed locally
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Project/App: Open GSD
|
|
3
|
+
// File Purpose: gsd-cloud CLI entry — inject the default gateway, then run the
|
|
4
|
+
// standalone cloud command handler. No @opengsd/daemon dependency.
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const binDir = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const distDir = join(binDir, '..', 'dist');
|
|
11
|
+
|
|
12
|
+
if (!existsSync(join(distDir, 'cli.js'))) {
|
|
13
|
+
process.stderr.write('gsd-cloud: build output missing. Run `pnpm --filter @opengsd/gsd-cloud run build`.\n');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { injectDefaultGateway } = await import('../dist/inject-gateway.js');
|
|
18
|
+
const { handleCloudCommand } = await import('../dist/cli.js');
|
|
19
|
+
|
|
20
|
+
const argv = injectDefaultGateway(process.argv.slice(2));
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
await handleCloudCommand(argv, { binaryName: 'gsd-cloud' });
|
|
24
|
+
} catch (err) {
|
|
25
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
26
|
+
process.stderr.write(`gsd-cloud: fatal: ${msg}\n`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opengsd/gsd-cloud",
|
|
3
|
+
"version": "1.7.0-dev.30c4ec2f",
|
|
4
|
+
"description": "Standalone CLI agent that connects a local GSD runtime to GSD Cloud (cloud.opengsd.net)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/open-gsd/gsd-pi.git",
|
|
9
|
+
"directory": "packages/gsd-cloud"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"gsd-cloud": "./bin/gsd-cloud.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "node ../../scripts/clean-package-dist.cjs && tsc",
|
|
20
|
+
"test": "pnpm run build && node --test dist/inject-gateway.test.js dist/cloud-gateway-lookup.test.js dist/cloud-runtime.test.js dist/executors/mcp-stdio-client.test.js dist/executors/gsd-pi-executor.test.js"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"ws": "^8.21.0",
|
|
24
|
+
"yaml": "^2.8.2"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^24.12.0",
|
|
28
|
+
"@types/ws": "^8.5.13",
|
|
29
|
+
"typescript": "^5.4.0"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=22.0.0"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"README.md",
|
|
36
|
+
"bin",
|
|
37
|
+
"dist",
|
|
38
|
+
"!dist/**/*.test.*"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengsd/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-dev.30c4ec2f",
|
|
4
4
|
"description": "MCP server exposing GSD orchestration tools for compatible clients",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"gsd": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"test": "pnpm run build:test && node --test dist/mcp-server.test.js dist/remote-questions.test.js dist/moonshot-tool-schema.test.js dist/pid-registry.test.js dist/probe-mode.test.js dist/stdio-watchdog.test.js dist/cli-runner.test.js dist/readers/graph.test.js dist/readers/paths.test.js dist/readers/readers.test.js"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@gsd/pi-ai": "^1.
|
|
54
|
-
"@opengsd/contracts": "^1.
|
|
55
|
-
"@opengsd/rpc-client": "^1.
|
|
53
|
+
"@gsd/pi-ai": "^1.7.0-dev.30c4ec2f",
|
|
54
|
+
"@opengsd/contracts": "^1.7.0-dev.30c4ec2f",
|
|
55
|
+
"@opengsd/rpc-client": "^1.7.0-dev.30c4ec2f",
|
|
56
56
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
57
57
|
"zod": "^4.0.0"
|
|
58
58
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsd/pi-coding-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-dev.30c4ec2f",
|
|
4
4
|
"description": "Coding agent CLI (vendored from earendil-works/pi)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"gsd": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"copy-assets": "node scripts/copy-assets.cjs"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@opengsd/contracts": "^1.
|
|
36
|
+
"@opengsd/contracts": "^1.7.0-dev.30c4ec2f",
|
|
37
37
|
"@mariozechner/jiti": "^2.6.2",
|
|
38
38
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
39
39
|
"chalk": "5.6.2",
|
|
@@ -53,11 +53,11 @@
|
|
|
53
53
|
"typebox": "1.1.38",
|
|
54
54
|
"undici": "7.28.0",
|
|
55
55
|
"yaml": "2.9.0",
|
|
56
|
-
"@gsd/agent-core": "^1.
|
|
57
|
-
"@gsd/native": "^1.
|
|
58
|
-
"@gsd/pi-agent-core": "^1.
|
|
59
|
-
"@gsd/pi-ai": "^1.
|
|
60
|
-
"@gsd/pi-tui": "^1.
|
|
56
|
+
"@gsd/agent-core": "^1.7.0-dev.30c4ec2f",
|
|
57
|
+
"@gsd/native": "^1.7.0-dev.30c4ec2f",
|
|
58
|
+
"@gsd/pi-agent-core": "^1.7.0-dev.30c4ec2f",
|
|
59
|
+
"@gsd/pi-ai": "^1.7.0-dev.30c4ec2f",
|
|
60
|
+
"@gsd/pi-tui": "^1.7.0-dev.30c4ec2f",
|
|
61
61
|
"@sinclair/typebox": "^0.34.41"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsd/pi-tui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-dev.30c4ec2f",
|
|
4
4
|
"description": "Terminal UI library (vendored from earendil-works/pi)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"gsd": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"build": "node ../../scripts/clean-package-dist.cjs && tsc -p tsconfig.json --incremental false"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@gsd/native": "^1.
|
|
24
|
+
"@gsd/native": "^1.7.0-dev.30c4ec2f",
|
|
25
25
|
"get-east-asian-width": "1.6.0",
|
|
26
26
|
"marked": "15.0.12",
|
|
27
27
|
"@sinclair/typebox": "^0.34.41"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengsd/rpc-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-dev.30c4ec2f",
|
|
4
4
|
"description": "Standalone RPC client SDK for GSD — zero internal dependencies",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"gsd": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"test": "node --test dist/rpc-client.test.js"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@opengsd/contracts": "^1.
|
|
37
|
+
"@opengsd/contracts": "^1.7.0-dev.30c4ec2f"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
40
|
"node": ">=22.0.0"
|
package/pkg/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import { dirname, join } from "node:path";
|
|
|
9
9
|
const {
|
|
10
10
|
resolveBundledGsdBrowserCliPath,
|
|
11
11
|
resolveGsdBrowserDaemonStartInvocation,
|
|
12
|
+
resolveGsdBrowserDaemonStopInvocation,
|
|
12
13
|
resolveGsdBrowserMcpLaunchConfig,
|
|
13
14
|
} = await import("../../shared/gsd-browser-cli.ts");
|
|
14
15
|
|
|
@@ -179,3 +180,21 @@ describe("resolveGsdBrowserDaemonStartInvocation", () => {
|
|
|
179
180
|
);
|
|
180
181
|
});
|
|
181
182
|
});
|
|
183
|
+
|
|
184
|
+
describe("resolveGsdBrowserDaemonStopInvocation", () => {
|
|
185
|
+
it("mirrors MCP session and identity flags with daemon stop", () => {
|
|
186
|
+
const launch = resolveGsdBrowserMcpLaunchConfig("/tmp/example-project", {});
|
|
187
|
+
const daemon = resolveGsdBrowserDaemonStopInvocation("/tmp/example-project", {});
|
|
188
|
+
|
|
189
|
+
assert.equal(daemon.command, launch.command);
|
|
190
|
+
assert.equal(daemon.cwd, launch.cwd);
|
|
191
|
+
assert.deepEqual(
|
|
192
|
+
daemon.args.slice(daemon.args.indexOf("--session")),
|
|
193
|
+
launch.args.slice(launch.args.indexOf("--session")),
|
|
194
|
+
);
|
|
195
|
+
assert.deepEqual(
|
|
196
|
+
daemon.args.slice(0, daemon.args.indexOf("--session")),
|
|
197
|
+
[...launch.args.slice(0, launch.args.indexOf("mcp")), "daemon", "stop"],
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -56,7 +56,7 @@ import { normalizeWorktreePathForCompare } from "./worktree-root.js";
|
|
|
56
56
|
import { isDbAvailable, getTask, getSlice, getMilestone, getMilestoneSlices, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
|
|
57
57
|
import { getWorkflowDatabasePath, refreshWorkflowDatabaseFromDisk } from "./db-workspace.js";
|
|
58
58
|
import { renderPlanCheckboxes, renderRoadmapFromDb, roadmapRenderMarksSliceDone } from "./markdown-renderer.js";
|
|
59
|
-
import { consumeSignal } from "./session-status-io.js";
|
|
59
|
+
import { awaitWorkerResume, consumeSignal } from "./session-status-io.js";
|
|
60
60
|
import {
|
|
61
61
|
checkPostUnitHooks,
|
|
62
62
|
consumeHookFailure,
|
|
@@ -1279,8 +1279,27 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
1279
1279
|
return "dispatched";
|
|
1280
1280
|
}
|
|
1281
1281
|
if (signal.signal === "pause") {
|
|
1282
|
-
|
|
1283
|
-
|
|
1282
|
+
// A worker `pause` is a *temporary* coordination request: the docs
|
|
1283
|
+
// contract (docs/user-docs/parallel-orchestration.md) is "finish current
|
|
1284
|
+
// unit, wait" followed by a matching `resume`. Only the interactive
|
|
1285
|
+
// dashboard runtime owns a resumer — unattended `gsd headless auto` does
|
|
1286
|
+
// not — so a terminal pauseAuto() here strands the worker at exit 10
|
|
1287
|
+
// (BLOCKED) after its first unit, with nothing able to lift it (#1273).
|
|
1288
|
+
// Wait for the coordinator to resume (or stop) us instead. If no resumer
|
|
1289
|
+
// lifts the pause within the window, degrade to in-process serialization
|
|
1290
|
+
// and continue the dispatch loop rather than halting the run forever.
|
|
1291
|
+
const resumeOutcome = await awaitWorkerResume(s.basePath, milestoneLock);
|
|
1292
|
+
if (resumeOutcome === "stop") {
|
|
1293
|
+
await stopAuto(ctx, pi);
|
|
1294
|
+
return "dispatched";
|
|
1295
|
+
}
|
|
1296
|
+
if (resumeOutcome === "timeout") {
|
|
1297
|
+
logWarning(
|
|
1298
|
+
"parallel",
|
|
1299
|
+
`pause signal for ${milestoneLock} was not resumed (no headless resumer, #1273); continuing dispatch`,
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
// "resume" or "timeout": fall through and continue post-unit processing.
|
|
1284
1303
|
}
|
|
1285
1304
|
}
|
|
1286
1305
|
}
|
|
@@ -2725,6 +2725,26 @@ export async function buildExecuteTaskPrompt(
|
|
|
2725
2725
|
let phaseAnchorSection = planAnchor ? formatAnchorForPrompt(planAnchor) : "";
|
|
2726
2726
|
trackPromptContext(contextTelemetry, "plan-anchor", phaseAnchorSection ? "inline" : "skipped", phaseAnchorSection, phaseAnchorSection ? undefined : "missing");
|
|
2727
2727
|
|
|
2728
|
+
// #1272: inject a pending reopen reason into this task's prompt. When a gate
|
|
2729
|
+
// (e.g. complete-slice's full-suite run) reopened the task via gsd_task_reopen
|
|
2730
|
+
// with a diagnosis, surface it here so the re-dispatched executor fixes the
|
|
2731
|
+
// regression instead of re-running the original (green) scoped verify. Claim
|
|
2732
|
+
// is one-shot (artifact deleted on read). Not feature-gated — a reopened task
|
|
2733
|
+
// must always know why. Prepended so it sits above the plan anchor.
|
|
2734
|
+
try {
|
|
2735
|
+
const { claimReopenReasonForInjection } = await import("./reopen-reason.js");
|
|
2736
|
+
const reopenClaimed = claimReopenReasonForInjection(base, mid, sid, tid);
|
|
2737
|
+
if (reopenClaimed) {
|
|
2738
|
+
const block = reopenClaimed.injectionBlock + "\n\n---\n\n";
|
|
2739
|
+
phaseAnchorSection = phaseAnchorSection
|
|
2740
|
+
? `${block}${phaseAnchorSection}`
|
|
2741
|
+
: block;
|
|
2742
|
+
trackPromptContext(contextTelemetry, "reopen-reason", "inline", reopenClaimed.injectionBlock);
|
|
2743
|
+
}
|
|
2744
|
+
} catch (reopenErr) {
|
|
2745
|
+
logWarning("prompt", `reopen reason injection failed: ${(reopenErr as Error).message}`);
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2728
2748
|
// ADR-011 Phase 2: inject any resolved-but-unapplied escalation override
|
|
2729
2749
|
// into this task's prompt. Claim is atomic via DB UPDATE WHERE IS NULL, so
|
|
2730
2750
|
// if a parallel build already injected it, we skip. Feature-gated by
|
|
@@ -185,6 +185,7 @@ import { enterAutoWorktree, isInAutoWorktree } from "./auto-worktree-entry.js";
|
|
|
185
185
|
import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
|
|
186
186
|
import { checkResourcesStale, readResourceVersion } from "./auto-worktree-resource-version.js";
|
|
187
187
|
import { escapeStaleWorktree } from "./auto-worktree-runtime-cleanup.js";
|
|
188
|
+
import { teardownWarmedBrowserDaemons } from "./browser-daemon-auto-prep.js";
|
|
188
189
|
import { getAutoWorktreeOriginalBase } from "./auto-worktree-session-registry.js";
|
|
189
190
|
import { syncWorktreeStateBack } from "./auto-worktree-sync.js";
|
|
190
191
|
import { teardownAutoWorktree } from "./auto-worktree-teardown.js";
|
|
@@ -1490,6 +1491,17 @@ export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void>
|
|
|
1490
1491
|
logWarning("session", `lock cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
1491
1492
|
}
|
|
1492
1493
|
|
|
1494
|
+
// Symmetric teardown for the browser-UAT warm-up preflight (#1259): stop any
|
|
1495
|
+
// gsd-browser daemon this session started so the Chrome process does not
|
|
1496
|
+
// linger after the loop exits. Re-warms on the next browser-backed run-uat.
|
|
1497
|
+
try {
|
|
1498
|
+
for (const { projectRoot, error } of teardownWarmedBrowserDaemons()) {
|
|
1499
|
+
logWarning("session", `gsd-browser daemon stop failed for ${projectRoot}: ${error}`, { file: "auto.ts" });
|
|
1500
|
+
}
|
|
1501
|
+
} catch (err) {
|
|
1502
|
+
logWarning("session", `browser daemon teardown failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1493
1505
|
// A transient provider-error pause intentionally leaves the paused badge
|
|
1494
1506
|
// visible so the user still has a resumable auto-mode signal on screen.
|
|
1495
1507
|
if (!s.paused) {
|
|
@@ -9,10 +9,19 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
resolveGsdBrowserCliAvailability,
|
|
11
11
|
resolveGsdBrowserDaemonStartInvocation,
|
|
12
|
+
resolveGsdBrowserDaemonStopInvocation,
|
|
12
13
|
} from "../shared/gsd-browser-cli.js";
|
|
13
14
|
import { uatTypeIncludesBrowser, type UatType } from "./uat-policy.js";
|
|
14
15
|
|
|
15
16
|
const DEFAULT_DAEMON_START_TIMEOUT_MS = 30_000;
|
|
17
|
+
const DEFAULT_DAEMON_STOP_TIMEOUT_MS = 15_000;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Project roots whose gsd-browser daemon this process warmed via
|
|
21
|
+
* {@link prepareBrowserDaemonForUat}. Teardown only stops daemons we actually
|
|
22
|
+
* started, so non-browser auto-mode sessions never spawn a stray `daemon stop`.
|
|
23
|
+
*/
|
|
24
|
+
const warmedDaemonProjectRoots = new Set<string>();
|
|
16
25
|
|
|
17
26
|
function isEnvDisabled(value: string | undefined): boolean {
|
|
18
27
|
if (!value) return false;
|
|
@@ -102,7 +111,74 @@ export function prepareBrowserDaemonForUat(ctx: BrowserDaemonWarmContext): strin
|
|
|
102
111
|
if (!shouldWarmBrowserDaemonForUat(ctx)) return null;
|
|
103
112
|
|
|
104
113
|
const result = ensureBrowserDaemonStarted(ctx.projectRoot, { env: ctx.env });
|
|
105
|
-
if (result.ok)
|
|
114
|
+
if (result.ok) {
|
|
115
|
+
warmedDaemonProjectRoots.add(resolve(ctx.projectRoot));
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
106
118
|
|
|
107
119
|
return `Cannot dispatch browser-backed run-uat: gsd-browser daemon failed to start (${result.error}). Ensure Chrome/Chromium is installed, run \`gsd-browser daemon health\` with the project session flags from .mcp.json, or set GSD_BROWSER_PATH to a Chromium binary.`;
|
|
108
120
|
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Best-effort stop of a gsd-browser session daemon warmed for browser UAT.
|
|
124
|
+
* Mirrors {@link ensureBrowserDaemonStarted}; returns the failure detail rather
|
|
125
|
+
* than throwing so teardown can stay non-fatal.
|
|
126
|
+
*/
|
|
127
|
+
export function stopBrowserDaemon(
|
|
128
|
+
projectRoot: string,
|
|
129
|
+
options: { env?: NodeJS.ProcessEnv; timeoutMs?: number } = {},
|
|
130
|
+
): { ok: true } | { ok: false; error: string } {
|
|
131
|
+
const env = options.env ?? process.env;
|
|
132
|
+
const availability = resolveGsdBrowserCliAvailability(env);
|
|
133
|
+
if (!availability.available) {
|
|
134
|
+
return { ok: false, error: availability.detail };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let invocation: ReturnType<typeof resolveGsdBrowserDaemonStopInvocation>;
|
|
138
|
+
try {
|
|
139
|
+
invocation = resolveGsdBrowserDaemonStopInvocation(projectRoot, env);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return {
|
|
142
|
+
ok: false,
|
|
143
|
+
error: error instanceof Error ? error.message : String(error),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
execFileSync(invocation.command, invocation.args, {
|
|
149
|
+
cwd: invocation.cwd,
|
|
150
|
+
env: { ...process.env, ...env, ...(invocation.env ?? {}) },
|
|
151
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
152
|
+
timeout: options.timeoutMs ?? DEFAULT_DAEMON_STOP_TIMEOUT_MS,
|
|
153
|
+
encoding: "utf-8",
|
|
154
|
+
});
|
|
155
|
+
return { ok: true };
|
|
156
|
+
} catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
ok: false,
|
|
159
|
+
error: error instanceof Error ? error.message : String(error),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Tear down every gsd-browser daemon this process warmed for browser UAT,
|
|
166
|
+
* clearing the tracked set. Called on auto-mode loop exit so the Chrome process
|
|
167
|
+
* does not survive session/task completion (mirrors the warm-up preflight).
|
|
168
|
+
* Best-effort: returns the project roots whose stop failed for logging.
|
|
169
|
+
*/
|
|
170
|
+
export function teardownWarmedBrowserDaemons(
|
|
171
|
+
options: { env?: NodeJS.ProcessEnv } = {},
|
|
172
|
+
): { projectRoot: string; error: string }[] {
|
|
173
|
+
if (warmedDaemonProjectRoots.size === 0) return [];
|
|
174
|
+
|
|
175
|
+
const failures: { projectRoot: string; error: string }[] = [];
|
|
176
|
+
for (const projectRoot of warmedDaemonProjectRoots) {
|
|
177
|
+
const result = stopBrowserDaemon(projectRoot, { env: options.env });
|
|
178
|
+
if (!result.ok) {
|
|
179
|
+
failures.push({ projectRoot, error: result.error });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
warmedDaemonProjectRoots.clear();
|
|
183
|
+
return failures;
|
|
184
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { saveFile } from "./files.js";
|
|
5
|
+
import { gsdRoot } from "./paths.js";
|
|
6
|
+
|
|
7
|
+
import type { DoctorReport } from "./doctor-types.js";
|
|
8
|
+
|
|
9
|
+
export interface DoctorHistoryEntry {
|
|
10
|
+
ts: string;
|
|
11
|
+
ok: boolean;
|
|
12
|
+
errors: number;
|
|
13
|
+
warnings: number;
|
|
14
|
+
fixes: number;
|
|
15
|
+
codes: string[];
|
|
16
|
+
/** Issue messages with severity and scope (added in Phase 2). */
|
|
17
|
+
issues?: Array<{ severity: string; code: string; message: string; unitId: string }>;
|
|
18
|
+
/** Fix descriptions applied during this run (added in Phase 2). */
|
|
19
|
+
fixDescriptions?: string[];
|
|
20
|
+
/** Milestone/slice scope this doctor run was scoped to (e.g. "M001/S02"). */
|
|
21
|
+
scope?: string;
|
|
22
|
+
/** Human-readable one-line summary of this doctor run. */
|
|
23
|
+
summary?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildDoctorHistorySummary(report: DoctorReport, errorCount: number, warningCount: number, issueDetails: DoctorHistoryEntry["issues"]): string {
|
|
27
|
+
const summaryParts: string[] = [];
|
|
28
|
+
if (report.ok) {
|
|
29
|
+
summaryParts.push("Clean");
|
|
30
|
+
} else {
|
|
31
|
+
const counts: string[] = [];
|
|
32
|
+
if (errorCount > 0) counts.push(`${errorCount} error${errorCount > 1 ? "s" : ""}`);
|
|
33
|
+
if (warningCount > 0) counts.push(`${warningCount} warning${warningCount > 1 ? "s" : ""}`);
|
|
34
|
+
summaryParts.push(counts.join(", "));
|
|
35
|
+
}
|
|
36
|
+
if (report.fixesApplied.length > 0) {
|
|
37
|
+
summaryParts.push(`${report.fixesApplied.length} fixed`);
|
|
38
|
+
}
|
|
39
|
+
if (issueDetails && issueDetails.length > 0) {
|
|
40
|
+
const topIssue = issueDetails.find(i => i.severity === "error") ?? issueDetails[0]!;
|
|
41
|
+
summaryParts.push(topIssue.message);
|
|
42
|
+
}
|
|
43
|
+
return summaryParts.join(" · ");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function appendDoctorHistory(basePath: string, report: DoctorReport): Promise<void> {
|
|
47
|
+
try {
|
|
48
|
+
const historyPath = join(gsdRoot(basePath), "doctor-history.jsonl");
|
|
49
|
+
const errorCount = report.issues.filter(i => i.severity === "error").length;
|
|
50
|
+
const warningCount = report.issues.filter(i => i.severity === "warning").length;
|
|
51
|
+
const issueDetails = report.issues
|
|
52
|
+
.filter(i => i.severity === "error" || i.severity === "warning")
|
|
53
|
+
.slice(0, 10) // cap to keep JSONL lines bounded
|
|
54
|
+
.map(i => ({ severity: i.severity, code: i.code, message: i.message, unitId: i.unitId }));
|
|
55
|
+
|
|
56
|
+
const entry = JSON.stringify({
|
|
57
|
+
ts: new Date().toISOString(),
|
|
58
|
+
ok: report.ok,
|
|
59
|
+
errors: errorCount,
|
|
60
|
+
warnings: warningCount,
|
|
61
|
+
fixes: report.fixesApplied.length,
|
|
62
|
+
codes: [...new Set(report.issues.map(i => i.code))],
|
|
63
|
+
issues: issueDetails.length > 0 ? issueDetails : undefined,
|
|
64
|
+
fixDescriptions: report.fixesApplied.length > 0 ? report.fixesApplied : undefined,
|
|
65
|
+
scope: (report as { scope?: string }).scope,
|
|
66
|
+
summary: buildDoctorHistorySummary(report, errorCount, warningCount, issueDetails),
|
|
67
|
+
} satisfies DoctorHistoryEntry);
|
|
68
|
+
const existing = existsSync(historyPath) ? readFileSync(historyPath, "utf-8") : "";
|
|
69
|
+
await saveFile(historyPath, existing + entry + "\n");
|
|
70
|
+
} catch { /* non-fatal */ }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Read the last N doctor history entries. Returns most-recent-first. */
|
|
74
|
+
export async function readDoctorHistory(basePath: string, lastN = 50): Promise<DoctorHistoryEntry[]> {
|
|
75
|
+
try {
|
|
76
|
+
const historyPath = join(gsdRoot(basePath), "doctor-history.jsonl");
|
|
77
|
+
if (!existsSync(historyPath)) return [];
|
|
78
|
+
const lines = readFileSync(historyPath, "utf-8").split("\n").filter(l => l.trim());
|
|
79
|
+
return lines.slice(-lastN).reverse().map(l => JSON.parse(l) as DoctorHistoryEntry);
|
|
80
|
+
} catch { return []; }
|
|
81
|
+
}
|