agentworth 0.1.15 → 0.1.17
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 +1 -1
- package/bin/agentworth.js +1 -1
- package/lib/resolver.js +164 -35
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ You can also install the native AgentWorth binary directly:
|
|
|
93
93
|
| :--- | :--- | :--- |
|
|
94
94
|
| **Standalone Script** | `curl -fsSL https://agentworth.dev/install.sh | sh` | Installs the pre-built native binary directly to `~/.local/bin`. |
|
|
95
95
|
| **Homebrew** | `brew install unfoundbox-crew/tap/agentworth` | Installs via official Homebrew tap. |
|
|
96
|
-
| **Cargo (Native)** | `cargo install agentworth-cli` | Compiles and installs `agentworth`
|
|
96
|
+
| **Cargo (Native)** | `cargo install agentworth-cli` | Compiles and installs `agentworth` and its short alias `archie` to `~/.cargo/bin`. |
|
|
97
97
|
| **NPX (Instant)** | `npx agentworth` | Zero-install runner that detects or downloads the native binary. |
|
|
98
98
|
|
|
99
99
|
---
|
package/bin/agentworth.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { run } from '../lib/resolver.js';
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// All three npm bin entries ('agentworth', 'archie' and 'agwt', see package.json) point at this same
|
|
7
7
|
// script -- process.argv[1] carries the name the shell actually resolved (the .bin
|
|
8
8
|
// symlink), which is how we tell the two invocations apart without a second script.
|
|
9
9
|
const invokedAs = path.basename(process.argv[1] || '').replace(/\.(js|cjs|mjs)$/, '');
|
package/lib/resolver.js
CHANGED
|
@@ -9,6 +9,80 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = path.dirname(__filename);
|
|
11
11
|
|
|
12
|
+
// -----------------------------------------------------------------------------
|
|
13
|
+
// The brand line
|
|
14
|
+
// -----------------------------------------------------------------------------
|
|
15
|
+
// The launcher's first run is an onboarding screen, so it prints the same one-line Archie
|
|
16
|
+
// form the CLI and the install script do -- packages/ui/brand/archie/archie-tui.txt:
|
|
17
|
+
//
|
|
18
|
+
// (o) archie downloading ----------....... 68% 15.9 / 22.2 MB
|
|
19
|
+
//
|
|
20
|
+
// The lamp is the state, the label says what is happening, the rest is evidence. Glyph set
|
|
21
|
+
// is docs/DESIGN.md's: ASCII, U+2500-259F, and the five extras. No emoji, no colour: this
|
|
22
|
+
// goes to stderr, which lands in CI logs and pipes with the colour stripped. Everything
|
|
23
|
+
// here writes to stderr so `npx agentworth stats --json | jq` still works.
|
|
24
|
+
|
|
25
|
+
const BRAND_LABEL_WIDTH = 11;
|
|
26
|
+
|
|
27
|
+
/** Fixed overhead of the wide layout: the lamp, the name, the label, the percent, the sizes. */
|
|
28
|
+
const BRAND_WIDE_OVERHEAD = 50;
|
|
29
|
+
/** The same for the narrow layout, which drops the label. */
|
|
30
|
+
const BRAND_NARROW_OVERHEAD = 28;
|
|
31
|
+
/** Below this the label gives way -- the CLI's ARCHIE_BLOCK_MIN_COLUMNS rule, same shape. */
|
|
32
|
+
const BRAND_WIDE_MIN = 56;
|
|
33
|
+
|
|
34
|
+
function brandColumns() {
|
|
35
|
+
const raw = process.stderr.columns || Number(process.env.COLUMNS) || 80;
|
|
36
|
+
return Math.min(100, Math.max(46, raw));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** ` (*) archie installed archie in ~/.agentworth/bin/v0.1.16` */
|
|
40
|
+
export function brandLine(lamp, label, rest) {
|
|
41
|
+
return ` (${lamp}) archie ${label.padEnd(BRAND_LABEL_WIDTH)} ${rest}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function mib(bytes) {
|
|
45
|
+
return (bytes / 1048576).toFixed(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* One redraw of the download line. Pure string building so the layout is testable without
|
|
50
|
+
* a socket: `cols` and `unicode` are injected rather than read off the terminal.
|
|
51
|
+
*/
|
|
52
|
+
export function downloadLine(done, total, { cols = 80, unicode = true, lamp = 'o' } = {}) {
|
|
53
|
+
const fill = unicode ? '─' : '-';
|
|
54
|
+
const track = unicode ? '·' : '.';
|
|
55
|
+
|
|
56
|
+
// A server that sent no Content-Length gets bytes and no bar: a bar with a made-up
|
|
57
|
+
// denominator is a progress indicator that lies.
|
|
58
|
+
if (!total || total < 100) {
|
|
59
|
+
return brandLine(lamp, 'downloading', `${mib(done)} MB`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const pct = Math.min(100, Math.floor((done / total) * 100));
|
|
63
|
+
const pctCell = `${String(pct).padStart(3)}%`;
|
|
64
|
+
|
|
65
|
+
if (cols >= BRAND_WIDE_MIN) {
|
|
66
|
+
const bw = Math.min(28, Math.max(6, cols - BRAND_WIDE_OVERHEAD));
|
|
67
|
+
const on = Math.round((pct * bw) / 100);
|
|
68
|
+
return brandLine(
|
|
69
|
+
lamp,
|
|
70
|
+
'downloading',
|
|
71
|
+
`${fill.repeat(on)}${track.repeat(bw - on)} ${pctCell} ${mib(done)} / ${mib(total)} MB`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const bw = Math.min(20, Math.max(6, cols - BRAND_NARROW_OVERHEAD));
|
|
76
|
+
const on = Math.round((pct * bw) / 100);
|
|
77
|
+
return ` (${lamp}) archie ${fill.repeat(on)}${track.repeat(bw - on)} ${pctCell} ${mib(done)} MB`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** `~/.agentworth/bin/v0.1.16` rather than the expanded home, so the line fits 80 columns. */
|
|
81
|
+
function tilde(p) {
|
|
82
|
+
const home = os.homedir();
|
|
83
|
+
return home && p.startsWith(home) ? `~${p.slice(home.length)}` : p;
|
|
84
|
+
}
|
|
85
|
+
|
|
12
86
|
/**
|
|
13
87
|
* Returns the normalized platform key (e.g. darwin-arm64, linux-x64, win32-x64).
|
|
14
88
|
*
|
|
@@ -77,18 +151,18 @@ export function getCacheDir(version, homeDir) {
|
|
|
77
151
|
/**
|
|
78
152
|
* Returns the expected native binary name for the target platform.
|
|
79
153
|
*
|
|
80
|
-
* `invokedAs` selects between the
|
|
81
|
-
* (`agentworth` and
|
|
82
|
-
* anything other than exactly 'agwt' resolves to the
|
|
83
|
-
* unrecognized or missing invocation name still gets the primary
|
|
84
|
-
* silently 404ing on a name nobody built.
|
|
154
|
+
* `invokedAs` selects between the three native binaries the release tarball ships
|
|
155
|
+
* (`agentworth`, the short `archie`, and the older `agwt`; see apps/cli/Cargo.toml's three
|
|
156
|
+
* [[bin]] targets) -- anything other than exactly 'archie' or 'agwt' resolves to the
|
|
157
|
+
* `agentworth` binary, so an unrecognized or missing invocation name still gets the primary
|
|
158
|
+
* binary rather than silently 404ing on a name nobody built.
|
|
85
159
|
*
|
|
86
160
|
* @param {string} [platform=process.platform]
|
|
87
|
-
* @param {string} [invokedAs] - basename the launcher was invoked as ('agentworth' or 'agwt')
|
|
161
|
+
* @param {string} [invokedAs] - basename the launcher was invoked as ('agentworth', 'archie' or 'agwt')
|
|
88
162
|
* @returns {string}
|
|
89
163
|
*/
|
|
90
164
|
export function getBinaryName(platform = process.platform, invokedAs) {
|
|
91
|
-
const base = invokedAs === '
|
|
165
|
+
const base = invokedAs === 'archie' || invokedAs === 'agwt' ? invokedAs : 'agentworth';
|
|
92
166
|
return platform === 'win32' ? `${base}.exe` : base;
|
|
93
167
|
}
|
|
94
168
|
|
|
@@ -167,7 +241,7 @@ export function findCargoTargetBinary(startDir, binName = getBinaryName()) {
|
|
|
167
241
|
* @param {number} [redirects=5]
|
|
168
242
|
* @returns {Promise<void>}
|
|
169
243
|
*/
|
|
170
|
-
export function downloadFile(url, destPath, redirects = 5) {
|
|
244
|
+
export function downloadFile(url, destPath, redirects = 5, onProgress = null) {
|
|
171
245
|
return new Promise((resolve, reject) => {
|
|
172
246
|
if (redirects < 0) {
|
|
173
247
|
return reject(new Error('Too many redirects while downloading binary.'));
|
|
@@ -175,17 +249,30 @@ export function downloadFile(url, destPath, redirects = 5) {
|
|
|
175
249
|
|
|
176
250
|
const request = https.get(url, { headers: { 'User-Agent': 'agentworth-npm-resolver' } }, (res) => {
|
|
177
251
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
178
|
-
return downloadFile(res.headers.location, destPath, redirects - 1).then(resolve, reject);
|
|
252
|
+
return downloadFile(res.headers.location, destPath, redirects - 1, onProgress).then(resolve, reject);
|
|
179
253
|
}
|
|
180
254
|
|
|
181
255
|
if (res.statusCode !== 200) {
|
|
182
256
|
return reject(new Error(`Failed to download binary: HTTP ${res.statusCode} from ${url}`));
|
|
183
257
|
}
|
|
184
258
|
|
|
259
|
+
// The asset is ~23 MB and the release CDN is often slow, so silence here reads as a
|
|
260
|
+
// hang. Content-Length comes off the final 200, never the 302 hops above.
|
|
261
|
+
const total = Number(res.headers['content-length']) || 0;
|
|
262
|
+
let received = 0;
|
|
263
|
+
if (onProgress) {
|
|
264
|
+
onProgress(0, total);
|
|
265
|
+
res.on('data', (chunk) => {
|
|
266
|
+
received += chunk.length;
|
|
267
|
+
onProgress(received, total);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
185
271
|
const fileStream = fs.createWriteStream(destPath);
|
|
186
272
|
res.pipe(fileStream);
|
|
187
273
|
|
|
188
274
|
fileStream.on('finish', () => {
|
|
275
|
+
if (onProgress) onProgress(received, total || received);
|
|
189
276
|
fileStream.close(resolve);
|
|
190
277
|
});
|
|
191
278
|
|
|
@@ -221,6 +308,49 @@ function pathBinaryMatchesVersion(binPath, expected) {
|
|
|
221
308
|
}
|
|
222
309
|
}
|
|
223
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Drives the download line: redraws in place on a TTY, prints one line to anything else.
|
|
313
|
+
*
|
|
314
|
+
* A pipe or a CI log cannot move the cursor, so it gets the size once and silence after --
|
|
315
|
+
* a loop that scrolls is a loop that lies. The redraw is throttled to 200ms and the lamp
|
|
316
|
+
* alternates on each frame, which is the dig loop's rhythm and the only thing that moves
|
|
317
|
+
* while the first byte is still in flight.
|
|
318
|
+
*/
|
|
319
|
+
function startDownloadProgress() {
|
|
320
|
+
const tty = Boolean(process.stderr.isTTY);
|
|
321
|
+
const cols = brandColumns();
|
|
322
|
+
let frame = 0;
|
|
323
|
+
let last = 0;
|
|
324
|
+
let announced = false;
|
|
325
|
+
let drawn = false;
|
|
326
|
+
|
|
327
|
+
const tick = (done, total) => {
|
|
328
|
+
if (!tty) {
|
|
329
|
+
if (announced) return;
|
|
330
|
+
announced = true;
|
|
331
|
+
console.error(
|
|
332
|
+
total > 0
|
|
333
|
+
? brandLine('*', 'downloading', `${(total / 1048576).toFixed(1)} MB`)
|
|
334
|
+
: brandLine('*', 'downloading', 'the native binary'),
|
|
335
|
+
);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const now = Date.now();
|
|
339
|
+
const complete = total > 0 && done >= total;
|
|
340
|
+
if (!complete && drawn && now - last < 200) return;
|
|
341
|
+
last = now;
|
|
342
|
+
const lamp = complete ? '*' : frame++ % 2 === 0 ? '*' : 'o';
|
|
343
|
+
process.stderr.write(`\r${downloadLine(done, total, { cols, unicode: true, lamp })}\x1b[K`);
|
|
344
|
+
drawn = true;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const done = () => {
|
|
348
|
+
if (tty && drawn) process.stderr.write('\n');
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
return { tick, done };
|
|
352
|
+
}
|
|
353
|
+
|
|
224
354
|
/**
|
|
225
355
|
* Downloads and extracts the precompiled native binary from GitHub Releases into the user's local cache.
|
|
226
356
|
*
|
|
@@ -230,7 +360,7 @@ function pathBinaryMatchesVersion(binPath, expected) {
|
|
|
230
360
|
* @param {string} [options.version]
|
|
231
361
|
* @param {string} [options.homeDir]
|
|
232
362
|
* @param {boolean} [options.silent=false]
|
|
233
|
-
* @param {string} [options.invokedAs] - 'agentworth' or 'agwt'; picks which extracted binary is returned
|
|
363
|
+
* @param {string} [options.invokedAs] - 'agentworth', 'archie' or 'agwt'; picks which extracted binary is returned
|
|
234
364
|
* @returns {Promise<string>} Path to extracted binary
|
|
235
365
|
*/
|
|
236
366
|
export async function downloadAndExtractBinary(options = {}) {
|
|
@@ -256,13 +386,18 @@ export async function downloadAndExtractBinary(options = {}) {
|
|
|
256
386
|
const archiveName = `agentworth-v${version}-${targetTriple}.tar.gz`;
|
|
257
387
|
const url = `https://github.com/unfoundbox-crew/agentworth/releases/download/v${version}/${archiveName}`;
|
|
258
388
|
|
|
259
|
-
if (!options.silent) {
|
|
260
|
-
console.error(`\x1b[36m⚡ AgentWorth native binary not found locally. Downloading v${version} for ${platform}-${arch}...\x1b[0m`);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
389
|
const archivePath = path.join(cacheDir, archiveName);
|
|
390
|
+
const progress = options.silent ? null : startDownloadProgress();
|
|
391
|
+
|
|
392
|
+
if (progress) {
|
|
393
|
+
console.error(brandLine('*', 'resolving', `v${version} ${targetTriple}`));
|
|
394
|
+
}
|
|
264
395
|
|
|
265
|
-
|
|
396
|
+
try {
|
|
397
|
+
await downloadFile(url, archivePath, 5, progress ? progress.tick : null);
|
|
398
|
+
} finally {
|
|
399
|
+
if (progress) progress.done();
|
|
400
|
+
}
|
|
266
401
|
|
|
267
402
|
// No --force-local here: it was a Windows-only GNU tar workaround, and Windows
|
|
268
403
|
// support was dropped in 8b837c3. BSD tar (macOS's default) doesn't recognize
|
|
@@ -289,7 +424,7 @@ export async function downloadAndExtractBinary(options = {}) {
|
|
|
289
424
|
}
|
|
290
425
|
|
|
291
426
|
if (!options.silent) {
|
|
292
|
-
console.error(
|
|
427
|
+
console.error(brandLine('*', 'installed', `${binName} in ${tilde(cacheDir)}`));
|
|
293
428
|
}
|
|
294
429
|
|
|
295
430
|
return cachedBinary;
|
|
@@ -378,7 +513,7 @@ export function findPathBinary(binName = getBinaryName(), pathEnv = process.env.
|
|
|
378
513
|
* @param {NodeJS.ProcessEnv} [options.env=process.env]
|
|
379
514
|
* @param {string} [options.baseDir=__dirname]
|
|
380
515
|
* @param {string} [options.homeDir]
|
|
381
|
-
* @param {string} [options.invokedAs] - 'agentworth' or 'agwt'; selects which native binary to look for
|
|
516
|
+
* @param {string} [options.invokedAs] - 'agentworth', 'archie' or 'agwt'; selects which native binary to look for
|
|
382
517
|
* @returns {{ found: boolean, path?: string, source?: string, error?: string }}
|
|
383
518
|
*/
|
|
384
519
|
export function resolveBinary(options = {}) {
|
|
@@ -515,25 +650,19 @@ export function resolveBinary(options = {}) {
|
|
|
515
650
|
* @returns {string}
|
|
516
651
|
*/
|
|
517
652
|
export function formatMissingBinaryMessage(platformKey = getPlatformKey()) {
|
|
653
|
+
// The error beat: the torch goes out, the failure line prints under it, nothing moves
|
|
654
|
+
// afterwards. Same voice as the CLI's own screens -- a label, then the command.
|
|
518
655
|
return [
|
|
519
|
-
|
|
520
|
-
'',
|
|
521
|
-
'AgentWorth requires the native binary to run. You can install or build it via:',
|
|
522
|
-
'',
|
|
523
|
-
' \x1b[36m• Standalone install script:\x1b[0m',
|
|
524
|
-
' curl -fsSL https://agentworth.dev/install.sh | sh',
|
|
525
|
-
'',
|
|
526
|
-
' \x1b[36m• Cargo:\x1b[0m',
|
|
527
|
-
' cargo install agentworth-cli',
|
|
528
|
-
'',
|
|
529
|
-
' \x1b[36m• Zero-install via npx:\x1b[0m',
|
|
530
|
-
' npx -y agentworth',
|
|
656
|
+
brandLine(' ', 'error', `no native binary for ${platformKey}`),
|
|
531
657
|
'',
|
|
532
|
-
'
|
|
533
|
-
|
|
658
|
+
' archie is a Rust binary with an npm launcher in front of it. Install the binary',
|
|
659
|
+
' with whichever of these matches how you got here:',
|
|
534
660
|
'',
|
|
535
|
-
'
|
|
536
|
-
'
|
|
661
|
+
' install script curl -fsSL https://agentworth.dev/install.sh | sh',
|
|
662
|
+
' cargo cargo install agentworth-cli',
|
|
663
|
+
' npx npx -y agentworth',
|
|
664
|
+
' release https://github.com/unfoundbox-crew/agentworth/releases/latest',
|
|
665
|
+
' a build you own export AGENTWORTH_BIN=/path/to/archie',
|
|
537
666
|
'',
|
|
538
667
|
].join('\n');
|
|
539
668
|
}
|
|
@@ -560,7 +689,7 @@ export function buildChildEnv(baseEnv, npmVersion) {
|
|
|
560
689
|
*
|
|
561
690
|
* @param {string[]} [argv=process.argv.slice(2)]
|
|
562
691
|
* @param {Object} [options={}]
|
|
563
|
-
* @param {string} [options.invokedAs] - 'agentworth' or 'agwt'; which native binary to resolve
|
|
692
|
+
* @param {string} [options.invokedAs] - 'agentworth', 'archie' or 'agwt'; which native binary to resolve
|
|
564
693
|
* @returns {number} Exit code
|
|
565
694
|
*/
|
|
566
695
|
export function run(argv = process.argv.slice(2), options = {}) {
|
|
@@ -610,7 +739,7 @@ export function run(argv = process.argv.slice(2), options = {}) {
|
|
|
610
739
|
});
|
|
611
740
|
|
|
612
741
|
if (result.error) {
|
|
613
|
-
console.error(
|
|
742
|
+
console.error(brandLine(' ', 'error', `could not run ${binaryResult.path}: ${result.error.message}`));
|
|
614
743
|
return 1;
|
|
615
744
|
}
|
|
616
745
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentworth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Discover, normalize, and understand AI-agent histories locally.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/resolver.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"agentworth": "bin/agentworth.js",
|
|
9
|
+
"archie": "bin/agentworth.js",
|
|
9
10
|
"agwt": "bin/agentworth.js"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|