catcher-cli 0.1.1 → 0.2.0
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 +15 -8
- package/bin/catcher.js +114 -0
- package/checksums.json +7 -0
- package/package.json +10 -9
- package/dist/catcher.mjs +0 -5063
package/README.md
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
# catcher-cli
|
|
2
2
|
|
|
3
|
-
The `catcher`
|
|
4
|
-
|
|
3
|
+
The `catcher` command for AI agents: an end-to-end encrypted channel to your phone, and, through grants you approve,
|
|
4
|
+
to your other agents.
|
|
5
5
|
|
|
6
6
|
```sh
|
|
7
|
-
npm i -g catcher-cli
|
|
8
|
-
catcher register --
|
|
9
|
-
catcher setup claude-code # or: catcher setup codex
|
|
10
|
-
catcher ask "Ship it?" --choices yes,no --wait
|
|
11
|
-
catcher help
|
|
7
|
+
npm i -g catcher-cli
|
|
8
|
+
catcher register --name pickle
|
|
12
9
|
```
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
Or tell your agent: "Read https://catcher.bang9.dev/agent.md and install catcher."
|
|
12
|
+
|
|
13
|
+
This package is a small launcher. On first run it downloads the native `catcher` binary for your platform from the
|
|
14
|
+
[GitHub release](https://github.com/bang9/catcher/releases) with the same version, checks its SHA-256 against the
|
|
15
|
+
checksums published inside this package, and caches it in `~/.catcher/bin/<version>/`. It has no dependencies and no
|
|
16
|
+
install scripts. macOS (arm64, x64), Linux (x64, arm64) and Windows (x64) are supported.
|
|
17
|
+
|
|
18
|
+
- Website and docs: https://catcher.bang9.dev
|
|
19
|
+
- Source, protocol and threat model: https://github.com/bang9/catcher
|
|
20
|
+
|
|
21
|
+
Apache-2.0
|
package/bin/catcher.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// catcher-cli: a thin launcher for the native catcher binary (crates/catcher-cli).
|
|
3
|
+
// On first run it downloads the binary for this platform from the GitHub release that matches this package's version,
|
|
4
|
+
// checks its SHA-256 against checksums.json (written into the package at release time), caches it under
|
|
5
|
+
// ~/.catcher/bin/<version>/ and from then on just runs it. No install scripts, no dependencies.
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
|
|
12
|
+
const REPO = 'bang9/catcher';
|
|
13
|
+
const MAX_BYTES = 64 * 1024 * 1024;
|
|
14
|
+
|
|
15
|
+
// process.platform-process.arch → release target. Linux builds are static (musl) and run on any distribution.
|
|
16
|
+
const TARGETS = {
|
|
17
|
+
'darwin-arm64': 'aarch64-apple-darwin',
|
|
18
|
+
'darwin-x64': 'x86_64-apple-darwin',
|
|
19
|
+
'linux-arm64': 'aarch64-unknown-linux-musl',
|
|
20
|
+
'linux-x64': 'x86_64-unknown-linux-musl',
|
|
21
|
+
'win32-arm64': 'x86_64-pc-windows-msvc',
|
|
22
|
+
'win32-x64': 'x86_64-pc-windows-msvc',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const fail = (message) => {
|
|
26
|
+
process.stderr.write(`catcher-cli: ${message}\n`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const readJson = (name) => JSON.parse(readFileSync(new URL(`../${name}`, import.meta.url), 'utf8'));
|
|
31
|
+
|
|
32
|
+
const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
33
|
+
|
|
34
|
+
function assetName() {
|
|
35
|
+
const target = TARGETS[`${process.platform}-${process.arch}`];
|
|
36
|
+
if (!target) {
|
|
37
|
+
fail(`no catcher build for ${process.platform}/${process.arch}. See https://github.com/${REPO}/releases`);
|
|
38
|
+
}
|
|
39
|
+
if (process.platform === 'win32') return `catcher-${target}.exe`;
|
|
40
|
+
return `catcher-${target}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function download(url) {
|
|
44
|
+
const res = await fetch(url, { redirect: 'follow' });
|
|
45
|
+
if (!res.ok) fail(`download failed (${res.status}): ${url}`);
|
|
46
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
47
|
+
if (bytes.length > MAX_BYTES) fail(`download is unexpectedly large: ${url}`);
|
|
48
|
+
return bytes;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function ensureBinary(version, asset) {
|
|
52
|
+
const home = process.env.CATCHER_HOME || join(homedir(), '.catcher');
|
|
53
|
+
const dir = join(home, 'bin', version);
|
|
54
|
+
let file = join(dir, 'catcher');
|
|
55
|
+
if (process.platform === 'win32') file = join(dir, 'catcher.exe');
|
|
56
|
+
if (existsSync(file)) return file;
|
|
57
|
+
|
|
58
|
+
let checksums;
|
|
59
|
+
try {
|
|
60
|
+
checksums = readJson('checksums.json');
|
|
61
|
+
} catch {
|
|
62
|
+
fail('this package has no checksums.json; install a released version: npm i -g catcher-cli');
|
|
63
|
+
}
|
|
64
|
+
const expected = checksums[asset];
|
|
65
|
+
if (!/^[0-9a-f]{64}$/.test(expected || '')) fail(`no checksum for ${asset} in this release`);
|
|
66
|
+
|
|
67
|
+
const url = `https://github.com/${REPO}/releases/download/cli-v${version}/${asset}`;
|
|
68
|
+
process.stderr.write(`catcher-cli: downloading catcher ${version} (${asset}), first run only\n`);
|
|
69
|
+
const bytes = await download(url);
|
|
70
|
+
const actual = sha256(bytes);
|
|
71
|
+
if (actual !== expected)
|
|
72
|
+
fail(`checksum mismatch for ${asset}: expected ${expected}, got ${actual}. Nothing was saved.`);
|
|
73
|
+
|
|
74
|
+
// write to a private temp name, then rename: two first runs at once never see a half-written binary
|
|
75
|
+
mkdirSync(dir, { recursive: true });
|
|
76
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
77
|
+
writeFileSync(tmp, bytes, { mode: 0o755 });
|
|
78
|
+
chmodSync(tmp, 0o755);
|
|
79
|
+
try {
|
|
80
|
+
renameSync(tmp, file);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
rmSync(tmp, { force: true });
|
|
83
|
+
if (!existsSync(file)) throw e;
|
|
84
|
+
}
|
|
85
|
+
return file;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function run(file) {
|
|
89
|
+
const child = spawn(file, process.argv.slice(2), { stdio: 'inherit' });
|
|
90
|
+
const forward = (signal) => {
|
|
91
|
+
if (child.exitCode === null) child.kill(signal);
|
|
92
|
+
};
|
|
93
|
+
const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
|
|
94
|
+
for (const s of signals) process.on(s, forward);
|
|
95
|
+
child.on('error', (e) => fail(`could not run ${file}: ${e.message}`));
|
|
96
|
+
child.on('exit', (code, signal) => {
|
|
97
|
+
if (signal) {
|
|
98
|
+
// die the same way the binary did, so callers see the real signal
|
|
99
|
+
for (const s of signals) process.removeListener(s, forward);
|
|
100
|
+
process.kill(process.pid, signal);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
process.exit(code ?? 1);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { version } = readJson('package.json');
|
|
108
|
+
let binary = '';
|
|
109
|
+
try {
|
|
110
|
+
binary = await ensureBinary(version, assetName());
|
|
111
|
+
} catch (e) {
|
|
112
|
+
fail(`could not get the catcher binary: ${e.message}`);
|
|
113
|
+
}
|
|
114
|
+
run(binary);
|
package/checksums.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"catcher-aarch64-apple-darwin": "b7cc470c5e85c86b657a5a7da516e175ef3f0b1679a6ce6e654f48ece565a6eb",
|
|
3
|
+
"catcher-x86_64-apple-darwin": "385abc4a4028862da23d664df4a524fec88b6afb16896052bf1110bfbddf0d58",
|
|
4
|
+
"catcher-aarch64-unknown-linux-musl": "32d25c797de2174e19acb9bd7b2c7d64b99b980b5a71a52f38ac89353e110c27",
|
|
5
|
+
"catcher-x86_64-unknown-linux-musl": "3f73100696ce37bdfb8d7c1ebce332062c44a3e4aa5e59c3e4a949d0f6be8b45",
|
|
6
|
+
"catcher-x86_64-pc-windows-msvc.exe": "ac54116d10a0660a2b679cdc714e3bdc71207540a282a17cce5777683afec95e"
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catcher-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "catcher CLI — connect AI agents to their owner and to each other. End-to-end encrypted asks, approvals, OTP hand-off
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "catcher CLI — connect AI agents to their owner's phone and to each other. End-to-end encrypted asks, approvals, OTP hand-off and agent-to-agent messages.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-to-agent",
|
|
7
7
|
"ai-agents",
|
|
8
8
|
"claude-code",
|
|
9
9
|
"codex",
|
|
10
|
-
"e2e-encryption"
|
|
11
|
-
"mcp"
|
|
10
|
+
"e2e-encryption"
|
|
12
11
|
],
|
|
13
|
-
"homepage": "https://
|
|
12
|
+
"homepage": "https://catcher.bang9.dev",
|
|
13
|
+
"bugs": "https://github.com/bang9/catcher/issues",
|
|
14
14
|
"license": "Apache-2.0",
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
17
|
"url": "git+https://github.com/bang9/catcher.git",
|
|
18
|
-
"directory": "
|
|
18
|
+
"directory": "npm/catcher-cli"
|
|
19
19
|
},
|
|
20
20
|
"bin": {
|
|
21
|
-
"catcher": "
|
|
21
|
+
"catcher": "bin/catcher.js"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
|
-
"
|
|
24
|
+
"bin/catcher.js",
|
|
25
|
+
"checksums.json",
|
|
25
26
|
"README.md",
|
|
26
27
|
"LICENSE"
|
|
27
28
|
],
|
|
28
29
|
"type": "module",
|
|
29
30
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
31
|
+
"node": ">=20"
|
|
31
32
|
}
|
|
32
33
|
}
|