offwatch 0.5.17 → 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.
- package/bin/0.5.17/offwatch.js +20373 -0
- package/bin/offwatch.cjs +174 -0
- package/bin/offwatch.js +12 -12
- package/bin/package.json +49 -0
- package/package.json +2 -3
- package/postinstall.js +172 -11
- package/lib/downloader.js +0 -133
package/bin/offwatch.cjs
ADDED
|
@@ -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" });
|
package/bin/package.json
ADDED
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
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)
|
|
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,133 +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 { spawn } from "child_process";
|
|
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 using tar via spawn
|
|
87
|
-
// The tar archive contains just offwatch.js without a directory prefix
|
|
88
|
-
const toGitPath = (p) => p.replace(/\\/g, "/").replace(/^([A-Za-z]):/, (_, d) => `/${d.toLowerCase()}`);
|
|
89
|
-
const tarPath = toGitPath(tempPath);
|
|
90
|
-
const tarCwd = toGitPath(versionDir);
|
|
91
|
-
|
|
92
|
-
await new Promise((resolve, reject) => {
|
|
93
|
-
// Archive contains offwatch.js directly, so don't use --strip=1
|
|
94
|
-
const args = ["-xzf", tarPath, "-C", tarCwd];
|
|
95
|
-
logDebug(`Running: tar ${args.join(" ")}`);
|
|
96
|
-
const proc = spawn("tar", args);
|
|
97
|
-
let stderr = "";
|
|
98
|
-
proc.stderr.on("data", (data) => { stderr += data.toString(); });
|
|
99
|
-
proc.on("close", (code) => {
|
|
100
|
-
if (code === 0) {
|
|
101
|
-
fs.unlinkSync(tempPath);
|
|
102
|
-
resolve();
|
|
103
|
-
} else {
|
|
104
|
-
reject(new Error(`tar exited with code ${code}: ${stderr}`));
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
proc.on("error", reject);
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Copy package.json to bin directory so bundled code can find it
|
|
112
|
-
const binDir = path.join(versionDir, "..");
|
|
113
|
-
fs.copyFileSync(__dirname + "/../package.json", path.join(binDir, "package.json"));
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
export const loadCLIBinPath = async (cwd) => {
|
|
117
|
-
const versionDir = path.join(cwd, pkg.version);
|
|
118
|
-
const ext = process.platform === "win32" ? ".js" : "";
|
|
119
|
-
const binPath = path.join(versionDir, CLI_FILENAME + ext);
|
|
120
|
-
logDebug(`loadCLIBinPath: ${binPath}`);
|
|
121
|
-
|
|
122
|
-
if (!fs.existsSync(binPath)) {
|
|
123
|
-
await downloadRelease(versionDir);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
logDebug(`returning ${binPath}`);
|
|
127
|
-
return binPath;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const extractVersionParts = (version) => {
|
|
131
|
-
const [major, minor, patch] = version.replace(/^v/, "").split(".");
|
|
132
|
-
return { major, minor, patch };
|
|
133
|
-
};
|