gipity 1.0.428 → 1.0.429
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/deploy.js +19 -1
- package/dist/commands/generate.js +61 -4
- package/dist/commands/page-eval.js +387 -50
- package/dist/commands/page-inspect.js +8 -2
- package/dist/commands/page-screenshot.js +209 -43
- package/dist/hooks/capture-runner.js +9 -1
- package/dist/index.js +1057 -488
- package/dist/page-fixtures.js +92 -3
- package/dist/provider-docs.js +10 -10
- package/dist/relay/daemon.js +16 -2
- package/dist/relay/device-http.js +22 -0
- package/dist/relay/media-upload.js +184 -0
- package/dist/setup.js +4 -1
- package/package.json +2 -2
package/dist/commands/deploy.js
CHANGED
|
@@ -28,11 +28,24 @@ export const deployCommand = new Command('deploy')
|
|
|
28
28
|
.option('--no-optimize', 'Skip build optimization and upload files as-is - the escape hatch for plain-HTML apps whose <script src> tags are not type="module"')
|
|
29
29
|
.option('--json', 'Output as JSON')
|
|
30
30
|
.option('--inspect [path]', 'After a successful deploy, run `page inspect` on the deployed URL (or URL + path) in the same command - one build-loop step instead of two. With --json, emits two JSON lines: deploy, then inspect.')
|
|
31
|
+
// The settle knobs `page inspect` already has. An app whose first paint is behind
|
|
32
|
+
// a model load / async boot inspects as an empty page without them, so a caller
|
|
33
|
+
// that reaches for `--inspect` on such an app needs them HERE - it should not have
|
|
34
|
+
// to abandon the combined command and re-run inspect standalone just to wait.
|
|
35
|
+
.option('--wait <ms>', 'With --inspect: sleep this many ms before capturing (max 30000)')
|
|
36
|
+
.option('--wait-for <selector>', "With --inspect: wait until this CSS selector appears before capturing (deterministic; the app's own ready signal)")
|
|
37
|
+
.option('--wait-timeout <ms>', 'With --inspect: max ms to wait for --wait-for before giving up', '5000')
|
|
31
38
|
.action((target, opts) => run('Deploy', async () => {
|
|
32
39
|
if (target !== 'dev' && target !== 'prod') {
|
|
33
40
|
console.error(clrError('Target must be "dev" or "prod"'));
|
|
34
41
|
process.exit(1);
|
|
35
42
|
}
|
|
43
|
+
// A settle flag only means one thing on a deploy: "verify the page once it's
|
|
44
|
+
// up, and give it time to come up." Asking for it without --inspect is not an
|
|
45
|
+
// error to bounce back — it's the intent, so honour it.
|
|
46
|
+
if (!opts.inspect && (opts.waitFor || deployCommand.getOptionValueSource('wait') !== undefined)) {
|
|
47
|
+
opts.inspect = true;
|
|
48
|
+
}
|
|
36
49
|
const config = requireConfig();
|
|
37
50
|
await syncBeforeAction(opts);
|
|
38
51
|
const doDeploy = () => post(`/projects/${config.projectGuid}/deploy`, {
|
|
@@ -61,7 +74,12 @@ export const deployCommand = new Command('deploy')
|
|
|
61
74
|
if (!opts.json)
|
|
62
75
|
console.log('');
|
|
63
76
|
try {
|
|
64
|
-
await inspectPage(url, {
|
|
77
|
+
await inspectPage(url, {
|
|
78
|
+
json: opts.json,
|
|
79
|
+
wait: opts.wait,
|
|
80
|
+
waitFor: opts.waitFor,
|
|
81
|
+
waitTimeout: opts.waitTimeout,
|
|
82
|
+
});
|
|
65
83
|
}
|
|
66
84
|
catch (err) {
|
|
67
85
|
console.error(warning(`Inspect failed (deploy itself succeeded): ${err?.message ?? err}`));
|
|
@@ -25,12 +25,66 @@ async function downloadFile(url, filename) {
|
|
|
25
25
|
if (!res.ok)
|
|
26
26
|
throw new Error(`Download failed: ${res.status}`);
|
|
27
27
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const outPath = correctExtension(filename, buffer);
|
|
29
|
+
ensureOutputDir(outPath);
|
|
30
|
+
writeFileSync(outPath, buffer);
|
|
31
|
+
const savedPath = resolvePath(outPath);
|
|
31
32
|
await pushGenerated(savedPath);
|
|
32
33
|
return savedPath;
|
|
33
34
|
}
|
|
35
|
+
/** What these bytes ACTUALLY are, by magic number — the only trustworthy source.
|
|
36
|
+
* A provider hands back whatever its model produced (BFL's "png" request comes
|
|
37
|
+
* back as JPEG), so neither the caller's -o extension nor the response's
|
|
38
|
+
* content_type describes the file on disk. */
|
|
39
|
+
function sniffExt(buf) {
|
|
40
|
+
// subarray past the end yields a short string, so every compare below is
|
|
41
|
+
// length-safe on its own — no blanket minimum (which would skip the check
|
|
42
|
+
// entirely on a small file and hand back the misnamed path).
|
|
43
|
+
const ascii = (start, end) => buf.subarray(start, end).toString('latin1');
|
|
44
|
+
if (buf.length < 4)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff)
|
|
47
|
+
return 'jpg';
|
|
48
|
+
if (ascii(0, 8) === '\x89PNG\r\n\x1a\n')
|
|
49
|
+
return 'png';
|
|
50
|
+
if (ascii(0, 4) === 'RIFF' && ascii(8, 12) === 'WEBP')
|
|
51
|
+
return 'webp';
|
|
52
|
+
if (ascii(0, 4) === 'RIFF' && ascii(8, 12) === 'WAVE')
|
|
53
|
+
return 'wav';
|
|
54
|
+
if (ascii(0, 3) === 'GIF')
|
|
55
|
+
return 'gif';
|
|
56
|
+
if (ascii(4, 8) === 'ftyp')
|
|
57
|
+
return 'mp4';
|
|
58
|
+
if (buf[0] === 0x1a && buf[1] === 0x45 && buf[2] === 0xdf && buf[3] === 0xa3)
|
|
59
|
+
return 'webm';
|
|
60
|
+
if (ascii(0, 3) === 'ID3' || (buf[0] === 0xff && (buf[1] & 0xe0) === 0xe0))
|
|
61
|
+
return 'mp3';
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
/** Extensions that name the same format, so a .jpeg holding JPEG bytes is right. */
|
|
65
|
+
const EXT_ALIASES = { jpeg: 'jpg', htm: 'html', yml: 'yaml' };
|
|
66
|
+
const canonicalExt = (ext) => EXT_ALIASES[ext] ?? ext;
|
|
67
|
+
/** The format's name, for the note — "JPEG", not the "JPG" of its extension. */
|
|
68
|
+
const formatName = (ext) => (canonicalExt(ext) === 'jpg' ? 'JPEG' : ext.toUpperCase());
|
|
69
|
+
/** Save under an extension that matches the bytes. A file whose extension lies
|
|
70
|
+
* about its format is not a cosmetic problem: `page eval --camera` validates and
|
|
71
|
+
* dispatches on the extension, image tools and browsers sniff it, and an agent
|
|
72
|
+
* that Reads a "*.png" full of JPEG bytes has to stop and work out which one is
|
|
73
|
+
* lying. The caller's -o path is a request, not a fact — honour its directory and
|
|
74
|
+
* stem, and let the bytes name the format. */
|
|
75
|
+
function correctExtension(filename, buf) {
|
|
76
|
+
const actual = sniffExt(buf);
|
|
77
|
+
if (!actual)
|
|
78
|
+
return filename; // unrecognized bytes — nothing better to say
|
|
79
|
+
const dot = filename.lastIndexOf('.');
|
|
80
|
+
const asked = dot > 0 ? filename.slice(dot + 1).toLowerCase() : '';
|
|
81
|
+
if (canonicalExt(asked) === actual)
|
|
82
|
+
return filename;
|
|
83
|
+
const corrected = `${dot > 0 ? filename.slice(0, dot) : filename}.${actual}`;
|
|
84
|
+
console.error(muted(`Note: the model returned ${formatName(actual)}, not ${asked ? formatName(asked) : 'the requested format'} — `
|
|
85
|
+
+ `saving as ${corrected} so the extension matches the actual bytes.`));
|
|
86
|
+
return corrected;
|
|
87
|
+
}
|
|
34
88
|
/** Create the parent directory of an -o path, so `-o src/audio/ding.mp3` works
|
|
35
89
|
* in a tree that has no `src/audio/` yet instead of dying on a raw ENOENT.
|
|
36
90
|
*
|
|
@@ -114,10 +168,13 @@ Examples:
|
|
|
114
168
|
else {
|
|
115
169
|
const sizeKb = Math.round(result.size_bytes / 1024);
|
|
116
170
|
console.log(`${muted(`Generated with ${result.provider}/${result.model} (${sizeKb}KB)`)}`);
|
|
117
|
-
console.log(success(`Saved to ${savedPath}`));
|
|
118
171
|
if (result.seed !== undefined) {
|
|
119
172
|
console.log(muted(`Seed ${result.seed} — pass --seed ${result.seed} to keep the next image coherent`));
|
|
120
173
|
}
|
|
174
|
+
// Saved-path last: a caller reading a truncated tail of this output (an
|
|
175
|
+
// agent batching several generates in one shell call) must still see
|
|
176
|
+
// WHERE the bytes landed - that is the one line the next command needs.
|
|
177
|
+
console.log(success(`Saved to ${savedPath}`));
|
|
121
178
|
}
|
|
122
179
|
}
|
|
123
180
|
catch (err) {
|