offwatch 0.5.16 → 0.5.18

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.
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env node
2
+ // Inlined downloader using native https module to avoid dependency issues
3
+ const { spawn } = require("child_process");
4
+ const fs = require("fs");
5
+ const fsa = require("fs-extra");
6
+ const os = require("os");
7
+ const path = require("path");
8
+ const https = require("https");
9
+
10
+ const debugMode = process.env.DEBUG != null;
11
+ const CLI_FILENAME = "offwatch";
12
+
13
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "../package.json"), "utf8"));
14
+
15
+ const logDebug = (message) => {
16
+ if (debugMode) {
17
+ console.debug(`[DEBUG] ${message}`);
18
+ }
19
+ };
20
+
21
+ const getPlatform = () => {
22
+ const platform = os.platform();
23
+ if (platform === "win32") return "win32-x64";
24
+ if (platform === "darwin") return "darwin-x64";
25
+ return "linux-x64";
26
+ };
27
+
28
+ const downloadFile = (url, dest) => {
29
+ return new Promise((resolve, reject) => {
30
+ const file = fs.createWriteStream(dest);
31
+ https.get(url, { headers: { "User-Agent": "offwatch-cli" } }, (response) => {
32
+ if (response.statusCode === 301 || response.statusCode === 302) {
33
+ https.get(response.headers.location, (resp) => {
34
+ resp.pipe(file);
35
+ file.on("finish", () => { file.close(); resolve(); });
36
+ }).on("error", reject);
37
+ } else {
38
+ response.pipe(file);
39
+ file.on("finish", () => { file.close(); resolve(); });
40
+ }
41
+ }).on("error", reject);
42
+ });
43
+ };
44
+
45
+ const downloadRelease = async (versionDir) => {
46
+ logDebug(`downloadRelease(${versionDir})`);
47
+ const version = pkg.version;
48
+
49
+ const releasesJson = await new Promise((resolve, reject) => {
50
+ https.get(
51
+ `https://api.github.com/repos/triss-smith/offwatch/releases`,
52
+ { headers: { "User-Agent": "offwatch-cli" } },
53
+ (res) => {
54
+ let data = "";
55
+ res.on("data", (chunk) => { data += chunk; });
56
+ res.on("end", () => { resolve(JSON.parse(data)); });
57
+ }
58
+ ).on("error", reject);
59
+ });
60
+
61
+ const currentParts = extractVersionParts(version);
62
+ const matchingRelease = releasesJson
63
+ .filter((release) => {
64
+ const releaseVersion = extractVersionParts(release.tag_name);
65
+ return (
66
+ releaseVersion.major === currentParts.major &&
67
+ releaseVersion.minor === currentParts.minor
68
+ );
69
+ })
70
+ .sort((a, b) => {
71
+ return (
72
+ Number(extractVersionParts(b.tag_name).patch) >
73
+ Number(extractVersionParts(a.tag_name).patch)
74
+ ? 1
75
+ : -1
76
+ );
77
+ })[0] || releasesJson[0];
78
+
79
+ if (!matchingRelease) {
80
+ throw new Error(
81
+ `No releases found for ${currentParts.major}.${currentParts.minor}`
82
+ );
83
+ }
84
+
85
+ const platform = getPlatform();
86
+ logDebug(
87
+ `Looking for platform: ${platform} in release ${matchingRelease.tag_name}`
88
+ );
89
+
90
+ const asset = matchingRelease.assets.find((asset) => {
91
+ return asset.name.includes(platform);
92
+ });
93
+
94
+ if (!asset) {
95
+ throw new Error(
96
+ `${platform} is not currently supported. Check GitHub releases for available platforms.`
97
+ );
98
+ }
99
+
100
+ try {
101
+ fsa.mkdirpSync(versionDir);
102
+ } catch (e) {}
103
+
104
+ const ext = asset.name.endsWith(".js") ? ".js" : "";
105
+ const filePath = path.join(versionDir, CLI_FILENAME + ext);
106
+ const tempPath = path.join(versionDir, asset.name);
107
+
108
+ logDebug(`Downloading ${asset.browser_download_url}`);
109
+
110
+ await downloadFile(asset.browser_download_url, tempPath);
111
+
112
+ if (ext) {
113
+ fs.renameSync(tempPath, filePath);
114
+ } else {
115
+ const toGitPath = (p) =>
116
+ p
117
+ .replace(/\\/g, "/")
118
+ .replace(/^([A-Za-z]):/, (_, d) => `/${d.toLowerCase()}`);
119
+ const tarPath = toGitPath(tempPath);
120
+ const tarCwd = toGitPath(versionDir);
121
+
122
+ await new Promise((resolve, reject) => {
123
+ const args = ["-xzf", tarPath, "-C", tarCwd];
124
+ logDebug(`Running: tar ${args.join(" ")}`);
125
+ const proc = spawn("tar", args);
126
+ let stderr = "";
127
+ proc.stderr.on("data", (data) => {
128
+ stderr += data.toString();
129
+ });
130
+ proc.on("close", (code) => {
131
+ if (code === 0) {
132
+ fs.unlinkSync(tempPath);
133
+ resolve();
134
+ } else {
135
+ reject(new Error(`tar exited with code ${code}: ${stderr}`));
136
+ }
137
+ });
138
+ proc.on("error", reject);
139
+ });
140
+ }
141
+
142
+ const binDir = path.join(versionDir, "..");
143
+ fs.copyFileSync(
144
+ path.join(__dirname, "../package.json"),
145
+ path.join(binDir, "package.json")
146
+ );
147
+ };
148
+
149
+ const loadCLIBinPath = async (cwd) => {
150
+ const versionDir = path.join(cwd, pkg.version);
151
+ const ext = process.platform === "win32" ? ".js" : "";
152
+ const binPath = path.join(versionDir, CLI_FILENAME + ext);
153
+ logDebug(`loadCLIBinPath: ${binPath}`);
154
+
155
+ if (!fs.existsSync(binPath)) {
156
+ await downloadRelease(versionDir);
157
+ }
158
+
159
+ logDebug(`returning ${binPath}`);
160
+ return binPath;
161
+ };
162
+
163
+ const extractVersionParts = (version) => {
164
+ const [major, minor, patch] = version.replace(/^v/, "").split(".");
165
+ return { major, minor, patch };
166
+ };
167
+
168
+ // Run the downloaded CLI binary
169
+ loadCLIBinPath(__dirname).then((binPath) => {
170
+ spawn("node", [binPath, ...process.argv.slice(2)], { stdio: "inherit" });
171
+ }).catch((err) => {
172
+ console.error(err.message);
173
+ process.exit(1);
174
+ });
package/bin/offwatch.js CHANGED
@@ -1,12 +1,12 @@
1
- #!/usr/bin/env node
2
- import { loadCLIBinPath } from "../lib/downloader.js";
3
- import { spawn } from "child_process";
4
- import { dirname } from "path";
5
- import { fileURLToPath } from "url";
6
-
7
- const __dirname = dirname(fileURLToPath(import.meta.url));
8
-
9
- const binPath = await loadCLIBinPath(__dirname);
10
-
11
- // Run the downloaded CLI binary
12
- spawn("node", [binPath, ...process.argv.slice(2)], { stdio: "inherit" });
1
+ #!/usr/bin/env node
2
+ import { loadCLIBinPath } from "../lib/downloader.js";
3
+ import { spawn } from "child_process";
4
+ import { dirname } from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+
9
+ const binPath = await loadCLIBinPath(__dirname);
10
+
11
+ // Run the downloaded CLI binary
12
+ spawn("node", [binPath, ...process.argv.slice(2)], { stdio: "inherit" });
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "offwatch",
3
+ "version": "0.5.17",
4
+ "description": "Offwatch — orchestrate AI agent teams to automate dev work",
5
+ "type": "module",
6
+ "bin": {
7
+ "offwatch": "bin/offwatch.cjs"
8
+ },
9
+ "keywords": [
10
+ "offwatch",
11
+ "ai",
12
+ "agents",
13
+ "orchestration",
14
+ "cli"
15
+ ],
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/triss-smith/offwatch.git",
20
+ "directory": "cli"
21
+ },
22
+ "homepage": "https://github.com/triss-smith/offwatch",
23
+ "bugs": {
24
+ "url": "https://github.com/triss-smith/offwatch/issues"
25
+ },
26
+ "files": [
27
+ "bin",
28
+ "postinstall.js"
29
+ ],
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "dependencies": {
34
+ "@clack/prompts": "^0.7.0",
35
+ "commander": "^13.0.0",
36
+ "dotenv": "^16.4.5",
37
+ "drizzle-orm": "^0.38.0",
38
+ "embedded-postgres": "^18.1.0-beta.16",
39
+ "fs-extra": "^10.1.0",
40
+ "got": "^14.0.0",
41
+ "picocolors": "^1.1.1",
42
+ "postgres": "^3.4.5",
43
+ "superjson": "^2.2.1",
44
+ "tar": "^7.5.0",
45
+ "undici": "^6.21.0",
46
+ "ws": "^8.19.0",
47
+ "zod": "^3.24.0"
48
+ }
49
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "offwatch",
3
- "version": "0.5.16",
3
+ "version": "0.5.18",
4
4
  "description": "Offwatch — orchestrate AI agent teams to automate dev work",
5
5
  "type": "module",
6
6
  "bin": {
7
- "offwatch": "bin/offwatch.js"
7
+ "offwatch": "bin/offwatch.cjs"
8
8
  },
9
9
  "keywords": [
10
10
  "offwatch",
@@ -25,7 +25,6 @@
25
25
  },
26
26
  "files": [
27
27
  "bin",
28
- "lib",
29
28
  "postinstall.js"
30
29
  ],
31
30
  "engines": {
package/postinstall.js CHANGED
@@ -1,18 +1,179 @@
1
- import { loadCLIBinPath } from "./lib/downloader.js";
2
- import { dirname } from "path";
3
- import { fileURLToPath } from "url";
1
+ #!/usr/bin/env node
2
+ // Inlined downloader using native https module to avoid dependency issues
3
+ const { spawn } = require("child_process");
4
+ const fs = require("fs");
5
+ const fsa = require("fs-extra");
6
+ const os = require("os");
7
+ const path = require("path");
8
+ const https = require("https");
4
9
 
5
- const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const debugMode = process.env.DEBUG != null;
11
+ const CLI_FILENAME = "offwatch";
12
+
13
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"));
14
+
15
+ const logDebug = (message) => {
16
+ if (debugMode) {
17
+ console.debug(`[DEBUG] ${message}`);
18
+ }
19
+ };
20
+
21
+ const getPlatform = () => {
22
+ const platform = os.platform();
23
+ if (platform === "win32") return "win32-x64";
24
+ if (platform === "darwin") return "darwin-x64";
25
+ return "linux-x64";
26
+ };
27
+
28
+ const downloadFile = (url, dest) => {
29
+ return new Promise((resolve, reject) => {
30
+ const file = fs.createWriteStream(dest);
31
+ https.get(url, { headers: { "User-Agent": "offwatch-cli" } }, (response) => {
32
+ if (response.statusCode === 301 || response.statusCode === 302) {
33
+ // Follow redirect
34
+ https.get(response.headers.location, { headers: { "User-Agent": "offwatch-cli" } }, (resp) => {
35
+ resp.pipe(file);
36
+ file.on("finish", () => { file.close(); resolve(); });
37
+ }).on("error", reject);
38
+ } else {
39
+ response.pipe(file);
40
+ file.on("finish", () => { file.close(); resolve(); });
41
+ }
42
+ }).on("error", reject);
43
+ });
44
+ };
45
+
46
+ const downloadRelease = async (versionDir) => {
47
+ logDebug(`downloadRelease(${versionDir})`);
48
+ const version = pkg.version;
49
+
50
+ const releasesJson = await new Promise((resolve, reject) => {
51
+ https.get(
52
+ `https://api.github.com/repos/triss-smith/offwatch/releases`,
53
+ { headers: { "User-Agent": "offwatch-cli" } },
54
+ (res) => {
55
+ let data = "";
56
+ res.on("data", (chunk) => { data += chunk; });
57
+ res.on("end", () => { resolve(JSON.parse(data)); });
58
+ }
59
+ ).on("error", reject);
60
+ });
61
+
62
+ const currentParts = extractVersionParts(version);
63
+ const matchingRelease = releasesJson
64
+ .filter((release) => {
65
+ const releaseVersion = extractVersionParts(release.tag_name);
66
+ return (
67
+ releaseVersion.major === currentParts.major &&
68
+ releaseVersion.minor === currentParts.minor
69
+ );
70
+ })
71
+ .sort((a, b) => {
72
+ return (
73
+ Number(extractVersionParts(b.tag_name).patch) >
74
+ Number(extractVersionParts(a.tag_name).patch)
75
+ ? 1
76
+ : -1
77
+ );
78
+ })[0] || releasesJson[0];
79
+
80
+ if (!matchingRelease) {
81
+ throw new Error(
82
+ `No releases found for ${currentParts.major}.${currentParts.minor}`
83
+ );
84
+ }
85
+
86
+ const platform = getPlatform();
87
+ logDebug(
88
+ `Looking for platform: ${platform} in release ${matchingRelease.tag_name}`
89
+ );
90
+
91
+ const asset = matchingRelease.assets.find((asset) => {
92
+ return asset.name.includes(platform);
93
+ });
94
+
95
+ if (!asset) {
96
+ throw new Error(
97
+ `${platform} is not currently supported. Check GitHub releases for available platforms.`
98
+ );
99
+ }
100
+
101
+ try {
102
+ fsa.mkdirpSync(versionDir);
103
+ } catch (e) {}
104
+
105
+ const ext = asset.name.endsWith(".js") ? ".js" : "";
106
+ const filePath = path.join(versionDir, CLI_FILENAME + ext);
107
+ const tempPath = path.join(versionDir, asset.name);
108
+
109
+ logDebug(`Downloading ${asset.browser_download_url}`);
110
+
111
+ await downloadFile(asset.browser_download_url, tempPath);
112
+
113
+ if (ext) {
114
+ fs.renameSync(tempPath, filePath);
115
+ } else {
116
+ const toGitPath = (p) =>
117
+ p
118
+ .replace(/\\/g, "/")
119
+ .replace(/^([A-Za-z]):/, (_, d) => `/${d.toLowerCase()}`);
120
+ const tarPath = toGitPath(tempPath);
121
+ const tarCwd = toGitPath(versionDir);
122
+
123
+ await new Promise((resolve, reject) => {
124
+ const args = ["-xzf", tarPath, "-C", tarCwd];
125
+ logDebug(`Running: tar ${args.join(" ")}`);
126
+ const proc = spawn("tar", args);
127
+ let stderr = "";
128
+ proc.stderr.on("data", (data) => {
129
+ stderr += data.toString();
130
+ });
131
+ proc.on("close", (code) => {
132
+ if (code === 0) {
133
+ fs.unlinkSync(tempPath);
134
+ resolve();
135
+ } else {
136
+ reject(new Error(`tar exited with code ${code}: ${stderr}`));
137
+ }
138
+ });
139
+ proc.on("error", reject);
140
+ });
141
+ }
142
+
143
+ const binDir = path.join(versionDir, "..");
144
+ fs.copyFileSync(
145
+ path.join(__dirname, "package.json"),
146
+ path.join(binDir, "package.json")
147
+ );
148
+ };
149
+
150
+ const loadCLIBinPath = async (cwd) => {
151
+ const versionDir = path.join(cwd, pkg.version);
152
+ const ext = process.platform === "win32" ? ".js" : "";
153
+ const binPath = path.join(versionDir, CLI_FILENAME + ext);
154
+ logDebug(`loadCLIBinPath: ${binPath}`);
155
+
156
+ if (!fs.existsSync(binPath)) {
157
+ await downloadRelease(versionDir);
158
+ }
159
+
160
+ logDebug(`returning ${binPath}`);
161
+ return binPath;
162
+ };
163
+
164
+ const extractVersionParts = (version) => {
165
+ const [major, minor, patch] = version.replace(/^v/, "").split(".");
166
+ return { major, minor, patch };
167
+ };
6
168
 
7
169
  // Skip download in monorepo development (workspace packages are used directly)
8
170
  if (!process.cwd().includes("node_modules")) {
9
- loadCLIBinPath(__dirname).then(
10
- () => {
171
+ loadCLIBinPath(__dirname)
172
+ .then(() => {
11
173
  process.exit(0);
12
- },
13
- (err) => {
14
- console.error(err);
174
+ })
175
+ .catch((err) => {
176
+ console.error(err.message);
15
177
  process.exit(1);
16
- }
17
- );
178
+ });
18
179
  }
package/lib/downloader.js DELETED
@@ -1,117 +0,0 @@
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 tar.extract({
88
- cwd: versionDir,
89
- strip: 1,
90
- file: tempPath,
91
- });
92
- fs.unlinkSync(tempPath);
93
- }
94
-
95
- // Copy package.json to bin directory so bundled code can find it
96
- const binDir = path.join(versionDir, "..");
97
- fs.copyFileSync(__dirname + "/../package.json", path.join(binDir, "package.json"));
98
- };
99
-
100
- export const loadCLIBinPath = async (cwd) => {
101
- const versionDir = path.join(cwd, pkg.version);
102
- const ext = process.platform === "win32" ? ".js" : "";
103
- const binPath = path.join(versionDir, CLI_FILENAME + ext);
104
- logDebug(`loadCLIBinPath: ${binPath}`);
105
-
106
- if (!fs.existsSync(binPath)) {
107
- await downloadRelease(versionDir);
108
- }
109
-
110
- logDebug(`returning ${binPath}`);
111
- return binPath;
112
- };
113
-
114
- const extractVersionParts = (version) => {
115
- const [major, minor, patch] = version.replace(/^v/, "").split(".");
116
- return { major, minor, patch };
117
- };