patchwarden 0.4.0
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/Check-PatchWarden-Health.cmd +6 -0
- package/LICENSE +21 -0
- package/README.md +526 -0
- package/Reset-PatchWarden-Tunnel-Key.cmd +6 -0
- package/Restart-PatchWarden.cmd +19 -0
- package/Start-PatchWarden-Tunnel.cmd +7 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +131 -0
- package/dist/doctor.d.ts +10 -0
- package/dist/doctor.js +388 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.js +27 -0
- package/dist/httpServer.d.ts +17 -0
- package/dist/httpServer.js +255 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +24 -0
- package/dist/runner/changeCapture.d.ts +43 -0
- package/dist/runner/changeCapture.js +245 -0
- package/dist/runner/cli.d.ts +9 -0
- package/dist/runner/cli.js +26 -0
- package/dist/runner/runTask.d.ts +8 -0
- package/dist/runner/runTask.js +821 -0
- package/dist/runner/watch.d.ts +19 -0
- package/dist/runner/watch.js +136 -0
- package/dist/security/commandGuard.d.ts +22 -0
- package/dist/security/commandGuard.js +56 -0
- package/dist/security/contentRedaction.d.ts +12 -0
- package/dist/security/contentRedaction.js +65 -0
- package/dist/security/pathGuard.d.ts +3 -0
- package/dist/security/pathGuard.js +98 -0
- package/dist/security/planGuard.d.ts +1 -0
- package/dist/security/planGuard.js +65 -0
- package/dist/security/sensitiveGuard.d.ts +2 -0
- package/dist/security/sensitiveGuard.js +60 -0
- package/dist/smoke-test.d.ts +15 -0
- package/dist/smoke-test.js +989 -0
- package/dist/taskProgress.d.ts +6 -0
- package/dist/taskProgress.js +36 -0
- package/dist/taskRuntime.d.ts +10 -0
- package/dist/taskRuntime.js +25 -0
- package/dist/tools/auditTask.d.ts +19 -0
- package/dist/tools/auditTask.js +307 -0
- package/dist/tools/cancelTask.d.ts +30 -0
- package/dist/tools/cancelTask.js +64 -0
- package/dist/tools/createTask.d.ts +42 -0
- package/dist/tools/createTask.js +188 -0
- package/dist/tools/getPlan.d.ts +12 -0
- package/dist/tools/getPlan.js +29 -0
- package/dist/tools/getTaskFile.d.ts +24 -0
- package/dist/tools/getTaskFile.js +84 -0
- package/dist/tools/getTaskProgress.d.ts +1 -0
- package/dist/tools/getTaskProgress.js +4 -0
- package/dist/tools/getTaskStatus.d.ts +46 -0
- package/dist/tools/getTaskStatus.js +36 -0
- package/dist/tools/getTaskStdoutTail.d.ts +9 -0
- package/dist/tools/getTaskStdoutTail.js +56 -0
- package/dist/tools/getTaskSummary.d.ts +66 -0
- package/dist/tools/getTaskSummary.js +237 -0
- package/dist/tools/healthCheck.d.ts +79 -0
- package/dist/tools/healthCheck.js +230 -0
- package/dist/tools/killTask.d.ts +15 -0
- package/dist/tools/killTask.js +4 -0
- package/dist/tools/listAgents.d.ts +12 -0
- package/dist/tools/listAgents.js +42 -0
- package/dist/tools/listTasks.d.ts +36 -0
- package/dist/tools/listTasks.js +94 -0
- package/dist/tools/listWorkspace.d.ts +10 -0
- package/dist/tools/listWorkspace.js +54 -0
- package/dist/tools/readWorkspaceFile.d.ts +8 -0
- package/dist/tools/readWorkspaceFile.js +26 -0
- package/dist/tools/registry.d.ts +24 -0
- package/dist/tools/registry.js +566 -0
- package/dist/tools/retryTask.d.ts +8 -0
- package/dist/tools/retryTask.js +45 -0
- package/dist/tools/savePlan.d.ts +10 -0
- package/dist/tools/savePlan.js +28 -0
- package/dist/tools/taskOutputs.d.ts +49 -0
- package/dist/tools/taskOutputs.js +136 -0
- package/dist/tools/taskTemplates.d.ts +15 -0
- package/dist/tools/taskTemplates.js +116 -0
- package/dist/tools/toolCatalog.d.ts +19 -0
- package/dist/tools/toolCatalog.js +71 -0
- package/dist/tools/waitForTask.d.ts +25 -0
- package/dist/tools/waitForTask.js +59 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/dist/watcherStatus.d.ts +20 -0
- package/dist/watcherStatus.js +77 -0
- package/docs/assets/patchwarden-chatgpt-demo.svg +30 -0
- package/docs/demo.md +58 -0
- package/docs/migration-from-safe-bifrost.md +31 -0
- package/docs/release-v0.3.0.md +43 -0
- package/docs/release-v0.4.0.md +74 -0
- package/examples/config.example.json +40 -0
- package/examples/openai-tunnel/README.md +48 -0
- package/examples/openai-tunnel/chatgpt-test-prompt.md +53 -0
- package/examples/openai-tunnel/tunnel-client.example.yaml +39 -0
- package/package.json +73 -0
- package/scripts/brand-check.js +44 -0
- package/scripts/doctor-smoke.js +52 -0
- package/scripts/get-patchwarden-health.ps1 +114 -0
- package/scripts/http-mcp-smoke.js +351 -0
- package/scripts/lifecycle-smoke.js +453 -0
- package/scripts/mcp-manifest-check.js +120 -0
- package/scripts/mcp-smoke.js +336 -0
- package/scripts/pack-clean.js +142 -0
- package/scripts/patchwarden-mcp-stdio.cmd +7 -0
- package/scripts/restart-patchwarden.ps1 +242 -0
- package/scripts/start-patchwarden-tunnel.ps1 +503 -0
- package/scripts/tunnel-supervisor-smoke.js +77 -0
- package/scripts/watcher-supervisor-smoke.js +195 -0
- package/src/config.ts +165 -0
- package/src/doctor.ts +438 -0
- package/src/errors.ts +26 -0
- package/src/httpServer.ts +277 -0
- package/src/index.ts +34 -0
- package/src/runner/changeCapture.ts +304 -0
- package/src/runner/cli.ts +34 -0
- package/src/runner/runTask.ts +935 -0
- package/src/runner/watch.ts +152 -0
- package/src/security/commandGuard.ts +97 -0
- package/src/security/contentRedaction.ts +85 -0
- package/src/security/pathGuard.ts +175 -0
- package/src/security/planGuard.ts +93 -0
- package/src/security/sensitiveGuard.ts +72 -0
- package/src/smoke-test.ts +1078 -0
- package/src/taskProgress.ts +41 -0
- package/src/taskRuntime.ts +37 -0
- package/src/tools/auditTask.ts +340 -0
- package/src/tools/cancelTask.ts +70 -0
- package/src/tools/createTask.ts +337 -0
- package/src/tools/getPlan.ts +48 -0
- package/src/tools/getTaskFile.ts +111 -0
- package/src/tools/getTaskProgress.ts +5 -0
- package/src/tools/getTaskStatus.ts +85 -0
- package/src/tools/getTaskStdoutTail.ts +61 -0
- package/src/tools/getTaskSummary.ts +307 -0
- package/src/tools/healthCheck.ts +234 -0
- package/src/tools/killTask.ts +5 -0
- package/src/tools/listAgents.ts +54 -0
- package/src/tools/listTasks.ts +139 -0
- package/src/tools/listWorkspace.ts +71 -0
- package/src/tools/readWorkspaceFile.ts +41 -0
- package/src/tools/registry.ts +666 -0
- package/src/tools/retryTask.ts +53 -0
- package/src/tools/savePlan.ts +46 -0
- package/src/tools/taskOutputs.ts +155 -0
- package/src/tools/taskTemplates.ts +162 -0
- package/src/tools/toolCatalog.ts +92 -0
- package/src/tools/waitForTask.ts +90 -0
- package/src/version.ts +2 -0
- package/src/watcherStatus.ts +102 -0
- package/tsconfig.json +17 -0
package/docs/demo.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# PatchWarden Demo
|
|
2
|
+
|
|
3
|
+
This is a privacy-safe demo flow for GitHub. It uses placeholder workspace
|
|
4
|
+
names and avoids real tunnel IDs, account names, API keys, or local user paths.
|
|
5
|
+
|
|
6
|
+
## Demo: ChatGPT Lists A Workspace
|
|
7
|
+
|
|
8
|
+
Prompt in ChatGPT with the PatchWarden connector selected:
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
Use the patchwarden tool list_workspace to list the configured workspace.
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Expected response shape:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
Workspace files:
|
|
18
|
+
|
|
19
|
+
| Name | Type | Size |
|
|
20
|
+
|--------------|------|------|
|
|
21
|
+
| README.md | file | ... |
|
|
22
|
+
| package.json | file | ... |
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
## Demo: Plan And Execute
|
|
28
|
+
|
|
29
|
+
Prompt in ChatGPT:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Use patchwarden to:
|
|
33
|
+
1. read README.md
|
|
34
|
+
2. save a plan that appends a Usage section
|
|
35
|
+
3. create a task with an explicit repo_path and verify_commands
|
|
36
|
+
4. call wait_for_task repeatedly while continuation_required is true
|
|
37
|
+
5. review get_task_summary, audit_task, result, diff, and verification logs
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Expected artifacts under the configured workspace:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
.patchwarden/tasks/<task_id>/status.json
|
|
44
|
+
.patchwarden/tasks/<task_id>/result.md
|
|
45
|
+
.patchwarden/tasks/<task_id>/git.diff
|
|
46
|
+
.patchwarden/tasks/<task_id>/test.log
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Privacy Notes
|
|
50
|
+
|
|
51
|
+
Do not publish:
|
|
52
|
+
|
|
53
|
+
- real API keys or runtime keys
|
|
54
|
+
- real tunnel IDs
|
|
55
|
+
- ChatGPT account names or workspace IDs
|
|
56
|
+
- screenshots containing sidebars with private chat/project names
|
|
57
|
+
- `patchwarden.config.json`
|
|
58
|
+
- `.patchwarden/` task history from private workspaces
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Migrating from Safe-Bifrost to PatchWarden
|
|
2
|
+
|
|
3
|
+
PatchWarden v0.4.0 is an intentional pre-1.0 breaking rename. It does not load
|
|
4
|
+
legacy names automatically.
|
|
5
|
+
|
|
6
|
+
## Package and commands
|
|
7
|
+
|
|
8
|
+
- Replace `safe-bifrost` with `patchwarden` in npm dependencies and MCP client
|
|
9
|
+
commands.
|
|
10
|
+
- Replace the old runner command with `patchwarden-runner`.
|
|
11
|
+
- Replace Windows launchers with the corresponding `PatchWarden` launchers.
|
|
12
|
+
|
|
13
|
+
## Configuration
|
|
14
|
+
|
|
15
|
+
1. Copy `safe-bifrost.config.json` to `patchwarden.config.json`.
|
|
16
|
+
2. Change `plansDir` and `tasksDir` from `.safe-bifrost/...` to
|
|
17
|
+
`.patchwarden/...`.
|
|
18
|
+
3. Rename every `SAFE_BIFROST_*` environment variable to `PATCHWARDEN_*`.
|
|
19
|
+
4. Replace `x-safe-bifrost-token` with `x-patchwarden-token` for local HTTP
|
|
20
|
+
authentication.
|
|
21
|
+
|
|
22
|
+
## Local state
|
|
23
|
+
|
|
24
|
+
PatchWarden writes new task state under `.patchwarden`, runtime status under
|
|
25
|
+
`%LOCALAPPDATA%\patchwarden`, and the DPAPI tunnel credential under
|
|
26
|
+
`%APPDATA%\patchwarden`. Old directories are not deleted or migrated
|
|
27
|
+
automatically. Keep them as a backup until the new installation is verified.
|
|
28
|
+
|
|
29
|
+
The old DPAPI credential is intentionally not copied. Run the new tunnel
|
|
30
|
+
launcher and enter the control-plane key again so PatchWarden can save it under
|
|
31
|
+
the new application directory.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Safe-Bifrost v0.3.0
|
|
2
|
+
|
|
3
|
+
Safe-Bifrost v0.3.0 makes ChatGPT-managed local agent work easier to monitor,
|
|
4
|
+
verify, and accept without weakening the workspace and command boundaries.
|
|
5
|
+
|
|
6
|
+
## Highlights
|
|
7
|
+
|
|
8
|
+
- Adds `wait_for_task` so ChatGPT can stay in the tool loop until a task is
|
|
9
|
+
terminal, plus `get_task_summary` for one-call acceptance evidence.
|
|
10
|
+
- Requires an explicit existing repository directory and supports both
|
|
11
|
+
workspace-relative and absolute `repo_path` values.
|
|
12
|
+
- Adds independent multi-command `verify_commands` while keeping
|
|
13
|
+
`test_command` compatibility.
|
|
14
|
+
- Generates structured `result.json`, `verify.json`, readable `verify.log`,
|
|
15
|
+
and a complete `diff.patch` for every terminal task.
|
|
16
|
+
- Adds task-scoped file statistics, clear large/no-diff responses, and avoids
|
|
17
|
+
including unchanged pre-existing workspace files in task diff evidence.
|
|
18
|
+
- Detects changes outside `resolved_repo_path`, marks the task
|
|
19
|
+
`failed_scope_violation`, invalidates verification acceptance, and creates a
|
|
20
|
+
review-only `rollback_scope_violation_plan.md`.
|
|
21
|
+
- Allows ordinary long build/test/release plans while blocking explicit
|
|
22
|
+
credential theft, destructive disk deletion, and malicious persistence.
|
|
23
|
+
- Redacts secret-like values in ordinary task artifacts instead of rejecting
|
|
24
|
+
the entire read.
|
|
25
|
+
- Expands `doctor`, stdio/HTTP MCP smoke tests, lifecycle coverage, packaging
|
|
26
|
+
checks, and ChatGPT workflow documentation.
|
|
27
|
+
|
|
28
|
+
## Compatibility note
|
|
29
|
+
|
|
30
|
+
`create_task` now requires `repo_path`. Existing clients that omitted it must
|
|
31
|
+
pass an existing directory under `workspaceRoot`. `test_command` remains
|
|
32
|
+
supported and is converted into a one-entry verification list.
|
|
33
|
+
|
|
34
|
+
## Verification
|
|
35
|
+
|
|
36
|
+
- `npm test`: 63 security tests, 14 lifecycle tests, and doctor smoke passed
|
|
37
|
+
- `npm run test:mcp`: passed
|
|
38
|
+
- `npm run test:http-mcp`: 11 passed
|
|
39
|
+
- `npm run doctor`: 47 OK, 0 WARN, 0 FAIL
|
|
40
|
+
- `npm run pack:clean`: passed
|
|
41
|
+
- `npm run verify:package`: passed
|
|
42
|
+
|
|
43
|
+
Tracked in [issue #1](https://github.com/jiezeng2004-design/safe-bifrost/issues/1).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# PatchWarden v0.4.0
|
|
2
|
+
|
|
3
|
+
PatchWarden v0.4.0 hardens the ChatGPT-to-local-agent task loop without
|
|
4
|
+
weakening workspace, command, or sensitive-file boundaries.
|
|
5
|
+
|
|
6
|
+
This release also completes the project rename from Safe-Bifrost to
|
|
7
|
+
PatchWarden. The npm package is now `patchwarden`; the old package is retained
|
|
8
|
+
only to point existing users to the new name.
|
|
9
|
+
|
|
10
|
+
## Highlights
|
|
11
|
+
|
|
12
|
+
- Adds supervised Windows tunnel recovery with local readiness probes,
|
|
13
|
+
structured failure categories, capped retry backoff, and redacted runtime
|
|
14
|
+
state under `%LOCALAPPDATA%\patchwarden\runtime`.
|
|
15
|
+
- Adds `Check-PatchWarden-Health.cmd` for diagnostics when the MCP tunnel itself
|
|
16
|
+
is unreachable.
|
|
17
|
+
- Expands `health_check` with workspace, tasks directory, watcher, agent, tunnel,
|
|
18
|
+
and last-error evidence; local HTTP `/healthz` and `/readyz` are also available.
|
|
19
|
+
- Lets `create_task` use exactly one of a saved `plan_id`, persisted
|
|
20
|
+
`inline_plan`, or a guarded task template.
|
|
21
|
+
- Adds `inspect_only`, `feature_small`, `fix_tests`, `release_check`, and safe
|
|
22
|
+
review-only `rollback_scope_violation` templates.
|
|
23
|
+
- Enforces no-change templates with `failed_policy_violation`.
|
|
24
|
+
- Adds deterministic failure guidance through `failure_reason`,
|
|
25
|
+
`failed_command`, `suggested_next_action`, and `safe_followup_prompt`.
|
|
26
|
+
- Adds standalone `file-stats.json` while preserving complete `diff.patch`,
|
|
27
|
+
`changed-files.json`, and the v0.3.0 acceptance contract.
|
|
28
|
+
- Adds deterministic `full` and `chatgpt_core` tool profiles. Tunnel stdio uses
|
|
29
|
+
an exact 16-tool core manifest while ordinary local launches remain on the
|
|
30
|
+
22-tool full profile.
|
|
31
|
+
- Adds schema-inclusive tool manifest hashing, `schema_epoch`, server version,
|
|
32
|
+
and a real MCP stdio preflight before tunnel startup.
|
|
33
|
+
- Adds structured `create_task.next_tool_call`, preferred
|
|
34
|
+
`wait_for_task(timeout_seconds)`, legacy `wait_seconds` compatibility, and a
|
|
35
|
+
complete terminal summary in the wait response.
|
|
36
|
+
- Adds recursive redaction for structured result/verification summaries and
|
|
37
|
+
concise verification counts/headlines such as `166 passed`.
|
|
38
|
+
- Adds explicit `get_diff.patch_mode` values for textual, no-change, and
|
|
39
|
+
hash-only evidence, including a reason when a textual patch is unavailable.
|
|
40
|
+
- Enhances the local health report with process source/version evidence and
|
|
41
|
+
mixed-version warnings without automatically ending any process.
|
|
42
|
+
- Adds first-class watcher stale/missing evidence, structured pending artifact
|
|
43
|
+
responses, and controlled recovery for launcher-owned watcher processes.
|
|
44
|
+
- Reports stale client catalogs as `tool_catalog_mismatch` with the active
|
|
45
|
+
profile, schema epoch, manifest hash, and Connector refresh guidance.
|
|
46
|
+
- Fixes a plan-guard bypass where generic security wording could suppress a
|
|
47
|
+
later credential-access instruction. Every dangerous occurrence is now
|
|
48
|
+
evaluated independently and only directly negated actions are allowed.
|
|
49
|
+
|
|
50
|
+
## Compatibility
|
|
51
|
+
|
|
52
|
+
The MCP tool names and `save_plan` -> `create_task` workflow remain stable, but
|
|
53
|
+
the product rename is intentionally breaking: old CLI names, `SAFE_BIFROST_*`
|
|
54
|
+
variables, `safe-bifrost.config.json`, `.safe-bifrost/`, and legacy AppData
|
|
55
|
+
paths are not loaded. Follow the migration guide before replacing an existing
|
|
56
|
+
installation. `repo_path`, configured agent validation, and exact
|
|
57
|
+
verification-command allowlists remain mandatory.
|
|
58
|
+
|
|
59
|
+
## Verification
|
|
60
|
+
|
|
61
|
+
Run the following from Windows PowerShell:
|
|
62
|
+
|
|
63
|
+
```powershell
|
|
64
|
+
npm.cmd test
|
|
65
|
+
npm.cmd run test:mcp
|
|
66
|
+
npm.cmd run test:http-mcp
|
|
67
|
+
npm.cmd run doctor
|
|
68
|
+
npm.cmd run check:tool-manifest
|
|
69
|
+
npm.cmd run check:brand
|
|
70
|
+
npm.cmd run test:tunnel-supervisor
|
|
71
|
+
npm.cmd run test:watcher-supervisor
|
|
72
|
+
npm.cmd run pack:clean
|
|
73
|
+
npm.cmd run verify:package
|
|
74
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"workspaceRoot": "D:/path/to/test-project",
|
|
3
|
+
"plansDir": ".patchwarden/plans",
|
|
4
|
+
"tasksDir": ".patchwarden/tasks",
|
|
5
|
+
"toolProfile": "full",
|
|
6
|
+
"agents": {
|
|
7
|
+
"codex": {
|
|
8
|
+
"command": "codex",
|
|
9
|
+
"args": ["exec", "--cd", "{repo}", "{prompt}"]
|
|
10
|
+
},
|
|
11
|
+
"opencode": {
|
|
12
|
+
"command": "opencode",
|
|
13
|
+
"args": ["run", "{prompt}"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"allowedTestCommands": [
|
|
17
|
+
"npm test",
|
|
18
|
+
"npm run test",
|
|
19
|
+
"npm run lint",
|
|
20
|
+
"npm run format:check",
|
|
21
|
+
"npm run build",
|
|
22
|
+
"npm run dist",
|
|
23
|
+
"npm run doctor",
|
|
24
|
+
"pnpm test",
|
|
25
|
+
"pnpm run test",
|
|
26
|
+
"pnpm run lint",
|
|
27
|
+
"pnpm run format:check",
|
|
28
|
+
"pnpm run build",
|
|
29
|
+
"pnpm run dist",
|
|
30
|
+
"pnpm run doctor",
|
|
31
|
+
"yarn test",
|
|
32
|
+
"pytest",
|
|
33
|
+
"cargo test",
|
|
34
|
+
"go test ./..."
|
|
35
|
+
],
|
|
36
|
+
"maxReadFileBytes": 200000,
|
|
37
|
+
"defaultTaskTimeoutSeconds": 900,
|
|
38
|
+
"maxTaskTimeoutSeconds": 3600,
|
|
39
|
+
"watcherStaleSeconds": 30
|
|
40
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# OpenAI Tunnel Examples
|
|
2
|
+
|
|
3
|
+
This directory contains privacy-safe examples for connecting PatchWarden to
|
|
4
|
+
ChatGPT through an OpenAI Secure MCP Tunnel.
|
|
5
|
+
|
|
6
|
+
Do not commit real API keys, tunnel IDs, ChatGPT workspace IDs, account names,
|
|
7
|
+
or local private paths.
|
|
8
|
+
|
|
9
|
+
## Files
|
|
10
|
+
|
|
11
|
+
- `tunnel-client.example.yaml` - profile snippets for stdio and HTTP mode
|
|
12
|
+
- `chatgpt-test-prompt.md` - prompt to verify the connector from ChatGPT
|
|
13
|
+
|
|
14
|
+
## Recommended Windows Flow
|
|
15
|
+
|
|
16
|
+
1. Configure `patchwarden.config.json`.
|
|
17
|
+
2. Run `npm.cmd run build`.
|
|
18
|
+
3. Use `scripts/patchwarden-mcp-stdio.cmd` as the tunnel MCP command.
|
|
19
|
+
4. Start `npm.cmd run watch` in a separate terminal.
|
|
20
|
+
5. Start `tunnel-client run` or use `Start-PatchWarden-Tunnel.cmd`.
|
|
21
|
+
6. Create a ChatGPT Connector using the tunnel channel.
|
|
22
|
+
7. After a tunnel/schema refresh, reconnect the Connector and validate from a
|
|
23
|
+
new ChatGPT conversation; an already-open conversation may retain its older
|
|
24
|
+
tool catalog.
|
|
25
|
+
|
|
26
|
+
The Windows launcher prompts for the runtime API key once and stores only a
|
|
27
|
+
Windows DPAPI-encrypted value under `%APPDATA%\patchwarden`. Use
|
|
28
|
+
`Reset-PatchWarden-Tunnel-Key.cmd` to remove the saved credential.
|
|
29
|
+
|
|
30
|
+
Before the launcher starts the tunnel it performs a real MCP stdio handshake
|
|
31
|
+
and requires the exact `chatgpt_core` manifest. Run
|
|
32
|
+
`Check-PatchWarden-Health.cmd` to see the version, profile, tool names, schema
|
|
33
|
+
hash, process sources, and any mixed-version warnings. The check is read-only.
|
|
34
|
+
The v0.4.0 core manifest contains 16 tools. A different count or schema hash
|
|
35
|
+
requires a Connector refresh and validation from a new ChatGPT conversation.
|
|
36
|
+
|
|
37
|
+
## Architecture
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
ChatGPT Web
|
|
41
|
+
-> ChatGPT Connector
|
|
42
|
+
-> OpenAI Secure MCP Tunnel
|
|
43
|
+
-> PatchWarden MCP Server
|
|
44
|
+
-> watcher
|
|
45
|
+
-> local agent
|
|
46
|
+
-> .patchwarden/tasks/<task_id>/
|
|
47
|
+
-> ChatGPT reads result, diff, and test log
|
|
48
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# PatchWarden ChatGPT Test Prompt
|
|
2
|
+
|
|
3
|
+
Paste this into a ChatGPT conversation where the PatchWarden Connector is
|
|
4
|
+
selected.
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
Use the patchwarden connector tools to verify the workflow.
|
|
8
|
+
|
|
9
|
+
Step 1:
|
|
10
|
+
Call health_check and list_agents. Continue only if the watcher and selected
|
|
11
|
+
agent are available. Then call list_workspace for the configured workspace.
|
|
12
|
+
Confirm schema_epoch is 2026-06-20-v2, tool_profile is chatgpt_core, and
|
|
13
|
+
tool_count is 16. If the watcher is stale or missing, stop the task flow and
|
|
14
|
+
report the health_check recovery instruction.
|
|
15
|
+
|
|
16
|
+
Step 2:
|
|
17
|
+
Call read_workspace_file for README.md.
|
|
18
|
+
|
|
19
|
+
Step 3:
|
|
20
|
+
Call save_plan with:
|
|
21
|
+
|
|
22
|
+
title: Add Usage Section
|
|
23
|
+
|
|
24
|
+
content:
|
|
25
|
+
Add a "## Usage" section to the end of README.md. The section should include
|
|
26
|
+
one sentence: "This repository was updated through PatchWarden."
|
|
27
|
+
Do not modify other files. After the change, run the configured test command.
|
|
28
|
+
|
|
29
|
+
Step 4:
|
|
30
|
+
Call create_task with the returned plan_id, agent "opencode", an explicit
|
|
31
|
+
repo_path for the target repository, and verify_commands ["npm test"]. Set
|
|
32
|
+
timeout_seconds to 600.
|
|
33
|
+
|
|
34
|
+
Step 5:
|
|
35
|
+
Immediately call wait_for_task with timeout_seconds 25. If it returns
|
|
36
|
+
continuation_required: true, call wait_for_task again in this same assistant
|
|
37
|
+
turn. Do not stop or reply to the user until terminal is true.
|
|
38
|
+
If execution_blocked is true or next_tool_call is health_check, do not keep
|
|
39
|
+
polling; report the watcher recovery requirement.
|
|
40
|
+
|
|
41
|
+
Step 6:
|
|
42
|
+
When terminal is true, review the included summary, call get_task_summary and
|
|
43
|
+
audit_task, then call get_diff, get_test_log, and get_result_json as needed.
|
|
44
|
+
|
|
45
|
+
Step 7:
|
|
46
|
+
Summarize:
|
|
47
|
+
- final task status
|
|
48
|
+
- files changed
|
|
49
|
+
- whether tests passed
|
|
50
|
+
- whether any out-of-scope changes were detected
|
|
51
|
+
- acceptance_status and audit verdict
|
|
52
|
+
- whether the diff is acceptable
|
|
53
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# PatchWarden tunnel-client example configuration
|
|
2
|
+
#
|
|
3
|
+
# This is an example only. Do not commit real keys, tunnel IDs, account names,
|
|
4
|
+
# workspace IDs, or local private paths.
|
|
5
|
+
|
|
6
|
+
# Mode A: stdio
|
|
7
|
+
#
|
|
8
|
+
# On Windows, prefer the wrapper. It sets PATCHWARDEN_CONFIG and starts the
|
|
9
|
+
# MCP server from the PatchWarden project root.
|
|
10
|
+
#
|
|
11
|
+
# tunnel-client init \
|
|
12
|
+
# --sample sample_mcp_stdio_local \
|
|
13
|
+
# --profile patchwarden \
|
|
14
|
+
# --tunnel-id tunnel_xxx \
|
|
15
|
+
# --mcp-command "C:/path/to/patchwarden/scripts/patchwarden-mcp-stdio.cmd"
|
|
16
|
+
|
|
17
|
+
mcp:
|
|
18
|
+
commands:
|
|
19
|
+
- channel: main
|
|
20
|
+
command: "C:/path/to/patchwarden/scripts/patchwarden-mcp-stdio.cmd"
|
|
21
|
+
|
|
22
|
+
# Mode B: HTTP
|
|
23
|
+
#
|
|
24
|
+
# Start PatchWarden HTTP first:
|
|
25
|
+
#
|
|
26
|
+
# npm run start:http
|
|
27
|
+
#
|
|
28
|
+
# Then point tunnel-client at it:
|
|
29
|
+
#
|
|
30
|
+
# tunnel-client init \
|
|
31
|
+
# --sample sample_mcp_with_dcr \
|
|
32
|
+
# --profile patchwarden-http \
|
|
33
|
+
# --tunnel-id tunnel_xxx \
|
|
34
|
+
# --mcp-server-url "http://127.0.0.1:7331/mcp"
|
|
35
|
+
#
|
|
36
|
+
# mcp:
|
|
37
|
+
# server_urls:
|
|
38
|
+
# - channel: main
|
|
39
|
+
# url: "http://127.0.0.1:7331/mcp"
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "patchwarden",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "A secure local MCP bridge: clients save plans, local agents execute tasks, and results are returned through workspace-scoped files.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"patchwarden": "dist/index.js",
|
|
10
|
+
"patchwarden-runner": "dist/runner/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/",
|
|
14
|
+
"docs/",
|
|
15
|
+
"src/",
|
|
16
|
+
"examples/",
|
|
17
|
+
"scripts/",
|
|
18
|
+
"Start-PatchWarden-Tunnel.cmd",
|
|
19
|
+
"Restart-PatchWarden.cmd",
|
|
20
|
+
"Check-PatchWarden-Health.cmd",
|
|
21
|
+
"Reset-PatchWarden-Tunnel-Key.cmd",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"tsconfig.json"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "node ./node_modules/typescript/bin/tsc",
|
|
28
|
+
"start": "node dist/index.js",
|
|
29
|
+
"start:http": "node dist/httpServer.js",
|
|
30
|
+
"dev": "npm run build && node dist/index.js",
|
|
31
|
+
"runner": "node dist/runner/cli.js",
|
|
32
|
+
"watch": "node dist/runner/watch.js",
|
|
33
|
+
"doctor": "node dist/doctor.js",
|
|
34
|
+
"test": "node dist/smoke-test.js && node scripts/lifecycle-smoke.js && node scripts/doctor-smoke.js && node scripts/tunnel-supervisor-smoke.js && node scripts/watcher-supervisor-smoke.js && node scripts/mcp-manifest-check.js && node scripts/brand-check.js",
|
|
35
|
+
"test:lifecycle": "npm run build && node scripts/lifecycle-smoke.js",
|
|
36
|
+
"test:doctor": "npm run build && node scripts/doctor-smoke.js",
|
|
37
|
+
"test:tunnel-supervisor": "npm run build && node scripts/tunnel-supervisor-smoke.js",
|
|
38
|
+
"test:watcher-supervisor": "npm run build && node scripts/watcher-supervisor-smoke.js",
|
|
39
|
+
"check:tool-manifest": "npm run build && node scripts/mcp-manifest-check.js",
|
|
40
|
+
"check:brand": "node scripts/brand-check.js",
|
|
41
|
+
"test:mcp": "npm run build && node scripts/mcp-smoke.js",
|
|
42
|
+
"test:http-mcp": "npm run build && node scripts/http-mcp-smoke.js",
|
|
43
|
+
"pretest": "npm run build",
|
|
44
|
+
"pack:clean": "npm run build && node scripts/pack-clean.js",
|
|
45
|
+
"verify:package": "npm run build && npm pack --dry-run"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^20.0.0",
|
|
52
|
+
"typescript": "^5.0.0"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0"
|
|
56
|
+
},
|
|
57
|
+
"keywords": [
|
|
58
|
+
"mcp",
|
|
59
|
+
"model-context-protocol",
|
|
60
|
+
"codex",
|
|
61
|
+
"opencode",
|
|
62
|
+
"local-agent"
|
|
63
|
+
],
|
|
64
|
+
"repository": {
|
|
65
|
+
"type": "git",
|
|
66
|
+
"url": "git+https://github.com/jiezeng2004-design/PatchWarden.git"
|
|
67
|
+
},
|
|
68
|
+
"bugs": {
|
|
69
|
+
"url": "https://github.com/jiezeng2004-design/PatchWarden/issues"
|
|
70
|
+
},
|
|
71
|
+
"homepage": "https://github.com/jiezeng2004-design/PatchWarden#readme",
|
|
72
|
+
"license": "MIT"
|
|
73
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
|
|
5
|
+
const allowedLegacyFiles = new Set([
|
|
6
|
+
".gitignore",
|
|
7
|
+
".npmignore",
|
|
8
|
+
"README.md",
|
|
9
|
+
"docs/migration-from-safe-bifrost.md",
|
|
10
|
+
"docs/release-v0.3.0.md",
|
|
11
|
+
"docs/release-v0.4.0.md",
|
|
12
|
+
"scripts/brand-check.js",
|
|
13
|
+
"scripts/pack-clean.js",
|
|
14
|
+
]);
|
|
15
|
+
const legacyPattern = /safe-bifrost|Safe-Bifrost|SAFE_BIFROST|SafeBifrost|safe_bifrost/;
|
|
16
|
+
const trackedFiles = execFileSync(
|
|
17
|
+
"git",
|
|
18
|
+
["ls-files", "--cached", "--others", "--exclude-standard"],
|
|
19
|
+
{ encoding: "utf-8" }
|
|
20
|
+
)
|
|
21
|
+
.split(/\r?\n/)
|
|
22
|
+
.filter(Boolean);
|
|
23
|
+
const failures = [];
|
|
24
|
+
|
|
25
|
+
for (const file of trackedFiles) {
|
|
26
|
+
const normalized = file.replace(/\\/g, "/");
|
|
27
|
+
if (allowedLegacyFiles.has(normalized)) continue;
|
|
28
|
+
if (legacyPattern.test(normalized)) {
|
|
29
|
+
failures.push(`${normalized}: legacy brand in path`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const content = readFileSync(file);
|
|
33
|
+
if (!content.includes(0) && legacyPattern.test(content.toString("utf-8"))) {
|
|
34
|
+
failures.push(`${normalized}: legacy brand in content`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (failures.length > 0) {
|
|
39
|
+
console.error("[brand-check] Legacy brand found outside the approved migration/history files:");
|
|
40
|
+
for (const failure of failures) console.error(` ${failure}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(`[brand-check] OK: ${trackedFiles.length} tracked files checked.`);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
|
|
9
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "patchwarden-doctor-"));
|
|
10
|
+
const workspaceRoot = join(tempRoot, "workspace");
|
|
11
|
+
const configPath = join(tempRoot, "patchwarden.config.json");
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
mkdirSync(workspaceRoot, { recursive: true });
|
|
15
|
+
writeFileSync(configPath, JSON.stringify({
|
|
16
|
+
workspaceRoot,
|
|
17
|
+
plansDir: ".patchwarden/plans",
|
|
18
|
+
tasksDir: ".patchwarden/tasks",
|
|
19
|
+
agents: {
|
|
20
|
+
fixture: { command: process.execPath, args: ["-e", "console.log('fixture')"] },
|
|
21
|
+
},
|
|
22
|
+
allowedTestCommands: ["npm test", "npm run lint"],
|
|
23
|
+
maxReadFileBytes: 200000,
|
|
24
|
+
defaultTaskTimeoutSeconds: 30,
|
|
25
|
+
maxTaskTimeoutSeconds: 60,
|
|
26
|
+
}, null, 2), "utf-8");
|
|
27
|
+
|
|
28
|
+
const result = spawnSync(process.execPath, ["dist/doctor.js"], {
|
|
29
|
+
cwd: root,
|
|
30
|
+
env: { ...process.env, PATCHWARDEN_CONFIG: configPath },
|
|
31
|
+
encoding: "utf-8",
|
|
32
|
+
timeout: 30000,
|
|
33
|
+
});
|
|
34
|
+
if (result.status !== 0) {
|
|
35
|
+
throw new Error(`doctor exited ${result.status}: ${result.stdout}\n${result.stderr}`);
|
|
36
|
+
}
|
|
37
|
+
const requiredLines = [
|
|
38
|
+
"[OK] workspaceRoot exists",
|
|
39
|
+
"[OK] repo_path resolver supports relative and absolute paths",
|
|
40
|
+
"[OK] save_plan security rules loaded",
|
|
41
|
+
"[OK] Read-only task artifact allowlist",
|
|
42
|
+
"[OK] Task directory writable",
|
|
43
|
+
"[OK] Example task directory read/write",
|
|
44
|
+
"[OK] allowedTestCommands is non-empty",
|
|
45
|
+
];
|
|
46
|
+
for (const line of requiredLines) {
|
|
47
|
+
if (!result.stdout.includes(line)) throw new Error(`doctor output missing stable line: ${line}`);
|
|
48
|
+
}
|
|
49
|
+
console.log("ok - doctor stable output and self-checks");
|
|
50
|
+
} finally {
|
|
51
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
52
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
$ErrorActionPreference = "Stop"
|
|
2
|
+
|
|
3
|
+
$ProjectRoot = Split-Path -Parent $PSScriptRoot
|
|
4
|
+
$ConfigPath = Join-Path $ProjectRoot "patchwarden.config.json"
|
|
5
|
+
$RuntimeDirectory = Join-Path $env:LOCALAPPDATA "patchwarden\runtime"
|
|
6
|
+
$HealthUrlFile = Join-Path $RuntimeDirectory "tunnel-health-url.txt"
|
|
7
|
+
|
|
8
|
+
$env:PATCHWARDEN_CONFIG = $ConfigPath
|
|
9
|
+
$healthJson = node --input-type=module -e "Promise.all([import('./dist/tools/healthCheck.js'),import('./dist/tools/registry.js')]).then(([h,r])=>console.log(JSON.stringify(h.healthCheck(r.getToolCatalogSnapshot()))))" 2>$null
|
|
10
|
+
if ($LASTEXITCODE -ne 0 -or -not $healthJson) {
|
|
11
|
+
throw "Could not load PatchWarden health information. Run npm.cmd run build first."
|
|
12
|
+
}
|
|
13
|
+
$health = $healthJson | ConvertFrom-Json
|
|
14
|
+
$packageVersion = [string](Get-Content -LiteralPath (Join-Path $ProjectRoot "package.json") -Raw | ConvertFrom-Json).version
|
|
15
|
+
|
|
16
|
+
$configuredTunnelManifest = $null
|
|
17
|
+
try {
|
|
18
|
+
$manifestJson = node (Join-Path $ProjectRoot "scripts\mcp-manifest-check.js") 2>$null
|
|
19
|
+
if ($LASTEXITCODE -eq 0 -and $manifestJson) {
|
|
20
|
+
$configuredTunnelManifest = $manifestJson | ConvertFrom-Json
|
|
21
|
+
}
|
|
22
|
+
} catch {}
|
|
23
|
+
|
|
24
|
+
$safeProcesses = @()
|
|
25
|
+
try {
|
|
26
|
+
$allProcesses = Get-CimInstance Win32_Process
|
|
27
|
+
foreach ($process in $allProcesses) {
|
|
28
|
+
$commandLine = [string]$process.CommandLine
|
|
29
|
+
if (-not $commandLine -or $commandLine -notmatch '(?i)patchwarden') { continue }
|
|
30
|
+
if ($process.ProcessId -eq $PID -or $commandLine -match 'get-patchwarden-health\.ps1') { continue }
|
|
31
|
+
$detectedVersion = "unknown"
|
|
32
|
+
$origin = "unknown"
|
|
33
|
+
if ($commandLine -match '(?i)patchwarden@([0-9]+(?:\.[0-9]+){1,2})') {
|
|
34
|
+
$detectedVersion = $matches[1]
|
|
35
|
+
$origin = "npm_package"
|
|
36
|
+
} elseif ($commandLine -like "*$ProjectRoot*") {
|
|
37
|
+
$detectedVersion = [string]$health.server_version
|
|
38
|
+
$origin = "current_workspace"
|
|
39
|
+
}
|
|
40
|
+
$safeCommand = $commandLine `
|
|
41
|
+
-replace '(?i)(CONTROL_PLANE_API_KEY\s*=\s*)\S+', '$1[REDACTED]' `
|
|
42
|
+
-replace '(?i)tunnel_[a-z0-9_-]{12,}', '[REDACTED_TUNNEL_ID]'
|
|
43
|
+
$safeProcesses += [ordered]@{
|
|
44
|
+
pid = $process.ProcessId
|
|
45
|
+
parent_pid = $process.ParentProcessId
|
|
46
|
+
name = $process.Name
|
|
47
|
+
detected_version = $detectedVersion
|
|
48
|
+
origin = $origin
|
|
49
|
+
command_line = $safeCommand.Substring(0, [Math]::Min(500, $safeCommand.Length))
|
|
50
|
+
version_conflict = $detectedVersion -ne "unknown" -and $detectedVersion -ne [string]$health.server_version
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch {}
|
|
54
|
+
|
|
55
|
+
$liveTunnel = [ordered]@{ observed = $false; healthz = $false; readyz = $false; error = $null }
|
|
56
|
+
if (Test-Path -LiteralPath $HealthUrlFile) {
|
|
57
|
+
try {
|
|
58
|
+
$baseUrl = (Get-Content -LiteralPath $HealthUrlFile -Raw).Trim().TrimEnd('/')
|
|
59
|
+
if ($baseUrl -notmatch '^http://127\.0\.0\.1:\d+$') { throw "Health URL is not loopback-only." }
|
|
60
|
+
$healthz = Invoke-WebRequest -Uri "$baseUrl/healthz" -UseBasicParsing -TimeoutSec 3
|
|
61
|
+
$readyz = Invoke-WebRequest -Uri "$baseUrl/readyz" -UseBasicParsing -TimeoutSec 3
|
|
62
|
+
$liveTunnel.observed = $true
|
|
63
|
+
$liveTunnel.healthz = $healthz.StatusCode -eq 200
|
|
64
|
+
$liveTunnel.readyz = $readyz.StatusCode -eq 200
|
|
65
|
+
} catch {
|
|
66
|
+
$liveTunnel.observed = $true
|
|
67
|
+
$liveTunnel.error = "Tunnel local health endpoint is unavailable."
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
$tunnelProcesses = @($safeProcesses | Where-Object { $_.name -eq "tunnel-client.exe" })
|
|
72
|
+
$mcpChildProcesses = @($safeProcesses | Where-Object {
|
|
73
|
+
$_.command_line -match '(?i)patchwarden-mcp-stdio\.cmd|patchwarden\\dist\\index\.js|patchwarden/scripts/\.\./dist/index\.js'
|
|
74
|
+
})
|
|
75
|
+
$deploymentConsistent = [bool](
|
|
76
|
+
$configuredTunnelManifest -and
|
|
77
|
+
$health.tunnel.tool_manifest_sha256 -and
|
|
78
|
+
[string]$configuredTunnelManifest.tool_manifest_sha256 -eq [string]$health.tunnel.tool_manifest_sha256
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
[ordered]@{
|
|
82
|
+
checked_at = (Get-Date).ToUniversalTime().ToString("o")
|
|
83
|
+
overall_status = $health.status
|
|
84
|
+
source_version = $packageVersion
|
|
85
|
+
dist_version = $health.server_version
|
|
86
|
+
schema_epoch = $health.schema_epoch
|
|
87
|
+
local_server_catalog = [ordered]@{
|
|
88
|
+
tool_profile = $health.tool_profile
|
|
89
|
+
tool_count = $health.tool_count
|
|
90
|
+
tool_names = $health.tool_names
|
|
91
|
+
tool_manifest_sha256 = $health.tool_manifest_sha256
|
|
92
|
+
catalog_consistent = $health.catalog_consistent
|
|
93
|
+
mismatch_report = $health.mismatch_report
|
|
94
|
+
tunnel_catalog_comparison = $health.tunnel_catalog_comparison
|
|
95
|
+
}
|
|
96
|
+
configured_tunnel_manifest = $configuredTunnelManifest
|
|
97
|
+
deployment_consistency = [ordered]@{
|
|
98
|
+
consistent = $deploymentConsistent
|
|
99
|
+
reason = if ($deploymentConsistent) { $null } else { "Configured tunnel manifest differs from the running tunnel state. Restart the owned tunnel, then refresh the Connector and use a new conversation." }
|
|
100
|
+
}
|
|
101
|
+
connector_visibility = $health.connector_visibility
|
|
102
|
+
mcp_server = $health.mcp_server
|
|
103
|
+
workspace_root = $health.workspace_root
|
|
104
|
+
tasks_dir = $health.tasks_dir
|
|
105
|
+
watcher = $health.watcher
|
|
106
|
+
agents = $health.agents
|
|
107
|
+
tunnel_state = $health.tunnel
|
|
108
|
+
tunnel_live_probe = $liveTunnel
|
|
109
|
+
tunnel_processes = $tunnelProcesses
|
|
110
|
+
mcp_child_processes = $mcpChildProcesses
|
|
111
|
+
patchwarden_processes = $safeProcesses
|
|
112
|
+
version_conflicts = @($safeProcesses | Where-Object { $_.version_conflict })
|
|
113
|
+
last_error = $health.last_error
|
|
114
|
+
} | ConvertTo-Json -Depth 8
|