offwatch 0.5.12 → 0.5.14

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.
Files changed (95) hide show
  1. package/README.md +132 -178
  2. package/bin/offwatch.js +6 -7
  3. package/lib/downloader.js +112 -0
  4. package/package.json +18 -11
  5. package/postinstall.js +18 -0
  6. package/src/__tests__/agent-jwt-env.test.ts +0 -79
  7. package/src/__tests__/allowed-hostname.test.ts +0 -80
  8. package/src/__tests__/auth-command-registration.test.ts +0 -16
  9. package/src/__tests__/board-auth.test.ts +0 -53
  10. package/src/__tests__/common.test.ts +0 -98
  11. package/src/__tests__/company-delete.test.ts +0 -95
  12. package/src/__tests__/company-import-export-e2e.test.ts +0 -502
  13. package/src/__tests__/company-import-url.test.ts +0 -74
  14. package/src/__tests__/company-import-zip.test.ts +0 -44
  15. package/src/__tests__/company.test.ts +0 -599
  16. package/src/__tests__/context.test.ts +0 -70
  17. package/src/__tests__/data-dir.test.ts +0 -79
  18. package/src/__tests__/doctor.test.ts +0 -102
  19. package/src/__tests__/feedback.test.ts +0 -177
  20. package/src/__tests__/helpers/embedded-postgres.ts +0 -6
  21. package/src/__tests__/helpers/zip.ts +0 -87
  22. package/src/__tests__/home-paths.test.ts +0 -44
  23. package/src/__tests__/http.test.ts +0 -106
  24. package/src/__tests__/network-bind.test.ts +0 -62
  25. package/src/__tests__/onboard.test.ts +0 -166
  26. package/src/__tests__/routines.test.ts +0 -249
  27. package/src/__tests__/telemetry.test.ts +0 -117
  28. package/src/__tests__/worktree-merge-history.test.ts +0 -492
  29. package/src/__tests__/worktree.test.ts +0 -982
  30. package/src/adapters/http/format-event.ts +0 -4
  31. package/src/adapters/http/index.ts +0 -7
  32. package/src/adapters/index.ts +0 -2
  33. package/src/adapters/process/format-event.ts +0 -4
  34. package/src/adapters/process/index.ts +0 -7
  35. package/src/adapters/registry.ts +0 -63
  36. package/src/checks/agent-jwt-secret-check.ts +0 -40
  37. package/src/checks/config-check.ts +0 -33
  38. package/src/checks/database-check.ts +0 -59
  39. package/src/checks/deployment-auth-check.ts +0 -88
  40. package/src/checks/index.ts +0 -18
  41. package/src/checks/llm-check.ts +0 -82
  42. package/src/checks/log-check.ts +0 -30
  43. package/src/checks/path-resolver.ts +0 -1
  44. package/src/checks/port-check.ts +0 -24
  45. package/src/checks/secrets-check.ts +0 -146
  46. package/src/checks/storage-check.ts +0 -51
  47. package/src/client/board-auth.ts +0 -282
  48. package/src/client/command-label.ts +0 -4
  49. package/src/client/context.ts +0 -175
  50. package/src/client/http.ts +0 -255
  51. package/src/commands/allowed-hostname.ts +0 -40
  52. package/src/commands/auth-bootstrap-ceo.ts +0 -138
  53. package/src/commands/client/activity.ts +0 -71
  54. package/src/commands/client/agent.ts +0 -315
  55. package/src/commands/client/approval.ts +0 -259
  56. package/src/commands/client/auth.ts +0 -113
  57. package/src/commands/client/common.ts +0 -221
  58. package/src/commands/client/company.ts +0 -1578
  59. package/src/commands/client/context.ts +0 -125
  60. package/src/commands/client/dashboard.ts +0 -34
  61. package/src/commands/client/feedback.ts +0 -645
  62. package/src/commands/client/issue.ts +0 -411
  63. package/src/commands/client/plugin.ts +0 -374
  64. package/src/commands/client/zip.ts +0 -129
  65. package/src/commands/configure.ts +0 -201
  66. package/src/commands/db-backup.ts +0 -102
  67. package/src/commands/doctor.ts +0 -203
  68. package/src/commands/env.ts +0 -411
  69. package/src/commands/heartbeat-run.ts +0 -344
  70. package/src/commands/onboard.ts +0 -692
  71. package/src/commands/routines.ts +0 -352
  72. package/src/commands/run.ts +0 -216
  73. package/src/commands/worktree-lib.ts +0 -279
  74. package/src/commands/worktree-merge-history-lib.ts +0 -764
  75. package/src/commands/worktree.ts +0 -2876
  76. package/src/config/data-dir.ts +0 -48
  77. package/src/config/env.ts +0 -125
  78. package/src/config/home.ts +0 -80
  79. package/src/config/hostnames.ts +0 -26
  80. package/src/config/schema.ts +0 -30
  81. package/src/config/secrets-key.ts +0 -48
  82. package/src/config/server-bind.ts +0 -183
  83. package/src/config/store.ts +0 -120
  84. package/src/index.ts +0 -182
  85. package/src/prompts/database.ts +0 -157
  86. package/src/prompts/llm.ts +0 -43
  87. package/src/prompts/logging.ts +0 -37
  88. package/src/prompts/secrets.ts +0 -99
  89. package/src/prompts/server.ts +0 -221
  90. package/src/prompts/storage.ts +0 -146
  91. package/src/telemetry.ts +0 -49
  92. package/src/utils/banner.ts +0 -24
  93. package/src/utils/net.ts +0 -18
  94. package/src/utils/path-resolver.ts +0 -25
  95. package/src/version.ts +0 -10
package/README.md CHANGED
@@ -1,178 +1,132 @@
1
- <p align="center">
2
- <img src="https://raw.githubusercontent.com/triss-smith/offwatch/main/doc/assets/header.png" alt="Offwatch — automate dev work" width="720" />
3
- </p>
4
-
5
- <p align="center">
6
- <a href="#quickstart"><strong>Quickstart</strong></a> &middot;
7
- <a href="https://github.com/triss-smith/offwatch"><strong>GitHub</strong></a>
8
- </p>
9
-
10
- <p align="center">
11
- <a href="https://github.com/triss-smith/offwatch/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License" /></a>
12
- <a href="https://github.com/triss-smith/offwatch/stargazers"><img src="https://img.shields.io/github/stars/triss-smith/offwatch?style=flat" alt="Stars" /></a>
13
- </p>
14
-
15
- <br/>
16
-
17
- <div align="center">
18
- <video src="https://github.com/user-attachments/assets/773bdfb2-6d1e-4e30-8c5f-3487d5b70c8f" width="600" controls></video>
19
- </div>
20
-
21
- <br/>
22
-
23
- ## What is Offwatch?
24
-
25
- # Open-source orchestration for AI agent teams
26
-
27
- **If OpenClaw is an _employee_, Offwatch is the _team lead_**
28
-
29
- Offwatch is a Node.js CLI that orchestrates a team of AI agents to automate dev work. Bring your own agents, assign tasks, and track their work from the command line.
30
-
31
- **Manage development work, not pull requests.**
32
-
33
- | | Step | Example |
34
- | ------ | --------------- | ------------------------------------------------------------------ |
35
- | **01** | Define the task | _"Build a REST API for user management."_ |
36
- | **02** | Hire the team | Engineers, code reviewers, deployers any bot, any provider. |
37
- | **03** | Approve and run | Review strategy. Set budgets. Hit go. Monitor from the CLI. |
38
-
39
- <br/>
40
-
41
- <div align="center">
42
- <table>
43
- <tr>
44
- <td align="center"><strong>Supports</strong></td>
45
- <td align="center"><img src="https://raw.githubusercontent.com/triss-smith/offwatch/main/doc/assets/logos/openclaw.svg" width="32" alt="OpenClaw" /><br/><sub>OpenClaw</sub></td>
46
- <td align="center"><img src="https://raw.githubusercontent.com/triss-smith/offwatch/main/doc/assets/logos/claude.svg" width="32" alt="Claude" /><br/><sub>Claude Code</sub></td>
47
- <td align="center"><img src="https://raw.githubusercontent.com/triss-smith/offwatch/main/doc/assets/logos/codex.svg" width="32" alt="Codex" /><br/><sub>Codex</sub></td>
48
- <td align="center"><img src="https://raw.githubusercontent.com/triss-smith/offwatch/main/doc/assets/logos/cursor.svg" width="32" alt="Cursor" /><br/><sub>Cursor</sub></td>
49
- <td align="center"><img src="https://raw.githubusercontent.com/triss-smith/offwatch/main/doc/assets/logos/bash.svg" width="32" alt="Bash" /><br/><sub>Bash</sub></td>
50
- <td align="center"><img src="https://raw.githubusercontent.com/triss-smith/offwatch/main/doc/assets/logos/http.svg" width="32" alt="HTTP" /><br/><sub>HTTP</sub></td>
51
- </tr>
52
- </table>
53
-
54
- <em>If it can receive a heartbeat, it's hired.</em>
55
-
56
- </div>
57
-
58
- <br/>
59
-
60
- ## Offwatch is right for you if
61
-
62
- - ✅ You want to **automate development work** with AI agents
63
- - ✅ You **coordinate many different agents** (OpenClaw, Codex, Claude, Cursor) toward a common goal
64
- - ✅ You have **multiple AI agent terminals** open and lose track of what everyone is doing
65
- - ✅ You want agents running **autonomously**, but still want to audit work and chime in when needed
66
- - You want **persistent task tracking** across reboots
67
- - ✅ You want a process for managing agents that **feels like using a task manager**
68
-
69
- <br/>
70
-
71
- ## Features
72
-
73
- <table>
74
- <tr>
75
- <td align="center" width="33%">
76
- <h3>🔌 Bring Your Own Agent</h3>
77
- Any agent, any runtime, one team. If it can receive a heartbeat, it's hired.
78
- </td>
79
- <td align="center" width="33%">
80
- <h3>🎯 Task Management</h3>
81
- Every task traces back to the project goal. Agents know <em>what</em> to do and <em>why</em>.
82
- </td>
83
- <td align="center" width="33%">
84
- <h3>💓 Heartbeats</h3>
85
- Agents wake on a schedule, check work, and act. Delegation flows up and down.
86
- </td>
87
- </tr>
88
- <tr>
89
- <td align="center">
90
- <h3>💰 Cost Control</h3>
91
- Monthly budgets per agent. When they hit the limit, they stop. No runaway costs.
92
- </td>
93
- <td align="center">
94
- <h3>🏢 Multi-Workspace</h3>
95
- One deployment, many workspaces. Complete data isolation.
96
- </td>
97
- <td align="center">
98
- <h3>🎫 Ticket System</h3>
99
- Every conversation traced. Every decision explained. Full tool-call tracing and audit log.
100
- </td>
101
- </tr>
102
- <tr>
103
- <td align="center">
104
- <h3>🛡️ Governance</h3>
105
- You're the lead. Approve work, override strategy, pause or terminate any agent — at any time.
106
- </td>
107
- <td align="center">
108
- <h3>📊 Project Tracking</h3>
109
- Tasks, progress, status. Your agents have work to do, with clear reporting.
110
- </td>
111
- <td align="center">
112
- <h3>🔄 Persistent Sessions</h3>
113
- Agents resume the same task context across heartbeats instead of restarting from scratch.
114
- </td>
115
- </tr>
116
- </table>
117
-
118
- <br/>
119
-
120
- ## Problems Offwatch solves
121
-
122
- | Without Offwatch | With Offwatch |
123
- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
124
- | ❌ You have 20 AI agent tabs open and can't track which one does what. On reboot you lose everything. | ✅ Tasks are ticket-based, conversations are threaded, sessions persist across reboots. |
125
- | ❌ You manually gather context from several places to remind your bot what you're actually doing. | ✅ Context flows from the task up through the project — your agent always knows what to do and why. |
126
- | ❌ Folders of agent configs are disorganized and you're re-inventing task management and coordination. | Offwatch gives you task management, ticketing, delegation, and governance out of the box. |
127
- | ❌ Runaway loops waste hundreds of dollars of tokens and max your quota before you even know what happened. | ✅ Cost tracking surfaces token budgets and throttles agents when they're out. |
128
- | ❌ You have recurring jobs (maintenance, reports, deployments) and have to remember to manually kick them off. | ✅ Heartbeats handle regular work on a schedule. |
129
- | ❌ You have an idea, you have to find your repo, fire up Claude Code, keep a tab open, and babysit it. | ✅ Add a task in Offwatch. Your coding agent works on it until it's done. |
130
-
131
- <br/>
132
-
133
- ## Quickstart
134
-
135
- ```bash
136
- npm install -g offwatch
137
- offwatch --help
138
- ```
139
-
140
- Or run directly:
141
-
142
- ```bash
143
- npx offwatch --help
144
- ```
145
-
146
- <br/>
147
-
148
- ## CLI Commands
149
-
150
- ```bash
151
- offwatch worktree create # Create a new isolated worktree for agent tasks
152
- offwatch worktree list # List all worktrees
153
- offwatch worktree sync # Sync issues to worktree
154
- ```
155
-
156
- <br/>
157
-
158
- ## Development
159
-
160
- ```bash
161
- pnpm install
162
- pnpm build
163
- pnpm dev
164
- ```
165
-
166
- <br/>
167
-
168
- ## License
169
-
170
- MIT &copy; 2026 Offwatch
171
-
172
- <br/>
173
-
174
- ---
175
-
176
- <p align="center">
177
- <sub>Open source under MIT. Built for developers who want to automate work, not babysit agents.</sub>
178
- </p>
1
+ # OffWatch
2
+
3
+ **Your issues. Your agents. They ship while you're off-watch.**
4
+
5
+ OffWatch is a self-hosted server that connects your issue tracker to a fleet of AI coding agents. Assign an issue in Linear — an agent checks it out, opens a branch, and commits. You review and merge.
6
+
7
+ No babysitting. No tab management. Work lands in your repo.
8
+
9
+ ---
10
+
11
+ ## How it works
12
+
13
+ | Step | What happens |
14
+ | ---- | ------------ |
15
+ | **01** | An issue is assigned to you in Linear |
16
+ | **02** | OffWatch syncs it and makes it available to your agents |
17
+ | **03** | An agent checks it out atomically, works the issue, and commits |
18
+ | **04** | You get a summary in the issue thread and review the PR |
19
+
20
+ Agents wake on a schedule and on events — new assignments, @-mentions, approval resolutions. When there's nothing to do, they sleep.
21
+
22
+ ---
23
+
24
+ ## Features
25
+
26
+ **Issue tracker sync** — Linear adapter included. Issues sync in, completions sync back. Tracker-agnostic by design; additional adapters can be added.
27
+
28
+ **Atomic checkout** — one agent per issue, enforced. No duplicate work, no conflicts.
29
+
30
+ **Bring your own agent** — Claude Code, Codex, Cursor, OpenClaw, any HTTP endpoint, any local process. If it can receive a heartbeat, it works.
31
+
32
+ **Heartbeat protocol** — agents wake on schedule or on trigger, do their work, and sleep. Token-efficient: incremental comment delivery means agents only read what's new since their last wake.
33
+
34
+ **Approval queue** agents can propose issues to your tracker before creating them. You approve or reject from the OffWatch UI.
35
+
36
+ **Multiple workspaces** one deployment, multiple codebases. Each workspace has its own agents, projects, and tracker config.
37
+
38
+ **Skills** — inject context and workflow instructions into agents at runtime. Share skills across agents.
39
+
40
+ **Plugins** — extend OffWatch with custom tracing, knowledge bases, queues, and more via the plugin SDK.
41
+
42
+ **Activity log** — every agent action is recorded. Full audit trail, always.
43
+
44
+ **Mobile-ready UI** — manage your agents from anywhere.
45
+
46
+ ---
47
+
48
+ ## OffWatch is right for you if
49
+
50
+ - You want issues from your tracker to become commits without manual intervention
51
+ - You run multiple AI coding agents and lose track of who is doing what
52
+ - You want agents running 24/7 but still want to review their work before it merges
53
+ - You want a persistent session layer so agents don't restart from scratch every time
54
+ - You're hitting large token bills and want a leaner heartbeat layer
55
+
56
+ ## OffWatch is not right for you if
57
+
58
+ - You want a chatbot or prompt manager — agents have jobs, not chat windows
59
+ - You want a drag-and-drop workflow builder — OffWatch models agents working issues, not pipelines
60
+ - You have one agent — this is for fleets
61
+
62
+ ---
63
+
64
+ ## Quickstart
65
+
66
+ Open source. Self-hosted. No account required.
67
+
68
+ ```bash
69
+ git clone https://github.com/triss-smith/offwatch.git
70
+ cd offwatch
71
+ pnpm install
72
+ pnpm dev
73
+ ```
74
+
75
+ This starts the API server at `http://localhost:3100` with an embedded PostgreSQL database. No setup required.
76
+
77
+ > **Requirements:** Node.js 20+, pnpm 9.15+
78
+
79
+ ---
80
+
81
+ ## Configuration
82
+
83
+ On first run, create a workspace and configure your issue tracker:
84
+
85
+ 1. Open `http://localhost:3100` and create a workspace
86
+ 2. Go to **Settings → Issue Tracker** and connect Linear (API key + team)
87
+ 3. Add an agent and point it at your adapter of choice
88
+ 4. Assign an issue to yourself in Linear and watch it appear
89
+
90
+ ---
91
+
92
+ ## Development
93
+
94
+ ```bash
95
+ pnpm dev # Full dev (API + UI, watch mode)
96
+ pnpm dev:server # Server only
97
+ pnpm build # Build all
98
+ pnpm typecheck # Type checking
99
+ pnpm test:run # Run tests
100
+ pnpm db:generate # Generate DB migration
101
+ pnpm db:migrate # Apply migrations
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Roadmap
107
+
108
+ - Issue tracker adapter system (Linear)
109
+ - Atomic issue checkout
110
+ - ✅ Heartbeat context compaction
111
+ - ✅ Skills manager
112
+ - Scheduled routines
113
+ - Approval queue
114
+ - ✅ Plugin system
115
+ - ✅ Multiple workspaces
116
+ - ⚪ GitHub Issues adapter
117
+ - ⚪ Git worktree isolation per checkout
118
+ - ⚪ Security audit routine
119
+ - ⚪ Desktop app
120
+ - Additional tracker adapters
121
+
122
+ ---
123
+
124
+ ## Contributing
125
+
126
+ Contributions welcome. Open an issue to discuss before sending large PRs.
127
+
128
+ ---
129
+
130
+ ## License
131
+
132
+ MIT &copy; 2026 OffWatch
package/bin/offwatch.js CHANGED
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import { loadCLIBinPath } from "../lib/downloader.js";
2
3
  import { spawn } from "child_process";
3
- import { dirname, resolve } from "path";
4
+ import { dirname } from "path";
4
5
  import { fileURLToPath } from "url";
5
6
 
6
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const srcDir = resolve(__dirname, "..", "src");
8
8
 
9
- // Run the CLI source with tsx (TypeScript executor)
10
- spawn("npx", ["tsx", resolve(srcDir, "index.ts")], {
11
- stdio: "inherit",
12
- shell: true,
13
- });
9
+ const binPath = await loadCLIBinPath(__dirname);
10
+
11
+ // Run the downloaded CLI binary
12
+ spawn("node", [binPath], { stdio: "inherit" });
@@ -0,0 +1,112 @@
1
+ import got from "got";
2
+ import fs from "fs";
3
+ import fsa from "fs-extra";
4
+ import * as os from "node:os";
5
+ import tar from "tar";
6
+ import stream from "node:stream";
7
+ import { promisify } from "node:util";
8
+ import path from "node:path";
9
+ import { dirname } from "path";
10
+ import { fileURLToPath } from "url";
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const debugMode = process.env.DEBUG != null;
14
+ const pipeline = promisify(stream.pipeline);
15
+
16
+ const pkg = JSON.parse(fs.readFileSync(__dirname + "/../package.json", "utf8"));
17
+ const CLI_FILENAME = "offwatch";
18
+
19
+ const logDebug = (message) => {
20
+ if (debugMode) {
21
+ console.debug(`[DEBUG] ${message}`);
22
+ }
23
+ };
24
+
25
+ const getPlatform = () => {
26
+ const platform = os.platform();
27
+ if (platform === "win32") return "win32-x64";
28
+ if (platform === "darwin") return "darwin-x64";
29
+ return "linux-x64";
30
+ };
31
+
32
+ const downloadRelease = async (versionDir) => {
33
+ logDebug(`downloadRelease(${versionDir})`);
34
+ const version = pkg.version;
35
+
36
+ const releases = (await got
37
+ .get(`https://api.github.com/repos/triss-smith/offwatch/releases`)
38
+ .json());
39
+
40
+ // Find the latest release that matches our major.minor
41
+ const currentParts = extractVersionParts(version);
42
+ const matchingRelease = releases
43
+ .filter((release) => {
44
+ const releaseVersion = extractVersionParts(release.tag_name);
45
+ return (releaseVersion.major === currentParts.major &&
46
+ releaseVersion.minor === currentParts.minor);
47
+ })
48
+ .sort((a, b) => {
49
+ return Number(extractVersionParts(b.tag_name).patch) >
50
+ Number(extractVersionParts(a.tag_name).patch)
51
+ ? 1
52
+ : -1;
53
+ })[0] || releases[0];
54
+
55
+ if (!matchingRelease) {
56
+ throw new Error(`No releases found for ${currentParts.major}.${currentParts.minor}`);
57
+ }
58
+
59
+ const platform = getPlatform();
60
+ logDebug(`Looking for platform: ${platform} in release ${matchingRelease.tag_name}`);
61
+
62
+ const asset = matchingRelease.assets.find((asset) => {
63
+ return asset.name.includes(platform);
64
+ });
65
+
66
+ if (!asset) {
67
+ throw new Error(`${platform} is not currently supported. Check GitHub releases for available platforms.`);
68
+ }
69
+
70
+ try {
71
+ fsa.mkdirpSync(versionDir);
72
+ } catch (e) { }
73
+
74
+ const ext = asset.name.endsWith(".js") ? ".js" : "";
75
+ const filePath = path.join(versionDir, CLI_FILENAME + ext);
76
+ const tempPath = path.join(versionDir, asset.name);
77
+
78
+ logDebug(`Downloading ${asset.browser_download_url}`);
79
+
80
+ await pipeline(got.stream(asset.browser_download_url), fs.createWriteStream(tempPath));
81
+
82
+ if (ext) {
83
+ // For .js files, rename to expected name
84
+ fs.renameSync(tempPath, filePath);
85
+ } else {
86
+ // For compressed files, decompress
87
+ await pipeline(fs.createReadStream(tempPath), tar.x({
88
+ C: versionDir,
89
+ strip: 1,
90
+ }));
91
+ fs.unlinkSync(tempPath);
92
+ }
93
+ };
94
+
95
+ export const loadCLIBinPath = async (cwd) => {
96
+ const versionDir = path.join(cwd, pkg.version);
97
+ const ext = process.platform === "win32" ? ".js" : "";
98
+ const binPath = path.join(versionDir, CLI_FILENAME + ext);
99
+ logDebug(`loadCLIBinPath: ${binPath}`);
100
+
101
+ if (!fs.existsSync(binPath)) {
102
+ await downloadRelease(versionDir);
103
+ }
104
+
105
+ logDebug(`returning ${binPath}`);
106
+ return binPath;
107
+ };
108
+
109
+ const extractVersionParts = (version) => {
110
+ const [major, minor, patch] = version.replace(/^v/, "").split(".");
111
+ return { major, minor, patch };
112
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "offwatch",
3
- "version": "0.5.12",
3
+ "version": "0.5.14",
4
4
  "description": "Offwatch — orchestrate AI agent teams to automate dev work",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,20 +24,27 @@
24
24
  "url": "https://github.com/triss-smith/offwatch/issues"
25
25
  },
26
26
  "files": [
27
- "src",
28
27
  "bin",
29
- "lib"
28
+ "lib",
29
+ "postinstall.js"
30
30
  ],
31
- "publishConfig": {
32
- "access": "public"
31
+ "engines": {
32
+ "node": ">=20"
33
33
  },
34
34
  "dependencies": {
35
- "tsx": "^4.19.2",
36
- "commander": "^13.1.0",
37
- "@clack/prompts": "^0.10.0",
35
+ "@clack/prompts": "^0.7.0",
36
+ "commander": "^13.0.0",
37
+ "dotenv": "^16.4.5",
38
+ "drizzle-orm": "^0.38.0",
39
+ "embedded-postgres": "^18.1.0-beta.16",
40
+ "fs-extra": "^10.1.0",
41
+ "got": "^14.0.0",
38
42
  "picocolors": "^1.1.1",
39
- "dotenv": "^17.0.1",
40
- "drizzle-orm": "^0.38.4",
41
- "embedded-postgres": "^18.1.0-beta.16"
43
+ "postgres": "^3.4.5",
44
+ "superjson": "^2.2.1",
45
+ "tar": "^6.2.0",
46
+ "undici": "^6.21.0",
47
+ "ws": "^8.19.0",
48
+ "zod": "^3.24.0"
42
49
  }
43
50
  }
package/postinstall.js ADDED
@@ -0,0 +1,18 @@
1
+ import { loadCLIBinPath } from "./lib/downloader.js";
2
+ import { dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+
7
+ // Skip download in monorepo development (workspace packages are used directly)
8
+ if (!process.cwd().includes("node_modules")) {
9
+ loadCLIBinPath(__dirname).then(
10
+ () => {
11
+ process.exit(0);
12
+ },
13
+ (err) => {
14
+ console.error(err);
15
+ process.exit(1);
16
+ }
17
+ );
18
+ }
@@ -1,79 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
- import {
6
- ensureAgentJwtSecret,
7
- mergePaperclipEnvEntries,
8
- readAgentJwtSecretFromEnv,
9
- readPaperclipEnvEntries,
10
- resolveAgentJwtEnvFile,
11
- } from "../config/env.js";
12
- import { agentJwtSecretCheck } from "../checks/agent-jwt-secret-check.js";
13
-
14
- const ORIGINAL_ENV = { ...process.env };
15
-
16
- function tempConfigPath(): string {
17
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-jwt-env-"));
18
- const configDir = path.join(dir, "custom");
19
- fs.mkdirSync(configDir, { recursive: true });
20
- return path.join(configDir, "config.json");
21
- }
22
-
23
- describe("agent jwt env helpers", () => {
24
- beforeEach(() => {
25
- process.env = { ...ORIGINAL_ENV };
26
- delete process.env.PAPERCLIP_AGENT_JWT_SECRET;
27
- });
28
-
29
- afterEach(() => {
30
- process.env = { ...ORIGINAL_ENV };
31
- });
32
-
33
- it("writes .env next to explicit config path", () => {
34
- const configPath = tempConfigPath();
35
- const result = ensureAgentJwtSecret(configPath);
36
-
37
- expect(result.created).toBe(true);
38
-
39
- const envPath = resolveAgentJwtEnvFile(configPath);
40
- expect(fs.existsSync(envPath)).toBe(true);
41
- const contents = fs.readFileSync(envPath, "utf-8");
42
- expect(contents).toContain("PAPERCLIP_AGENT_JWT_SECRET=");
43
- });
44
-
45
- it("loads secret from .env next to explicit config path", () => {
46
- const configPath = tempConfigPath();
47
- const envPath = resolveAgentJwtEnvFile(configPath);
48
- fs.writeFileSync(envPath, "PAPERCLIP_AGENT_JWT_SECRET=test-secret\n", { mode: 0o600 });
49
-
50
- const loaded = readAgentJwtSecretFromEnv(configPath);
51
- expect(loaded).toBe("test-secret");
52
- expect(process.env.PAPERCLIP_AGENT_JWT_SECRET).toBe("test-secret");
53
- });
54
-
55
- it("doctor check passes when secret exists in adjacent .env", () => {
56
- const configPath = tempConfigPath();
57
- const envPath = resolveAgentJwtEnvFile(configPath);
58
- fs.writeFileSync(envPath, "PAPERCLIP_AGENT_JWT_SECRET=check-secret\n", { mode: 0o600 });
59
-
60
- const result = agentJwtSecretCheck(configPath);
61
- expect(result.status).toBe("pass");
62
- });
63
-
64
- it("quotes hash-prefixed env values so dotenv round-trips them", () => {
65
- const configPath = tempConfigPath();
66
- const envPath = resolveAgentJwtEnvFile(configPath);
67
-
68
- mergePaperclipEnvEntries(
69
- {
70
- PAPERCLIP_WORKTREE_COLOR: "#439edb",
71
- },
72
- envPath,
73
- );
74
-
75
- const contents = fs.readFileSync(envPath, "utf-8");
76
- expect(contents).toContain('PAPERCLIP_WORKTREE_COLOR="#439edb"');
77
- expect(readPaperclipEnvEntries(envPath).PAPERCLIP_WORKTREE_COLOR).toBe("#439edb");
78
- });
79
- });