@wavespeed/cli 0.4.26 → 0.4.27
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/dist/commands/history.js +2 -1
- package/dist/lib/output.js +78 -2
- package/package.json +1 -1
package/dist/commands/history.js
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { fetchHistory, PREDICTION_STATUSES } from '../lib/api.js';
|
|
4
4
|
import { toIso } from '../lib/time.js';
|
|
5
|
+
import { describeOutput } from '../lib/output.js';
|
|
5
6
|
function statusBadge(status) {
|
|
6
7
|
const map = {
|
|
7
8
|
completed: chalk.bgGreen.black,
|
|
@@ -40,7 +41,7 @@ export function beyondWindow(since, until) {
|
|
|
40
41
|
function firstOutput(item) {
|
|
41
42
|
const out = item.outputs?.[0];
|
|
42
43
|
if (typeof out === 'string')
|
|
43
|
-
return out;
|
|
44
|
+
return describeOutput(out);
|
|
44
45
|
return '';
|
|
45
46
|
}
|
|
46
47
|
export function registerHistory(program) {
|
package/dist/lib/output.js
CHANGED
|
@@ -11,6 +11,73 @@ function extFromUrl(url, fallback) {
|
|
|
11
11
|
return m[1].toLowerCase();
|
|
12
12
|
return fallback;
|
|
13
13
|
}
|
|
14
|
+
// With enable_base64_output=true each URL output comes back as naked base64,
|
|
15
|
+
// but an output over 64 MiB or one the API failed to fetch stays a URL, and
|
|
16
|
+
// text/JSON outputs are unchanged — so the kind is decided per output.
|
|
17
|
+
const BASE64_RE = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
18
|
+
const DATA_URI_RE = /^data:([^;,]*)(?:;[^,]*)?;base64,(.*)$/s;
|
|
19
|
+
export function isHttpUrl(value) {
|
|
20
|
+
return /^https?:\/\//i.test(value);
|
|
21
|
+
}
|
|
22
|
+
/** Naked base64 as returned for enable_base64_output (short strings are treated as text). */
|
|
23
|
+
export function isBase64Output(value) {
|
|
24
|
+
return value.length >= 64 && value.length % 4 === 0 && BASE64_RE.test(value);
|
|
25
|
+
}
|
|
26
|
+
const MAGIC = [
|
|
27
|
+
[[0x89, 0x50, 0x4e, 0x47], 'png'],
|
|
28
|
+
[[0xff, 0xd8, 0xff], 'jpg'],
|
|
29
|
+
[[0x47, 0x49, 0x46, 0x38], 'gif'],
|
|
30
|
+
[[0x1a, 0x45, 0xdf, 0xa3], 'webm'],
|
|
31
|
+
[[0x49, 0x44, 0x33], 'mp3'],
|
|
32
|
+
[[0x66, 0x4c, 0x61, 0x43], 'flac'],
|
|
33
|
+
[[0x4f, 0x67, 0x67, 0x53], 'ogg'],
|
|
34
|
+
[[0x67, 0x6c, 0x54, 0x46], 'glb'],
|
|
35
|
+
[[0x50, 0x4b, 0x03, 0x04], 'zip'],
|
|
36
|
+
[[0x25, 0x50, 0x44, 0x46], 'pdf'],
|
|
37
|
+
];
|
|
38
|
+
export function extFromBytes(buf, fallback) {
|
|
39
|
+
for (const [sig, ext] of MAGIC) {
|
|
40
|
+
if (sig.every((b, i) => buf[i] === b))
|
|
41
|
+
return ext;
|
|
42
|
+
}
|
|
43
|
+
const at = (o, s) => buf.subarray(o, o + s.length).toString('latin1') === s;
|
|
44
|
+
if (at(0, 'RIFF') && at(8, 'WEBP'))
|
|
45
|
+
return 'webp';
|
|
46
|
+
if (at(0, 'RIFF') && at(8, 'WAVE'))
|
|
47
|
+
return 'wav';
|
|
48
|
+
if (at(4, 'ftyp'))
|
|
49
|
+
return at(8, 'M4A') ? 'm4a' : at(8, 'qt') ? 'mov' : 'mp4';
|
|
50
|
+
if (buf.length >= 2 && buf[0] === 0xff && (buf[1] & 0xe0) === 0xe0)
|
|
51
|
+
return 'mp3';
|
|
52
|
+
return fallback;
|
|
53
|
+
}
|
|
54
|
+
const EXT_FROM_MIME = {
|
|
55
|
+
'image/jpeg': 'jpg', 'image/png': 'png', 'image/webp': 'webp', 'image/gif': 'gif',
|
|
56
|
+
'video/mp4': 'mp4', 'video/webm': 'webm', 'audio/mpeg': 'mp3', 'audio/wav': 'wav',
|
|
57
|
+
};
|
|
58
|
+
/** Bytes of an inline (base64 or data URI) output, or null when it is not inline. */
|
|
59
|
+
function inlineBytes(value, fallbackExt) {
|
|
60
|
+
const m = value.match(DATA_URI_RE);
|
|
61
|
+
if (m) {
|
|
62
|
+
const buf = Buffer.from(m[2], 'base64');
|
|
63
|
+
return { buf, ext: EXT_FROM_MIME[m[1].toLowerCase()] ?? extFromBytes(buf, fallbackExt) };
|
|
64
|
+
}
|
|
65
|
+
if (!isBase64Output(value))
|
|
66
|
+
return null;
|
|
67
|
+
const buf = Buffer.from(value, 'base64');
|
|
68
|
+
return { buf, ext: extFromBytes(buf, fallbackExt) };
|
|
69
|
+
}
|
|
70
|
+
/** Short label for an output: URLs as-is, inline data summarised instead of dumped. */
|
|
71
|
+
export function describeOutput(value) {
|
|
72
|
+
if (isHttpUrl(value))
|
|
73
|
+
return value;
|
|
74
|
+
const m = value.match(DATA_URI_RE);
|
|
75
|
+
const b64 = m ? m[2] : isBase64Output(value) ? value : null;
|
|
76
|
+
if (b64 === null)
|
|
77
|
+
return value;
|
|
78
|
+
const bytes = Math.floor((b64.length * 3) / 4);
|
|
79
|
+
return `(base64 data, ${bytes >= 1024 * 1024 ? (bytes / 1024 / 1024).toFixed(1) + ' MB' : Math.ceil(bytes / 1024) + ' KB'})`;
|
|
80
|
+
}
|
|
14
81
|
function slug(input) {
|
|
15
82
|
return input
|
|
16
83
|
.toLowerCase()
|
|
@@ -31,7 +98,8 @@ export async function downloadOutputs(urls, opts = {}) {
|
|
|
31
98
|
const saved = [];
|
|
32
99
|
for (let i = 0; i < urls.length; i++) {
|
|
33
100
|
const url = urls[i];
|
|
34
|
-
const
|
|
101
|
+
const inline = isHttpUrl(url) ? null : inlineBytes(url, opts.ext || 'bin');
|
|
102
|
+
const ext = inline ? inline.ext : extFromUrl(url, opts.ext || 'bin');
|
|
35
103
|
let filepath;
|
|
36
104
|
if (opts.template) {
|
|
37
105
|
const rendered = renderTemplate(opts.template, {
|
|
@@ -53,6 +121,14 @@ export async function downloadOutputs(urls, opts = {}) {
|
|
|
53
121
|
filepath = path.join(baseDir, `${ts}-${tag}${suffix}.${ext}`);
|
|
54
122
|
}
|
|
55
123
|
fs.mkdirSync(path.dirname(filepath), { recursive: true });
|
|
124
|
+
if (inline) {
|
|
125
|
+
fs.writeFileSync(filepath, inline.buf);
|
|
126
|
+
saved.push(filepath);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (!isHttpUrl(url)) {
|
|
130
|
+
throw new Error(`Output ${i + 1} is neither a URL nor base64 data, so it cannot be downloaded: ${url.slice(0, 80)}`);
|
|
131
|
+
}
|
|
56
132
|
const res = await fetch(url);
|
|
57
133
|
if (!res.ok || !res.body) {
|
|
58
134
|
throw new Error(`Download failed for ${url}: ${res.status} ${res.statusText}`);
|
|
@@ -72,6 +148,6 @@ export function printOutputs(saved, remote) {
|
|
|
72
148
|
}
|
|
73
149
|
console.log(chalk.gray(saved.length ? 'Source URLs:' : 'URLs:'));
|
|
74
150
|
for (const u of remote)
|
|
75
|
-
console.log(' ' + chalk.cyan(u));
|
|
151
|
+
console.log(' ' + chalk.cyan(describeOutput(u)));
|
|
76
152
|
console.log();
|
|
77
153
|
}
|