@obedience-corp/agent-stream-dbg 0.1.1
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 +37 -0
- package/bin/agent-stream-dbg +8 -0
- package/install.js +257 -0
- package/lib/run.js +27 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @obedience-corp/agent-stream-dbg
|
|
2
|
+
|
|
3
|
+
npm distribution of the [agent-stream-dbg](https://github.com/Obedience-Corp/agent-stream-dbg) Go CLI.
|
|
4
|
+
|
|
5
|
+
Same model as [`@obedience-corp/festival`](https://www.npmjs.com/package/@obedience-corp/festival):
|
|
6
|
+
on install, download the matching GitHub Release archive and verify `checksums.txt`.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @obedience-corp/agent-stream-dbg
|
|
12
|
+
npx @obedience-corp/agent-stream-dbg --help
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Also works with pnpm / bun.
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js 18+
|
|
20
|
+
- macOS or Linux (`x64` / `arm64`)
|
|
21
|
+
- Network access to GitHub Releases on install
|
|
22
|
+
|
|
23
|
+
## Alternatives
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
go install github.com/Obedience-Corp/agent-stream-dbg/cmd/agent-stream-dbg@latest
|
|
27
|
+
|
|
28
|
+
brew tap Obedience-Corp/tap
|
|
29
|
+
brew trust Obedience-Corp/tap
|
|
30
|
+
brew install agent-stream-dbg
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Maintainers
|
|
34
|
+
|
|
35
|
+
Releases are automated via GoReleaser + this package’s `scripts/publish_npm_package.sh`
|
|
36
|
+
(on tag push). Do not hand-edit package version on `main` (stays `0.0.0`); publish
|
|
37
|
+
sets the version from the git tag.
|
package/install.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Festival-style npm installer: download GoReleaser archive + checksums.txt.
|
|
3
|
+
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const https = require("https");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { spawnSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
const REPO = "Obedience-Corp/agent-stream-dbg";
|
|
11
|
+
const BINARY = "agent-stream-dbg";
|
|
12
|
+
const DOWNLOAD_ATTEMPTS = 3;
|
|
13
|
+
const DOWNLOAD_TIMEOUT_MS = 60_000;
|
|
14
|
+
const RETRY_BASE_DELAY_MS = 750;
|
|
15
|
+
|
|
16
|
+
const PLATFORM_MAP = {
|
|
17
|
+
darwin: "macOS",
|
|
18
|
+
linux: "linux",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const ARCH_MAP = {
|
|
22
|
+
x64: "x86_64",
|
|
23
|
+
arm64: "arm64",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function packageVersion() {
|
|
27
|
+
return require("./package.json").version;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function releaseTag(version = packageVersion()) {
|
|
31
|
+
return `v${version}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function targetForCurrentPlatform() {
|
|
35
|
+
const platform = PLATFORM_MAP[process.platform];
|
|
36
|
+
const arch = ARCH_MAP[process.arch];
|
|
37
|
+
if (!platform || !arch) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Unsupported platform: ${process.platform}/${process.arch}. Supported: macOS/Linux x64/arm64.`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return `${platform}-${arch}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function archiveName(version = packageVersion()) {
|
|
46
|
+
return `agent-stream-dbg-${version}-${targetForCurrentPlatform()}.tar.gz`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function releaseURL(asset, version = packageVersion()) {
|
|
50
|
+
return `https://github.com/${REPO}/releases/download/${releaseTag(version)}/${asset}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function binaryPath() {
|
|
54
|
+
const name =
|
|
55
|
+
process.platform === "win32" ? `${BINARY}.exe` : `${BINARY}-bin`;
|
|
56
|
+
return path.join(__dirname, "bin", name);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function haveInstallArtifacts() {
|
|
60
|
+
return fs.existsSync(binaryPath());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function sleep(ms) {
|
|
64
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function downloadOnce(url, dest) {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
let settled = false;
|
|
70
|
+
const fail = (err) => {
|
|
71
|
+
if (settled) return;
|
|
72
|
+
settled = true;
|
|
73
|
+
fs.rmSync(dest, { force: true });
|
|
74
|
+
reject(err);
|
|
75
|
+
};
|
|
76
|
+
const done = () => {
|
|
77
|
+
if (settled) return;
|
|
78
|
+
settled = true;
|
|
79
|
+
resolve();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const follow = (nextURL, redirects = 0) => {
|
|
83
|
+
if (redirects > 10) {
|
|
84
|
+
fail(new Error("too many redirects"));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const request = https
|
|
88
|
+
.get(
|
|
89
|
+
nextURL,
|
|
90
|
+
{ headers: { "User-Agent": "agent-stream-dbg-npm" } },
|
|
91
|
+
(response) => {
|
|
92
|
+
if (
|
|
93
|
+
response.statusCode >= 300 &&
|
|
94
|
+
response.statusCode < 400 &&
|
|
95
|
+
response.headers.location
|
|
96
|
+
) {
|
|
97
|
+
response.resume();
|
|
98
|
+
follow(response.headers.location, redirects + 1);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (response.statusCode !== 200) {
|
|
102
|
+
response.resume();
|
|
103
|
+
fail(
|
|
104
|
+
new Error(
|
|
105
|
+
`failed to download ${url}: HTTP ${response.statusCode}`,
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const file = fs.createWriteStream(dest);
|
|
111
|
+
response.pipe(file);
|
|
112
|
+
file.on("finish", () => file.close(done));
|
|
113
|
+
file.on("error", fail);
|
|
114
|
+
},
|
|
115
|
+
)
|
|
116
|
+
.on("error", fail);
|
|
117
|
+
request.setTimeout(DOWNLOAD_TIMEOUT_MS, () => {
|
|
118
|
+
request.destroy(
|
|
119
|
+
new Error(`download timeout after ${DOWNLOAD_TIMEOUT_MS / 1000}s`),
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
follow(url);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function download(url, dest, attempts = DOWNLOAD_ATTEMPTS) {
|
|
128
|
+
let lastErr;
|
|
129
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
130
|
+
try {
|
|
131
|
+
await downloadOnce(url, dest);
|
|
132
|
+
return;
|
|
133
|
+
} catch (err) {
|
|
134
|
+
lastErr = err;
|
|
135
|
+
if (attempt === attempts) break;
|
|
136
|
+
const delay = RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);
|
|
137
|
+
console.warn(
|
|
138
|
+
`Download failed (${attempt}/${attempts}) for ${path.basename(dest)}: ${err.message}. Retrying in ${delay}ms...`,
|
|
139
|
+
);
|
|
140
|
+
await sleep(delay);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
throw lastErr;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function sha256(filePath) {
|
|
147
|
+
return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function expectedChecksum(checksumsPath, filename) {
|
|
151
|
+
for (const line of fs.readFileSync(checksumsPath, "utf8").split(/\r?\n/)) {
|
|
152
|
+
const parts = line.trim().split(/\s+/);
|
|
153
|
+
if (parts.length >= 2 && parts[1] === filename) {
|
|
154
|
+
return parts[0];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
throw new Error(`checksum for ${filename} not found in checksums.txt`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function verifyChecksum(archivePath, checksumsPath, filename) {
|
|
161
|
+
const expected = expectedChecksum(checksumsPath, filename);
|
|
162
|
+
const actual = sha256(archivePath);
|
|
163
|
+
if (actual !== expected) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`checksum mismatch for ${filename}: expected ${expected}, got ${actual}`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function extractArchive(archivePath, destDir) {
|
|
171
|
+
const result = spawnSync("tar", ["-xzf", archivePath, "-C", destDir], {
|
|
172
|
+
stdio: "inherit",
|
|
173
|
+
});
|
|
174
|
+
if (result.error) throw result.error;
|
|
175
|
+
if (result.status !== 0) {
|
|
176
|
+
throw new Error(`tar exited with status ${result.status}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function install(options = {}) {
|
|
181
|
+
const force = options.force === true;
|
|
182
|
+
if (!force && haveInstallArtifacts()) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const version = packageVersion();
|
|
187
|
+
if (version === "0.0.0") {
|
|
188
|
+
throw new Error(
|
|
189
|
+
"package version is 0.0.0 (dev checkout). Publish a release or install with Go/Homebrew.",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const filename = archiveName(version);
|
|
194
|
+
const binDir = path.join(__dirname, "bin");
|
|
195
|
+
const tempDir = path.join(__dirname, ".tmp-extract");
|
|
196
|
+
const archivePath = path.join(__dirname, filename);
|
|
197
|
+
const checksumsPath = path.join(__dirname, "checksums.txt");
|
|
198
|
+
|
|
199
|
+
console.log(
|
|
200
|
+
`Installing agent-stream-dbg ${version} (${targetForCurrentPlatform()})...`,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
204
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
205
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await download(releaseURL(filename, version), archivePath);
|
|
209
|
+
await download(releaseURL("checksums.txt", version), checksumsPath);
|
|
210
|
+
verifyChecksum(archivePath, checksumsPath, filename);
|
|
211
|
+
extractArchive(archivePath, tempDir);
|
|
212
|
+
|
|
213
|
+
const extractedPath = path.join(tempDir, BINARY);
|
|
214
|
+
const targetPath = binaryPath();
|
|
215
|
+
if (!fs.existsSync(extractedPath)) {
|
|
216
|
+
throw new Error(`${BINARY} not found in ${filename}`);
|
|
217
|
+
}
|
|
218
|
+
fs.renameSync(extractedPath, targetPath);
|
|
219
|
+
if (process.platform !== "win32") {
|
|
220
|
+
fs.chmodSync(targetPath, 0o755);
|
|
221
|
+
}
|
|
222
|
+
console.log("Installed agent-stream-dbg successfully");
|
|
223
|
+
} finally {
|
|
224
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
225
|
+
fs.rmSync(archivePath, { force: true });
|
|
226
|
+
fs.rmSync(checksumsPath, { force: true });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function main() {
|
|
231
|
+
try {
|
|
232
|
+
await install({ force: false });
|
|
233
|
+
} catch (err) {
|
|
234
|
+
console.error(`Failed to install agent-stream-dbg: ${err.message}`);
|
|
235
|
+
console.error(
|
|
236
|
+
[
|
|
237
|
+
"",
|
|
238
|
+
"Fallbacks:",
|
|
239
|
+
" go install github.com/Obedience-Corp/agent-stream-dbg/cmd/agent-stream-dbg@latest",
|
|
240
|
+
" brew tap Obedience-Corp/tap && brew trust Obedience-Corp/tap && brew install agent-stream-dbg",
|
|
241
|
+
"",
|
|
242
|
+
].join("\n"),
|
|
243
|
+
);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (require.main === module) {
|
|
249
|
+
main();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module.exports = {
|
|
253
|
+
archiveName,
|
|
254
|
+
binaryPath,
|
|
255
|
+
install,
|
|
256
|
+
targetForCurrentPlatform,
|
|
257
|
+
};
|
package/lib/run.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const { spawnSync } = require("child_process");
|
|
3
|
+
|
|
4
|
+
const { binaryPath, install } = require("../install");
|
|
5
|
+
|
|
6
|
+
async function runBinary() {
|
|
7
|
+
const target = binaryPath();
|
|
8
|
+
|
|
9
|
+
if (!fs.existsSync(target)) {
|
|
10
|
+
await install();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const result = spawnSync(target, process.argv.slice(2), {
|
|
14
|
+
stdio: "inherit",
|
|
15
|
+
shell: false,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (result.error) {
|
|
19
|
+
throw result.error;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
process.exit(result.status ?? 1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
runBinary,
|
|
27
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@obedience-corp/agent-stream-dbg",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "TUI debugger for multi-agent event streams (SSE, gRPC, ACP) — Go binary via npm",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Obedience Corp",
|
|
7
|
+
"homepage": "https://github.com/Obedience-Corp/agent-stream-dbg#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Obedience-Corp/agent-stream-dbg.git",
|
|
11
|
+
"directory": "npm"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Obedience-Corp/agent-stream-dbg/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"agents",
|
|
18
|
+
"debugging",
|
|
19
|
+
"tui",
|
|
20
|
+
"grpc",
|
|
21
|
+
"sse",
|
|
22
|
+
"acp",
|
|
23
|
+
"cli"
|
|
24
|
+
],
|
|
25
|
+
"bin": {
|
|
26
|
+
"agent-stream-dbg": "bin/agent-stream-dbg"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"postinstall": "node install.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"README.md",
|
|
33
|
+
"bin",
|
|
34
|
+
"install.js",
|
|
35
|
+
"lib"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"os": [
|
|
41
|
+
"darwin",
|
|
42
|
+
"linux"
|
|
43
|
+
],
|
|
44
|
+
"cpu": [
|
|
45
|
+
"x64",
|
|
46
|
+
"arm64"
|
|
47
|
+
]
|
|
48
|
+
}
|