@thaddeus.run/cli 0.1.0-alpha

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.md ADDED
@@ -0,0 +1,24 @@
1
+ # @thaddeus.run/cli
2
+
3
+ The [Thaddeus](https://thaddeus.run) CLI — a post-Git, agent-native
4
+ source-control substrate — distributed as a prebuilt binary. Installs the
5
+ `thaddeus` (and `thad`) command.
6
+
7
+ ```sh
8
+ npm i -g @thaddeus.run/cli@alpha
9
+ thaddeus --version
10
+ thaddeus help
11
+ ```
12
+
13
+ Installing fetches the `thaddeus` binary for your platform from the
14
+ [GitHub releases](https://github.com/thaddeus-run/thaddeus/releases) (with a
15
+ download-on-first-run fallback if install scripts are disabled). No Bun or Node
16
+ is needed at runtime — the binary is self-contained; the npm package is just the
17
+ launcher.
18
+
19
+ Prefer a single command?
20
+ `curl -fsSL https://raw.githubusercontent.com/thaddeus-run/thaddeus/main/install.sh | sh`
21
+ installs both `thaddeus` and `lazythad` and sets up your `PATH`.
22
+
23
+ See the
24
+ [getting-started guide](https://github.com/thaddeus-run/thaddeus/blob/main/docs/getting-started.md).
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ // Launcher: ensure the prebuilt `thaddeus` binary exists (download on first run
3
+ // if the postinstall was skipped), then exec it with the passed args.
4
+ 'use strict';
5
+
6
+ const { spawnSync } = require('node:child_process');
7
+ const { ensureBinary } = require('../download.cjs');
8
+
9
+ ensureBinary('thaddeus')
10
+ .then((bin) => {
11
+ const res = spawnSync(bin, process.argv.slice(2), { stdio: 'inherit' });
12
+ if (res.error) {
13
+ throw res.error;
14
+ }
15
+ process.exit(res.status === null ? 1 : res.status);
16
+ })
17
+ .catch((err) => {
18
+ console.error(`thaddeus: ${err.message}`);
19
+ process.exit(1);
20
+ });
package/download.cjs ADDED
@@ -0,0 +1,141 @@
1
+ // Resolve and fetch the prebuilt binary for this platform from the GitHub
2
+ // Release matching this package's version, verified against the release's
3
+ // SHA256SUMS. Shared by the postinstall (install.cjs) and the launcher
4
+ // (bin/*.cjs), so the tool works even when install scripts are disabled — the
5
+ // launcher downloads on first run.
6
+ 'use strict';
7
+
8
+ const fs = require('node:fs');
9
+ const path = require('node:path');
10
+ const https = require('node:https');
11
+ const crypto = require('node:crypto');
12
+
13
+ const REPO = 'thaddeus-run/thaddeus';
14
+ const MAX_REDIRECTS = 10;
15
+
16
+ // The release asset name for the current platform, e.g. `thaddeus-darwin-arm64`.
17
+ function assetFor(tool) {
18
+ const os = { linux: 'linux', darwin: 'darwin', win32: 'windows' }[
19
+ process.platform
20
+ ];
21
+ const arch = { x64: 'x64', arm64: 'arm64' }[process.arch];
22
+ if (!os || !arch) {
23
+ throw new Error(
24
+ `unsupported platform ${process.platform}-${process.arch}; ` +
25
+ 'build from source or use the install script'
26
+ );
27
+ }
28
+ const ext = os === 'windows' ? '.exe' : '';
29
+ return { asset: `${tool}-${os}-${arch}${ext}`, ext };
30
+ }
31
+
32
+ // GET a URL, following redirects up to a bounded depth (a redirect cycle would
33
+ // otherwise exhaust the stack). `onResponse(res)` consumes the final 200 body.
34
+ function request(url, onResponse, redirects = 0) {
35
+ return new Promise((resolve, reject) => {
36
+ if (redirects > MAX_REDIRECTS) {
37
+ reject(new Error(`too many redirects fetching ${url}`));
38
+ return;
39
+ }
40
+ https
41
+ .get(
42
+ url,
43
+ { headers: { 'user-agent': 'thaddeus-npm-installer' } },
44
+ (res) => {
45
+ if (
46
+ res.statusCode >= 300 &&
47
+ res.statusCode < 400 &&
48
+ res.headers.location
49
+ ) {
50
+ res.resume();
51
+ resolve(request(res.headers.location, onResponse, redirects + 1));
52
+ return;
53
+ }
54
+ if (res.statusCode !== 200) {
55
+ res.resume();
56
+ reject(new Error(`HTTP ${res.statusCode} for ${url}`));
57
+ return;
58
+ }
59
+ onResponse(res, resolve, reject);
60
+ }
61
+ )
62
+ .on('error', reject);
63
+ });
64
+ }
65
+
66
+ // Download a URL to `dest`.
67
+ function download(url, dest) {
68
+ return request(url, (res, resolve, reject) => {
69
+ const file = fs.createWriteStream(dest);
70
+ res.pipe(file);
71
+ file.on('finish', () => file.close(() => resolve()));
72
+ file.on('error', reject);
73
+ });
74
+ }
75
+
76
+ // Fetch a URL's text body (used for SHA256SUMS).
77
+ function fetchText(url) {
78
+ return request(url, (res, resolve) => {
79
+ let body = '';
80
+ res.setEncoding('utf8');
81
+ res.on('data', (chunk) => (body += chunk));
82
+ res.on('end', () => resolve(body));
83
+ });
84
+ }
85
+
86
+ function sha256(file) {
87
+ return crypto
88
+ .createHash('sha256')
89
+ .update(fs.readFileSync(file))
90
+ .digest('hex');
91
+ }
92
+
93
+ // The expected hex digest for `asset` from a `SHA256SUMS` body (lines are
94
+ // `<hash> <name>` or `<hash> *<name>`); null if the asset is not listed.
95
+ function expectedHash(sums, asset) {
96
+ for (const line of sums.split('\n')) {
97
+ const m = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
98
+ if (m && m[2] === asset) {
99
+ return m[1].toLowerCase();
100
+ }
101
+ }
102
+ return null;
103
+ }
104
+
105
+ // Ensure the binary exists locally (idempotent) and return its path. The
106
+ // download is checksum-verified against the release's SHA256SUMS before it is
107
+ // trusted; a mismatch or a missing entry is fatal.
108
+ async function ensureBinary(tool) {
109
+ const { version } = require('./package.json');
110
+ const { asset, ext } = assetFor(tool);
111
+ const binDir = path.join(__dirname, 'bin');
112
+ const binPath = path.join(binDir, `${tool}${ext}`);
113
+ if (fs.existsSync(binPath)) {
114
+ return binPath;
115
+ }
116
+ fs.mkdirSync(binDir, { recursive: true });
117
+ const releaseBase = `https://github.com/${REPO}/releases/download/v${version}`;
118
+ const tmp = `${binPath}.download`;
119
+ try {
120
+ await download(`${releaseBase}/${asset}`, tmp);
121
+ const sums = await fetchText(`${releaseBase}/SHA256SUMS`);
122
+ const expected = expectedHash(sums, asset);
123
+ if (!expected) {
124
+ throw new Error(`no checksum for ${asset} in SHA256SUMS`);
125
+ }
126
+ const actual = sha256(tmp);
127
+ if (actual !== expected) {
128
+ throw new Error(
129
+ `checksum mismatch for ${asset}: expected ${expected}, got ${actual}`
130
+ );
131
+ }
132
+ fs.chmodSync(tmp, 0o755);
133
+ fs.renameSync(tmp, binPath);
134
+ } catch (err) {
135
+ fs.rmSync(tmp, { force: true });
136
+ throw new Error(`failed to install ${asset}: ${err.message}`);
137
+ }
138
+ return binPath;
139
+ }
140
+
141
+ module.exports = { ensureBinary };
package/install.cjs ADDED
@@ -0,0 +1,15 @@
1
+ // postinstall: prefetch the binary so the first `thaddeus` run is instant.
2
+ // Never fail the install — the launcher downloads on first run as a fallback
3
+ // (e.g. when install scripts are disabled or the machine is offline).
4
+ 'use strict';
5
+
6
+ const { ensureBinary } = require('./download.cjs');
7
+
8
+ ensureBinary('thaddeus').then(
9
+ (bin) => console.log(`thaddeus: ready (${bin})`),
10
+ (err) =>
11
+ console.warn(
12
+ `thaddeus: could not prefetch the binary (${err.message}); ` +
13
+ 'it will download on first run.'
14
+ )
15
+ );
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@thaddeus.run/cli",
3
+ "version": "0.1.0-alpha",
4
+ "description": "The Thaddeus CLI — a post-Git, agent-native source-control substrate. Installs the prebuilt `thaddeus` binary for your platform.",
5
+ "keywords": [
6
+ "cli",
7
+ "git-alternative",
8
+ "source-control",
9
+ "thaddeus"
10
+ ],
11
+ "homepage": "https://thaddeus.run",
12
+ "bugs": {
13
+ "url": "https://github.com/thaddeus-run/thaddeus/issues"
14
+ },
15
+ "license": "Apache-2.0",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/thaddeus-run/thaddeus.git",
19
+ "directory": "npm/thaddeus"
20
+ },
21
+ "bin": {
22
+ "thad": "bin/thaddeus.cjs",
23
+ "thaddeus": "bin/thaddeus.cjs"
24
+ },
25
+ "files": [
26
+ "bin/thaddeus.cjs",
27
+ "download.cjs",
28
+ "install.cjs"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "postinstall": "node install.cjs"
35
+ },
36
+ "engines": {
37
+ "node": ">=18"
38
+ }
39
+ }