utharnessly 0.2.21 → 0.2.25
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/download.js +50 -0
- package/bin/utharnessly.js +5 -11
- package/package.json +6 -1
package/bin/download.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ProgressBar, Badge } from '@vr_patel/tui';
|
|
2
|
+
import { createWriteStream, promises as fs } from 'node:fs';
|
|
3
|
+
import { Transform } from 'node:stream';
|
|
4
|
+
import { pipeline } from 'node:stream/promises';
|
|
5
|
+
|
|
6
|
+
export function statusBadge(kind, label) {
|
|
7
|
+
return process.stderr.isTTY && process.env.NO_COLOR === undefined && process.env.TERM !== 'dumb'
|
|
8
|
+
? Badge[kind](label) : `[${label}]`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function download(url, destination, { timeoutMs = 120_000 } = {}) {
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
14
|
+
const interrupt = () => controller.abort();
|
|
15
|
+
process.once('SIGINT', interrupt);
|
|
16
|
+
let bar;
|
|
17
|
+
let cursorHidden = false;
|
|
18
|
+
const columns = process.stdout.columns ?? 80;
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(url, { redirect: 'follow', signal: controller.signal });
|
|
21
|
+
if (!response.ok || !response.body) throw new Error(`download failed (${response.status}) for ${url}`);
|
|
22
|
+
const total = response.headers.get('content-encoding') ? 0 : Number(response.headers.get('content-length'));
|
|
23
|
+
if (process.stdout.isTTY && !process.env.CI && process.env.NO_COLOR === undefined && process.env.TERM !== 'dumb' && process.env.UTHARNESS_ASCII !== '1' && columns >= 60 && total > 0 && Number.isSafeInteger(total)) {
|
|
24
|
+
bar = new ProgressBar({ total, width: Math.max(4, Math.min(40, columns - 55)), title: '', gradient: true, showCount: columns >= 80, showETA: columns >= 80 });
|
|
25
|
+
cursorHidden = true;
|
|
26
|
+
bar.start();
|
|
27
|
+
}
|
|
28
|
+
let received = 0;
|
|
29
|
+
let lastDraw = 0;
|
|
30
|
+
const counter = new Transform({ transform(chunk, _encoding, callback) {
|
|
31
|
+
received += chunk.length;
|
|
32
|
+
if (bar && process.stdout.columns !== columns) {
|
|
33
|
+
process.stdout.write('\r\x1b[2K\x1b[?25h');
|
|
34
|
+
bar = undefined;
|
|
35
|
+
cursorHidden = false;
|
|
36
|
+
}
|
|
37
|
+
if (bar && Date.now() - lastDraw >= 80) { bar.update(received); lastDraw = Date.now(); }
|
|
38
|
+
callback(null, chunk);
|
|
39
|
+
} });
|
|
40
|
+
await pipeline(response.body, counter, createWriteStream(destination), { signal: controller.signal });
|
|
41
|
+
if (bar) bar.finish('Download complete');
|
|
42
|
+
} catch (error) {
|
|
43
|
+
await fs.rm(destination, { force: true });
|
|
44
|
+
throw error;
|
|
45
|
+
} finally {
|
|
46
|
+
if (cursorHidden) process.stdout.write('\x1b[?25h');
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
process.removeListener('SIGINT', interrupt);
|
|
49
|
+
}
|
|
50
|
+
}
|
package/bin/utharnessly.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
-
import { createWriteStream } from 'node:fs';
|
|
4
3
|
import { promises as fs } from 'node:fs';
|
|
5
4
|
import os from 'node:os';
|
|
6
5
|
import path from 'node:path';
|
|
7
|
-
import {
|
|
6
|
+
import { download, statusBadge } from './download.js';
|
|
8
7
|
import { execFile, spawn } from 'node:child_process';
|
|
9
8
|
import { promisify } from 'node:util';
|
|
10
9
|
|
|
11
10
|
const execFileAsync = promisify(execFile);
|
|
12
11
|
|
|
13
|
-
const VERSION = '0.2.
|
|
12
|
+
const VERSION = '0.2.25';
|
|
14
13
|
const REPOSITORY = 'uthumany/utharnessly';
|
|
15
14
|
const BASE_URL = (process.env.UTHARNESSLY_RELEASE_BASE_URL || `https://github.com/${REPOSITORY}/releases/download/v${VERSION}`).replace(/\/$/, '');
|
|
16
15
|
|
|
@@ -29,12 +28,6 @@ function cacheRoot() {
|
|
|
29
28
|
return path.join(base, 'utharnessly', VERSION);
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
async function download(url, destination) {
|
|
33
|
-
const response = await fetch(url, { redirect: 'follow' });
|
|
34
|
-
if (!response.ok || !response.body) throw new Error(`download failed (${response.status}) for ${url}`);
|
|
35
|
-
await pipeline(response.body, createWriteStream(destination));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
31
|
async function verifyChecksum(archive, checksumFile) {
|
|
39
32
|
const contents = await fs.readFile(checksumFile, 'utf8');
|
|
40
33
|
const asset = path.basename(archive);
|
|
@@ -73,10 +66,11 @@ async function ensureBinary(force = false) {
|
|
|
73
66
|
const archive = path.join(temp, asset);
|
|
74
67
|
const checksums = path.join(temp, 'SHA256SUMS');
|
|
75
68
|
try {
|
|
76
|
-
process.stderr.write(
|
|
69
|
+
process.stderr.write(`${statusBadge('info', 'INFO')} Downloading utharnessly v${VERSION} (${process.platform}/${process.arch})…\n`);
|
|
77
70
|
await download(`${BASE_URL}/${asset}`, archive);
|
|
78
71
|
await download(`${BASE_URL}/SHA256SUMS`, checksums);
|
|
79
72
|
await verifyChecksum(archive, checksums);
|
|
73
|
+
process.stderr.write(`${statusBadge('success', 'PASS')} Release checksum verified\n`);
|
|
80
74
|
const extracted = path.join(temp, 'extracted');
|
|
81
75
|
await fs.mkdir(extracted);
|
|
82
76
|
await extractArchive(archive, format, extracted);
|
|
@@ -113,7 +107,7 @@ if (args[0] === 'update') {
|
|
|
113
107
|
const { binary } = await ensureBinary();
|
|
114
108
|
run(binary, args);
|
|
115
109
|
} catch (error) {
|
|
116
|
-
console.error(
|
|
110
|
+
console.error(`${statusBadge('error', 'FAIL')} utharnessly: ${error.message}`);
|
|
117
111
|
process.exitCode = 1;
|
|
118
112
|
}
|
|
119
113
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "utharnessly",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.25",
|
|
4
4
|
"description": "Cross-platform launcher for the utharness local-first agent terminal",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"bin/utharnessly.js",
|
|
17
|
+
"bin/download.js",
|
|
17
18
|
"README.md",
|
|
18
19
|
"LICENSE"
|
|
19
20
|
],
|
|
@@ -34,7 +35,11 @@
|
|
|
34
35
|
"tui"
|
|
35
36
|
],
|
|
36
37
|
"scripts": {
|
|
38
|
+
"test": "node --test test/*.test.mjs",
|
|
37
39
|
"check": "node --check bin/utharnessly.js",
|
|
38
40
|
"pack:check": "npm pack --dry-run"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@vr_patel/tui": "1.0.0"
|
|
39
44
|
}
|
|
40
45
|
}
|