patchwork-os 0.2.0-alpha.3 → 0.2.0-alpha.31
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.bridge.md +6 -0
- package/README.md +40 -15
- package/deploy/bootstrap-vps.sh +184 -0
- package/deploy/deploy-dashboard.sh +174 -0
- package/deploy/deploy-landing.sh +79 -0
- package/dist/activationMetrics.d.ts +67 -0
- package/dist/activationMetrics.js +255 -0
- package/dist/activationMetrics.js.map +1 -0
- package/dist/approvalHttp.d.ts +24 -2
- package/dist/approvalHttp.js +150 -10
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalQueue.d.ts +16 -1
- package/dist/approvalQueue.js +44 -3
- package/dist/approvalQueue.js.map +1 -1
- package/dist/automation.d.ts +20 -0
- package/dist/automation.js +54 -1
- package/dist/automation.js.map +1 -1
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.js +55 -130
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeToken.js +57 -19
- package/dist/bridgeToken.js.map +1 -1
- package/dist/ccPermissions.js +6 -4
- package/dist/ccPermissions.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +1 -1
- package/dist/claudeOrchestrator.js +14 -8
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/launchd.d.ts +2 -0
- package/dist/commands/launchd.js +94 -0
- package/dist/commands/launchd.js.map +1 -0
- package/dist/commands/recipe.d.ts +258 -0
- package/dist/commands/recipe.js +1130 -0
- package/dist/commands/recipe.js.map +1 -0
- package/dist/commands/recipeInstall.d.ts +72 -0
- package/dist/commands/recipeInstall.js +339 -0
- package/dist/commands/recipeInstall.js.map +1 -0
- package/dist/config.d.ts +14 -1
- package/dist/config.js +99 -8
- package/dist/config.js.map +1 -1
- package/dist/connectors/baseConnector.d.ts +117 -0
- package/dist/connectors/baseConnector.js +213 -0
- package/dist/connectors/baseConnector.js.map +1 -0
- package/dist/connectors/confluence.d.ts +111 -0
- package/dist/connectors/confluence.js +406 -0
- package/dist/connectors/confluence.js.map +1 -0
- package/dist/connectors/datadog.d.ts +116 -0
- package/dist/connectors/datadog.js +385 -0
- package/dist/connectors/datadog.js.map +1 -0
- package/dist/connectors/fixtureLibrary.d.ts +21 -0
- package/dist/connectors/fixtureLibrary.js +70 -0
- package/dist/connectors/fixtureLibrary.js.map +1 -0
- package/dist/connectors/fixtureRecorder.d.ts +1 -0
- package/dist/connectors/fixtureRecorder.js +35 -0
- package/dist/connectors/fixtureRecorder.js.map +1 -0
- package/dist/connectors/github.d.ts +58 -8
- package/dist/connectors/github.js +312 -84
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gmail.d.ts +4 -1
- package/dist/connectors/gmail.js +79 -16
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.d.ts +60 -0
- package/dist/connectors/googleCalendar.js +345 -0
- package/dist/connectors/googleCalendar.js.map +1 -0
- package/dist/connectors/hubspot.d.ts +112 -0
- package/dist/connectors/hubspot.js +408 -0
- package/dist/connectors/hubspot.js.map +1 -0
- package/dist/connectors/intercom.d.ts +102 -0
- package/dist/connectors/intercom.js +402 -0
- package/dist/connectors/intercom.js.map +1 -0
- package/dist/connectors/jira.d.ts +98 -0
- package/dist/connectors/jira.js +379 -0
- package/dist/connectors/jira.js.map +1 -0
- package/dist/connectors/linear.d.ts +69 -19
- package/dist/connectors/linear.js +170 -129
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpClient.d.ts +56 -0
- package/dist/connectors/mcpClient.js +189 -0
- package/dist/connectors/mcpClient.js.map +1 -0
- package/dist/connectors/mcpOAuth.d.ts +84 -0
- package/dist/connectors/mcpOAuth.js +389 -0
- package/dist/connectors/mcpOAuth.js.map +1 -0
- package/dist/connectors/mockConnector.d.ts +28 -0
- package/dist/connectors/mockConnector.js +81 -0
- package/dist/connectors/mockConnector.js.map +1 -0
- package/dist/connectors/notion.d.ts +143 -0
- package/dist/connectors/notion.js +424 -0
- package/dist/connectors/notion.js.map +1 -0
- package/dist/connectors/sentry.d.ts +17 -21
- package/dist/connectors/sentry.js +115 -131
- package/dist/connectors/sentry.js.map +1 -1
- package/dist/connectors/slack.d.ts +50 -0
- package/dist/connectors/slack.js +324 -0
- package/dist/connectors/slack.js.map +1 -0
- package/dist/connectors/stripe.d.ts +116 -0
- package/dist/connectors/stripe.js +379 -0
- package/dist/connectors/stripe.js.map +1 -0
- package/dist/connectors/tokenStorage.d.ts +35 -0
- package/dist/connectors/tokenStorage.js +459 -0
- package/dist/connectors/tokenStorage.js.map +1 -0
- package/dist/connectors/zendesk.d.ts +104 -0
- package/dist/connectors/zendesk.js +424 -0
- package/dist/connectors/zendesk.js.map +1 -0
- package/dist/drivers/gemini/index.d.ts +5 -1
- package/dist/drivers/gemini/index.js +39 -5
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/index.d.ts +5 -0
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/index.js.map +1 -1
- package/dist/featureFlags.d.ts +73 -0
- package/dist/featureFlags.js +203 -0
- package/dist/featureFlags.js.map +1 -0
- package/dist/fp/automationInterpreter.js +1 -0
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationProgram.d.ts +1 -1
- package/dist/fp/automationProgram.js.map +1 -1
- package/dist/fp/policyParser.js +17 -0
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/index.js +621 -61
- package/dist/index.js.map +1 -1
- package/dist/installGuard.d.ts +25 -0
- package/dist/installGuard.js +48 -0
- package/dist/installGuard.js.map +1 -0
- package/dist/oauth.d.ts +4 -1
- package/dist/oauth.js +50 -14
- package/dist/oauth.js.map +1 -1
- package/dist/patchworkConfig.d.ts +9 -0
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +53 -0
- package/dist/recipeOrchestration.js +272 -0
- package/dist/recipeOrchestration.js.map +1 -0
- package/dist/recipes/RecipeOrchestrator.d.ts +40 -0
- package/dist/recipes/RecipeOrchestrator.js +51 -0
- package/dist/recipes/RecipeOrchestrator.js.map +1 -0
- package/dist/recipes/agentExecutor.d.ts +28 -0
- package/dist/recipes/agentExecutor.js +42 -0
- package/dist/recipes/agentExecutor.js.map +1 -0
- package/dist/recipes/chainedRunner.d.ts +140 -0
- package/dist/recipes/chainedRunner.js +539 -0
- package/dist/recipes/chainedRunner.js.map +1 -0
- package/dist/recipes/dependencyGraph.d.ts +39 -0
- package/dist/recipes/dependencyGraph.js +199 -0
- package/dist/recipes/dependencyGraph.js.map +1 -0
- package/dist/recipes/legacyRecipeCompat.d.ts +2 -0
- package/dist/recipes/legacyRecipeCompat.js +112 -0
- package/dist/recipes/legacyRecipeCompat.js.map +1 -0
- package/dist/recipes/manifest.d.ts +47 -0
- package/dist/recipes/manifest.js +141 -0
- package/dist/recipes/manifest.js.map +1 -0
- package/dist/recipes/nestedRecipeStep.d.ts +58 -0
- package/dist/recipes/nestedRecipeStep.js +95 -0
- package/dist/recipes/nestedRecipeStep.js.map +1 -0
- package/dist/recipes/outputRegistry.d.ts +28 -0
- package/dist/recipes/outputRegistry.js +52 -0
- package/dist/recipes/outputRegistry.js.map +1 -0
- package/dist/recipes/scheduler.d.ts +23 -7
- package/dist/recipes/scheduler.js +131 -41
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/schema.d.ts +17 -2
- package/dist/recipes/schemaGenerator.d.ts +28 -0
- package/dist/recipes/schemaGenerator.js +565 -0
- package/dist/recipes/schemaGenerator.js.map +1 -0
- package/dist/recipes/templateEngine.d.ts +62 -0
- package/dist/recipes/templateEngine.js +182 -0
- package/dist/recipes/templateEngine.js.map +1 -0
- package/dist/recipes/toolRegistry.d.ts +181 -0
- package/dist/recipes/toolRegistry.js +300 -0
- package/dist/recipes/toolRegistry.js.map +1 -0
- package/dist/recipes/tools/calendar.d.ts +6 -0
- package/dist/recipes/tools/calendar.js +61 -0
- package/dist/recipes/tools/calendar.js.map +1 -0
- package/dist/recipes/tools/confluence.d.ts +6 -0
- package/dist/recipes/tools/confluence.js +254 -0
- package/dist/recipes/tools/confluence.js.map +1 -0
- package/dist/recipes/tools/datadog.d.ts +6 -0
- package/dist/recipes/tools/datadog.js +239 -0
- package/dist/recipes/tools/datadog.js.map +1 -0
- package/dist/recipes/tools/diagnostics.d.ts +6 -0
- package/dist/recipes/tools/diagnostics.js +36 -0
- package/dist/recipes/tools/diagnostics.js.map +1 -0
- package/dist/recipes/tools/file.d.ts +6 -0
- package/dist/recipes/tools/file.js +170 -0
- package/dist/recipes/tools/file.js.map +1 -0
- package/dist/recipes/tools/git.d.ts +6 -0
- package/dist/recipes/tools/git.js +63 -0
- package/dist/recipes/tools/git.js.map +1 -0
- package/dist/recipes/tools/github.d.ts +6 -0
- package/dist/recipes/tools/github.js +91 -0
- package/dist/recipes/tools/github.js.map +1 -0
- package/dist/recipes/tools/gmail.d.ts +6 -0
- package/dist/recipes/tools/gmail.js +210 -0
- package/dist/recipes/tools/gmail.js.map +1 -0
- package/dist/recipes/tools/hubspot.d.ts +6 -0
- package/dist/recipes/tools/hubspot.js +232 -0
- package/dist/recipes/tools/hubspot.js.map +1 -0
- package/dist/recipes/tools/index.d.ts +22 -0
- package/dist/recipes/tools/index.js +25 -0
- package/dist/recipes/tools/index.js.map +1 -0
- package/dist/recipes/tools/intercom.d.ts +6 -0
- package/dist/recipes/tools/intercom.js +226 -0
- package/dist/recipes/tools/intercom.js.map +1 -0
- package/dist/recipes/tools/linear.d.ts +6 -0
- package/dist/recipes/tools/linear.js +83 -0
- package/dist/recipes/tools/linear.js.map +1 -0
- package/dist/recipes/tools/notion.d.ts +6 -0
- package/dist/recipes/tools/notion.js +278 -0
- package/dist/recipes/tools/notion.js.map +1 -0
- package/dist/recipes/tools/slack.d.ts +6 -0
- package/dist/recipes/tools/slack.js +72 -0
- package/dist/recipes/tools/slack.js.map +1 -0
- package/dist/recipes/tools/stripe.d.ts +6 -0
- package/dist/recipes/tools/stripe.js +265 -0
- package/dist/recipes/tools/stripe.js.map +1 -0
- package/dist/recipes/tools/zendesk.d.ts +6 -0
- package/dist/recipes/tools/zendesk.js +245 -0
- package/dist/recipes/tools/zendesk.js.map +1 -0
- package/dist/recipes/validation.d.ts +13 -0
- package/dist/recipes/validation.js +433 -0
- package/dist/recipes/validation.js.map +1 -0
- package/dist/recipes/yamlRunner.d.ts +87 -0
- package/dist/recipes/yamlRunner.js +693 -409
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +34 -6
- package/dist/recipesHttp.js +285 -15
- package/dist/recipesHttp.js.map +1 -1
- package/dist/riskTier.js +1 -0
- package/dist/riskTier.js.map +1 -1
- package/dist/runLog.d.ts +23 -0
- package/dist/runLog.js +56 -1
- package/dist/runLog.js.map +1 -1
- package/dist/schemas/dry-run-plan.v1.json +139 -0
- package/dist/schemas/recipe.v1.json +684 -0
- package/dist/server.d.ts +32 -1
- package/dist/server.js +980 -97
- package/dist/server.js.map +1 -1
- package/dist/streamableHttp.js +2 -0
- package/dist/streamableHttp.js.map +1 -1
- package/dist/tools/addLinearComment.d.ts +55 -0
- package/dist/tools/addLinearComment.js +72 -0
- package/dist/tools/addLinearComment.js.map +1 -0
- package/dist/tools/bridgeDoctor.js +2 -2
- package/dist/tools/bridgeDoctor.js.map +1 -1
- package/dist/tools/createLinearIssue.d.ts +84 -0
- package/dist/tools/createLinearIssue.js +146 -0
- package/dist/tools/createLinearIssue.js.map +1 -0
- package/dist/tools/fetchCalendarEvents.d.ts +94 -0
- package/dist/tools/fetchCalendarEvents.js +97 -0
- package/dist/tools/fetchCalendarEvents.js.map +1 -0
- package/dist/tools/fetchGithubIssue.d.ts +80 -0
- package/dist/tools/fetchGithubIssue.js +84 -0
- package/dist/tools/fetchGithubIssue.js.map +1 -0
- package/dist/tools/fetchGithubPR.d.ts +89 -0
- package/dist/tools/fetchGithubPR.js +96 -0
- package/dist/tools/fetchGithubPR.js.map +1 -0
- package/dist/tools/fetchSlackProfile.d.ts +43 -0
- package/dist/tools/fetchSlackProfile.js +46 -0
- package/dist/tools/fetchSlackProfile.js.map +1 -0
- package/dist/tools/getConnectorStatus.d.ts +58 -0
- package/dist/tools/getConnectorStatus.js +56 -0
- package/dist/tools/getConnectorStatus.js.map +1 -0
- package/dist/tools/github/actions.js +4 -2
- package/dist/tools/github/actions.js.map +1 -1
- package/dist/tools/github/composite.d.ts +339 -0
- package/dist/tools/github/composite.js +343 -0
- package/dist/tools/github/composite.js.map +1 -0
- package/dist/tools/github/index.d.ts +2 -1
- package/dist/tools/github/index.js +2 -1
- package/dist/tools/github/index.js.map +1 -1
- package/dist/tools/github/issues.js +8 -4
- package/dist/tools/github/issues.js.map +1 -1
- package/dist/tools/github/pr.d.ts +122 -0
- package/dist/tools/github/pr.js +195 -5
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/index.js +32 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/searchTools.js +1 -1
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/slackListChannels.d.ts +65 -0
- package/dist/tools/slackListChannels.js +70 -0
- package/dist/tools/slackListChannels.js.map +1 -0
- package/dist/tools/slackPostMessage.d.ts +57 -0
- package/dist/tools/slackPostMessage.js +77 -0
- package/dist/tools/slackPostMessage.js.map +1 -0
- package/dist/tools/testTraceToSource.js +2 -2
- package/dist/tools/testTraceToSource.js.map +1 -1
- package/dist/tools/updateLinearIssue.d.ts +89 -0
- package/dist/tools/updateLinearIssue.js +117 -0
- package/dist/tools/updateLinearIssue.js.map +1 -0
- package/dist/transport.d.ts +7 -1
- package/dist/transport.js +85 -11
- package/dist/transport.js.map +1 -1
- package/package.json +5 -2
- package/scripts/start-all.sh +56 -19
- package/templates/automation-policies/recipe-authoring.json +25 -0
- package/templates/automation-policy.example.json +6 -0
- package/templates/co.patchwork-os.bridge.plist +34 -0
- package/templates/recipes/ctx-loop-test.yaml +75 -0
- package/templates/recipes/lint-on-save.yaml +1 -2
- package/templates/recipes/morning-brief-slack.yaml +57 -0
- package/templates/recipes/morning-brief.yaml +14 -6
- package/templates/recipes/project-health-check.yaml +50 -0
- package/templates/recipes/sentry-to-linear.yaml +77 -0
package/README.bridge.md
CHANGED
|
@@ -40,6 +40,12 @@ claude --ide
|
|
|
40
40
|
|
|
41
41
|
> **Updating?** Use `npm install -g claude-ide-bridge@latest` — `npm update -g` may lag the registry cache after a new release.
|
|
42
42
|
|
|
43
|
+
> **macOS LaunchAgent users:** always install from the registry (`npm install -g patchwork-os`) or
|
|
44
|
+
> from a tarball (`npm pack && npm install -g patchwork-os-*.tgz`).
|
|
45
|
+
> Running `npm install -g .` from a repo checkout creates a symlink — the macOS sandbox
|
|
46
|
+
> cannot follow it, causing EPERM when launchctl starts the bridge.
|
|
47
|
+
> `patchwork-os launchd install` will refuse to proceed and print fix instructions.
|
|
48
|
+
|
|
43
49
|
After `init`, type `/mcp` in Claude Code to confirm the bridge is connected. Type `/ide` to see open files, diagnostics, and editor state.
|
|
44
50
|
|
|
45
51
|
> **One bridge per workspace.** Each project runs its own bridge instance on its own port. Start a separate `claude-ide-bridge --watch` in each directory.
|
package/README.md
CHANGED
|
@@ -25,14 +25,16 @@ Patchwork OS watches for things that matter, acts, and asks before anything risk
|
|
|
25
25
|
## After init
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
patchwork-os recipe list
|
|
29
|
-
patchwork-os recipe run daily-status
|
|
30
|
-
patchwork-os
|
|
28
|
+
patchwork-os recipe list # see installed recipes
|
|
29
|
+
patchwork-os recipe run daily-status # run one now
|
|
30
|
+
patchwork-os recipe run morning-brief --local # run with local LLM
|
|
31
|
+
patchwork-os tools list # browse all 140+ tools
|
|
32
|
+
patchwork-os # open terminal dashboard
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
The oversight web UI runs at `http://localhost:3100` when the bridge is active.
|
|
35
|
+
The oversight web UI runs at `http://localhost:3100` when the bridge is active. The dashboard shows live sessions, pending approvals, recent recipe runs, and analytics.
|
|
34
36
|
|
|
35
|
-
##
|
|
37
|
+
## Starter recipes (no external API keys needed)
|
|
36
38
|
|
|
37
39
|
| Recipe | Trigger | What it does |
|
|
38
40
|
|---|---|---|
|
|
@@ -41,25 +43,48 @@ The oversight web UI runs at `http://localhost:3100` when the bridge is active.
|
|
|
41
43
|
| `watch-failing-tests` | test run | drops triage note to inbox on failure |
|
|
42
44
|
| `lint-on-save` | file save | surfaces new TS/JS diagnostics to inbox |
|
|
43
45
|
| `stale-branches` | cron weekly | lists branches older than 30 days |
|
|
46
|
+
| `morning-brief` | cron 08:00 | commits + Linear issues + Calendar events |
|
|
47
|
+
| `sentry-to-linear` | manual | Sentry issue → Linear ticket (one-shot) |
|
|
44
48
|
|
|
45
|
-
|
|
49
|
+
Local recipes write to `~/.patchwork/inbox/` only. Connectors (Linear, Sentry, etc.) require API keys and approval-gated writes.
|
|
46
50
|
|
|
47
|
-
##
|
|
51
|
+
## What's working today
|
|
48
52
|
|
|
49
|
-
|
|
|
53
|
+
| Feature | Status |
|
|
50
54
|
|---|---|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
| `patchwork-init` — one-command setup | **shipped** |
|
|
56
|
+
| Terminal dashboard (`patchwork-os`) | **shipped** |
|
|
57
|
+
| Web oversight UI (approvals, sessions, recipes) | **shipped** |
|
|
58
|
+
| Recipe runner (YAML, cron, manual, webhook) | **shipped** |
|
|
59
|
+
| Multi-provider LLM (Claude, Gemini, OpenAI, Grok, Ollama) | **shipped** |
|
|
60
|
+
| Linear connector (read + approval-gated write) | **shipped** |
|
|
61
|
+
| Sentry connector (fetch issues, stack traces) | **shipped** |
|
|
62
|
+
| Google Calendar connector (read-only) | **shipped** |
|
|
63
|
+
| Slack connector (post messages, list channels) | **shipped** |
|
|
64
|
+
| 140+ MCP tools (LSP, git, tests, diagnostics) | **shipped** |
|
|
65
|
+
| Cross-session memory (traces, handoff notes) | **shipped** |
|
|
66
|
+
| Gmail connector | W2 |
|
|
67
|
+
| Mobile oversight PWA | W3 |
|
|
68
|
+
| Community recipe marketplace | Q3 |
|
|
69
|
+
|
|
70
|
+
## Install
|
|
71
|
+
|
|
72
|
+
**From the registry (recommended):**
|
|
73
|
+
```bash
|
|
74
|
+
npm install -g patchwork-os
|
|
75
|
+
patchwork-os patchwork-init
|
|
76
|
+
```
|
|
57
77
|
|
|
78
|
+
**From a local build (development / CI):**
|
|
58
79
|
```bash
|
|
59
80
|
git clone https://github.com/Oolab-labs/patchwork-os
|
|
60
81
|
cd patchwork-os
|
|
61
82
|
npm install && npm run build
|
|
62
|
-
|
|
83
|
+
# Use npm pack to create a real copy — do NOT use `npm install -g .`
|
|
84
|
+
# That creates a symlink which breaks the macOS LaunchAgent (EPERM at startup).
|
|
85
|
+
npm pack
|
|
86
|
+
npm install -g patchwork-os-*.tgz
|
|
87
|
+
patchwork-os patchwork-init
|
|
63
88
|
```
|
|
64
89
|
|
|
65
90
|
## License
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# deploy/bootstrap-vps.sh
|
|
3
|
+
# Full VPS bootstrap for patchworkos.com
|
|
4
|
+
# Run as root on a fresh Ubuntu 24.04 VPS
|
|
5
|
+
# Usage: bash bootstrap-vps.sh
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
DOMAIN="patchworkos.com"
|
|
10
|
+
EMAIL="support@gigsecure.co.ke"
|
|
11
|
+
BRIDGE_PORT=3284
|
|
12
|
+
BRIDGE_USER="patchwork"
|
|
13
|
+
BRIDGE_HOME="/opt/patchwork"
|
|
14
|
+
NODE_VERSION="22"
|
|
15
|
+
|
|
16
|
+
# ── Colours ──────────────────────────────────────────────────────────────────
|
|
17
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
|
18
|
+
info() { echo -e "${GREEN}[bootstrap]${NC} $*"; }
|
|
19
|
+
warn() { echo -e "${YELLOW}[bootstrap]${NC} $*"; }
|
|
20
|
+
error() { echo -e "${RED}[bootstrap]${NC} $*"; exit 1; }
|
|
21
|
+
|
|
22
|
+
# ── 1. System updates ─────────────────────────────────────────────────────────
|
|
23
|
+
info "Updating system packages..."
|
|
24
|
+
apt-get update -qq
|
|
25
|
+
apt-get upgrade -y -qq
|
|
26
|
+
apt-get install -y -qq curl wget gnupg2 ca-certificates lsb-release \
|
|
27
|
+
nginx certbot python3-certbot-nginx ufw git jq unzip
|
|
28
|
+
|
|
29
|
+
# ── 2. Node.js ────────────────────────────────────────────────────────────────
|
|
30
|
+
info "Installing Node.js ${NODE_VERSION}..."
|
|
31
|
+
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - >/dev/null
|
|
32
|
+
apt-get install -y -qq nodejs
|
|
33
|
+
node --version
|
|
34
|
+
npm --version
|
|
35
|
+
|
|
36
|
+
# ── 3. Service user ───────────────────────────────────────────────────────────
|
|
37
|
+
info "Creating service user '${BRIDGE_USER}'..."
|
|
38
|
+
id "${BRIDGE_USER}" &>/dev/null || useradd -r -m -d "${BRIDGE_HOME}" -s /bin/bash "${BRIDGE_USER}"
|
|
39
|
+
mkdir -p "${BRIDGE_HOME}"
|
|
40
|
+
chown "${BRIDGE_USER}:${BRIDGE_USER}" "${BRIDGE_HOME}"
|
|
41
|
+
|
|
42
|
+
# ── 4. Install bridge globally ────────────────────────────────────────────────
|
|
43
|
+
info "Installing claude-ide-bridge from npm..."
|
|
44
|
+
npm install -g claude-ide-bridge 2>&1 | tail -5
|
|
45
|
+
BRIDGE_BIN="$(which claude-ide-bridge)"
|
|
46
|
+
info "Bridge binary: ${BRIDGE_BIN}"
|
|
47
|
+
|
|
48
|
+
# ── 5. Generate fixed token ───────────────────────────────────────────────────
|
|
49
|
+
FIXED_TOKEN="$(uuidgen | tr '[:upper:]' '[:lower:]')"
|
|
50
|
+
info "Generated bridge token (save this!): ${FIXED_TOKEN}"
|
|
51
|
+
|
|
52
|
+
# Persist token to a file readable only by root + patchwork user
|
|
53
|
+
TOKEN_FILE="/etc/patchwork/bridge-token"
|
|
54
|
+
mkdir -p /etc/patchwork
|
|
55
|
+
echo "${FIXED_TOKEN}" > "${TOKEN_FILE}"
|
|
56
|
+
chown root:"${BRIDGE_USER}" "${TOKEN_FILE}"
|
|
57
|
+
chmod 640 "${TOKEN_FILE}"
|
|
58
|
+
|
|
59
|
+
# ── 6. Systemd service ────────────────────────────────────────────────────────
|
|
60
|
+
info "Writing systemd service..."
|
|
61
|
+
cat > /etc/systemd/system/patchwork-bridge.service <<EOF
|
|
62
|
+
[Unit]
|
|
63
|
+
Description=Patchwork OS — Claude IDE Bridge
|
|
64
|
+
After=network-online.target
|
|
65
|
+
Wants=network-online.target
|
|
66
|
+
|
|
67
|
+
[Service]
|
|
68
|
+
Type=simple
|
|
69
|
+
User=${BRIDGE_USER}
|
|
70
|
+
Group=${BRIDGE_USER}
|
|
71
|
+
WorkingDirectory=${BRIDGE_HOME}
|
|
72
|
+
Environment=NODE_ENV=production
|
|
73
|
+
Environment=HOME=${BRIDGE_HOME}
|
|
74
|
+
|
|
75
|
+
ExecStart=${BRIDGE_BIN} \\
|
|
76
|
+
--bind 0.0.0.0 \\
|
|
77
|
+
--port ${BRIDGE_PORT} \\
|
|
78
|
+
--vps \\
|
|
79
|
+
--issuer-url https://${DOMAIN} \\
|
|
80
|
+
--cors-origin https://claude.ai \\
|
|
81
|
+
--cors-origin https://app.patchworkos.com \\
|
|
82
|
+
--fixed-token ${FIXED_TOKEN}
|
|
83
|
+
|
|
84
|
+
Restart=always
|
|
85
|
+
RestartSec=5
|
|
86
|
+
StandardOutput=journal
|
|
87
|
+
StandardError=journal
|
|
88
|
+
SyslogIdentifier=patchwork-bridge
|
|
89
|
+
|
|
90
|
+
# Hardening
|
|
91
|
+
NoNewPrivileges=true
|
|
92
|
+
ProtectSystem=strict
|
|
93
|
+
ProtectHome=read-only
|
|
94
|
+
ReadWritePaths=${BRIDGE_HOME}
|
|
95
|
+
PrivateTmp=true
|
|
96
|
+
|
|
97
|
+
[Install]
|
|
98
|
+
WantedBy=multi-user.target
|
|
99
|
+
EOF
|
|
100
|
+
|
|
101
|
+
systemctl daemon-reload
|
|
102
|
+
systemctl enable patchwork-bridge
|
|
103
|
+
|
|
104
|
+
# ── 7. UFW firewall ───────────────────────────────────────────────────────────
|
|
105
|
+
info "Configuring firewall..."
|
|
106
|
+
ufw --force reset >/dev/null
|
|
107
|
+
ufw default deny incoming >/dev/null
|
|
108
|
+
ufw default allow outgoing >/dev/null
|
|
109
|
+
ufw allow ssh >/dev/null
|
|
110
|
+
ufw allow http >/dev/null
|
|
111
|
+
ufw allow https >/dev/null
|
|
112
|
+
ufw --force enable >/dev/null
|
|
113
|
+
ufw status
|
|
114
|
+
|
|
115
|
+
# ── 8. Nginx config — HTTP only first (certbot needs nginx up to issue cert) ──
|
|
116
|
+
info "Writing nginx config (HTTP only)..."
|
|
117
|
+
cat > /etc/nginx/sites-available/patchworkos <<'NGINX'
|
|
118
|
+
server {
|
|
119
|
+
listen 80;
|
|
120
|
+
listen [::]:80;
|
|
121
|
+
server_name patchworkos.com www.patchworkos.com;
|
|
122
|
+
|
|
123
|
+
location /.well-known/acme-challenge/ { root /var/www/html; }
|
|
124
|
+
|
|
125
|
+
location / {
|
|
126
|
+
proxy_pass http://127.0.0.1:3284;
|
|
127
|
+
proxy_http_version 1.1;
|
|
128
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
129
|
+
proxy_set_header Connection "upgrade";
|
|
130
|
+
proxy_set_header Host $host;
|
|
131
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
132
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
133
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
134
|
+
proxy_read_timeout 3600s;
|
|
135
|
+
proxy_send_timeout 3600s;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
NGINX
|
|
139
|
+
|
|
140
|
+
ln -sf /etc/nginx/sites-available/patchworkos /etc/nginx/sites-enabled/patchworkos
|
|
141
|
+
rm -f /etc/nginx/sites-enabled/default
|
|
142
|
+
|
|
143
|
+
nginx -t
|
|
144
|
+
systemctl reload nginx
|
|
145
|
+
|
|
146
|
+
# ── 9. TLS certificate ────────────────────────────────────────────────────────
|
|
147
|
+
info "Issuing Let's Encrypt certificate for ${DOMAIN}..."
|
|
148
|
+
certbot --nginx \
|
|
149
|
+
-d "${DOMAIN}" \
|
|
150
|
+
-d "www.${DOMAIN}" \
|
|
151
|
+
--non-interactive \
|
|
152
|
+
--agree-tos \
|
|
153
|
+
--email "${EMAIL}" \
|
|
154
|
+
--redirect
|
|
155
|
+
|
|
156
|
+
# Certbot rewrites the nginx config with SSL — reload to apply
|
|
157
|
+
systemctl reload nginx
|
|
158
|
+
|
|
159
|
+
# ── 10. Start bridge ──────────────────────────────────────────────────────────
|
|
160
|
+
info "Starting bridge service..."
|
|
161
|
+
systemctl start patchwork-bridge
|
|
162
|
+
sleep 3
|
|
163
|
+
systemctl is-active patchwork-bridge && info "Bridge is running." || warn "Bridge may have failed — check: journalctl -u patchwork-bridge -n 50"
|
|
164
|
+
|
|
165
|
+
# ── 11. Print summary ─────────────────────────────────────────────────────────
|
|
166
|
+
echo ""
|
|
167
|
+
echo -e "${GREEN}═══════════════════════════════════════════════════${NC}"
|
|
168
|
+
echo -e "${GREEN} Patchwork OS — VPS bootstrap complete${NC}"
|
|
169
|
+
echo -e "${GREEN}═══════════════════════════════════════════════════${NC}"
|
|
170
|
+
echo ""
|
|
171
|
+
echo " Domain: https://${DOMAIN}"
|
|
172
|
+
echo " Bridge port: ${BRIDGE_PORT} (internal, nginx proxied)"
|
|
173
|
+
echo " Token file: ${TOKEN_FILE}"
|
|
174
|
+
echo " Token: ${FIXED_TOKEN}"
|
|
175
|
+
echo ""
|
|
176
|
+
echo " Service: systemctl status patchwork-bridge"
|
|
177
|
+
echo " Logs: journalctl -u patchwork-bridge -f"
|
|
178
|
+
echo " Nginx logs: tail -f /var/log/nginx/access.log"
|
|
179
|
+
echo ""
|
|
180
|
+
echo -e "${YELLOW} Save the token above — you'll need it to connect Claude Code.${NC}"
|
|
181
|
+
echo ""
|
|
182
|
+
echo " To connect Claude Code remotely:"
|
|
183
|
+
echo " claude mcp add patchwork https://${DOMAIN}/mcp --token ${FIXED_TOKEN}"
|
|
184
|
+
echo ""
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# deploy-dashboard.sh — Build dashboard locally and deploy to VPS
|
|
3
|
+
# Run from Mac: bash deploy/deploy-dashboard.sh
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
VPS="root@185.167.97.141"
|
|
7
|
+
REMOTE_DIR="/opt/patchwork-dashboard"
|
|
8
|
+
PM2_NAME="patchwork-dashboard"
|
|
9
|
+
PORT=3200
|
|
10
|
+
NGINX_CONF="/etc/nginx/sites-available/patchworkos"
|
|
11
|
+
DASHBOARD_URL="https://patchworkos.com/dashboard"
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
15
|
+
DASHBOARD_DIR="$REPO_ROOT/dashboard"
|
|
16
|
+
|
|
17
|
+
echo "==> Building dashboard..."
|
|
18
|
+
cd "$DASHBOARD_DIR"
|
|
19
|
+
npm run build
|
|
20
|
+
|
|
21
|
+
echo "==> Packaging standalone build..."
|
|
22
|
+
TARBALL="/tmp/patchwork-dashboard.tar.gz"
|
|
23
|
+
STAGE="/tmp/patchwork-dashboard-stage"
|
|
24
|
+
rm -rf "$STAGE" && mkdir -p "$STAGE"
|
|
25
|
+
|
|
26
|
+
# Copy standalone output
|
|
27
|
+
cp -r "$DASHBOARD_DIR/.next/standalone/." "$STAGE/"
|
|
28
|
+
# Standalone needs static assets in .next/static
|
|
29
|
+
mkdir -p "$STAGE/.next/static"
|
|
30
|
+
cp -r "$DASHBOARD_DIR/.next/static/." "$STAGE/.next/static/"
|
|
31
|
+
# Copy public dir if it exists
|
|
32
|
+
if [ -d "$DASHBOARD_DIR/public" ]; then
|
|
33
|
+
cp -r "$DASHBOARD_DIR/public/." "$STAGE/public/"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
tar -czf "$TARBALL" --no-xattrs -C "$STAGE" .
|
|
37
|
+
|
|
38
|
+
echo "==> Copying tarball to VPS..."
|
|
39
|
+
scp "$TARBALL" "$VPS:/tmp/patchwork-dashboard.tar.gz"
|
|
40
|
+
|
|
41
|
+
echo "==> Deploying on VPS..."
|
|
42
|
+
# shellcheck disable=SC2087
|
|
43
|
+
ssh "$VPS" bash <<'REMOTE'
|
|
44
|
+
set -euo pipefail
|
|
45
|
+
REMOTE_DIR="/opt/patchwork-dashboard"
|
|
46
|
+
PM2_NAME="patchwork-dashboard"
|
|
47
|
+
PORT=3200
|
|
48
|
+
|
|
49
|
+
# Stop existing PM2 process if running
|
|
50
|
+
if pm2 list | grep -q "$PM2_NAME"; then
|
|
51
|
+
pm2 stop "$PM2_NAME" || true
|
|
52
|
+
pm2 delete "$PM2_NAME" || true
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Wipe and recreate deploy dir
|
|
56
|
+
rm -rf "$REMOTE_DIR"
|
|
57
|
+
mkdir -p "$REMOTE_DIR"
|
|
58
|
+
|
|
59
|
+
# Extract
|
|
60
|
+
tar -xzf /tmp/patchwork-dashboard.tar.gz -C "$REMOTE_DIR"
|
|
61
|
+
rm /tmp/patchwork-dashboard.tar.gz
|
|
62
|
+
|
|
63
|
+
# Copy static assets into standalone's expected location
|
|
64
|
+
mkdir -p "$REMOTE_DIR/.next"
|
|
65
|
+
if [ -d "$REMOTE_DIR/.next/static" ]; then
|
|
66
|
+
echo "static dir already in place"
|
|
67
|
+
else
|
|
68
|
+
# tar may have extracted flat; handle both layouts
|
|
69
|
+
if [ -d /tmp/dashboard-static ]; then
|
|
70
|
+
cp -r /tmp/dashboard-static "$REMOTE_DIR/.next/static"
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Also copy public dir if present
|
|
75
|
+
if [ -d "$REMOTE_DIR/public" ]; then
|
|
76
|
+
echo "public dir in place"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Write .env.local — secrets must be set via environment before running this script:
|
|
80
|
+
# PATCHWORK_BRIDGE_TOKEN, DASHBOARD_PASSWORD
|
|
81
|
+
# PATCHWORK_BRIDGE_TOKEN is the bridge auth token (from: patchwork print-token)
|
|
82
|
+
# DASHBOARD_PASSWORD protects the dashboard UI (leave blank to disable auth)
|
|
83
|
+
if [ -f "$REMOTE_DIR/.env.local" ]; then
|
|
84
|
+
echo ".env.local already exists on VPS — preserving (delete manually to reset)"
|
|
85
|
+
else
|
|
86
|
+
cat > "$REMOTE_DIR/.env.local" <<ENV
|
|
87
|
+
NEXT_PUBLIC_BASE_PATH=/dashboard
|
|
88
|
+
PATCHWORK_BRIDGE_URL=https://patchworkos.com
|
|
89
|
+
PATCHWORK_BRIDGE_TOKEN=${PATCHWORK_BRIDGE_TOKEN:-REPLACE_ME}
|
|
90
|
+
VAPID_SUBJECT=mailto:support@gigsecure.co.ke
|
|
91
|
+
DASHBOARD_PASSWORD=${DASHBOARD_PASSWORD:-}
|
|
92
|
+
ENV
|
|
93
|
+
chmod 600 "$REMOTE_DIR/.env.local"
|
|
94
|
+
echo "Wrote .env.local — review and update secrets if placeholders remain"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Install PM2 if missing
|
|
98
|
+
which pm2 || npm install -g pm2
|
|
99
|
+
|
|
100
|
+
# Start with PM2
|
|
101
|
+
cd "$REMOTE_DIR"
|
|
102
|
+
PORT=3200 pm2 start server.js --name "$PM2_NAME"
|
|
103
|
+
|
|
104
|
+
pm2 save
|
|
105
|
+
echo "PM2 started: $PM2_NAME on port $PORT"
|
|
106
|
+
REMOTE
|
|
107
|
+
|
|
108
|
+
echo "==> Configuring nginx..."
|
|
109
|
+
ssh "$VPS" bash <<'NGINX'
|
|
110
|
+
set -euo pipefail
|
|
111
|
+
NGINX_CONF="/etc/nginx/sites-available/patchworkos"
|
|
112
|
+
|
|
113
|
+
# Add SSE location block if missing
|
|
114
|
+
if ! grep -q "location /dashboard/api/bridge/stream" "$NGINX_CONF"; then
|
|
115
|
+
# Insert before the closing brace of the SSL server block
|
|
116
|
+
# We insert just before the last `}` that closes the server block listening on 443
|
|
117
|
+
python3 - "$NGINX_CONF" <<'PYEOF'
|
|
118
|
+
import sys, re
|
|
119
|
+
|
|
120
|
+
path = sys.argv[1]
|
|
121
|
+
with open(path) as f:
|
|
122
|
+
content = f.read()
|
|
123
|
+
|
|
124
|
+
sse_block = """
|
|
125
|
+
# SSE passthrough — no buffering
|
|
126
|
+
location /dashboard/api/bridge/stream {
|
|
127
|
+
proxy_pass http://127.0.0.1:3200;
|
|
128
|
+
proxy_http_version 1.1;
|
|
129
|
+
proxy_buffering off;
|
|
130
|
+
proxy_cache off;
|
|
131
|
+
proxy_read_timeout 86400s;
|
|
132
|
+
proxy_send_timeout 86400s;
|
|
133
|
+
proxy_set_header Host $host;
|
|
134
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
135
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
136
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
137
|
+
add_header X-Accel-Buffering no;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Dashboard app
|
|
141
|
+
location /dashboard {
|
|
142
|
+
proxy_pass http://127.0.0.1:3200;
|
|
143
|
+
proxy_http_version 1.1;
|
|
144
|
+
proxy_set_header Host $host;
|
|
145
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
146
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
147
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
148
|
+
proxy_read_timeout 60s;
|
|
149
|
+
}
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
# Find the ssl/443 server block and insert before its closing brace
|
|
153
|
+
# Strategy: find last `}` in file and insert before it
|
|
154
|
+
idx = content.rfind('\n}')
|
|
155
|
+
if idx == -1:
|
|
156
|
+
print("ERROR: could not find closing brace in nginx config", file=sys.stderr)
|
|
157
|
+
sys.exit(1)
|
|
158
|
+
|
|
159
|
+
new_content = content[:idx] + sse_block + content[idx:]
|
|
160
|
+
with open(path, 'w') as f:
|
|
161
|
+
f.write(new_content)
|
|
162
|
+
print("nginx: location blocks inserted")
|
|
163
|
+
PYEOF
|
|
164
|
+
else
|
|
165
|
+
echo "nginx: location blocks already present, skipping"
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
nginx -t && systemctl reload nginx
|
|
169
|
+
echo "nginx reloaded"
|
|
170
|
+
NGINX
|
|
171
|
+
|
|
172
|
+
echo ""
|
|
173
|
+
echo "==> Deploy complete!"
|
|
174
|
+
echo " Dashboard: https://patchworkos.com/dashboard"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# deploy-landing.sh — Deploy static landing page to VPS
|
|
3
|
+
# Run from Mac: bash deploy/deploy-landing.sh
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
VPS="root@185.167.97.141"
|
|
7
|
+
LANDING_DIR="/var/www/patchwork-landing"
|
|
8
|
+
NGINX_CONF="/etc/nginx/sites-available/patchworkos"
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
11
|
+
|
|
12
|
+
echo "==> Copying landing page to VPS..."
|
|
13
|
+
ssh "$VPS" "mkdir -p $LANDING_DIR"
|
|
14
|
+
scp "$REPO_ROOT/landing/index.html" "$VPS:$LANDING_DIR/index.html"
|
|
15
|
+
|
|
16
|
+
echo "==> Updating nginx to serve landing page at root..."
|
|
17
|
+
ssh "$VPS" bash <<'REMOTE'
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
NGINX_CONF="/etc/nginx/sites-available/patchworkos"
|
|
20
|
+
|
|
21
|
+
# Insert landing page root location if not already present
|
|
22
|
+
if ! grep -q "location = /" "$NGINX_CONF"; then
|
|
23
|
+
python3 - "$NGINX_CONF" <<'PYEOF'
|
|
24
|
+
import sys, re
|
|
25
|
+
|
|
26
|
+
path = sys.argv[1]
|
|
27
|
+
with open(path) as f:
|
|
28
|
+
content = f.read()
|
|
29
|
+
|
|
30
|
+
landing_blocks = """
|
|
31
|
+
# Static landing page at root
|
|
32
|
+
location = / {
|
|
33
|
+
root /var/www/patchwork-landing;
|
|
34
|
+
try_files /index.html =404;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Bridge API paths — proxy to bridge
|
|
38
|
+
location ~ ^/(mcp|oauth|notify|\.well-known|metrics|health|approvals|activity|sessions|traces|recipes|connectors|settings|schemas|push) {
|
|
39
|
+
proxy_pass http://127.0.0.1:3284;
|
|
40
|
+
proxy_http_version 1.1;
|
|
41
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
42
|
+
proxy_set_header Connection "upgrade";
|
|
43
|
+
proxy_set_header Host $host;
|
|
44
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
45
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
46
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
47
|
+
proxy_read_timeout 3600s;
|
|
48
|
+
proxy_send_timeout 3600s;
|
|
49
|
+
}
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
# Insert before the SSE/dashboard blocks (before first "location /dashboard")
|
|
53
|
+
# or before the last closing brace if those don't exist
|
|
54
|
+
if 'location /dashboard' in content:
|
|
55
|
+
idx = content.find(' # SSE passthrough')
|
|
56
|
+
if idx == -1:
|
|
57
|
+
idx = content.find(' location /dashboard')
|
|
58
|
+
else:
|
|
59
|
+
idx = content.rfind('\n}')
|
|
60
|
+
|
|
61
|
+
if idx == -1:
|
|
62
|
+
idx = content.rfind('\n}')
|
|
63
|
+
|
|
64
|
+
new_content = content[:idx] + landing_blocks + content[idx:]
|
|
65
|
+
with open(path, 'w') as f:
|
|
66
|
+
f.write(new_content)
|
|
67
|
+
print("nginx: landing + bridge location blocks inserted")
|
|
68
|
+
PYEOF
|
|
69
|
+
else
|
|
70
|
+
echo "nginx: landing location already present, skipping"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
nginx -t && systemctl reload nginx
|
|
74
|
+
echo "nginx reloaded"
|
|
75
|
+
REMOTE
|
|
76
|
+
|
|
77
|
+
echo ""
|
|
78
|
+
echo "==> Deploy complete!"
|
|
79
|
+
echo " Landing page: https://patchworkos.com"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activation metrics — a small, local-only counter file that tracks how far
|
|
3
|
+
* along the user is in the "first success" journey. Nothing here is ever
|
|
4
|
+
* transmitted off-machine. The outbound usage analytics pipeline lives in
|
|
5
|
+
* `analyticsAggregator.ts` / `analyticsSend.ts` and is a separate concern.
|
|
6
|
+
*
|
|
7
|
+
* Intended counters:
|
|
8
|
+
* - installedAt first time any metric was recorded
|
|
9
|
+
* - firstRecipeRunAt timestamp of the first successful recipe run
|
|
10
|
+
* - recipeRunsTotal running count of successful recipe runs
|
|
11
|
+
* - recipeRunsByDay map of YYYY-MM-DD -> count, trimmed to 14 days
|
|
12
|
+
* - approvalsPrompted count of approval requests created
|
|
13
|
+
* - approvalsCompleted count of approvals approved or rejected (not timed out)
|
|
14
|
+
*
|
|
15
|
+
* These feed a single "first success in N days" KPI card on the dashboard.
|
|
16
|
+
*
|
|
17
|
+
* Opt-out: if the existing outbound analytics preference is explicitly `false`
|
|
18
|
+
* via `getAnalyticsPref()`, all record operations become no-ops. The user does
|
|
19
|
+
* not need to re-opt-out; the existing opt-out is sufficient. Reads are
|
|
20
|
+
* always allowed (they never leave the machine).
|
|
21
|
+
*
|
|
22
|
+
* Storage: `~/.patchwork/telemetry.json` (respects PATCHWORK_HOME override).
|
|
23
|
+
* File is written atomically via tmp+rename with 0o600 permissions.
|
|
24
|
+
*/
|
|
25
|
+
export interface ActivationMetrics {
|
|
26
|
+
installedAt: number;
|
|
27
|
+
firstRecipeRunAt: number | null;
|
|
28
|
+
recipeRunsTotal: number;
|
|
29
|
+
recipeRunsByDay: Record<string, number>;
|
|
30
|
+
approvalsPrompted: number;
|
|
31
|
+
approvalsCompleted: number;
|
|
32
|
+
}
|
|
33
|
+
export interface ActivationSummary {
|
|
34
|
+
installedAt: number;
|
|
35
|
+
firstRecipeRunAt: number | null;
|
|
36
|
+
/** Milliseconds between install and first successful recipe run. Null until first success. */
|
|
37
|
+
timeToFirstRecipeRunMs: number | null;
|
|
38
|
+
/** Total successful recipe runs across all time. */
|
|
39
|
+
recipeRunsTotal: number;
|
|
40
|
+
/** Recipe runs in the last 7 days (inclusive of today). */
|
|
41
|
+
recipeRunsLast7Days: number;
|
|
42
|
+
/** Distinct calendar days with at least one recipe run in the last 7 days. */
|
|
43
|
+
activeDaysLast7: number;
|
|
44
|
+
/** Fraction of prompted approvals that were completed (approved or rejected). 0..1 or null. */
|
|
45
|
+
approvalCompletionRate: number | null;
|
|
46
|
+
approvalsPrompted: number;
|
|
47
|
+
approvalsCompleted: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Load metrics from disk. Returns a fresh empty record if the file is missing
|
|
51
|
+
* or malformed. Never throws on I/O errors.
|
|
52
|
+
*/
|
|
53
|
+
export declare function loadMetrics(configDir?: string, now?: number): ActivationMetrics;
|
|
54
|
+
/**
|
|
55
|
+
* Record one successful recipe run. Sets `firstRecipeRunAt` on the first call
|
|
56
|
+
* and increments total + per-day counters. Trims the per-day map so the file
|
|
57
|
+
* stays O(14 entries).
|
|
58
|
+
*
|
|
59
|
+
* No-op if the user has explicitly opted out of analytics.
|
|
60
|
+
*/
|
|
61
|
+
export declare function recordRecipeRun(configDir?: string, now?: number): void;
|
|
62
|
+
/** Record an approval prompt being surfaced to the user. */
|
|
63
|
+
export declare function recordApprovalPrompted(configDir?: string, now?: number): void;
|
|
64
|
+
/** Record an approval being acted on (approved or rejected — not a timeout). */
|
|
65
|
+
export declare function recordApprovalCompleted(configDir?: string, now?: number): void;
|
|
66
|
+
/** Derive a dashboard-friendly summary from a metrics record. Pure function. */
|
|
67
|
+
export declare function computeSummary(metrics: ActivationMetrics, now?: number): ActivationSummary;
|