@vosjs/cli 0.50.4 → 0.52.0
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 +14 -6
- package/dist/{chunk-GCWE75OA.js → chunk-3OIYR5VL.js} +27 -2
- package/dist/chunk-3OIYR5VL.js.map +1 -0
- package/dist/{chunk-D6AP7Y3G.js → chunk-F2QCWV4U.js} +684 -723
- package/dist/chunk-F2QCWV4U.js.map +1 -0
- package/dist/chunk-KSEU3YK5.js +776 -0
- package/dist/chunk-KSEU3YK5.js.map +1 -0
- package/dist/cli.js +175 -44
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +12 -12
- package/dist/{run-TA44SOE5.js → run-ASYY2YTV.js} +3 -3
- package/package.json +6 -6
- package/skills/VERSION +1 -1
- package/skills/product-video/SKILL.md +6 -1
- package/skills/vos-footage/SKILL.md +7 -5
- package/skills/vos-migrate/SKILL.md +34 -38
- package/dist/chunk-D6AP7Y3G.js.map +0 -1
- package/dist/chunk-FACYY7VV.js +0 -308
- package/dist/chunk-FACYY7VV.js.map +0 -1
- package/dist/chunk-GCWE75OA.js.map +0 -1
- /package/dist/{run-TA44SOE5.js.map → run-ASYY2YTV.js.map} +0 -0
|
@@ -1,19 +1,94 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
BrowserUnavailableError,
|
|
4
|
+
CREDENTIALS_PATH,
|
|
5
|
+
MEDIA_HEAD_BYTES,
|
|
6
|
+
SYNC_STATE_NAME,
|
|
7
|
+
TAKE_MEDIA_EXTENSIONS,
|
|
8
|
+
UsageError,
|
|
9
|
+
apiError,
|
|
10
|
+
apiJson,
|
|
11
|
+
clientId,
|
|
12
|
+
containerMismatch,
|
|
13
|
+
deriveSlug,
|
|
4
14
|
directoryKind,
|
|
15
|
+
extensionFor,
|
|
16
|
+
formatChanges,
|
|
17
|
+
hasFlag,
|
|
5
18
|
launchBrowser,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
19
|
+
nameForType,
|
|
20
|
+
numFlag2 as numFlag,
|
|
21
|
+
parseArgs2 as parseArgs,
|
|
22
|
+
parseVosId,
|
|
23
|
+
platformOrigin,
|
|
24
|
+
readSyncState,
|
|
25
|
+
requireCredential,
|
|
26
|
+
resolveCredential,
|
|
27
|
+
resolveMediaType,
|
|
28
|
+
sniffMediaType,
|
|
29
|
+
strFlag,
|
|
30
|
+
videoFormat,
|
|
31
|
+
writeCredential,
|
|
32
|
+
writeSyncState
|
|
33
|
+
} from "./chunk-KSEU3YK5.js";
|
|
9
34
|
import {
|
|
10
35
|
deadLine,
|
|
11
36
|
deadWarns
|
|
12
37
|
} from "./chunk-6XPFWQKP.js";
|
|
13
38
|
|
|
39
|
+
// src/plugin/landed.ts
|
|
40
|
+
var absolute = (origin, url) => typeof url === "string" && url ? /^https?:/.test(url) ? url : `${origin}${url.startsWith("/") ? "" : "/"}${url}` : null;
|
|
41
|
+
async function waitForLanded(opts) {
|
|
42
|
+
const origin = opts.origin.replace(/\/+$/, "");
|
|
43
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
44
|
+
const now = opts.now ?? (() => Date.now());
|
|
45
|
+
const timeoutMs = opts.timeoutMs ?? 3e5;
|
|
46
|
+
const intervalMs = opts.intervalMs ?? 4e3;
|
|
47
|
+
const started = now();
|
|
48
|
+
let still = false;
|
|
49
|
+
if (opts.versionId) {
|
|
50
|
+
opts.log?.("waiting for the still to render\u2026");
|
|
51
|
+
for (; ; ) {
|
|
52
|
+
const res = await fetch(
|
|
53
|
+
`${origin}/api/vos/${opts.vosId}/versions/${opts.versionId}/thumbnail`,
|
|
54
|
+
{ headers: { authorization: `Bearer ${opts.key}` } }
|
|
55
|
+
);
|
|
56
|
+
await res.arrayBuffer().catch(() => void 0);
|
|
57
|
+
if (res.ok) {
|
|
58
|
+
still = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
if (now() - started >= timeoutMs) break;
|
|
62
|
+
await sleep(intervalMs);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const meta = await apiJson(origin, `/api/vos/${opts.vosId}`, {
|
|
66
|
+
key: opts.key
|
|
67
|
+
});
|
|
68
|
+
const vos = meta.body.vos ?? {};
|
|
69
|
+
const urls = vos.contentUrls ?? {};
|
|
70
|
+
return {
|
|
71
|
+
still,
|
|
72
|
+
watchUrl: `${origin}/vos/${opts.vosId}`,
|
|
73
|
+
studioUrl: `${origin}/studio?vos=${opts.vosId}`,
|
|
74
|
+
thumbnailUrl: still ? absolute(origin, urls.thumbnail) : null,
|
|
75
|
+
previewUrl: still ? absolute(origin, urls.preview) : null,
|
|
76
|
+
waitedMs: now() - started
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function landedLines(l) {
|
|
80
|
+
if (!l.still) {
|
|
81
|
+
return ` still: not rendered yet (the fleet renders it after the push; open the watch page in a minute)`;
|
|
82
|
+
}
|
|
83
|
+
return [
|
|
84
|
+
l.thumbnailUrl ? ` still: ${l.thumbnailUrl}` : null,
|
|
85
|
+
l.previewUrl ? ` preview: ${l.previewUrl}` : null
|
|
86
|
+
].filter(Boolean).join("\n");
|
|
87
|
+
}
|
|
88
|
+
|
|
14
89
|
// src/plugin/run.ts
|
|
15
|
-
import { mkdir as mkdir11, readFile as
|
|
16
|
-
import { existsSync as
|
|
90
|
+
import { mkdir as mkdir11, readFile as readFile17, rm as rm5 } from "fs/promises";
|
|
91
|
+
import { existsSync as existsSync20 } from "fs";
|
|
17
92
|
import { join as join22, resolve as resolve10 } from "path";
|
|
18
93
|
import {
|
|
19
94
|
END_CARD_FROM as END_CARD_FROM2,
|
|
@@ -21,63 +96,6 @@ import {
|
|
|
21
96
|
migrateHostedDoc as migrateHostedDoc5
|
|
22
97
|
} from "@vosjs/studio-core";
|
|
23
98
|
|
|
24
|
-
// src/plugin/args.ts
|
|
25
|
-
var UsageError = class extends Error {
|
|
26
|
-
};
|
|
27
|
-
function parseArgs(argv, booleanFlags, multiFlags = /* @__PURE__ */ new Set()) {
|
|
28
|
-
const positionals = [];
|
|
29
|
-
const flags = {};
|
|
30
|
-
const multi = {};
|
|
31
|
-
const put = (name, value) => {
|
|
32
|
-
if (multiFlags.has(name)) (multi[name] ??= []).push(value);
|
|
33
|
-
else flags[name] = value;
|
|
34
|
-
};
|
|
35
|
-
for (let i = 0; i < argv.length; i++) {
|
|
36
|
-
const arg = argv[i];
|
|
37
|
-
if (arg === "--") {
|
|
38
|
-
positionals.push(...argv.slice(i + 1));
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
if (arg.startsWith("--")) {
|
|
42
|
-
const eq = arg.indexOf("=");
|
|
43
|
-
if (eq !== -1) {
|
|
44
|
-
put(arg.slice(2, eq), arg.slice(eq + 1));
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
const name = arg.slice(2);
|
|
48
|
-
if (booleanFlags.has(name)) {
|
|
49
|
-
flags[name] = true;
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (i + 1 >= argv.length || argv[i + 1].startsWith("--")) {
|
|
53
|
-
throw new UsageError(`--${name} expects a value`);
|
|
54
|
-
}
|
|
55
|
-
put(name, argv[i + 1]);
|
|
56
|
-
i++;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
positionals.push(arg);
|
|
60
|
-
}
|
|
61
|
-
for (const name of Object.keys(flags)) noteGivenFlag(name);
|
|
62
|
-
for (const name of Object.keys(multi)) noteGivenFlag(name);
|
|
63
|
-
return { positionals, flags: trackFlags(flags), multi: trackFlags(multi) };
|
|
64
|
-
}
|
|
65
|
-
function numFlag(flags, name, fallback) {
|
|
66
|
-
const v = flags[name];
|
|
67
|
-
if (typeof v !== "string") return fallback;
|
|
68
|
-
const n = Number(v);
|
|
69
|
-
if (!Number.isFinite(n))
|
|
70
|
-
throw new UsageError(`--${name} expects a number, got "${v}"`);
|
|
71
|
-
return n;
|
|
72
|
-
}
|
|
73
|
-
function strFlag(flags, name) {
|
|
74
|
-
const v = flags[name];
|
|
75
|
-
return typeof v === "string" ? v : void 0;
|
|
76
|
-
}
|
|
77
|
-
function hasFlag(flags, name) {
|
|
78
|
-
return Object.prototype.hasOwnProperty.call(flags, name);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
99
|
// src/plugin/output.ts
|
|
82
100
|
var EXIT_OK = 0;
|
|
83
101
|
var EXIT_ERROR = 1;
|
|
@@ -1634,111 +1652,14 @@ function framingWarnings(docIn, doc, zoom, overlays, warnings) {
|
|
|
1634
1652
|
}
|
|
1635
1653
|
}
|
|
1636
1654
|
|
|
1637
|
-
// src/plugin/container.ts
|
|
1638
|
-
var OCTET_STREAM = "application/octet-stream";
|
|
1639
|
-
var TYPES_BY_EXTENSION = {
|
|
1640
|
-
svg: "image/svg+xml",
|
|
1641
|
-
png: "image/png",
|
|
1642
|
-
jpg: "image/jpeg",
|
|
1643
|
-
jpeg: "image/jpeg",
|
|
1644
|
-
webp: "image/webp",
|
|
1645
|
-
gif: "image/gif",
|
|
1646
|
-
avif: "image/avif",
|
|
1647
|
-
webm: "video/webm",
|
|
1648
|
-
mp4: "video/mp4",
|
|
1649
|
-
mov: "video/quicktime",
|
|
1650
|
-
mp3: "audio/mpeg",
|
|
1651
|
-
wav: "audio/wav",
|
|
1652
|
-
ogg: "audio/ogg",
|
|
1653
|
-
m4a: "audio/mp4"
|
|
1654
|
-
};
|
|
1655
|
-
var EXTENSION_BY_TYPE = {
|
|
1656
|
-
"image/jpeg": ".jpg",
|
|
1657
|
-
"video/mp4": ".mp4",
|
|
1658
|
-
"audio/mp4": ".m4a"
|
|
1659
|
-
};
|
|
1660
|
-
var TAKE_MEDIA_EXTENSIONS = [
|
|
1661
|
-
".webm",
|
|
1662
|
-
".mp4",
|
|
1663
|
-
".mov",
|
|
1664
|
-
".m4a",
|
|
1665
|
-
".mp3",
|
|
1666
|
-
".ogg",
|
|
1667
|
-
".wav"
|
|
1668
|
-
];
|
|
1669
|
-
var MEDIA_HEAD_BYTES = 16;
|
|
1670
|
-
var ISO_BRANDS = {
|
|
1671
|
-
"qt ": "video/quicktime",
|
|
1672
|
-
"M4A ": "audio/mp4",
|
|
1673
|
-
"M4B ": "audio/mp4",
|
|
1674
|
-
avif: "image/avif",
|
|
1675
|
-
avis: "image/avif"
|
|
1676
|
-
};
|
|
1677
|
-
var RIFF_FORMS = {
|
|
1678
|
-
WEBP: "image/webp",
|
|
1679
|
-
WAVE: "audio/wav",
|
|
1680
|
-
"AVI ": "video/x-msvideo"
|
|
1681
|
-
};
|
|
1682
|
-
function sniffMediaType(head) {
|
|
1683
|
-
const ascii = (at2, len) => head.length >= at2 + len ? String.fromCharCode(...head.subarray(at2, at2 + len)) : "";
|
|
1684
|
-
const startsWith = (...bytes) => bytes.every((b, i) => head[i] === b);
|
|
1685
|
-
if (startsWith(26, 69, 223, 163)) return "video/webm";
|
|
1686
|
-
if (ascii(4, 4) === "ftyp") return ISO_BRANDS[ascii(8, 4)] ?? "video/mp4";
|
|
1687
|
-
if (ascii(0, 4) === "RIFF") return RIFF_FORMS[ascii(8, 4)] ?? null;
|
|
1688
|
-
if (startsWith(137, 80, 78, 71, 13, 10, 26, 10))
|
|
1689
|
-
return "image/png";
|
|
1690
|
-
if (startsWith(255, 216, 255)) return "image/jpeg";
|
|
1691
|
-
if (ascii(0, 4) === "GIF8") return "image/gif";
|
|
1692
|
-
if (ascii(0, 4) === "OggS") return "audio/ogg";
|
|
1693
|
-
if (ascii(0, 3) === "ID3") return "audio/mpeg";
|
|
1694
|
-
return null;
|
|
1695
|
-
}
|
|
1696
|
-
function bareType(value) {
|
|
1697
|
-
const type = (value ?? "").split(";")[0].trim().toLowerCase();
|
|
1698
|
-
return !type || type === OCTET_STREAM || type === "binary/octet-stream" ? "" : type;
|
|
1699
|
-
}
|
|
1700
|
-
function mediaContentType(file) {
|
|
1701
|
-
const ext = (/\.([a-z0-9]+)$/i.exec(file)?.[1] ?? "").toLowerCase();
|
|
1702
|
-
return TYPES_BY_EXTENSION[ext] ?? OCTET_STREAM;
|
|
1703
|
-
}
|
|
1704
|
-
function extensionFor(contentType) {
|
|
1705
|
-
const type = (contentType ?? "").split(";")[0].trim().toLowerCase();
|
|
1706
|
-
if (EXTENSION_BY_TYPE[type]) return EXTENSION_BY_TYPE[type];
|
|
1707
|
-
const ext = Object.keys(TYPES_BY_EXTENSION).find(
|
|
1708
|
-
(e) => TYPES_BY_EXTENSION[e] === type
|
|
1709
|
-
);
|
|
1710
|
-
return ext ? `.${ext}` : "";
|
|
1711
|
-
}
|
|
1712
|
-
function resolveMediaType(claims) {
|
|
1713
|
-
const sniffed = claims.head?.length ? sniffMediaType(claims.head) : null;
|
|
1714
|
-
if (sniffed) return { type: sniffed, from: "bytes" };
|
|
1715
|
-
const declared = bareType(claims.declared);
|
|
1716
|
-
if (declared) return { type: declared, from: "declared" };
|
|
1717
|
-
const named = mediaContentType(claims.filename ?? "");
|
|
1718
|
-
if (named !== OCTET_STREAM) return { type: named, from: "filename" };
|
|
1719
|
-
return { type: OCTET_STREAM, from: "unknown" };
|
|
1720
|
-
}
|
|
1721
|
-
function nameForType(filename, type) {
|
|
1722
|
-
const ext = extensionFor(type);
|
|
1723
|
-
if (!ext) return filename;
|
|
1724
|
-
const current = /\.[a-z0-9]+$/i.exec(filename)?.[0] ?? "";
|
|
1725
|
-
if (current && mediaContentType(`x${current}`) === type) return filename;
|
|
1726
|
-
return `${filename.slice(0, filename.length - current.length)}${ext}`;
|
|
1727
|
-
}
|
|
1728
|
-
function containerMismatch(filename, head) {
|
|
1729
|
-
const actual = sniffMediaType(head);
|
|
1730
|
-
if (!actual) return null;
|
|
1731
|
-
const claimed = mediaContentType(filename);
|
|
1732
|
-
if (claimed === actual) return null;
|
|
1733
|
-
return `${filename} is ${actual}, not the ${claimed} its name claims \u2014 rename it to ${nameForType(filename, actual)}`;
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
1655
|
// src/plugin/media.ts
|
|
1737
1656
|
import { isKeyedOverlay } from "@vosjs/studio-core";
|
|
1657
|
+
import { createHash } from "crypto";
|
|
1738
1658
|
import { createWriteStream, existsSync as existsSync2 } from "fs";
|
|
1739
1659
|
import {
|
|
1740
1660
|
mkdir as mkdir2,
|
|
1741
1661
|
open,
|
|
1662
|
+
readFile as readFile2,
|
|
1742
1663
|
readdir,
|
|
1743
1664
|
rename as rename2,
|
|
1744
1665
|
rm as rm2,
|
|
@@ -1847,6 +1768,150 @@ async function loadTake(dir) {
|
|
|
1847
1768
|
return { paths, cursor, meta, actions, doc };
|
|
1848
1769
|
}
|
|
1849
1770
|
|
|
1771
|
+
// src/plugin/uploadAsset.ts
|
|
1772
|
+
var SINGLE_SHOT_MAX_BYTES = 32 * 1024 * 1024;
|
|
1773
|
+
var PART_ATTEMPTS = 3;
|
|
1774
|
+
function planParts(totalBytes, partBytes) {
|
|
1775
|
+
if (totalBytes <= 0 || partBytes <= 0) return [];
|
|
1776
|
+
const parts = [];
|
|
1777
|
+
for (let start = 0; start < totalBytes; start += partBytes) {
|
|
1778
|
+
parts.push({
|
|
1779
|
+
partNumber: parts.length + 1,
|
|
1780
|
+
start,
|
|
1781
|
+
end: Math.min(start + partBytes, totalBytes)
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
return parts;
|
|
1785
|
+
}
|
|
1786
|
+
function failed(what, r) {
|
|
1787
|
+
const detail = typeof r.body.error === "string" ? r.body.error : "";
|
|
1788
|
+
return new Error(`${what} (${r.status})${detail ? `: ${detail}` : ""}`);
|
|
1789
|
+
}
|
|
1790
|
+
function asAsset(body) {
|
|
1791
|
+
return {
|
|
1792
|
+
id: String(body.id),
|
|
1793
|
+
url: String(body.url),
|
|
1794
|
+
size: typeof body.size === "number" ? body.size : 0,
|
|
1795
|
+
reused: body.reused === true
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
function transient(status) {
|
|
1799
|
+
return status >= 500 || status === 429;
|
|
1800
|
+
}
|
|
1801
|
+
var wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1802
|
+
async function uploadAsset(target, bytes, opts) {
|
|
1803
|
+
if (bytes.length > SINGLE_SHOT_MAX_BYTES && opts.folderId) {
|
|
1804
|
+
throw new Error(
|
|
1805
|
+
`${opts.filename} is over ${SINGLE_SHOT_MAX_BYTES / 1024 / 1024} MB, and a file that large cannot be filed into a project on upload yet. Push it without --folder and reference it from a document, which keeps it`
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
return bytes.length > SINGLE_SHOT_MAX_BYTES ? uploadInParts(target, bytes, opts) : uploadWhole(target, bytes, opts);
|
|
1809
|
+
}
|
|
1810
|
+
async function uploadWhole(target, bytes, opts) {
|
|
1811
|
+
const res = await apiJson(target.origin, "/api/assets/recording", {
|
|
1812
|
+
method: "POST",
|
|
1813
|
+
key: target.key,
|
|
1814
|
+
headers: {
|
|
1815
|
+
"Content-Type": opts.contentType,
|
|
1816
|
+
"Content-Length": String(bytes.length),
|
|
1817
|
+
"X-Filename": opts.filename,
|
|
1818
|
+
"X-Content-Hash": opts.contentHash,
|
|
1819
|
+
...opts.durationSeconds && opts.durationSeconds > 0 ? { "X-Content-Duration": opts.durationSeconds.toFixed(3) } : {},
|
|
1820
|
+
...opts.folderId ? { "X-Folder-Id": opts.folderId } : {}
|
|
1821
|
+
},
|
|
1822
|
+
raw: bytes
|
|
1823
|
+
});
|
|
1824
|
+
if (res.status !== 201 && res.status !== 200)
|
|
1825
|
+
throw failed("upload failed", res);
|
|
1826
|
+
return asAsset(res.body);
|
|
1827
|
+
}
|
|
1828
|
+
async function uploadInParts(target, bytes, opts) {
|
|
1829
|
+
const begun = await apiJson(
|
|
1830
|
+
target.origin,
|
|
1831
|
+
"/api/assets/recording/multipart",
|
|
1832
|
+
{
|
|
1833
|
+
method: "POST",
|
|
1834
|
+
key: target.key,
|
|
1835
|
+
body: {
|
|
1836
|
+
filename: opts.filename,
|
|
1837
|
+
contentType: opts.contentType,
|
|
1838
|
+
size: bytes.length,
|
|
1839
|
+
contentHash: opts.contentHash,
|
|
1840
|
+
...opts.durationSeconds && opts.durationSeconds > 0 ? { durationSeconds: opts.durationSeconds } : {}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
);
|
|
1844
|
+
if (begun.status !== 201 && begun.status !== 200) {
|
|
1845
|
+
throw failed("upload failed", begun);
|
|
1846
|
+
}
|
|
1847
|
+
if (begun.body.reused === true || typeof begun.body.url === "string") {
|
|
1848
|
+
return asAsset(begun.body);
|
|
1849
|
+
}
|
|
1850
|
+
const uploadId = String(begun.body.id);
|
|
1851
|
+
const partBytes = Number(begun.body.partBytes);
|
|
1852
|
+
if (!Number.isFinite(partBytes) || partBytes <= 0) {
|
|
1853
|
+
throw new Error("upload failed: the platform returned no part size");
|
|
1854
|
+
}
|
|
1855
|
+
const parts = planParts(bytes.length, partBytes);
|
|
1856
|
+
const etags = [];
|
|
1857
|
+
try {
|
|
1858
|
+
for (const part of parts) {
|
|
1859
|
+
const chunk2 = bytes.subarray(part.start, part.end);
|
|
1860
|
+
let sent = null;
|
|
1861
|
+
for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt++) {
|
|
1862
|
+
try {
|
|
1863
|
+
sent = await apiJson(
|
|
1864
|
+
target.origin,
|
|
1865
|
+
`/api/assets/recording/multipart/${uploadId}/parts/${part.partNumber}`,
|
|
1866
|
+
{
|
|
1867
|
+
method: "PUT",
|
|
1868
|
+
key: target.key,
|
|
1869
|
+
headers: {
|
|
1870
|
+
"Content-Type": "application/octet-stream",
|
|
1871
|
+
"Content-Length": String(chunk2.length)
|
|
1872
|
+
},
|
|
1873
|
+
raw: chunk2
|
|
1874
|
+
}
|
|
1875
|
+
);
|
|
1876
|
+
} catch (networkError) {
|
|
1877
|
+
if (attempt === PART_ATTEMPTS) throw networkError;
|
|
1878
|
+
await wait(500 * 2 ** (attempt - 1));
|
|
1879
|
+
continue;
|
|
1880
|
+
}
|
|
1881
|
+
if (sent.status === 200) break;
|
|
1882
|
+
if (!transient(sent.status) || attempt === PART_ATTEMPTS) {
|
|
1883
|
+
throw failed(`part ${part.partNumber} failed`, sent);
|
|
1884
|
+
}
|
|
1885
|
+
await wait(500 * 2 ** (attempt - 1));
|
|
1886
|
+
}
|
|
1887
|
+
if (!sent || sent.status !== 200) {
|
|
1888
|
+
throw new Error(`part ${part.partNumber} failed`);
|
|
1889
|
+
}
|
|
1890
|
+
etags.push({
|
|
1891
|
+
partNumber: Number(sent.body.partNumber),
|
|
1892
|
+
etag: String(sent.body.etag)
|
|
1893
|
+
});
|
|
1894
|
+
opts.onPart?.(etags.length, parts.length);
|
|
1895
|
+
}
|
|
1896
|
+
const sealed = await apiJson(
|
|
1897
|
+
target.origin,
|
|
1898
|
+
`/api/assets/recording/multipart/${uploadId}/complete`,
|
|
1899
|
+
{ method: "POST", key: target.key, body: { parts: etags } }
|
|
1900
|
+
);
|
|
1901
|
+
if (sealed.status !== 201 && sealed.status !== 200) {
|
|
1902
|
+
throw failed("upload failed", sealed);
|
|
1903
|
+
}
|
|
1904
|
+
return asAsset(sealed.body);
|
|
1905
|
+
} catch (err) {
|
|
1906
|
+
await apiJson(
|
|
1907
|
+
target.origin,
|
|
1908
|
+
`/api/assets/recording/multipart/${uploadId}`,
|
|
1909
|
+
{ method: "DELETE", key: target.key }
|
|
1910
|
+
).catch(() => void 0);
|
|
1911
|
+
throw err;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1850
1915
|
// src/plugin/media.ts
|
|
1851
1916
|
var SOURCE_MEDIA = [
|
|
1852
1917
|
{ key: "videoKey", stem: "recording", fallbackExt: ".webm" },
|
|
@@ -1915,8 +1980,58 @@ function docMediaRefs(doc, keep = isTakeRelativeKey) {
|
|
|
1915
1980
|
bg.key = next;
|
|
1916
1981
|
}
|
|
1917
1982
|
});
|
|
1983
|
+
out.push(...docAudioRefs(doc, keep));
|
|
1984
|
+
return out;
|
|
1985
|
+
}
|
|
1986
|
+
function docAudioRefs(doc, keep = isTakeRelativeKey) {
|
|
1987
|
+
const out = [];
|
|
1988
|
+
for (const clip of doc.audio ?? []) {
|
|
1989
|
+
if (!keep(clip.key)) continue;
|
|
1990
|
+
out.push({
|
|
1991
|
+
where: `audio ${clip.id}`,
|
|
1992
|
+
key: clip.key,
|
|
1993
|
+
set: (next) => {
|
|
1994
|
+
clip.key = next;
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1918
1998
|
return out;
|
|
1919
1999
|
}
|
|
2000
|
+
async function uploadDocRefs(refs, dir, target, log) {
|
|
2001
|
+
for (const ref of refs) {
|
|
2002
|
+
const file = join2(dir, takeRelativeFile(ref.key));
|
|
2003
|
+
if (!existsSync2(file)) {
|
|
2004
|
+
log(` ${ref.where}: ${ref.key} is not beside the document \u2014 left as is`);
|
|
2005
|
+
continue;
|
|
2006
|
+
}
|
|
2007
|
+
const media = await readFile2(file);
|
|
2008
|
+
const mediaHash = createHash("sha256").update(media).digest("hex");
|
|
2009
|
+
const { type } = resolveMediaType({
|
|
2010
|
+
head: media.subarray(0, MEDIA_HEAD_BYTES),
|
|
2011
|
+
filename: file
|
|
2012
|
+
});
|
|
2013
|
+
const name = nameForType(basename2(file), type);
|
|
2014
|
+
if (name !== basename2(file)) {
|
|
2015
|
+
log(` ${ref.where}: ${basename2(file)} holds ${type} \u2014 sent as ${name}`);
|
|
2016
|
+
}
|
|
2017
|
+
let put;
|
|
2018
|
+
try {
|
|
2019
|
+
put = await uploadAsset(target, new Uint8Array(media), {
|
|
2020
|
+
filename: name,
|
|
2021
|
+
contentType: type,
|
|
2022
|
+
contentHash: mediaHash
|
|
2023
|
+
});
|
|
2024
|
+
} catch (e) {
|
|
2025
|
+
throw new Error(
|
|
2026
|
+
`${ref.where} upload failed: ${e instanceof Error ? e.message : String(e)}`
|
|
2027
|
+
);
|
|
2028
|
+
}
|
|
2029
|
+
ref.set(put.url);
|
|
2030
|
+
log(
|
|
2031
|
+
` ${ref.where}: ${ref.key} \u2192 asset ${put.id}${put.reused ? " (reused)" : ""}`
|
|
2032
|
+
);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
1920
2035
|
function assetIdOf(url) {
|
|
1921
2036
|
if (!url) return null;
|
|
1922
2037
|
const m = /\/api\/assets\/([A-Za-z0-9_-]+)\/file/.exec(url);
|
|
@@ -2105,7 +2220,7 @@ function mediaProbeLints(results) {
|
|
|
2105
2220
|
}
|
|
2106
2221
|
|
|
2107
2222
|
// src/plugin/framesTake.ts
|
|
2108
|
-
import { mkdir as mkdir4, readFile as
|
|
2223
|
+
import { mkdir as mkdir4, readFile as readFile5, rename as rename4, writeFile as writeFile5 } from "fs/promises";
|
|
2109
2224
|
import { existsSync as existsSync5 } from "fs";
|
|
2110
2225
|
import { join as join5, resolve as resolve2 } from "path";
|
|
2111
2226
|
import { compileVosConfig } from "@vosjs/core";
|
|
@@ -2246,7 +2361,7 @@ async function waitForPageDone(page, label, onProgress, timeoutMs) {
|
|
|
2246
2361
|
}
|
|
2247
2362
|
|
|
2248
2363
|
// src/plugin/renderAnimation.ts
|
|
2249
|
-
import { readFile as
|
|
2364
|
+
import { readFile as readFile3, rm as rm3 } from "fs/promises";
|
|
2250
2365
|
import { tweenRuntimeCode } from "@vosjs/tween/bundle";
|
|
2251
2366
|
import { elementsBundleCode } from "@vosjs/elements/bundle";
|
|
2252
2367
|
import { concatEncodedVideo, planChunks } from "@vosjs/render-core";
|
|
@@ -2541,7 +2656,7 @@ async function renderAnimation(browser, opts) {
|
|
|
2541
2656
|
)
|
|
2542
2657
|
);
|
|
2543
2658
|
const files = await Promise.all(
|
|
2544
|
-
outNames.map((name) =>
|
|
2659
|
+
outNames.map((name) => readFile3(`${opts.workDir}/${name}`))
|
|
2545
2660
|
);
|
|
2546
2661
|
await Promise.all(
|
|
2547
2662
|
outNames.map((name) => rm3(`${opts.workDir}/${name}`, { force: true }))
|
|
@@ -2799,7 +2914,7 @@ function applyAndValidate(doc, o) {
|
|
|
2799
2914
|
}
|
|
2800
2915
|
|
|
2801
2916
|
// src/plugin/digestTake.ts
|
|
2802
|
-
import { mkdir as mkdir3, readFile as
|
|
2917
|
+
import { mkdir as mkdir3, readFile as readFile4, rename as rename3, writeFile as writeFile4 } from "fs/promises";
|
|
2803
2918
|
import { existsSync as existsSync4 } from "fs";
|
|
2804
2919
|
import { join as join4, resolve } from "path";
|
|
2805
2920
|
import {
|
|
@@ -3199,7 +3314,7 @@ async function discoverTranscript(dir) {
|
|
|
3199
3314
|
const file = join4(dir, "transcript.json");
|
|
3200
3315
|
if (!existsSync4(file)) return null;
|
|
3201
3316
|
try {
|
|
3202
|
-
return parseTranscript(JSON.parse(await
|
|
3317
|
+
return parseTranscript(JSON.parse(await readFile4(file, "utf8")));
|
|
3203
3318
|
} catch {
|
|
3204
3319
|
return null;
|
|
3205
3320
|
}
|
|
@@ -3207,7 +3322,7 @@ async function discoverTranscript(dir) {
|
|
|
3207
3322
|
async function readPriorDigest(outDir) {
|
|
3208
3323
|
try {
|
|
3209
3324
|
const raw = JSON.parse(
|
|
3210
|
-
await
|
|
3325
|
+
await readFile4(join4(outDir, "digest.json"), "utf8")
|
|
3211
3326
|
);
|
|
3212
3327
|
return raw.digestVersion === DIGEST_VERSION && Array.isArray(raw.moments) ? raw : null;
|
|
3213
3328
|
} catch {
|
|
@@ -3216,7 +3331,7 @@ async function readPriorDigest(outDir) {
|
|
|
3216
3331
|
}
|
|
3217
3332
|
async function pngSize(file) {
|
|
3218
3333
|
try {
|
|
3219
|
-
const buf = await
|
|
3334
|
+
const buf = await readFile4(file);
|
|
3220
3335
|
if (buf.length < 24 || buf.readUInt32BE(0) !== 2303741511) return null;
|
|
3221
3336
|
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
|
|
3222
3337
|
} catch {
|
|
@@ -3456,7 +3571,7 @@ async function momentsFor(dir, doc) {
|
|
|
3456
3571
|
const file = join5(dir, DIGEST_DIR, "digest.json");
|
|
3457
3572
|
if (existsSync5(file)) {
|
|
3458
3573
|
try {
|
|
3459
|
-
const d = JSON.parse(await
|
|
3574
|
+
const d = JSON.parse(await readFile5(file, "utf8"));
|
|
3460
3575
|
if (Array.isArray(d.moments)) return d.moments;
|
|
3461
3576
|
} catch {
|
|
3462
3577
|
}
|
|
@@ -3496,9 +3611,9 @@ async function writeIndexJson(result) {
|
|
|
3496
3611
|
// src/plugin/deliver.ts
|
|
3497
3612
|
import { mkdir as mkdir5, mkdtemp, rename as rename5, rm as rm4, stat as stat2, writeFile as writeFile7 } from "fs/promises";
|
|
3498
3613
|
import { tmpdir } from "os";
|
|
3499
|
-
import { join as
|
|
3614
|
+
import { join as join7, relative, resolve as resolve4 } from "path";
|
|
3500
3615
|
import { existsSync as existsSync7 } from "fs";
|
|
3501
|
-
import { readFile as
|
|
3616
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
3502
3617
|
import { parseFrontmatter } from "@vosjs/shared/frontmatter";
|
|
3503
3618
|
import {
|
|
3504
3619
|
CHANNEL_SPECS_VERIFIED,
|
|
@@ -4478,176 +4593,15 @@ function isLightHexGround(hex2) {
|
|
|
4478
4593
|
|
|
4479
4594
|
// src/plugin/posterDoc.ts
|
|
4480
4595
|
import { existsSync as existsSync6 } from "fs";
|
|
4481
|
-
import { readFile as
|
|
4482
|
-
import { isAbsolute, join as
|
|
4596
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
4597
|
+
import { isAbsolute, join as join6, resolve as resolve3 } from "path";
|
|
4483
4598
|
import {
|
|
4484
4599
|
computeCardLayout,
|
|
4485
|
-
docStillTime as docStillTime2,
|
|
4486
|
-
migrateHostedDoc,
|
|
4487
|
-
overlayRect,
|
|
4488
|
-
resolveOverlayStyle
|
|
4489
|
-
} from "@vosjs/studio-core";
|
|
4490
|
-
|
|
4491
|
-
// src/plugin/platform.ts
|
|
4492
|
-
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
4493
|
-
import { homedir } from "os";
|
|
4494
|
-
import { join as join6 } from "path";
|
|
4495
|
-
function platformOrigin(flags = {}) {
|
|
4496
|
-
const legacyEnv = process.env.VOS_API_BASE?.trim();
|
|
4497
|
-
const raw = flags.origin ?? flags.api ?? process.env.VOS_ORIGIN?.trim() ?? legacyEnv ?? "https://vos.so";
|
|
4498
|
-
if (!flags.origin && !flags.api && !process.env.VOS_ORIGIN && legacyEnv) {
|
|
4499
|
-
process.stderr.write(
|
|
4500
|
-
"note: VOS_API_BASE is deprecated \u2014 set VOS_ORIGIN (an origin, no /api suffix)\n"
|
|
4501
|
-
);
|
|
4502
|
-
}
|
|
4503
|
-
return raw.replace(/\/+$/, "").replace(/\/api$/, "");
|
|
4504
|
-
}
|
|
4505
|
-
function parseVosId(input) {
|
|
4506
|
-
if (!/^https?:\/\//.test(input)) {
|
|
4507
|
-
if (/^[A-Za-z0-9_-]+$/.test(input)) return input;
|
|
4508
|
-
throw new UsageError(`"${input}" is not a vos id or URL`);
|
|
4509
|
-
}
|
|
4510
|
-
let url;
|
|
4511
|
-
try {
|
|
4512
|
-
url = new URL(input);
|
|
4513
|
-
} catch {
|
|
4514
|
-
throw new UsageError(`"${input}" is not a valid URL`);
|
|
4515
|
-
}
|
|
4516
|
-
const query = url.searchParams.get("vos");
|
|
4517
|
-
if (query) return query;
|
|
4518
|
-
const path = url.pathname.match(/\/vos\/([A-Za-z0-9_-]+)/);
|
|
4519
|
-
if (path) return path[1];
|
|
4520
|
-
throw new UsageError(
|
|
4521
|
-
`could not find a vos id in ${input} \u2014 expected /vos/{id} or ?vos={id}`
|
|
4522
|
-
);
|
|
4523
|
-
}
|
|
4524
|
-
function deriveSlug(title) {
|
|
4525
|
-
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 50).replace(/-+$/, "") || "remix";
|
|
4526
|
-
}
|
|
4527
|
-
var CREDENTIALS_PATH = join6(homedir(), ".config", "vos", "credentials");
|
|
4528
|
-
function resolveCredential(explicit) {
|
|
4529
|
-
const flag = explicit?.trim();
|
|
4530
|
-
if (flag) return flag;
|
|
4531
|
-
const env = process.env.VOS_API_KEY?.trim();
|
|
4532
|
-
if (env) return env;
|
|
4533
|
-
try {
|
|
4534
|
-
const first = readFileSync2(CREDENTIALS_PATH, "utf8").split("\n")[0].trim();
|
|
4535
|
-
return first || null;
|
|
4536
|
-
} catch {
|
|
4537
|
-
return null;
|
|
4538
|
-
}
|
|
4539
|
-
}
|
|
4540
|
-
function requireCredential(explicit) {
|
|
4541
|
-
const key = resolveCredential(explicit);
|
|
4542
|
-
if (!key) {
|
|
4543
|
-
throw new Error(
|
|
4544
|
-
"no credential found \u2014 pass --key, set VOS_API_KEY, or run `vos login` (mint a content key at https://vos.so/app/api; a vos_rg_ remix grant works too)"
|
|
4545
|
-
);
|
|
4546
|
-
}
|
|
4547
|
-
return key;
|
|
4548
|
-
}
|
|
4549
|
-
function writeCredential(key) {
|
|
4550
|
-
mkdirSync(join6(homedir(), ".config", "vos"), { recursive: true, mode: 448 });
|
|
4551
|
-
writeFileSync(CREDENTIALS_PATH, `${key.trim()}
|
|
4552
|
-
`, { mode: 384 });
|
|
4553
|
-
return CREDENTIALS_PATH;
|
|
4554
|
-
}
|
|
4555
|
-
function clientId() {
|
|
4556
|
-
const env = process.env.VOS_CLIENT?.trim();
|
|
4557
|
-
if (env && env.length <= 60 && !/[\r\n]/.test(env)) return env;
|
|
4558
|
-
return "vos-cli";
|
|
4559
|
-
}
|
|
4560
|
-
async function apiJson(origin, path, init = {}) {
|
|
4561
|
-
const headers = {
|
|
4562
|
-
accept: "application/json",
|
|
4563
|
-
...init.headers
|
|
4564
|
-
};
|
|
4565
|
-
if (init.key) headers.authorization = `Bearer ${init.key}`;
|
|
4566
|
-
if (init.body !== void 0) headers["content-type"] = "application/json";
|
|
4567
|
-
const reqBody = init.raw !== void 0 ? init.raw : init.body === void 0 ? void 0 : JSON.stringify(init.body);
|
|
4568
|
-
const res = await fetch(`${origin}${path}`, {
|
|
4569
|
-
method: init.method ?? "GET",
|
|
4570
|
-
headers,
|
|
4571
|
-
body: reqBody
|
|
4572
|
-
});
|
|
4573
|
-
let body = {};
|
|
4574
|
-
try {
|
|
4575
|
-
body = await res.json();
|
|
4576
|
-
} catch {
|
|
4577
|
-
}
|
|
4578
|
-
return { status: res.status, body };
|
|
4579
|
-
}
|
|
4580
|
-
function apiError(what, r) {
|
|
4581
|
-
const detail = typeof r.body.error === "string" ? r.body.error : r.body.details !== void 0 ? JSON.stringify(r.body.details) : "";
|
|
4582
|
-
const hint = r.status === 401 ? " (run `vos login`, or set VOS_API_KEY \u2014 mint a key at https://vos.so/app/api)" : "";
|
|
4583
|
-
return `${what} \u2192 ${r.status}${detail ? `: ${detail}` : ""}${hint}`;
|
|
4584
|
-
}
|
|
4585
|
-
var SYNC_STATE_NAME = "vos.json";
|
|
4586
|
-
function readJsonFile(file) {
|
|
4587
|
-
try {
|
|
4588
|
-
return JSON.parse(readFileSync2(file, "utf8"));
|
|
4589
|
-
} catch {
|
|
4590
|
-
return null;
|
|
4591
|
-
}
|
|
4592
|
-
}
|
|
4593
|
-
function readSyncState(dir) {
|
|
4594
|
-
const own = readJsonFile(join6(dir, SYNC_STATE_NAME));
|
|
4595
|
-
if (own && typeof own.vosId === "string") {
|
|
4596
|
-
return {
|
|
4597
|
-
vosId: own.vosId,
|
|
4598
|
-
versionId: typeof own.versionId === "string" ? own.versionId : null,
|
|
4599
|
-
...typeof own.pushedAt === "string" ? { pushedAt: own.pushedAt } : {},
|
|
4600
|
-
...typeof own.title === "string" ? { title: own.title } : {},
|
|
4601
|
-
...typeof own.slug === "string" ? { slug: own.slug } : {},
|
|
4602
|
-
...typeof own.remixOfId === "string" ? { remixOfId: own.remixOfId } : {}
|
|
4603
|
-
};
|
|
4604
|
-
}
|
|
4605
|
-
const push = readJsonFile(join6(dir, "push.json"));
|
|
4606
|
-
if (push && typeof push.vosId === "string") {
|
|
4607
|
-
return {
|
|
4608
|
-
vosId: push.vosId,
|
|
4609
|
-
versionId: typeof push.versionId === "string" ? push.versionId : null,
|
|
4610
|
-
...typeof push.pushedAt === "string" ? { pushedAt: push.pushedAt } : {}
|
|
4611
|
-
};
|
|
4612
|
-
}
|
|
4613
|
-
const meta = readJsonFile(join6(dir, "meta.json"));
|
|
4614
|
-
if (meta && typeof meta.id === "string") {
|
|
4615
|
-
return {
|
|
4616
|
-
vosId: meta.id,
|
|
4617
|
-
versionId: typeof meta.currentVersionId === "string" ? meta.currentVersionId : null,
|
|
4618
|
-
...typeof meta.title === "string" ? { title: meta.title } : {},
|
|
4619
|
-
...typeof meta.slug === "string" ? { slug: meta.slug } : {},
|
|
4620
|
-
...typeof meta.remixOfId === "string" ? { remixOfId: meta.remixOfId } : {}
|
|
4621
|
-
};
|
|
4622
|
-
}
|
|
4623
|
-
return null;
|
|
4624
|
-
}
|
|
4625
|
-
function writeSyncState(dir, patch) {
|
|
4626
|
-
const merged = {
|
|
4627
|
-
...readSyncState(dir) ?? { versionId: null },
|
|
4628
|
-
...patch
|
|
4629
|
-
};
|
|
4630
|
-
writeFileSync(
|
|
4631
|
-
join6(dir, SYNC_STATE_NAME),
|
|
4632
|
-
`${JSON.stringify(merged, null, 2)}
|
|
4633
|
-
`
|
|
4634
|
-
);
|
|
4635
|
-
return merged;
|
|
4636
|
-
}
|
|
4637
|
-
function formatChanges(changes) {
|
|
4638
|
-
const lines = [];
|
|
4639
|
-
for (const ch of changes) {
|
|
4640
|
-
const label = ch.label ? ` \xB7 ${ch.label}` : "";
|
|
4641
|
-
const who = ch.origin === "studio" ? "human" : ch.origin ?? "unknown";
|
|
4642
|
-
lines.push(
|
|
4643
|
-
`v${String(ch.versionNumber ?? "?")} (${who}${label}): ${ch.summary ?? ""}`
|
|
4644
|
-
);
|
|
4645
|
-
if (ch.note) lines.push(` note: ${ch.note}`);
|
|
4646
|
-
}
|
|
4647
|
-
return lines;
|
|
4648
|
-
}
|
|
4649
|
-
|
|
4650
|
-
// src/plugin/posterDoc.ts
|
|
4600
|
+
docStillTime as docStillTime2,
|
|
4601
|
+
migrateHostedDoc,
|
|
4602
|
+
overlayRect,
|
|
4603
|
+
resolveOverlayStyle
|
|
4604
|
+
} from "@vosjs/studio-core";
|
|
4651
4605
|
var POSTER_CLASSES = [
|
|
4652
4606
|
"landscape",
|
|
4653
4607
|
"square",
|
|
@@ -4667,8 +4621,8 @@ var roleFor = (cls) => `poster-${cls}`;
|
|
|
4667
4621
|
async function readPosterAt(cls, path, takeDir, from) {
|
|
4668
4622
|
let file = path;
|
|
4669
4623
|
let docDir = path;
|
|
4670
|
-
if (existsSync6(
|
|
4671
|
-
file =
|
|
4624
|
+
if (existsSync6(join6(path, "doc.json"))) {
|
|
4625
|
+
file = join6(path, "doc.json");
|
|
4672
4626
|
} else if (!/\.json$/i.test(path) || !existsSync6(path)) {
|
|
4673
4627
|
return null;
|
|
4674
4628
|
} else {
|
|
@@ -4676,7 +4630,7 @@ async function readPosterAt(cls, path, takeDir, from) {
|
|
|
4676
4630
|
}
|
|
4677
4631
|
let raw;
|
|
4678
4632
|
try {
|
|
4679
|
-
raw = JSON.parse(await
|
|
4633
|
+
raw = JSON.parse(await readFile6(file, "utf8"));
|
|
4680
4634
|
} catch {
|
|
4681
4635
|
return null;
|
|
4682
4636
|
}
|
|
@@ -4686,7 +4640,7 @@ async function readPosterAt(cls, path, takeDir, from) {
|
|
|
4686
4640
|
const doc = migrateHostedDoc(
|
|
4687
4641
|
raw
|
|
4688
4642
|
);
|
|
4689
|
-
const ownFootage = existsSync6(
|
|
4643
|
+
const ownFootage = existsSync6(join6(docDir, "meta.json")) && existsSync6(join6(docDir, source.videoKey));
|
|
4690
4644
|
const sync = readSyncState(docDir);
|
|
4691
4645
|
return {
|
|
4692
4646
|
cls,
|
|
@@ -4709,10 +4663,10 @@ async function findPosterDocs(takeDir, launchRoles) {
|
|
|
4709
4663
|
candidates.push({ path: at2(named.trim()), from: `LAUNCH.md ${role}` });
|
|
4710
4664
|
}
|
|
4711
4665
|
candidates.push({
|
|
4712
|
-
path:
|
|
4666
|
+
path: join6(takeDir, "poster", cls),
|
|
4713
4667
|
from: `poster/${cls}`
|
|
4714
4668
|
});
|
|
4715
|
-
candidates.push({ path:
|
|
4669
|
+
candidates.push({ path: join6(takeDir, "poster"), from: "poster" });
|
|
4716
4670
|
for (const c of candidates) {
|
|
4717
4671
|
const ref = await readPosterAt(cls, c.path, takeDir, c.from);
|
|
4718
4672
|
if (ref) {
|
|
@@ -4808,19 +4762,19 @@ function resolveChannels(raw) {
|
|
|
4808
4762
|
return out;
|
|
4809
4763
|
}
|
|
4810
4764
|
async function readBrandBesideTake(dir, explicit) {
|
|
4811
|
-
const candidates = explicit ? [explicit] : [
|
|
4765
|
+
const candidates = explicit ? [explicit] : [join7(dir, "BRAND.md"), join7(dir, "..", "BRAND.md")];
|
|
4812
4766
|
for (const file of candidates) {
|
|
4813
4767
|
if (!existsSync7(file)) continue;
|
|
4814
|
-
const roles = parseFrontmatter(await
|
|
4768
|
+
const roles = parseFrontmatter(await readFile7(file, "utf8"));
|
|
4815
4769
|
return { file, roles };
|
|
4816
4770
|
}
|
|
4817
4771
|
return null;
|
|
4818
4772
|
}
|
|
4819
4773
|
async function readLaunchBesideTake(dir, explicit) {
|
|
4820
|
-
const candidates = explicit ? [explicit] : [
|
|
4774
|
+
const candidates = explicit ? [explicit] : [join7(dir, "LAUNCH.md"), join7(dir, "..", "LAUNCH.md")];
|
|
4821
4775
|
for (const file of candidates) {
|
|
4822
4776
|
if (!existsSync7(file)) continue;
|
|
4823
|
-
return { file, roles: parseFrontmatter(await
|
|
4777
|
+
return { file, roles: parseFrontmatter(await readFile7(file, "utf8")) };
|
|
4824
4778
|
}
|
|
4825
4779
|
return null;
|
|
4826
4780
|
}
|
|
@@ -4884,7 +4838,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
|
|
|
4884
4838
|
const meta = doc.source.meta;
|
|
4885
4839
|
const vw = meta.captureWidth ?? meta.width;
|
|
4886
4840
|
const vh = meta.captureHeight ?? meta.height;
|
|
4887
|
-
const probeDir = await mkdtemp(
|
|
4841
|
+
const probeDir = await mkdtemp(join7(tmpdir(), "vos-moments-"));
|
|
4888
4842
|
try {
|
|
4889
4843
|
const probe2 = await framesTake(browser, dir, {
|
|
4890
4844
|
times: candidates.map((c) => c.time),
|
|
@@ -4898,7 +4852,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
|
|
|
4898
4852
|
});
|
|
4899
4853
|
const byTime = /* @__PURE__ */ new Map();
|
|
4900
4854
|
for (const frame of probe2.frames) {
|
|
4901
|
-
const img = decodePng(new Uint8Array(await
|
|
4855
|
+
const img = decodePng(new Uint8Array(await readFile7(frame.file)));
|
|
4902
4856
|
if (!img) continue;
|
|
4903
4857
|
const whole = { x: 0, y: 0, w: img.w, h: img.h };
|
|
4904
4858
|
byTime.set(+frame.time.toFixed(3), {
|
|
@@ -4971,7 +4925,7 @@ async function deliverTake(browser, dir, opts) {
|
|
|
4971
4925
|
const doc = take.doc;
|
|
4972
4926
|
const duration = docOutputDuration2(doc);
|
|
4973
4927
|
const videoSeconds = opts.range ? Math.min(opts.range[1], duration) - Math.min(opts.range[0], duration) : duration;
|
|
4974
|
-
const outDir = resolve4(opts.outDir ??
|
|
4928
|
+
const outDir = resolve4(opts.outDir ?? join7(dir, "kit"));
|
|
4975
4929
|
await mkdir5(outDir, { recursive: true });
|
|
4976
4930
|
const destinations = opts.channels.flatMap((c) => destinationsForChannel(c));
|
|
4977
4931
|
const assets = [];
|
|
@@ -5032,7 +4986,7 @@ async function deliverTake(browser, dir, opts) {
|
|
|
5032
4986
|
opts.onPhase?.(
|
|
5033
4987
|
`${label} (${specWords(d)}) from ${ref.from}${ref.vosId ? ` ${ref.vosId}` : ""}, the rest at ${time.toFixed(2)}s`
|
|
5034
4988
|
);
|
|
5035
|
-
const shotDir = await mkdtemp(
|
|
4989
|
+
const shotDir = await mkdtemp(join7(tmpdir(), "vos-poster-"));
|
|
5036
4990
|
try {
|
|
5037
4991
|
const captured = await framesTake(browser, ref.takeDir, {
|
|
5038
4992
|
times: [time],
|
|
@@ -5042,7 +4996,7 @@ async function deliverTake(browser, dir, opts) {
|
|
|
5042
4996
|
doc: ref.doc,
|
|
5043
4997
|
overrides: opts.overrides
|
|
5044
4998
|
});
|
|
5045
|
-
const to =
|
|
4999
|
+
const to = join7(outDir, `${d.id}.png`);
|
|
5046
5000
|
await rename5(captured.frames[0].file, to);
|
|
5047
5001
|
const bytes = (await stat2(to)).size;
|
|
5048
5002
|
if (d.maxBytes !== void 0 && bytes > d.maxBytes) {
|
|
@@ -5114,7 +5068,7 @@ async function deliverTake(browser, dir, opts) {
|
|
|
5114
5068
|
}
|
|
5115
5069
|
}
|
|
5116
5070
|
opts.onPhase?.(`${label} (${specWords(d)})`);
|
|
5117
|
-
const outFile =
|
|
5071
|
+
const outFile = join7(outDir, `${d.id}.${d.format}`);
|
|
5118
5072
|
const bitrate = d.maxBytes !== void 0 ? Math.min(
|
|
5119
5073
|
1e7,
|
|
5120
5074
|
Math.floor(d.maxBytes * 8 / seconds * 0.85)
|
|
@@ -5161,7 +5115,7 @@ async function deliverTake(browser, dir, opts) {
|
|
|
5161
5115
|
for (let i = 0; i < captured.frames.length; i++) {
|
|
5162
5116
|
const frame = captured.frames[i];
|
|
5163
5117
|
const name = captured.frames.length > 1 ? `${d.id}-${i + 1}.png` : `${d.id}.png`;
|
|
5164
|
-
const to =
|
|
5118
|
+
const to = join7(outDir, name);
|
|
5165
5119
|
await rename5(frame.file, to);
|
|
5166
5120
|
const bytes = (await stat2(to)).size;
|
|
5167
5121
|
if (d.maxBytes !== void 0 && bytes > d.maxBytes) {
|
|
@@ -5199,15 +5153,15 @@ async function deliverTake(browser, dir, opts) {
|
|
|
5199
5153
|
skipped,
|
|
5200
5154
|
assets
|
|
5201
5155
|
};
|
|
5202
|
-
const kitFile =
|
|
5156
|
+
const kitFile = join7(outDir, "kit.json");
|
|
5203
5157
|
await writeFile7(kitFile, JSON.stringify(kit, null, 2));
|
|
5204
5158
|
return { kit, kitFile, outDir };
|
|
5205
5159
|
}
|
|
5206
5160
|
|
|
5207
5161
|
// src/plugin/markAsset.ts
|
|
5208
|
-
import { mkdir as mkdir6, readFile as
|
|
5162
|
+
import { mkdir as mkdir6, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
5209
5163
|
import { existsSync as existsSync8 } from "fs";
|
|
5210
|
-
import { isAbsolute as isAbsolute2, join as
|
|
5164
|
+
import { isAbsolute as isAbsolute2, join as join8, resolve as resolve5 } from "path";
|
|
5211
5165
|
function markExtension(url) {
|
|
5212
5166
|
const m = /\.(svg|png|webp|jpe?g)(?:[?#]|$)/i.exec(url);
|
|
5213
5167
|
if (!m) return null;
|
|
@@ -5290,7 +5244,7 @@ async function fetchBrandMarks(takeDir, roles) {
|
|
|
5290
5244
|
} else {
|
|
5291
5245
|
const file = isAbsolute2(src) ? src : resolve5(takeDir, src);
|
|
5292
5246
|
if (!existsSync8(file)) throw new Error("no such file");
|
|
5293
|
-
bytes = new Uint8Array(await
|
|
5247
|
+
bytes = new Uint8Array(await readFile8(file));
|
|
5294
5248
|
}
|
|
5295
5249
|
} catch (e) {
|
|
5296
5250
|
out.notes.push(
|
|
@@ -5305,8 +5259,8 @@ async function fetchBrandMarks(takeDir, roles) {
|
|
|
5305
5259
|
);
|
|
5306
5260
|
return null;
|
|
5307
5261
|
}
|
|
5308
|
-
await mkdir6(
|
|
5309
|
-
await writeFile8(
|
|
5262
|
+
await mkdir6(join8(takeDir, "brand"), { recursive: true });
|
|
5263
|
+
await writeFile8(join8(takeDir, "brand", `${name}.${ext}`), bytes);
|
|
5310
5264
|
return { key: `/brand/${name}.${ext}`, aspect };
|
|
5311
5265
|
};
|
|
5312
5266
|
out.light = await one("logoUrl", "mark");
|
|
@@ -5343,8 +5297,8 @@ async function fetchMusicCatalog(origin) {
|
|
|
5343
5297
|
|
|
5344
5298
|
// src/plugin/judge.ts
|
|
5345
5299
|
import { existsSync as existsSync9 } from "fs";
|
|
5346
|
-
import { mkdir as mkdir7, readFile as
|
|
5347
|
-
import { dirname as dirname2, isAbsolute as isAbsolute3, join as
|
|
5300
|
+
import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
|
|
5301
|
+
import { dirname as dirname2, isAbsolute as isAbsolute3, join as join9, resolve as resolve6 } from "path";
|
|
5348
5302
|
import { DESTINATIONS as DESTINATIONS2 } from "@vosjs/studio-core";
|
|
5349
5303
|
|
|
5350
5304
|
// src/plugin/shotBake.ts
|
|
@@ -5515,28 +5469,28 @@ function composeSheet(left, right, height = 540, gutter = 32) {
|
|
|
5515
5469
|
return { w, h, data: out };
|
|
5516
5470
|
}
|
|
5517
5471
|
async function judgeKit(kitPath, manifestPath, outDir) {
|
|
5518
|
-
const kit = JSON.parse(await
|
|
5472
|
+
const kit = JSON.parse(await readFile9(kitPath, "utf8"));
|
|
5519
5473
|
const manifest = JSON.parse(
|
|
5520
|
-
await
|
|
5474
|
+
await readFile9(manifestPath, "utf8")
|
|
5521
5475
|
);
|
|
5522
5476
|
const manifestDir = dirname2(resolve6(manifestPath));
|
|
5523
5477
|
const kitDir = dirname2(resolve6(kitPath));
|
|
5524
|
-
const out = resolve6(outDir ??
|
|
5478
|
+
const out = resolve6(outDir ?? join9(kitDir, "judge"));
|
|
5525
5479
|
await mkdir7(out, { recursive: true });
|
|
5526
5480
|
const sheets = [];
|
|
5527
5481
|
const skipped = [];
|
|
5528
5482
|
const refCache = /* @__PURE__ */ new Map();
|
|
5529
5483
|
const loadRef = async (file) => {
|
|
5530
5484
|
if (refCache.has(file)) return refCache.get(file) ?? null;
|
|
5531
|
-
const p =
|
|
5532
|
-
const img = existsSync9(p) ? decodePng(new Uint8Array(await
|
|
5485
|
+
const p = join9(manifestDir, file);
|
|
5486
|
+
const img = existsSync9(p) ? decodePng(new Uint8Array(await readFile9(p))) : null;
|
|
5533
5487
|
refCache.set(file, img);
|
|
5534
5488
|
return img;
|
|
5535
5489
|
};
|
|
5536
5490
|
for (const a of kit.assets) {
|
|
5537
5491
|
if (!/\.png$/i.test(a.path)) continue;
|
|
5538
|
-
const file = isAbsolute3(a.path) && existsSync9(a.path) ? a.path :
|
|
5539
|
-
const img = existsSync9(file) ? decodePng(new Uint8Array(await
|
|
5492
|
+
const file = isAbsolute3(a.path) && existsSync9(a.path) ? a.path : join9(kitDir, a.path.split("/").pop() ?? a.path);
|
|
5493
|
+
const img = existsSync9(file) ? decodePng(new Uint8Array(await readFile9(file))) : null;
|
|
5540
5494
|
if (!img) {
|
|
5541
5495
|
skipped.push(`${a.destination ?? a.path}: unreadable`);
|
|
5542
5496
|
continue;
|
|
@@ -5567,8 +5521,8 @@ async function judgeKit(kitPath, manifestPath, outDir) {
|
|
|
5567
5521
|
const id = stem.replace(/[^A-Za-z0-9_-]+/g, "-") || a.destination || "asset";
|
|
5568
5522
|
const nameA = `${id}--A.png`;
|
|
5569
5523
|
const nameB = `${id}--B.png`;
|
|
5570
|
-
await writeFile9(
|
|
5571
|
-
await writeFile9(
|
|
5524
|
+
await writeFile9(join9(out, nameA), encodePng(composeSheet(img, refImg)));
|
|
5525
|
+
await writeFile9(join9(out, nameB), encodePng(composeSheet(refImg, img)));
|
|
5572
5526
|
const rubric = [
|
|
5573
5527
|
`# ${id} against ${ref.id} (${ref.file})`,
|
|
5574
5528
|
"",
|
|
@@ -5588,7 +5542,7 @@ async function judgeKit(kitPath, manifestPath, outDir) {
|
|
|
5588
5542
|
"In judge.json: `win` true when the kit asset is at least as good as the reference on the rules that apply to its genre, false when it is worse, null for a tie; `reasons` names the rules by number.",
|
|
5589
5543
|
""
|
|
5590
5544
|
].join("\n");
|
|
5591
|
-
await writeFile9(
|
|
5545
|
+
await writeFile9(join9(out, `${id}.md`), rubric);
|
|
5592
5546
|
sheets.push({
|
|
5593
5547
|
asset: id,
|
|
5594
5548
|
reference: ref.id,
|
|
@@ -5597,7 +5551,7 @@ async function judgeKit(kitPath, manifestPath, outDir) {
|
|
|
5597
5551
|
rubric: `${id}.md`
|
|
5598
5552
|
});
|
|
5599
5553
|
}
|
|
5600
|
-
const verdictFile =
|
|
5554
|
+
const verdictFile = join9(out, "judge.json");
|
|
5601
5555
|
if (!existsSync9(verdictFile)) {
|
|
5602
5556
|
await writeFile9(
|
|
5603
5557
|
verdictFile,
|
|
@@ -5651,8 +5605,8 @@ function catalogFamily(family) {
|
|
|
5651
5605
|
|
|
5652
5606
|
// src/plugin/callout.ts
|
|
5653
5607
|
import { existsSync as existsSync10 } from "fs";
|
|
5654
|
-
import { readFile as
|
|
5655
|
-
import { join as
|
|
5608
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
5609
|
+
import { join as join10 } from "path";
|
|
5656
5610
|
import { lerpArray, sample } from "@vosjs/timeline";
|
|
5657
5611
|
import {
|
|
5658
5612
|
CALLOUT_DEFAULT_BODY_PX,
|
|
@@ -5849,13 +5803,13 @@ async function footageFrames(browser, dir, doc, times, framesTake2) {
|
|
|
5849
5803
|
const out = /* @__PURE__ */ new Map();
|
|
5850
5804
|
if (!times.length) return out;
|
|
5851
5805
|
const bare = { ...doc, overlays: [] };
|
|
5852
|
-
const outDir =
|
|
5806
|
+
const outDir = join10(dir, ".ground-check");
|
|
5853
5807
|
const res = await framesTake2(browser, dir, { times, outDir, doc: bare });
|
|
5854
5808
|
for (const f of res.frames) {
|
|
5855
5809
|
if (!existsSync10(f.file)) continue;
|
|
5856
5810
|
out.set(
|
|
5857
5811
|
+f.time.toFixed(3),
|
|
5858
|
-
decodePng(new Uint8Array(await
|
|
5812
|
+
decodePng(new Uint8Array(await readFile10(f.file)))
|
|
5859
5813
|
);
|
|
5860
5814
|
}
|
|
5861
5815
|
return out;
|
|
@@ -5864,7 +5818,7 @@ async function footageFrames(browser, dir, doc, times, framesTake2) {
|
|
|
5864
5818
|
// src/plugin/kitPicture.ts
|
|
5865
5819
|
import { execFile } from "child_process";
|
|
5866
5820
|
import { promisify } from "util";
|
|
5867
|
-
import { readFile as
|
|
5821
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
5868
5822
|
var execFileP = promisify(execFile);
|
|
5869
5823
|
var SUBJECT_BAND = { min: 0.6, max: 0.92 };
|
|
5870
5824
|
var BLANK_INK = 0.12;
|
|
@@ -6140,7 +6094,7 @@ async function pictureChecks(assets) {
|
|
|
6140
6094
|
let ffmpeg = null;
|
|
6141
6095
|
for (const a of assets) {
|
|
6142
6096
|
if (/\.png$/i.test(a.file)) {
|
|
6143
|
-
const img = decodePng(new Uint8Array(await
|
|
6097
|
+
const img = decodePng(new Uint8Array(await readFile11(a.file)));
|
|
6144
6098
|
if (!img) {
|
|
6145
6099
|
findings.push({
|
|
6146
6100
|
code: "unreadable",
|
|
@@ -6234,8 +6188,8 @@ function formatFinding(f) {
|
|
|
6234
6188
|
|
|
6235
6189
|
// src/plugin/validateKit.ts
|
|
6236
6190
|
import { existsSync as existsSync11 } from "fs";
|
|
6237
|
-
import { readFile as
|
|
6238
|
-
import { dirname as dirname3, isAbsolute as isAbsolute4, join as
|
|
6191
|
+
import { readFile as readFile12, stat as stat3 } from "fs/promises";
|
|
6192
|
+
import { dirname as dirname3, isAbsolute as isAbsolute4, join as join11 } from "path";
|
|
6239
6193
|
import { DESTINATIONS as DESTINATIONS3 } from "@vosjs/studio-core";
|
|
6240
6194
|
var PNG_SIG2 = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
6241
6195
|
function pngDimensions(bytes) {
|
|
@@ -6254,7 +6208,7 @@ async function probeVideo(path) {
|
|
|
6254
6208
|
const MB = await import("mediabunny");
|
|
6255
6209
|
const input = new MB.Input({
|
|
6256
6210
|
formats: MB.ALL_FORMATS,
|
|
6257
|
-
source: new MB.BufferSource(new Uint8Array(await
|
|
6211
|
+
source: new MB.BufferSource(new Uint8Array(await readFile12(path)))
|
|
6258
6212
|
});
|
|
6259
6213
|
try {
|
|
6260
6214
|
const track = await input.getPrimaryVideoTrack();
|
|
@@ -6275,7 +6229,7 @@ async function validateKit(kitPath, opts = {}) {
|
|
|
6275
6229
|
const measured = [];
|
|
6276
6230
|
let kit;
|
|
6277
6231
|
try {
|
|
6278
|
-
kit = JSON.parse(await
|
|
6232
|
+
kit = JSON.parse(await readFile12(kitPath, "utf8"));
|
|
6279
6233
|
} catch (e) {
|
|
6280
6234
|
return {
|
|
6281
6235
|
valid: false,
|
|
@@ -6295,7 +6249,7 @@ async function validateKit(kitPath, opts = {}) {
|
|
|
6295
6249
|
const base = dirname3(kitPath);
|
|
6296
6250
|
const resolvePath = (p) => {
|
|
6297
6251
|
if (isAbsolute4(p) && existsSync11(p)) return p;
|
|
6298
|
-
const beside =
|
|
6252
|
+
const beside = join11(base, p.split("/").pop() ?? p);
|
|
6299
6253
|
return beside;
|
|
6300
6254
|
};
|
|
6301
6255
|
const perDestination = /* @__PURE__ */ new Map();
|
|
@@ -6321,7 +6275,7 @@ async function validateKit(kitPath, opts = {}) {
|
|
|
6321
6275
|
let h = null;
|
|
6322
6276
|
let seconds = null;
|
|
6323
6277
|
if (/\.(png|jpe?g|webp)$/i.test(file)) {
|
|
6324
|
-
const head = Buffer.from(await
|
|
6278
|
+
const head = Buffer.from(await readFile12(file));
|
|
6325
6279
|
const kind = sniffImage(head);
|
|
6326
6280
|
if (file.toLowerCase().endsWith(".png") && kind !== "png")
|
|
6327
6281
|
problems.push(
|
|
@@ -6430,8 +6384,8 @@ async function validateKit(kitPath, opts = {}) {
|
|
|
6430
6384
|
}
|
|
6431
6385
|
|
|
6432
6386
|
// src/plugin/recorder.ts
|
|
6433
|
-
import { writeFileSync
|
|
6434
|
-
import { join as
|
|
6387
|
+
import { writeFileSync } from "fs";
|
|
6388
|
+
import { join as join12 } from "path";
|
|
6435
6389
|
import { recordTake as recordTakeCore } from "@vosjs/render-core/record";
|
|
6436
6390
|
async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
6437
6391
|
const dry = opts.dryRun === true;
|
|
@@ -6441,7 +6395,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6441
6395
|
actions,
|
|
6442
6396
|
{
|
|
6443
6397
|
frame: (file, bytes) => {
|
|
6444
|
-
|
|
6398
|
+
writeFileSync(join12(paths.framesDir, file), bytes);
|
|
6445
6399
|
}
|
|
6446
6400
|
},
|
|
6447
6401
|
log,
|
|
@@ -6493,11 +6447,11 @@ import {
|
|
|
6493
6447
|
|
|
6494
6448
|
// src/plugin/session.ts
|
|
6495
6449
|
import { spawn } from "child_process";
|
|
6496
|
-
import { existsSync as existsSync12, mkdirSync
|
|
6497
|
-
import { homedir
|
|
6498
|
-
import { join as
|
|
6450
|
+
import { existsSync as existsSync12, mkdirSync, readdirSync as readdirSync2, rmSync, statSync as statSync3 } from "fs";
|
|
6451
|
+
import { homedir } from "os";
|
|
6452
|
+
import { join as join13 } from "path";
|
|
6499
6453
|
import { chromium } from "playwright";
|
|
6500
|
-
var SESSIONS_DIR =
|
|
6454
|
+
var SESSIONS_DIR = join13(homedir(), ".config", "vos", "sessions");
|
|
6501
6455
|
var NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
6502
6456
|
function sessionNameVerdict(name) {
|
|
6503
6457
|
if (!name) return "a session needs a name: --name <app>";
|
|
@@ -6506,20 +6460,20 @@ function sessionNameVerdict(name) {
|
|
|
6506
6460
|
return null;
|
|
6507
6461
|
}
|
|
6508
6462
|
function sessionDir(name) {
|
|
6509
|
-
return
|
|
6463
|
+
return join13(SESSIONS_DIR, name);
|
|
6510
6464
|
}
|
|
6511
6465
|
function systemChromePath() {
|
|
6512
6466
|
const explicit = process.env.VOS_BROWSER_PATH;
|
|
6513
6467
|
if (explicit) return explicit;
|
|
6514
6468
|
const candidates = process.platform === "darwin" ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"] : process.platform === "win32" ? [
|
|
6515
|
-
|
|
6469
|
+
join13(
|
|
6516
6470
|
process.env["PROGRAMFILES"] ?? "C:\\Program Files",
|
|
6517
6471
|
"Google",
|
|
6518
6472
|
"Chrome",
|
|
6519
6473
|
"Application",
|
|
6520
6474
|
"chrome.exe"
|
|
6521
6475
|
),
|
|
6522
|
-
|
|
6476
|
+
join13(
|
|
6523
6477
|
process.env["LOCALAPPDATA"] ?? "",
|
|
6524
6478
|
"Google",
|
|
6525
6479
|
"Chrome",
|
|
@@ -6541,7 +6495,7 @@ function systemChromePath() {
|
|
|
6541
6495
|
async function openSession(name, url, opts = { log: () => {
|
|
6542
6496
|
} }) {
|
|
6543
6497
|
const dir = sessionDir(name);
|
|
6544
|
-
|
|
6498
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
6545
6499
|
const exe = systemChromePath();
|
|
6546
6500
|
const args = [
|
|
6547
6501
|
`--user-data-dir=${dir}`,
|
|
@@ -6613,7 +6567,7 @@ function summaryLine(s) {
|
|
|
6613
6567
|
function listSessions() {
|
|
6614
6568
|
if (!existsSync12(SESSIONS_DIR)) return [];
|
|
6615
6569
|
return readdirSync2(SESSIONS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && NAME.test(d.name)).map((d) => {
|
|
6616
|
-
const st = statSync3(
|
|
6570
|
+
const st = statSync3(join13(SESSIONS_DIR, d.name));
|
|
6617
6571
|
return {
|
|
6618
6572
|
name: d.name,
|
|
6619
6573
|
ageDays: Math.floor(
|
|
@@ -6690,8 +6644,8 @@ async function encodeRecording(browser, takeDir, onProgress) {
|
|
|
6690
6644
|
|
|
6691
6645
|
// src/plugin/plan.ts
|
|
6692
6646
|
import { existsSync as existsSync13 } from "fs";
|
|
6693
|
-
import { readFile as
|
|
6694
|
-
import { join as
|
|
6647
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
6648
|
+
import { join as join14 } from "path";
|
|
6695
6649
|
import {
|
|
6696
6650
|
DEFAULT_FRAME_STYLE,
|
|
6697
6651
|
STYLE_FIELDS,
|
|
@@ -6913,10 +6867,10 @@ function retimeCut(prev, newSteps, newDurationMs) {
|
|
|
6913
6867
|
// src/plugin/plan.ts
|
|
6914
6868
|
var overlaps = (a, b) => a.in < b.out && b.in < a.out;
|
|
6915
6869
|
async function readDigestActivity(dir) {
|
|
6916
|
-
const file =
|
|
6870
|
+
const file = join14(dir, "digest", "digest.json");
|
|
6917
6871
|
if (!existsSync13(file)) return null;
|
|
6918
6872
|
try {
|
|
6919
|
-
const d = JSON.parse(await
|
|
6873
|
+
const d = JSON.parse(await readFile13(file, "utf8"));
|
|
6920
6874
|
return Array.isArray(d.activity) && d.activity.every((v) => typeof v === "number") ? d.activity : null;
|
|
6921
6875
|
} catch {
|
|
6922
6876
|
return null;
|
|
@@ -6977,7 +6931,7 @@ async function planTake(dir, opts = {}) {
|
|
|
6977
6931
|
const take = await loadTake(dir);
|
|
6978
6932
|
const { meta, cursor } = take;
|
|
6979
6933
|
const activity = await readDigestActivity(dir);
|
|
6980
|
-
const ingest = opts.backdrop ? { frame: withBackdrop(DEFAULT_FRAME_STYLE, opts.backdrop) } : {};
|
|
6934
|
+
const ingest = opts.footage ? { footage: true } : opts.backdrop ? { frame: withBackdrop(DEFAULT_FRAME_STYLE, opts.backdrop) } : {};
|
|
6981
6935
|
let doc;
|
|
6982
6936
|
let fresh;
|
|
6983
6937
|
let layout;
|
|
@@ -7129,16 +7083,16 @@ async function planTake(dir, opts = {}) {
|
|
|
7129
7083
|
}
|
|
7130
7084
|
|
|
7131
7085
|
// src/plugin/sync.ts
|
|
7132
|
-
import { createHash } from "crypto";
|
|
7133
|
-
import { existsSync as existsSync14, readFileSync as
|
|
7134
|
-
import { readFile as
|
|
7086
|
+
import { createHash as createHash2 } from "crypto";
|
|
7087
|
+
import { existsSync as existsSync14, readFileSync as readFileSync2, readdirSync as readdirSync3 } from "fs";
|
|
7088
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
7135
7089
|
import { createInterface } from "readline/promises";
|
|
7136
|
-
import { basename as basename3, join as
|
|
7090
|
+
import { basename as basename3, join as join16 } from "path";
|
|
7137
7091
|
import { lowerToComposition as lowerToComposition3, migrateHostedDoc as migrateHostedDoc3 } from "@vosjs/studio-core";
|
|
7138
7092
|
|
|
7139
7093
|
// src/plugin/folder.ts
|
|
7140
7094
|
import { mkdir as mkdir8, writeFile as writeFile10 } from "fs/promises";
|
|
7141
|
-
import { join as
|
|
7095
|
+
import { join as join15 } from "path";
|
|
7142
7096
|
import { recipeHints } from "@vosjs/shared/frontmatter";
|
|
7143
7097
|
import { migrateHostedDoc as migrateHostedDoc2 } from "@vosjs/studio-core";
|
|
7144
7098
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["json", "help", "media"]);
|
|
@@ -7314,18 +7268,18 @@ async function cmdPull(argv) {
|
|
|
7314
7268
|
throw new Error(apiError(`pull folder ${folder.slug}`, res));
|
|
7315
7269
|
const payload = res.body;
|
|
7316
7270
|
const out = strFlag(flags, "out") ?? folder.slug;
|
|
7317
|
-
await mkdir8(
|
|
7271
|
+
await mkdir8(join15(out, "recipes"), { recursive: true });
|
|
7318
7272
|
const lines = [];
|
|
7319
7273
|
const recipes = [];
|
|
7320
7274
|
for (const rec of payload.recipes) {
|
|
7321
|
-
const path =
|
|
7275
|
+
const path = join15(out, "recipes", safeName(rec.filename));
|
|
7322
7276
|
await writeFile10(path, rec.body ?? "");
|
|
7323
7277
|
recipes.push({ path, line: recipeLine(rec.filename, rec.body ?? "") });
|
|
7324
7278
|
}
|
|
7325
7279
|
for (const rec of payload.inheritedRecipes) {
|
|
7326
7280
|
const from = safeName(rec.folderSlug ?? "inherited");
|
|
7327
|
-
await mkdir8(
|
|
7328
|
-
const path =
|
|
7281
|
+
await mkdir8(join15(out, "recipes", "_inherited", from), { recursive: true });
|
|
7282
|
+
const path = join15(
|
|
7329
7283
|
out,
|
|
7330
7284
|
"recipes",
|
|
7331
7285
|
"_inherited",
|
|
@@ -7340,7 +7294,7 @@ async function cmdPull(argv) {
|
|
|
7340
7294
|
}
|
|
7341
7295
|
const members = [];
|
|
7342
7296
|
for (const v of payload.voses) {
|
|
7343
|
-
const dir =
|
|
7297
|
+
const dir = join15(out, "members", safeName(v.slug || v.title || v.id));
|
|
7344
7298
|
await mkdir8(dir, { recursive: true });
|
|
7345
7299
|
const cfg = await apiJson(origin, `/api/vos/${v.id}/config`, { key });
|
|
7346
7300
|
if (cfg.status !== 200) {
|
|
@@ -7348,7 +7302,7 @@ async function cmdPull(argv) {
|
|
|
7348
7302
|
continue;
|
|
7349
7303
|
}
|
|
7350
7304
|
await writeFile10(
|
|
7351
|
-
|
|
7305
|
+
join15(dir, "config.json"),
|
|
7352
7306
|
JSON.stringify(cfg.body.config, null, 2)
|
|
7353
7307
|
);
|
|
7354
7308
|
let take = false;
|
|
@@ -7360,7 +7314,7 @@ async function cmdPull(argv) {
|
|
|
7360
7314
|
);
|
|
7361
7315
|
if (doc.status === 200) {
|
|
7362
7316
|
await writeFile10(
|
|
7363
|
-
|
|
7317
|
+
join15(dir, "doc.json"),
|
|
7364
7318
|
JSON.stringify(doc.body, null, 2)
|
|
7365
7319
|
);
|
|
7366
7320
|
take = true;
|
|
@@ -7430,144 +7384,6 @@ async function cmdFolder(argv) {
|
|
|
7430
7384
|
}
|
|
7431
7385
|
}
|
|
7432
7386
|
|
|
7433
|
-
// src/plugin/uploadAsset.ts
|
|
7434
|
-
var SINGLE_SHOT_MAX_BYTES = 32 * 1024 * 1024;
|
|
7435
|
-
var PART_ATTEMPTS = 3;
|
|
7436
|
-
function planParts(totalBytes, partBytes) {
|
|
7437
|
-
if (totalBytes <= 0 || partBytes <= 0) return [];
|
|
7438
|
-
const parts = [];
|
|
7439
|
-
for (let start = 0; start < totalBytes; start += partBytes) {
|
|
7440
|
-
parts.push({
|
|
7441
|
-
partNumber: parts.length + 1,
|
|
7442
|
-
start,
|
|
7443
|
-
end: Math.min(start + partBytes, totalBytes)
|
|
7444
|
-
});
|
|
7445
|
-
}
|
|
7446
|
-
return parts;
|
|
7447
|
-
}
|
|
7448
|
-
function failed(what, r) {
|
|
7449
|
-
const detail = typeof r.body.error === "string" ? r.body.error : "";
|
|
7450
|
-
return new Error(`${what} (${r.status})${detail ? `: ${detail}` : ""}`);
|
|
7451
|
-
}
|
|
7452
|
-
function asAsset(body) {
|
|
7453
|
-
return {
|
|
7454
|
-
id: String(body.id),
|
|
7455
|
-
url: String(body.url),
|
|
7456
|
-
size: typeof body.size === "number" ? body.size : 0,
|
|
7457
|
-
reused: body.reused === true
|
|
7458
|
-
};
|
|
7459
|
-
}
|
|
7460
|
-
function transient(status) {
|
|
7461
|
-
return status >= 500 || status === 429;
|
|
7462
|
-
}
|
|
7463
|
-
var wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
7464
|
-
async function uploadAsset(target, bytes, opts) {
|
|
7465
|
-
return bytes.length > SINGLE_SHOT_MAX_BYTES ? uploadInParts(target, bytes, opts) : uploadWhole(target, bytes, opts);
|
|
7466
|
-
}
|
|
7467
|
-
async function uploadWhole(target, bytes, opts) {
|
|
7468
|
-
const res = await apiJson(target.origin, "/api/assets/recording", {
|
|
7469
|
-
method: "POST",
|
|
7470
|
-
key: target.key,
|
|
7471
|
-
headers: {
|
|
7472
|
-
"Content-Type": opts.contentType,
|
|
7473
|
-
"Content-Length": String(bytes.length),
|
|
7474
|
-
"X-Filename": opts.filename,
|
|
7475
|
-
"X-Content-Hash": opts.contentHash,
|
|
7476
|
-
...opts.durationSeconds && opts.durationSeconds > 0 ? { "X-Content-Duration": opts.durationSeconds.toFixed(3) } : {}
|
|
7477
|
-
},
|
|
7478
|
-
raw: bytes
|
|
7479
|
-
});
|
|
7480
|
-
if (res.status !== 201 && res.status !== 200)
|
|
7481
|
-
throw failed("upload failed", res);
|
|
7482
|
-
return asAsset(res.body);
|
|
7483
|
-
}
|
|
7484
|
-
async function uploadInParts(target, bytes, opts) {
|
|
7485
|
-
const begun = await apiJson(
|
|
7486
|
-
target.origin,
|
|
7487
|
-
"/api/assets/recording/multipart",
|
|
7488
|
-
{
|
|
7489
|
-
method: "POST",
|
|
7490
|
-
key: target.key,
|
|
7491
|
-
body: {
|
|
7492
|
-
filename: opts.filename,
|
|
7493
|
-
contentType: opts.contentType,
|
|
7494
|
-
size: bytes.length,
|
|
7495
|
-
contentHash: opts.contentHash,
|
|
7496
|
-
...opts.durationSeconds && opts.durationSeconds > 0 ? { durationSeconds: opts.durationSeconds } : {}
|
|
7497
|
-
}
|
|
7498
|
-
}
|
|
7499
|
-
);
|
|
7500
|
-
if (begun.status !== 201 && begun.status !== 200) {
|
|
7501
|
-
throw failed("upload failed", begun);
|
|
7502
|
-
}
|
|
7503
|
-
if (begun.body.reused === true || typeof begun.body.url === "string") {
|
|
7504
|
-
return asAsset(begun.body);
|
|
7505
|
-
}
|
|
7506
|
-
const uploadId = String(begun.body.id);
|
|
7507
|
-
const partBytes = Number(begun.body.partBytes);
|
|
7508
|
-
if (!Number.isFinite(partBytes) || partBytes <= 0) {
|
|
7509
|
-
throw new Error("upload failed: the platform returned no part size");
|
|
7510
|
-
}
|
|
7511
|
-
const parts = planParts(bytes.length, partBytes);
|
|
7512
|
-
const etags = [];
|
|
7513
|
-
try {
|
|
7514
|
-
for (const part of parts) {
|
|
7515
|
-
const chunk2 = bytes.subarray(part.start, part.end);
|
|
7516
|
-
let sent = null;
|
|
7517
|
-
for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt++) {
|
|
7518
|
-
try {
|
|
7519
|
-
sent = await apiJson(
|
|
7520
|
-
target.origin,
|
|
7521
|
-
`/api/assets/recording/multipart/${uploadId}/parts/${part.partNumber}`,
|
|
7522
|
-
{
|
|
7523
|
-
method: "PUT",
|
|
7524
|
-
key: target.key,
|
|
7525
|
-
headers: {
|
|
7526
|
-
"Content-Type": "application/octet-stream",
|
|
7527
|
-
"Content-Length": String(chunk2.length)
|
|
7528
|
-
},
|
|
7529
|
-
raw: chunk2
|
|
7530
|
-
}
|
|
7531
|
-
);
|
|
7532
|
-
} catch (networkError) {
|
|
7533
|
-
if (attempt === PART_ATTEMPTS) throw networkError;
|
|
7534
|
-
await wait(500 * 2 ** (attempt - 1));
|
|
7535
|
-
continue;
|
|
7536
|
-
}
|
|
7537
|
-
if (sent.status === 200) break;
|
|
7538
|
-
if (!transient(sent.status) || attempt === PART_ATTEMPTS) {
|
|
7539
|
-
throw failed(`part ${part.partNumber} failed`, sent);
|
|
7540
|
-
}
|
|
7541
|
-
await wait(500 * 2 ** (attempt - 1));
|
|
7542
|
-
}
|
|
7543
|
-
if (!sent || sent.status !== 200) {
|
|
7544
|
-
throw new Error(`part ${part.partNumber} failed`);
|
|
7545
|
-
}
|
|
7546
|
-
etags.push({
|
|
7547
|
-
partNumber: Number(sent.body.partNumber),
|
|
7548
|
-
etag: String(sent.body.etag)
|
|
7549
|
-
});
|
|
7550
|
-
opts.onPart?.(etags.length, parts.length);
|
|
7551
|
-
}
|
|
7552
|
-
const sealed = await apiJson(
|
|
7553
|
-
target.origin,
|
|
7554
|
-
`/api/assets/recording/multipart/${uploadId}/complete`,
|
|
7555
|
-
{ method: "POST", key: target.key, body: { parts: etags } }
|
|
7556
|
-
);
|
|
7557
|
-
if (sealed.status !== 201 && sealed.status !== 200) {
|
|
7558
|
-
throw failed("upload failed", sealed);
|
|
7559
|
-
}
|
|
7560
|
-
return asAsset(sealed.body);
|
|
7561
|
-
} catch (err) {
|
|
7562
|
-
await apiJson(
|
|
7563
|
-
target.origin,
|
|
7564
|
-
`/api/assets/recording/multipart/${uploadId}`,
|
|
7565
|
-
{ method: "DELETE", key: target.key }
|
|
7566
|
-
).catch(() => void 0);
|
|
7567
|
-
throw err;
|
|
7568
|
-
}
|
|
7569
|
-
}
|
|
7570
|
-
|
|
7571
7387
|
// src/plugin/sync.ts
|
|
7572
7388
|
function apiContext(flags) {
|
|
7573
7389
|
return {
|
|
@@ -7634,8 +7450,8 @@ function storageStateInDir(dir) {
|
|
|
7634
7450
|
if (!f.endsWith(".json") || f === "doc.json" || f === "meta.json" || f === "actions.json" || f === "cursor.json" || f === "vos.json" || f === "doc.prev.json")
|
|
7635
7451
|
continue;
|
|
7636
7452
|
try {
|
|
7637
|
-
const v = JSON.parse(
|
|
7638
|
-
if (looksLikeStorageState(v)) return
|
|
7453
|
+
const v = JSON.parse(readFileSync2(join16(dir, f), "utf8"));
|
|
7454
|
+
if (looksLikeStorageState(v)) return join16(dir, f);
|
|
7639
7455
|
} catch {
|
|
7640
7456
|
}
|
|
7641
7457
|
}
|
|
@@ -7705,8 +7521,8 @@ async function pushTake(dir, flags, r) {
|
|
|
7705
7521
|
throw new Error("push cancelled");
|
|
7706
7522
|
}
|
|
7707
7523
|
}
|
|
7708
|
-
const bytes = await
|
|
7709
|
-
const hash =
|
|
7524
|
+
const bytes = await readFile14(take.paths.recording);
|
|
7525
|
+
const hash = createHash2("sha256").update(bytes).digest("hex");
|
|
7710
7526
|
const { type: contentType } = resolveMediaType({
|
|
7711
7527
|
head: bytes.subarray(0, MEDIA_HEAD_BYTES),
|
|
7712
7528
|
filename: take.paths.recordingName
|
|
@@ -7747,39 +7563,7 @@ async function pushTake(dir, flags, r) {
|
|
|
7747
7563
|
r.log(" mic track is local-only \u2014 dropped from the push");
|
|
7748
7564
|
delete docForPush.source.micKey;
|
|
7749
7565
|
}
|
|
7750
|
-
|
|
7751
|
-
const file = join17(dir, takeRelativeFile(ref.key));
|
|
7752
|
-
if (!existsSync14(file)) {
|
|
7753
|
-
r.log(` ${ref.where}: ${ref.key} is not in the take \u2014 left as is`);
|
|
7754
|
-
continue;
|
|
7755
|
-
}
|
|
7756
|
-
const media = await readFile13(file);
|
|
7757
|
-
const mediaHash = createHash("sha256").update(media).digest("hex");
|
|
7758
|
-
const { type } = resolveMediaType({
|
|
7759
|
-
head: media.subarray(0, MEDIA_HEAD_BYTES),
|
|
7760
|
-
filename: file
|
|
7761
|
-
});
|
|
7762
|
-
const name = nameForType(basename3(file), type);
|
|
7763
|
-
if (name !== basename3(file)) {
|
|
7764
|
-
r.log(` ${ref.where}: ${basename3(file)} holds ${type} \u2014 sent as ${name}`);
|
|
7765
|
-
}
|
|
7766
|
-
let put;
|
|
7767
|
-
try {
|
|
7768
|
-
put = await uploadAsset(ctx, new Uint8Array(media), {
|
|
7769
|
-
filename: name,
|
|
7770
|
-
contentType: type,
|
|
7771
|
-
contentHash: mediaHash
|
|
7772
|
-
});
|
|
7773
|
-
} catch (e) {
|
|
7774
|
-
throw new Error(
|
|
7775
|
-
`${ref.where} upload failed: ${e instanceof Error ? e.message : String(e)}`
|
|
7776
|
-
);
|
|
7777
|
-
}
|
|
7778
|
-
ref.set(put.url);
|
|
7779
|
-
r.log(
|
|
7780
|
-
` ${ref.where}: ${ref.key} \u2192 asset ${put.id}${put.reused ? " (reused)" : ""}`
|
|
7781
|
-
);
|
|
7782
|
-
}
|
|
7566
|
+
await uploadDocRefs(docMediaRefs(docForPush), dir, ctx, (l) => r.log(l));
|
|
7783
7567
|
const lowered = lowerToComposition3(docForPush);
|
|
7784
7568
|
const config = { ...lowered.config, data: lowered.data };
|
|
7785
7569
|
if (!state) {
|
|
@@ -7893,9 +7677,9 @@ async function pullTake(dir, flags, r) {
|
|
|
7893
7677
|
};
|
|
7894
7678
|
}
|
|
7895
7679
|
let media2;
|
|
7896
|
-
if (flags.media && existsSync14(
|
|
7680
|
+
if (flags.media && existsSync14(join16(dir, "doc.json"))) {
|
|
7897
7681
|
const local = JSON.parse(
|
|
7898
|
-
await
|
|
7682
|
+
await readFile14(join16(dir, "doc.json"), "utf8")
|
|
7899
7683
|
);
|
|
7900
7684
|
media2 = await pullMedia(ctx, dir, local, r.log);
|
|
7901
7685
|
}
|
|
@@ -7959,7 +7743,7 @@ async function pullTake(dir, flags, r) {
|
|
|
7959
7743
|
if (existsSync14(localRecording)) {
|
|
7960
7744
|
doc.source.videoKey = basename3(localRecording);
|
|
7961
7745
|
}
|
|
7962
|
-
await writeJson(
|
|
7746
|
+
await writeJson(join16(dir, "doc.json"), doc, true);
|
|
7963
7747
|
const media = flags.media ? await pullMedia(ctx, dir, doc, r.log) : void 0;
|
|
7964
7748
|
const versions = await api(ctx, `/vos/${vosId}/versions`);
|
|
7965
7749
|
const headRow = (versions.json.versions ?? []).find((v) => v.id === head);
|
|
@@ -7979,9 +7763,9 @@ async function pullTake(dir, flags, r) {
|
|
|
7979
7763
|
|
|
7980
7764
|
// src/plugin/program.ts
|
|
7981
7765
|
import { existsSync as existsSync15 } from "fs";
|
|
7982
|
-
import { mkdir as mkdir9, readFile as
|
|
7766
|
+
import { mkdir as mkdir9, readFile as readFile15, writeFile as writeFile11 } from "fs/promises";
|
|
7983
7767
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
7984
|
-
import { basename as basename4, dirname as dirname4, join as
|
|
7768
|
+
import { basename as basename4, dirname as dirname4, join as join17 } from "path";
|
|
7985
7769
|
import {
|
|
7986
7770
|
CURRENT_CONFIG_VERSION,
|
|
7987
7771
|
migrateConfig,
|
|
@@ -8121,7 +7905,8 @@ var BOOLEAN_FLAGS2 = /* @__PURE__ */ new Set([
|
|
|
8121
7905
|
"check",
|
|
8122
7906
|
"yes",
|
|
8123
7907
|
"claimable",
|
|
8124
|
-
"no-browser"
|
|
7908
|
+
"no-browser",
|
|
7909
|
+
"wait"
|
|
8125
7910
|
]);
|
|
8126
7911
|
var MULTI_FLAGS = /* @__PURE__ */ new Set(["override"]);
|
|
8127
7912
|
function preflightConfig(parsed) {
|
|
@@ -8186,7 +7971,7 @@ function preflightConfig(parsed) {
|
|
|
8186
7971
|
}
|
|
8187
7972
|
function resolveConfigPath(target) {
|
|
8188
7973
|
if (target.endsWith(".json")) return target;
|
|
8189
|
-
const inDir =
|
|
7974
|
+
const inDir = join17(target, "config.json");
|
|
8190
7975
|
if (existsSync15(inDir)) return inDir;
|
|
8191
7976
|
throw new UsageError(
|
|
8192
7977
|
`${target} has no config.json (and is not a take \u2014 no doc.json)`
|
|
@@ -8215,7 +8000,7 @@ async function cmdFetch(argv) {
|
|
|
8215
8000
|
const out = strFlag(flags, "out") ?? slug;
|
|
8216
8001
|
await mkdir9(out, { recursive: true });
|
|
8217
8002
|
await writeFile11(
|
|
8218
|
-
|
|
8003
|
+
join17(out, "config.json"),
|
|
8219
8004
|
JSON.stringify(cfg.body.config, null, 2)
|
|
8220
8005
|
);
|
|
8221
8006
|
const title = typeof vosMeta.title === "string" ? vosMeta.title : "";
|
|
@@ -8240,12 +8025,12 @@ async function cmdFetch(argv) {
|
|
|
8240
8025
|
const hosted = migrateHostedDoc4(doc.body);
|
|
8241
8026
|
const { config: own } = await writeProgramDoc(out, hosted);
|
|
8242
8027
|
if (own) {
|
|
8243
|
-
await writeFile11(
|
|
8028
|
+
await writeFile11(join17(out, "config.json"), JSON.stringify(own, null, 2));
|
|
8244
8029
|
}
|
|
8245
8030
|
} else if (doc.status === 200) {
|
|
8246
8031
|
take = true;
|
|
8247
8032
|
const hosted = migrateHostedDoc4(doc.body);
|
|
8248
|
-
await writeFile11(
|
|
8033
|
+
await writeFile11(join17(out, "doc.json"), JSON.stringify(hosted, null, 2));
|
|
8249
8034
|
if (flags.media === true) {
|
|
8250
8035
|
const media = await pullMedia({ origin, key }, out, hosted, r.log);
|
|
8251
8036
|
mediaLine = media.downloaded.length ? ` + ${media.downloaded.map((m) => m.file).join(", ")}` : "";
|
|
@@ -8358,7 +8143,7 @@ async function cmdPushProgram(argv) {
|
|
|
8358
8143
|
api: strFlag(flags, "api")
|
|
8359
8144
|
});
|
|
8360
8145
|
const dir = dirname4(source);
|
|
8361
|
-
const parsed = JSON.parse(await
|
|
8146
|
+
const parsed = JSON.parse(await readFile15(source, "utf8"));
|
|
8362
8147
|
const pre = preflightConfig(parsed);
|
|
8363
8148
|
if (!pre.ok || !pre.config) {
|
|
8364
8149
|
for (const issue of pre.issues) r.log(`error ${issue}`);
|
|
@@ -8387,7 +8172,25 @@ async function cmdPushProgram(argv) {
|
|
|
8387
8172
|
}
|
|
8388
8173
|
const fallback = state?.title ? `${state.title} remix` : basename4(source).replace(/\.json$/i, "") || "vos program";
|
|
8389
8174
|
const title2 = (strFlag(flags, "title") ?? fallback).slice(0, 100);
|
|
8390
|
-
|
|
8175
|
+
let claimDoc = programDoc;
|
|
8176
|
+
if (programDoc) {
|
|
8177
|
+
const sound = soundOf(programDoc);
|
|
8178
|
+
const local = docAudioRefs(sound);
|
|
8179
|
+
if (local.length) {
|
|
8180
|
+
const dropped = new Set(local.map((ref) => ref.key));
|
|
8181
|
+
claimDoc = {
|
|
8182
|
+
...programDoc,
|
|
8183
|
+
audio: (sound.audio ?? []).filter((a) => !dropped.has(a.key))
|
|
8184
|
+
};
|
|
8185
|
+
r.log(
|
|
8186
|
+
`warning a claimable push carries no files, so ${local.length} local sound${local.length === 1 ? "" : "s"} (${[...dropped].join(", ")}) ${local.length === 1 ? "was" : "were"} left out. After the claim, push again with a key to add ${local.length === 1 ? "it" : "them"}`
|
|
8187
|
+
);
|
|
8188
|
+
}
|
|
8189
|
+
}
|
|
8190
|
+
const body = {
|
|
8191
|
+
title: title2,
|
|
8192
|
+
config: storedProgramConfig(config, claimDoc)
|
|
8193
|
+
};
|
|
8391
8194
|
const slug = strFlag(flags, "slug");
|
|
8392
8195
|
if (slug) body.slug = slug;
|
|
8393
8196
|
const res = await apiJson(origin, "/api/claim", { method: "POST", body });
|
|
@@ -8407,6 +8210,15 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8407
8210
|
return EXIT_OK;
|
|
8408
8211
|
}
|
|
8409
8212
|
const key = requireCredential(strFlag(flags, "key"));
|
|
8213
|
+
void flags.yes;
|
|
8214
|
+
if (programDoc) {
|
|
8215
|
+
await uploadDocRefs(
|
|
8216
|
+
docAudioRefs(soundOf(programDoc)),
|
|
8217
|
+
dir,
|
|
8218
|
+
{ origin, key },
|
|
8219
|
+
(l) => r.log(l)
|
|
8220
|
+
);
|
|
8221
|
+
}
|
|
8410
8222
|
const overrides = [
|
|
8411
8223
|
...multi.override ?? [],
|
|
8412
8224
|
...strFlag(flags, "overrides")?.split(",") ?? []
|
|
@@ -8476,6 +8288,13 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8476
8288
|
}
|
|
8477
8289
|
const watchUrl2 = `${origin}/vos/${vosId}`;
|
|
8478
8290
|
const studioUrl2 = `${origin}/studio?vos=${vosId}`;
|
|
8291
|
+
const landed2 = flags.wait === true ? await waitForLanded({
|
|
8292
|
+
origin,
|
|
8293
|
+
key,
|
|
8294
|
+
vosId,
|
|
8295
|
+
versionId: typeof version.id === "string" ? version.id : null,
|
|
8296
|
+
log: (l) => r.log(l)
|
|
8297
|
+
}) : null;
|
|
8479
8298
|
r.done(
|
|
8480
8299
|
{
|
|
8481
8300
|
id: vosId,
|
|
@@ -8483,11 +8302,17 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8483
8302
|
versionNumber: version.versionNumber ?? null,
|
|
8484
8303
|
base: base ?? null,
|
|
8485
8304
|
watchUrl: watchUrl2,
|
|
8486
|
-
studioUrl: studioUrl2
|
|
8305
|
+
studioUrl: studioUrl2,
|
|
8306
|
+
...landed2 ? {
|
|
8307
|
+
still: landed2.still,
|
|
8308
|
+
thumbnailUrl: landed2.thumbnailUrl,
|
|
8309
|
+
previewUrl: landed2.previewUrl
|
|
8310
|
+
} : {}
|
|
8487
8311
|
},
|
|
8488
8312
|
`Pushed version ${String(version.versionNumber ?? "?")} of ${vosId}
|
|
8489
8313
|
watch: ${watchUrl2}
|
|
8490
|
-
studio: ${studioUrl2}`
|
|
8314
|
+
studio: ${studioUrl2}` + (landed2 ? `
|
|
8315
|
+
${landedLines(landed2)}` : "")
|
|
8491
8316
|
);
|
|
8492
8317
|
return EXIT_OK;
|
|
8493
8318
|
}
|
|
@@ -8523,20 +8348,35 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8523
8348
|
});
|
|
8524
8349
|
const watchUrl = `${origin}/vos/${created.id}`;
|
|
8525
8350
|
const studioUrl = `${origin}/studio?vos=${created.id}`;
|
|
8351
|
+
const landed = flags.wait === true ? await waitForLanded({
|
|
8352
|
+
origin,
|
|
8353
|
+
key,
|
|
8354
|
+
vosId: created.id,
|
|
8355
|
+
versionId: created.currentVersionId,
|
|
8356
|
+
log: (l) => r.log(l)
|
|
8357
|
+
}) : null;
|
|
8526
8358
|
r.done(
|
|
8527
8359
|
{
|
|
8528
8360
|
id: created.id,
|
|
8529
8361
|
slug: created.slug,
|
|
8530
8362
|
title,
|
|
8531
8363
|
visibility: "private",
|
|
8364
|
+
folder: folderRef ?? null,
|
|
8532
8365
|
remixOfId: remixOfId ?? null,
|
|
8533
8366
|
currentVersionId: created.currentVersionId,
|
|
8534
8367
|
watchUrl,
|
|
8535
|
-
studioUrl
|
|
8368
|
+
studioUrl,
|
|
8369
|
+
...landed ? {
|
|
8370
|
+
still: landed.still,
|
|
8371
|
+
thumbnailUrl: landed.thumbnailUrl,
|
|
8372
|
+
previewUrl: landed.previewUrl
|
|
8373
|
+
} : {}
|
|
8536
8374
|
},
|
|
8537
8375
|
`Created private vos ${created.id} (${title})
|
|
8538
8376
|
watch: ${watchUrl}
|
|
8539
8377
|
studio: ${studioUrl}
|
|
8378
|
+
` + (landed ? `${landedLines(landed)}
|
|
8379
|
+
` : "") + ` shelf: ${folderRef ? `in ${folderRef}` : `unfiled, at the root of your shelf (file it: vos folder move ${created.id} --to <slug>)`}
|
|
8540
8380
|
Iterate with: vos push ${source} --vos ${created.id}`
|
|
8541
8381
|
);
|
|
8542
8382
|
return EXIT_OK;
|
|
@@ -8608,10 +8448,10 @@ async function cmdPullProgram(argv) {
|
|
|
8608
8448
|
const cfg = await apiJson(origin, `/api/vos/${vosId}/config`, { key });
|
|
8609
8449
|
if (cfg.status !== 200)
|
|
8610
8450
|
throw new Error(apiError(`fetch head config for ${vosId}`, cfg));
|
|
8611
|
-
const configPath =
|
|
8451
|
+
const configPath = join17(dir, "config.json");
|
|
8612
8452
|
let backedUp = false;
|
|
8613
8453
|
if (existsSync15(configPath)) {
|
|
8614
|
-
await writeFile11(
|
|
8454
|
+
await writeFile11(join17(dir, "config.backup.json"), await readFile15(configPath));
|
|
8615
8455
|
backedUp = true;
|
|
8616
8456
|
}
|
|
8617
8457
|
let headConfig = cfg.body.config;
|
|
@@ -8644,7 +8484,7 @@ async function cmdPullProgram(argv) {
|
|
|
8644
8484
|
protected: protectedIds,
|
|
8645
8485
|
changes,
|
|
8646
8486
|
out: configPath,
|
|
8647
|
-
backup: backedUp ?
|
|
8487
|
+
backup: backedUp ? join17(dir, "config.backup.json") : null
|
|
8648
8488
|
},
|
|
8649
8489
|
`Pulled ${changes.length} version${changes.length === 1 ? "" : "s"} \u2192 ${configPath}` + (backedUp ? ` (previous copy: config.backup.json)` : "") + `
|
|
8650
8490
|
Re-apply your edit on the new head, then: vos push ${configPath} --vos ${vosId}`
|
|
@@ -8715,15 +8555,18 @@ async function storeKey(key, origin, r) {
|
|
|
8715
8555
|
function isTakeDir(target) {
|
|
8716
8556
|
return directoryKind(target) === "take";
|
|
8717
8557
|
}
|
|
8558
|
+
function soundOf(doc) {
|
|
8559
|
+
return { audio: Array.isArray(doc.audio) ? doc.audio : [] };
|
|
8560
|
+
}
|
|
8718
8561
|
function storedProgramConfig(config, programDoc) {
|
|
8719
8562
|
if (!programDoc) return config;
|
|
8720
8563
|
const doc = { audio: [], ...programDoc };
|
|
8721
8564
|
return lowerProgramDoc(doc).config;
|
|
8722
8565
|
}
|
|
8723
8566
|
async function readProgramDoc(dir, config) {
|
|
8724
|
-
const docPath =
|
|
8567
|
+
const docPath = join17(dir, "doc.json");
|
|
8725
8568
|
if (!existsSync15(docPath)) return null;
|
|
8726
|
-
const parsed = JSON.parse(await
|
|
8569
|
+
const parsed = JSON.parse(await readFile15(docPath, "utf8"));
|
|
8727
8570
|
if (typeof parsed !== "object" || parsed === null || "source" in parsed)
|
|
8728
8571
|
return null;
|
|
8729
8572
|
const raw = parsed;
|
|
@@ -8734,14 +8577,15 @@ async function writeProgramDoc(dir, hosted) {
|
|
|
8734
8577
|
const program = hosted.program && typeof hosted.program === "object" ? hosted.program : {};
|
|
8735
8578
|
const { config, ...rest } = program;
|
|
8736
8579
|
const onDisk = { ...hosted, program: rest };
|
|
8737
|
-
await writeFile11(
|
|
8580
|
+
await writeFile11(join17(dir, "doc.json"), JSON.stringify(onDisk, null, 2));
|
|
8738
8581
|
return {
|
|
8739
8582
|
config: config && typeof config === "object" ? config : null
|
|
8740
8583
|
};
|
|
8741
8584
|
}
|
|
8742
8585
|
|
|
8743
8586
|
// src/plugin/asset.ts
|
|
8744
|
-
import {
|
|
8587
|
+
import { createHash as createHash3 } from "crypto";
|
|
8588
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
8745
8589
|
import { basename as basename5, extname as extname2 } from "path";
|
|
8746
8590
|
var BOOLEAN_FLAGS3 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
8747
8591
|
var MIME_BY_EXT = {
|
|
@@ -8792,10 +8636,38 @@ async function cmdPush(argv) {
|
|
|
8792
8636
|
const folderRef = strFlag(flags, "folder");
|
|
8793
8637
|
const folder = folderRef ? resolveFolder(await listFolders(origin, key), folderRef) : null;
|
|
8794
8638
|
const uploaded = [];
|
|
8639
|
+
let unfiledMedia = 0;
|
|
8795
8640
|
for (const file of positionals) {
|
|
8796
8641
|
const name = basename5(file);
|
|
8642
|
+
const body = readFileSync3(file);
|
|
8643
|
+
const media = resolveMediaType({
|
|
8644
|
+
head: body.subarray(0, MEDIA_HEAD_BYTES),
|
|
8645
|
+
filename: name
|
|
8646
|
+
});
|
|
8647
|
+
if (/^(audio|video)\//.test(media.type)) {
|
|
8648
|
+
const sent = nameForType(name, media.type);
|
|
8649
|
+
let put;
|
|
8650
|
+
try {
|
|
8651
|
+
put = await uploadAsset({ origin, key }, new Uint8Array(body), {
|
|
8652
|
+
filename: sent,
|
|
8653
|
+
contentType: media.type,
|
|
8654
|
+
contentHash: createHash3("sha256").update(body).digest("hex"),
|
|
8655
|
+
...folder ? { folderId: folder.id } : {}
|
|
8656
|
+
});
|
|
8657
|
+
} catch (e) {
|
|
8658
|
+
const landed = uploaded.length ? ` (${uploaded.length} of ${positionals.length} landed before it)` : "";
|
|
8659
|
+
throw new Error(
|
|
8660
|
+
`push ${name}${landed}: ${e instanceof Error ? e.message : String(e)}`
|
|
8661
|
+
);
|
|
8662
|
+
}
|
|
8663
|
+
if (!folder && !put.reused) unfiledMedia += 1;
|
|
8664
|
+
uploaded.push({ id: put.id, filename: sent });
|
|
8665
|
+
r.log(
|
|
8666
|
+
`uploaded ${sent} (${put.id})${put.reused ? " \u2014 already on your shelf" : ""} ${put.url}`
|
|
8667
|
+
);
|
|
8668
|
+
continue;
|
|
8669
|
+
}
|
|
8797
8670
|
const mime = MIME_BY_EXT[extname2(name).toLowerCase()] ?? "application/octet-stream";
|
|
8798
|
-
const body = readFileSync4(file);
|
|
8799
8671
|
const form = new FormData();
|
|
8800
8672
|
form.set("file", new Blob([body], { type: mime }), name);
|
|
8801
8673
|
if (folder) form.set("folderId", folder.id);
|
|
@@ -8829,7 +8701,8 @@ async function cmdPush(argv) {
|
|
|
8829
8701
|
count: uploaded.length,
|
|
8830
8702
|
folder: folder?.slug ?? null
|
|
8831
8703
|
},
|
|
8832
|
-
`pushed ${uploaded.length} asset${uploaded.length === 1 ? "" : "s"}${folder ? ` into ${folder.slug}` : ""}`
|
|
8704
|
+
`pushed ${uploaded.length} asset${uploaded.length === 1 ? "" : "s"}${folder ? ` into ${folder.slug}` : ""}` + (unfiledMedia ? `
|
|
8705
|
+
note: an unfiled sound or video that no document uses is removed after 7 days. Reference it from a doc (doc.audio key = its url) or push it with --folder <slug>` : "")
|
|
8833
8706
|
);
|
|
8834
8707
|
return EXIT_OK;
|
|
8835
8708
|
}
|
|
@@ -8848,10 +8721,69 @@ async function cmdAsset(argv) {
|
|
|
8848
8721
|
}
|
|
8849
8722
|
}
|
|
8850
8723
|
|
|
8724
|
+
// src/plugin/remove.ts
|
|
8725
|
+
import { existsSync as existsSync16, rmSync as rmSync2, statSync as statSync4 } from "fs";
|
|
8726
|
+
import { join as join18 } from "path";
|
|
8727
|
+
import { createInterface as createInterface3 } from "readline/promises";
|
|
8728
|
+
var BOOLEAN_FLAGS4 = /* @__PURE__ */ new Set(["json", "help", "yes"]);
|
|
8729
|
+
async function cmdDelete(argv) {
|
|
8730
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS4);
|
|
8731
|
+
const target = positionals[0];
|
|
8732
|
+
if (!target)
|
|
8733
|
+
throw new UsageError("vos delete <vosId|watch-url|dir> [--yes] [--json]");
|
|
8734
|
+
const r = createReporter(flags.json === true);
|
|
8735
|
+
const origin = platformOrigin({
|
|
8736
|
+
origin: strFlag(flags, "origin"),
|
|
8737
|
+
api: strFlag(flags, "api")
|
|
8738
|
+
}).replace(/\/+$/, "");
|
|
8739
|
+
const key = requireCredential(strFlag(flags, "key"));
|
|
8740
|
+
const dir = existsSync16(target) && statSync4(target).isDirectory() ? target : null;
|
|
8741
|
+
const vosId = dir ? readSyncState(dir)?.vosId : parseVosId(target);
|
|
8742
|
+
if (!vosId)
|
|
8743
|
+
throw new UsageError(
|
|
8744
|
+
`${target} tracks no vos (no ${SYNC_STATE_NAME}): pass the vos id or its watch URL`
|
|
8745
|
+
);
|
|
8746
|
+
const meta = await apiJson(origin, `/api/vos/${vosId}`, { key });
|
|
8747
|
+
if (meta.status !== 200) throw new Error(apiError(`read ${vosId}`, meta));
|
|
8748
|
+
const vos = meta.body.vos ?? {};
|
|
8749
|
+
const title = typeof vos.title === "string" ? vos.title : vosId;
|
|
8750
|
+
if (flags.yes !== true) {
|
|
8751
|
+
if (!process.stdin.isTTY)
|
|
8752
|
+
throw new UsageError(
|
|
8753
|
+
`deleting "${title}" (${vosId}) cannot be undone: re-run with --yes to confirm`
|
|
8754
|
+
);
|
|
8755
|
+
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
8756
|
+
const answer = await rl.question(
|
|
8757
|
+
`Delete "${title}" (${vosId}) and every version of it? This cannot be undone. [y/N] `
|
|
8758
|
+
);
|
|
8759
|
+
rl.close();
|
|
8760
|
+
if (!/^y(es)?$/i.test(answer.trim())) {
|
|
8761
|
+
r.done({ id: vosId, deleted: false }, "kept \u2014 nothing was deleted");
|
|
8762
|
+
return EXIT_OK;
|
|
8763
|
+
}
|
|
8764
|
+
}
|
|
8765
|
+
const res = await apiJson(origin, `/api/vos/${vosId}`, {
|
|
8766
|
+
method: "DELETE",
|
|
8767
|
+
key
|
|
8768
|
+
});
|
|
8769
|
+
if (res.status !== 200 && res.status !== 204)
|
|
8770
|
+
throw new Error(apiError(`delete ${vosId}`, res));
|
|
8771
|
+
let unlinked = false;
|
|
8772
|
+
if (dir) {
|
|
8773
|
+
rmSync2(join18(dir, SYNC_STATE_NAME), { force: true });
|
|
8774
|
+
unlinked = true;
|
|
8775
|
+
}
|
|
8776
|
+
r.done(
|
|
8777
|
+
{ id: vosId, title, deleted: true, unlinked },
|
|
8778
|
+
`deleted "${title}" (${vosId})${unlinked ? `; ${dir} no longer tracks it` : ""}`
|
|
8779
|
+
);
|
|
8780
|
+
return EXIT_OK;
|
|
8781
|
+
}
|
|
8782
|
+
|
|
8851
8783
|
// src/plugin/recipe.ts
|
|
8852
|
-
import { readFileSync as
|
|
8784
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
8853
8785
|
import { basename as basename6 } from "path";
|
|
8854
|
-
var
|
|
8786
|
+
var BOOLEAN_FLAGS5 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
8855
8787
|
var USAGE = "vos recipe push <file.md> (--folder <folderId|slug> | --asset <assetId>)";
|
|
8856
8788
|
function recipeFilename(file) {
|
|
8857
8789
|
const name = basename6(file);
|
|
@@ -8861,7 +8793,7 @@ function recipeFilename(file) {
|
|
|
8861
8793
|
return name;
|
|
8862
8794
|
}
|
|
8863
8795
|
async function cmdPush2(argv) {
|
|
8864
|
-
const { positionals, flags } = parseArgs(argv,
|
|
8796
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
|
|
8865
8797
|
const [file] = positionals;
|
|
8866
8798
|
const folderRef = strFlag(flags, "folder");
|
|
8867
8799
|
const assetId = strFlag(flags, "asset");
|
|
@@ -8869,7 +8801,7 @@ async function cmdPush2(argv) {
|
|
|
8869
8801
|
throw new UsageError(USAGE);
|
|
8870
8802
|
}
|
|
8871
8803
|
const filename = recipeFilename(file);
|
|
8872
|
-
const body =
|
|
8804
|
+
const body = readFileSync4(file);
|
|
8873
8805
|
const r = createReporter(flags.json === true);
|
|
8874
8806
|
const origin = platformOrigin({
|
|
8875
8807
|
origin: strFlag(flags, "origin"),
|
|
@@ -9890,8 +9822,8 @@ async function cmdActions(argv) {
|
|
|
9890
9822
|
if (!m) throw new UsageError("--viewport expects WxH, e.g. 1280x720");
|
|
9891
9823
|
viewport = { width: Number(m[1]), height: Number(m[2]) };
|
|
9892
9824
|
}
|
|
9893
|
-
const { readFile:
|
|
9894
|
-
const text = await
|
|
9825
|
+
const { readFile: readFile18 } = await import("fs/promises");
|
|
9826
|
+
const text = await readFile18(resolve8(input), "utf8");
|
|
9895
9827
|
const { records, problems } = parseAgentBrowserLog(text);
|
|
9896
9828
|
for (const p of problems) r.log(` ${p}`);
|
|
9897
9829
|
const result = convertAgentBrowser(records, {
|
|
@@ -9928,9 +9860,9 @@ function summary(result) {
|
|
|
9928
9860
|
}
|
|
9929
9861
|
async function cmdActionsScript(input, flags) {
|
|
9930
9862
|
const r = createReporter(flags.json === true);
|
|
9931
|
-
const { readFile:
|
|
9863
|
+
const { readFile: readFile18 } = await import("fs/promises");
|
|
9932
9864
|
const { shotList, formatShotList } = await import("./shotList-KH6FTX4L.js");
|
|
9933
|
-
const raw = JSON.parse(await
|
|
9865
|
+
const raw = JSON.parse(await readFile18(resolve8(input), "utf8"));
|
|
9934
9866
|
const errors = validateActions(raw);
|
|
9935
9867
|
if (errors.length) throw new UsageError(`${input}:
|
|
9936
9868
|
${errors.join("\n ")}`);
|
|
@@ -9940,8 +9872,8 @@ async function cmdActionsScript(input, flags) {
|
|
|
9940
9872
|
}
|
|
9941
9873
|
|
|
9942
9874
|
// src/plugin/ingest.ts
|
|
9943
|
-
import { existsSync as
|
|
9944
|
-
import { copyFile, mkdir as mkdir10, readFile as
|
|
9875
|
+
import { existsSync as existsSync17 } from "fs";
|
|
9876
|
+
import { copyFile, mkdir as mkdir10, readFile as readFile16, stat as stat4 } from "fs/promises";
|
|
9945
9877
|
import { basename as basename7, join as join19, resolve as resolve9 } from "path";
|
|
9946
9878
|
import { inflateRawSync } from "zlib";
|
|
9947
9879
|
import {
|
|
@@ -9962,7 +9894,7 @@ import {
|
|
|
9962
9894
|
Output,
|
|
9963
9895
|
WebMOutputFormat
|
|
9964
9896
|
} from "mediabunny";
|
|
9965
|
-
var
|
|
9897
|
+
var BOOLEAN_FLAGS6 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
9966
9898
|
async function probeVideoFile(path) {
|
|
9967
9899
|
const input = new Input({
|
|
9968
9900
|
source: new FilePathSource(path),
|
|
@@ -10030,7 +9962,7 @@ async function readTraceFile(path, offsetMs) {
|
|
|
10030
9962
|
);
|
|
10031
9963
|
}
|
|
10032
9964
|
if (kind === "zip") {
|
|
10033
|
-
const bytes = new Uint8Array(await
|
|
9965
|
+
const bytes = new Uint8Array(await readFile16(path));
|
|
10034
9966
|
const entry = playwrightTraceEntry(zipEntries(bytes));
|
|
10035
9967
|
if (!entry) {
|
|
10036
9968
|
throw new UsageError(
|
|
@@ -10043,7 +9975,7 @@ async function readTraceFile(path, offsetMs) {
|
|
|
10043
9975
|
);
|
|
10044
9976
|
return cursorFromPlaywrightTrace(text2, { offsetMs });
|
|
10045
9977
|
}
|
|
10046
|
-
const text = await
|
|
9978
|
+
const text = await readFile16(path, "utf8");
|
|
10047
9979
|
return cursorFromTrace(basename7(path), text, { offsetMs });
|
|
10048
9980
|
}
|
|
10049
9981
|
function parseViewport(v) {
|
|
@@ -10053,20 +9985,28 @@ function parseViewport(v) {
|
|
|
10053
9985
|
return { width: Number(m[1]), height: Number(m[2]) };
|
|
10054
9986
|
}
|
|
10055
9987
|
async function cmdIngest(argv) {
|
|
10056
|
-
const { positionals, flags } = parseArgs(argv,
|
|
9988
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6, MULTI_FLAGS2);
|
|
10057
9989
|
const source = positionals[0];
|
|
10058
9990
|
if (!source)
|
|
10059
9991
|
throw new UsageError(
|
|
10060
|
-
"vos ingest <video.webm|mp4> [--cursor <trace.zip|steps.jsonl|cursor.csv>] [--out take] [--offset <ms>] [--viewport WxH] [--background <slug|url|none>] [--json]"
|
|
9992
|
+
"vos ingest <video.webm|mp4> [--cursor <trace.zip|steps.jsonl|cursor.csv>] [--as footage|take] [--out take] [--offset <ms>] [--viewport WxH] [--background <slug|url|none>] [--json]"
|
|
10061
9993
|
);
|
|
10062
9994
|
const videoPath = resolve9(source);
|
|
10063
|
-
if (!
|
|
9995
|
+
if (!existsSync17(videoPath)) throw new UsageError(`${source}: no such file`);
|
|
10064
9996
|
const r = createReporter(flags.json === true);
|
|
10065
9997
|
const outDir = resolve9(strFlag(flags, "out") ?? "take");
|
|
10066
9998
|
const offsetMs = numFlag(flags, "offset", 0);
|
|
10067
9999
|
const viewportFlag = parseViewport(strFlag(flags, "viewport"));
|
|
10068
10000
|
const cursorPath = strFlag(flags, "cursor");
|
|
10069
|
-
const
|
|
10001
|
+
const asFlag = strFlag(flags, "as");
|
|
10002
|
+
if (asFlag !== void 0 && asFlag !== "footage" && asFlag !== "take")
|
|
10003
|
+
throw new UsageError("--as expects footage or take");
|
|
10004
|
+
const footage = asFlag ? asFlag === "footage" : !cursorPath;
|
|
10005
|
+
if (footage && strFlag(flags, "background") !== void 0)
|
|
10006
|
+
r.log(
|
|
10007
|
+
"note: finished footage opens bare, so --background is not used (pass --as take for the screen-recording card and its ground)"
|
|
10008
|
+
);
|
|
10009
|
+
const backdrop = footage ? null : await takeBackdrop(flags, r);
|
|
10070
10010
|
r.log(`probing ${basename7(videoPath)}\u2026`);
|
|
10071
10011
|
const probed = await probeVideoFile(videoPath);
|
|
10072
10012
|
if (!(probed.durationMs > 0)) {
|
|
@@ -10079,7 +10019,7 @@ async function cmdIngest(argv) {
|
|
|
10079
10019
|
trace = await readTraceFile(resolve9(cursorPath), offsetMs);
|
|
10080
10020
|
for (const n of trace.notes) r.log(`note: ${n}`);
|
|
10081
10021
|
}
|
|
10082
|
-
if (
|
|
10022
|
+
if (existsSync17(join19(outDir, "meta.json"))) {
|
|
10083
10023
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
10084
10024
|
r.log(
|
|
10085
10025
|
`note: re-ingesting into ${outDir}; kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? `; apply the previous cut with: vos plan ${outDir} --reuse` : "")
|
|
@@ -10112,12 +10052,13 @@ async function cmdIngest(argv) {
|
|
|
10112
10052
|
};
|
|
10113
10053
|
await writeJson(paths.meta, meta, true);
|
|
10114
10054
|
if (events.length) await writeJson(paths.cursor, events);
|
|
10115
|
-
const plan = await planTake(outDir, { backdrop });
|
|
10055
|
+
const plan = await planTake(outDir, { backdrop, footage });
|
|
10116
10056
|
const clicks = events.filter((e) => e.type === "down").length;
|
|
10117
10057
|
const seconds = (probed.durationMs / 1e3).toFixed(1);
|
|
10118
10058
|
const facts = `${seconds}s, ${probed.width}\xD7${probed.height} @ ${meta.fps} fps${probed.hasAudio ? ", audio" : ", no audio"}${remuxed ? "" : ", copied as is"}`;
|
|
10119
10059
|
const cursorLine = trace ? `${events.length} cursor events (${clicks} clicks) from ${basename7(cursorPath)}${trace.dropped ? `, ${trace.dropped} not placed` : ""}; ${plan.zoomAuto} zoom${plan.zoomAuto === 1 ? "" : "s"} planned` : "no cursor track, so nothing was planned";
|
|
10120
|
-
const
|
|
10060
|
+
const opened = footage ? `opened as finished footage: no card, no browser bar, no drawn cursor, ${meta.fps > 30 ? 60 : 30} fps export (--as take for a screen recording's card)` : `opened as a screen recording: the card, its ground and the drawn cursor (--as footage to open it bare)`;
|
|
10061
|
+
const next = footage && !trace ? `Next: vos push ${outDir} --wait \u2014 it lands as it is; trim or add to it in the studio` : trace ? `Next: vos digest ${outDir} \u2014 look before you cut; edit doc.json; vos push ${outDir}` : `Add --cursor <trace> recorded beside the video (a Playwright trace.zip, stamped JSON records, or a t,x,y,type CSV) to plan zooms from its clicks; else place them by eye: vos frames ${outDir} --times 0,25%,50%,75%,100%, then edit doc.json`;
|
|
10121
10062
|
r.done(
|
|
10122
10063
|
{
|
|
10123
10064
|
out: outDir,
|
|
@@ -10139,9 +10080,11 @@ async function cmdIngest(argv) {
|
|
|
10139
10080
|
notes: trace.notes
|
|
10140
10081
|
} : null,
|
|
10141
10082
|
zooms: plan.zoomAuto,
|
|
10142
|
-
backdrop: plan.backdrop ?? null
|
|
10083
|
+
backdrop: plan.backdrop ?? null,
|
|
10084
|
+
as: footage ? "footage" : "take"
|
|
10143
10085
|
},
|
|
10144
10086
|
`Ingested ${basename7(videoPath)} \u2192 ${outDir}/${basename7(recording)} (${facts})
|
|
10087
|
+
${opened}
|
|
10145
10088
|
${cursorLine}
|
|
10146
10089
|
${next}`
|
|
10147
10090
|
);
|
|
@@ -10152,18 +10095,18 @@ async function cmdIngest(argv) {
|
|
|
10152
10095
|
import { spawnSync } from "child_process";
|
|
10153
10096
|
import {
|
|
10154
10097
|
cpSync,
|
|
10155
|
-
existsSync as
|
|
10156
|
-
mkdirSync as
|
|
10157
|
-
readFileSync as
|
|
10158
|
-
rmSync as
|
|
10159
|
-
writeFileSync as
|
|
10098
|
+
existsSync as existsSync19,
|
|
10099
|
+
mkdirSync as mkdirSync2,
|
|
10100
|
+
readFileSync as readFileSync6,
|
|
10101
|
+
rmSync as rmSync3,
|
|
10102
|
+
writeFileSync as writeFileSync2
|
|
10160
10103
|
} from "fs";
|
|
10161
|
-
import { homedir as
|
|
10104
|
+
import { homedir as homedir2 } from "os";
|
|
10162
10105
|
import { join as join21 } from "path";
|
|
10163
10106
|
import { chromium as chromium2 } from "playwright";
|
|
10164
10107
|
|
|
10165
10108
|
// src/plugin/machine.ts
|
|
10166
|
-
import { existsSync as
|
|
10109
|
+
import { existsSync as existsSync18, readFileSync as readFileSync5, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
|
|
10167
10110
|
import { dirname as dirname5, join as join20 } from "path";
|
|
10168
10111
|
var AGENT_NAMES = [
|
|
10169
10112
|
"claude",
|
|
@@ -10184,7 +10127,7 @@ var HOME_DIRS = {
|
|
|
10184
10127
|
copilot: [".copilot"]
|
|
10185
10128
|
};
|
|
10186
10129
|
function detectAgents(root, opts = {}) {
|
|
10187
|
-
const exists = opts.exists ??
|
|
10130
|
+
const exists = opts.exists ?? existsSync18;
|
|
10188
10131
|
const out = [];
|
|
10189
10132
|
for (const agent of AGENT_NAMES) {
|
|
10190
10133
|
for (const rel of PROJECT_DIRS[agent]) {
|
|
@@ -10231,7 +10174,7 @@ function skillsDirFor(home) {
|
|
|
10231
10174
|
function fallbackSkillsHome(root) {
|
|
10232
10175
|
return { agent: "codex", dir: join20(root, ".agents"), scope: "project" };
|
|
10233
10176
|
}
|
|
10234
|
-
function installedSkills(dir, exists =
|
|
10177
|
+
function installedSkills(dir, exists = existsSync18, list = (p) => {
|
|
10235
10178
|
try {
|
|
10236
10179
|
return readdirSync4(p);
|
|
10237
10180
|
} catch {
|
|
@@ -10271,7 +10214,7 @@ ${RULES_BLOCK}
|
|
|
10271
10214
|
}
|
|
10272
10215
|
return { text, changed: text !== current };
|
|
10273
10216
|
}
|
|
10274
|
-
function rulesFileFor(root, exists =
|
|
10217
|
+
function rulesFileFor(root, exists = existsSync18) {
|
|
10275
10218
|
const agents = join20(root, "AGENTS.md");
|
|
10276
10219
|
if (exists(agents)) return agents;
|
|
10277
10220
|
const claude = join20(root, "CLAUDE.md");
|
|
@@ -10378,12 +10321,12 @@ function bundledSkillsDir(from) {
|
|
|
10378
10321
|
let dir = dirname5(from);
|
|
10379
10322
|
for (let i = 0; i < 6; i++) {
|
|
10380
10323
|
const pkg = join20(dir, "package.json");
|
|
10381
|
-
if (
|
|
10324
|
+
if (existsSync18(pkg)) {
|
|
10382
10325
|
try {
|
|
10383
|
-
const name = JSON.parse(
|
|
10326
|
+
const name = JSON.parse(readFileSync5(pkg, "utf8")).name;
|
|
10384
10327
|
if (name === "@vosjs/cli") {
|
|
10385
10328
|
const skills = join20(dir, "skills");
|
|
10386
|
-
return
|
|
10329
|
+
return existsSync18(skills) && statSync5(skills).isDirectory() ? skills : null;
|
|
10387
10330
|
}
|
|
10388
10331
|
} catch {
|
|
10389
10332
|
}
|
|
@@ -10396,7 +10339,7 @@ function bundledSkillsDir(from) {
|
|
|
10396
10339
|
}
|
|
10397
10340
|
|
|
10398
10341
|
// src/plugin/machineCmd.ts
|
|
10399
|
-
var
|
|
10342
|
+
var BOOLEAN_FLAGS7 = /* @__PURE__ */ new Set([
|
|
10400
10343
|
"json",
|
|
10401
10344
|
"help",
|
|
10402
10345
|
"global",
|
|
@@ -10414,7 +10357,7 @@ function npx() {
|
|
|
10414
10357
|
function bundledChromium() {
|
|
10415
10358
|
try {
|
|
10416
10359
|
const p = chromium2.executablePath();
|
|
10417
|
-
return p &&
|
|
10360
|
+
return p && existsSync19(p) ? p : null;
|
|
10418
10361
|
} catch {
|
|
10419
10362
|
return null;
|
|
10420
10363
|
}
|
|
@@ -10423,7 +10366,7 @@ function probe() {
|
|
|
10423
10366
|
return probeBrowser({
|
|
10424
10367
|
platform: process.platform,
|
|
10425
10368
|
env: process.env,
|
|
10426
|
-
exists:
|
|
10369
|
+
exists: existsSync19,
|
|
10427
10370
|
bundled: bundledChromium
|
|
10428
10371
|
});
|
|
10429
10372
|
}
|
|
@@ -10474,7 +10417,7 @@ async function doctorFacts(opts) {
|
|
|
10474
10417
|
};
|
|
10475
10418
|
}
|
|
10476
10419
|
async function cmdSetup(argv) {
|
|
10477
|
-
const { flags } = parseArgs(argv,
|
|
10420
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS7);
|
|
10478
10421
|
const r = reporterFor(flags);
|
|
10479
10422
|
const root = process.cwd();
|
|
10480
10423
|
const global = flags.global === true;
|
|
@@ -10489,7 +10432,7 @@ async function cmdSetup(argv) {
|
|
|
10489
10432
|
if ("error" in v) throw new UsageError(v.error);
|
|
10490
10433
|
homes = v.homes;
|
|
10491
10434
|
} else {
|
|
10492
|
-
homes = detectAgents(root, { home:
|
|
10435
|
+
homes = detectAgents(root, { home: homedir2(), global });
|
|
10493
10436
|
}
|
|
10494
10437
|
const agents = [...new Set(homes.map((h) => h.agent))];
|
|
10495
10438
|
r.event({ event: "agents", agents, global });
|
|
@@ -10525,7 +10468,7 @@ async function cmdSetup(argv) {
|
|
|
10525
10468
|
const targets = homes.length ? homes : [fallbackSkillsHome(root)];
|
|
10526
10469
|
for (const home of targets) {
|
|
10527
10470
|
const dest = skillsDirFor(home);
|
|
10528
|
-
|
|
10471
|
+
mkdirSync2(dest, { recursive: true });
|
|
10529
10472
|
for (const name of installedSkills(bundled)) {
|
|
10530
10473
|
cpSync(join21(bundled, name), join21(dest, name), { recursive: true });
|
|
10531
10474
|
}
|
|
@@ -10562,9 +10505,9 @@ async function cmdSetup(argv) {
|
|
|
10562
10505
|
let rules = null;
|
|
10563
10506
|
if (!noRules) {
|
|
10564
10507
|
const file = rulesFileFor(root);
|
|
10565
|
-
const existing =
|
|
10508
|
+
const existing = existsSync19(file) ? readFileSync6(file, "utf8") : null;
|
|
10566
10509
|
const { text, changed } = applyRulesBlock(existing);
|
|
10567
|
-
if (changed)
|
|
10510
|
+
if (changed) writeFileSync2(file, text);
|
|
10568
10511
|
rules = { file, changed };
|
|
10569
10512
|
r.log(
|
|
10570
10513
|
changed ? `rules: wrote the vosso block into ${file}` : `rules: ${file} already carries the block`
|
|
@@ -10593,10 +10536,10 @@ Next: ${report.next_step}`
|
|
|
10593
10536
|
return report.ok ? EXIT_OK : EXIT_NO_BROWSER;
|
|
10594
10537
|
}
|
|
10595
10538
|
async function cmdDoctor(argv) {
|
|
10596
|
-
const { flags } = parseArgs(argv,
|
|
10539
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS7);
|
|
10597
10540
|
const r = reporterFor(flags);
|
|
10598
10541
|
const root = process.cwd();
|
|
10599
|
-
const homes = detectAgents(root, { home:
|
|
10542
|
+
const homes = detectAgents(root, { home: homedir2(), global: true });
|
|
10600
10543
|
const report = doctorReport(
|
|
10601
10544
|
await doctorFacts({
|
|
10602
10545
|
homes: homes.length ? homes : [fallbackSkillsHome(root)],
|
|
@@ -10612,7 +10555,7 @@ Next: ${report.next_step}`
|
|
|
10612
10555
|
return report.ok ? EXIT_OK : EXIT_NO_BROWSER;
|
|
10613
10556
|
}
|
|
10614
10557
|
async function cmdWhoami(argv) {
|
|
10615
|
-
const { flags } = parseArgs(argv,
|
|
10558
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS7);
|
|
10616
10559
|
const r = reporterFor(flags);
|
|
10617
10560
|
const origin = platformOrigin({
|
|
10618
10561
|
origin: strFlag(flags, "origin"),
|
|
@@ -10674,10 +10617,10 @@ async function cmdWhoami(argv) {
|
|
|
10674
10617
|
return EXIT_OK;
|
|
10675
10618
|
}
|
|
10676
10619
|
function cmdLogout(argv) {
|
|
10677
|
-
const { flags } = parseArgs(argv,
|
|
10620
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS7);
|
|
10678
10621
|
const r = reporterFor(flags);
|
|
10679
|
-
const had =
|
|
10680
|
-
|
|
10622
|
+
const had = existsSync19(CREDENTIALS_PATH);
|
|
10623
|
+
rmSync3(CREDENTIALS_PATH, { force: true });
|
|
10681
10624
|
const envKey = Boolean(process.env.VOS_API_KEY?.trim());
|
|
10682
10625
|
r.done(
|
|
10683
10626
|
{
|
|
@@ -10692,9 +10635,10 @@ function cmdLogout(argv) {
|
|
|
10692
10635
|
}
|
|
10693
10636
|
|
|
10694
10637
|
// src/plugin/run.ts
|
|
10695
|
-
var
|
|
10638
|
+
var BOOLEAN_FLAGS8 = /* @__PURE__ */ new Set([
|
|
10696
10639
|
"json",
|
|
10697
10640
|
"help",
|
|
10641
|
+
"wait",
|
|
10698
10642
|
"picture",
|
|
10699
10643
|
"fresh",
|
|
10700
10644
|
"reuse",
|
|
@@ -10735,8 +10679,8 @@ Take pipeline
|
|
|
10735
10679
|
vos validate <actions.json|take|kit.json> [--picture] [--json]
|
|
10736
10680
|
vos judge <kit.json> --against <MANIFEST.json> [--out dir] [--json]
|
|
10737
10681
|
vos actions from-agent-browser <steps.jsonl> [--out actions.json] [--url <url>] [--viewport WxH] [--json]
|
|
10738
|
-
vos ingest <video.webm|mp4> [--cursor <trace.zip|steps.jsonl|cursor.csv>] [--out take] [--offset <ms>] [--viewport WxH] [--background <slug|url|none>] [--json]
|
|
10739
|
-
a take from a recording someone else made (a cloud agent's PR video, a Playwright run's video, a Loom export): the file becomes recording.<container>, meta.json from its own dimensions and duration, and a trace beside it becomes cursor.json so the planner has clicks to zoom on; without one, nothing is planned and the done event says so
|
|
10682
|
+
vos ingest <video.webm|mp4> [--cursor <trace.zip|steps.jsonl|cursor.csv>] [--as footage|take] [--out take] [--offset <ms>] [--viewport WxH] [--background <slug|url|none>] [--json]
|
|
10683
|
+
a take from a recording someone else made (a cloud agent's PR video, a Playwright run's video, a Loom export): the file becomes recording.<container>, meta.json from its own dimensions and duration, and a trace beside it becomes cursor.json so the planner has clicks to zoom on; without one, nothing is planned and the done event says so. Without a trace the file opens as FINISHED footage (a render from HyperFrames or Remotion, a film): no card, no browser bar, no drawn cursor, its own frame rate; --as take gives a screen demo the card
|
|
10740
10684
|
vos session open <url> --name <app> a plain Chrome window on a vos-owned profile; sign in, close it
|
|
10741
10685
|
vos session check <name> [--url <url>] headless: does the session still open the page? exit 0/4
|
|
10742
10686
|
vos session list | rm <name> what exists and how old; delete one
|
|
@@ -10772,7 +10716,7 @@ Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
|
|
|
10772
10716
|
vos push <config.json|take> [--vos id] [--title t] [--slug s] [--remix-of id]
|
|
10773
10717
|
[--desc d] [--tags a,b] [--folder <folderId|slug>]
|
|
10774
10718
|
[--note n] [--label l] [--base versionId] [--override <id>]... [--yes] [--json]
|
|
10775
|
-
[--claimable]
|
|
10719
|
+
[--claimable] [--wait]
|
|
10776
10720
|
a take DIRECTORY (doc.json) pushes recording + doc; a config.json
|
|
10777
10721
|
pushes the program. No --vos: create a PRIVATE vos; with --vos:
|
|
10778
10722
|
add a version against the tracked base \u2014 a stale push 409s WITH
|
|
@@ -10784,6 +10728,14 @@ Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
|
|
|
10784
10728
|
user and nowhere else; unclaimed work is deleted after 72h.
|
|
10785
10729
|
--override consents to touching
|
|
10786
10730
|
protected (human-edited) nodes, ONLY when the user asked.
|
|
10731
|
+
Without --folder a new vos lands unfiled, at the root of your
|
|
10732
|
+
shelf. A document's local sound (doc.json audio keys) uploads
|
|
10733
|
+
with the push. --wait stays until the version's still has
|
|
10734
|
+
rendered and prints absolute still and preview links.
|
|
10735
|
+
vos delete <vosId|watch-url|dir> [--yes] [--json]
|
|
10736
|
+
take a vos off vos.so, every version of it, for good. Asks on a
|
|
10737
|
+
terminal; headless it needs --yes. A dir that tracked it is
|
|
10738
|
+
unlinked (its vos.json removed)
|
|
10787
10739
|
vos pull [dir|take] [--vos <id>] [--since versionId] [--check] [--media] [--json]
|
|
10788
10740
|
what changed on vos.so since your base; syncs config.json (backup
|
|
10789
10741
|
kept) or the take's doc.json. ALWAYS pull before editing pushed work.
|
|
@@ -10805,7 +10757,10 @@ Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
|
|
|
10805
10757
|
vos asset push <file...> [--folder <folderId|slug>]
|
|
10806
10758
|
upload files onto your shelf (a key takes models .glb/.gltf,
|
|
10807
10759
|
recipes .md and images .png/.jpg/.webp/.gif \u2014 posters and
|
|
10808
|
-
store stills file into the release's project; 40 images/24h
|
|
10760
|
+
store stills file into the release's project; 40 images/24h \u2014
|
|
10761
|
+
and sound and video, which land in Sound > Uploads and print
|
|
10762
|
+
the url a doc.json audio key takes; an unfiled one that no
|
|
10763
|
+
document uses is removed after 7 days, so pass --folder)
|
|
10809
10764
|
vos recipe push <file.md> --folder <folderId|slug>
|
|
10810
10765
|
vos recipe push <file.md> --asset <assetId>
|
|
10811
10766
|
put a recipe (a plain .md) on the shelf: --folder creates it
|
|
@@ -10994,7 +10949,7 @@ lint-gated, so a bad override fails like a bad doc.json):
|
|
|
10994
10949
|
first ready loop (the house backdrop the studio opens on), or a flat ground offline
|
|
10995
10950
|
`;
|
|
10996
10951
|
async function loadActions(file) {
|
|
10997
|
-
const raw = JSON.parse(await
|
|
10952
|
+
const raw = JSON.parse(await readFile17(file, "utf8"));
|
|
10998
10953
|
const errors = validateActions(raw);
|
|
10999
10954
|
if (errors.length)
|
|
11000
10955
|
throw new UsageError(`invalid actions file:
|
|
@@ -11057,7 +11012,7 @@ function takeStorageState(flags) {
|
|
|
11057
11012
|
const raw = strFlag(flags, "storage-state");
|
|
11058
11013
|
if (!raw) return void 0;
|
|
11059
11014
|
const path = resolve10(raw);
|
|
11060
|
-
if (!
|
|
11015
|
+
if (!existsSync20(path))
|
|
11061
11016
|
throw new UsageError(`--storage-state: no such file: ${path}`);
|
|
11062
11017
|
return path;
|
|
11063
11018
|
}
|
|
@@ -11070,7 +11025,7 @@ function takeSessionName(flags, storageState) {
|
|
|
11070
11025
|
throw new UsageError(
|
|
11071
11026
|
"--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
|
|
11072
11027
|
);
|
|
11073
|
-
if (!
|
|
11028
|
+
if (!existsSync20(sessionDir(name)))
|
|
11074
11029
|
throw new UsageError(
|
|
11075
11030
|
`no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
|
|
11076
11031
|
);
|
|
@@ -11126,7 +11081,7 @@ function wallGate(flags, r, opts) {
|
|
|
11126
11081
|
async function cmdRecord(argv) {
|
|
11127
11082
|
const { positionals, flags, multi } = parseArgs(
|
|
11128
11083
|
argv,
|
|
11129
|
-
|
|
11084
|
+
BOOLEAN_FLAGS8,
|
|
11130
11085
|
MULTI_FLAGS2
|
|
11131
11086
|
);
|
|
11132
11087
|
const actionsPath = strFlag(flags, "actions") ?? positionals[0];
|
|
@@ -11293,7 +11248,7 @@ ${lines.join("\n")}
|
|
|
11293
11248
|
}
|
|
11294
11249
|
const paths = takePaths(outDir);
|
|
11295
11250
|
const prepare = async () => {
|
|
11296
|
-
if (
|
|
11251
|
+
if (existsSync20(join22(outDir, "meta.json"))) {
|
|
11297
11252
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
11298
11253
|
r.log(
|
|
11299
11254
|
`note: re-recording ${outDir} \u2014 kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? "; apply the previous cut to the new footage with: vos plan " + outDir + " --reuse" : "")
|
|
@@ -11368,7 +11323,7 @@ ${lines.join("\n")}
|
|
|
11368
11323
|
async function cmdCreate2(argv) {
|
|
11369
11324
|
const { positionals, flags, multi } = parseArgs(
|
|
11370
11325
|
argv,
|
|
11371
|
-
|
|
11326
|
+
BOOLEAN_FLAGS8,
|
|
11372
11327
|
MULTI_FLAGS2
|
|
11373
11328
|
);
|
|
11374
11329
|
const actionsPath = strFlag(flags, "actions");
|
|
@@ -11383,10 +11338,7 @@ async function cmdCreate2(argv) {
|
|
|
11383
11338
|
if (!url)
|
|
11384
11339
|
throw new UsageError('no URL \u2014 set "url" in the actions file or pass --url');
|
|
11385
11340
|
const outDir = resolve10(strFlag(flags, "out") ?? "take");
|
|
11386
|
-
const
|
|
11387
|
-
if (fmtRaw !== "webm" && fmtRaw !== "mp4")
|
|
11388
|
-
throw new UsageError("--format must be webm or mp4");
|
|
11389
|
-
const format = fmtRaw;
|
|
11341
|
+
const format = videoFormat(strFlag(flags, "format"), positionals[0]);
|
|
11390
11342
|
const out = positionals[0] ?? join22(outDir, `out.${format}`);
|
|
11391
11343
|
const parallel = numFlag(flags, "parallel", 1);
|
|
11392
11344
|
if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
|
|
@@ -11400,7 +11352,7 @@ async function cmdCreate2(argv) {
|
|
|
11400
11352
|
const headers = takeHeaders(multi);
|
|
11401
11353
|
const paths = takePaths(outDir);
|
|
11402
11354
|
const prepare = async () => {
|
|
11403
|
-
if (
|
|
11355
|
+
if (existsSync20(join22(outDir, "meta.json"))) {
|
|
11404
11356
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
11405
11357
|
r.log(
|
|
11406
11358
|
`note: re-recording ${outDir} \u2014 kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? "; apply the previous cut to the new footage with: vos plan " + outDir + " --reuse" : "")
|
|
@@ -11497,7 +11449,7 @@ async function cmdCreate2(argv) {
|
|
|
11497
11449
|
async function cmdPlan(argv) {
|
|
11498
11450
|
const { positionals, flags, multi } = parseArgs(
|
|
11499
11451
|
argv,
|
|
11500
|
-
|
|
11452
|
+
BOOLEAN_FLAGS8,
|
|
11501
11453
|
/* @__PURE__ */ new Set(["with"])
|
|
11502
11454
|
);
|
|
11503
11455
|
const dir = positionals[0];
|
|
@@ -11513,18 +11465,18 @@ async function cmdPlan(argv) {
|
|
|
11513
11465
|
let reuse;
|
|
11514
11466
|
if (flags.reuse === true) {
|
|
11515
11467
|
const from = resolve10(strFlag(flags, "from") ?? join22(dir, PREV_DOC_NAME));
|
|
11516
|
-
if (!
|
|
11468
|
+
if (!existsSync20(from)) {
|
|
11517
11469
|
throw new UsageError(
|
|
11518
11470
|
`--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
|
|
11519
11471
|
);
|
|
11520
11472
|
}
|
|
11521
11473
|
reuse = {
|
|
11522
11474
|
from,
|
|
11523
|
-
doc: JSON.parse(await
|
|
11475
|
+
doc: JSON.parse(await readFile17(from, "utf8"))
|
|
11524
11476
|
};
|
|
11525
11477
|
}
|
|
11526
11478
|
const style = await resolveStyleRef(flags);
|
|
11527
|
-
const hasDoc =
|
|
11479
|
+
const hasDoc = existsSync20(join22(dir, "doc.json"));
|
|
11528
11480
|
const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
|
|
11529
11481
|
const release = await releaseInputs(dir, flags, r, multi.with ?? []);
|
|
11530
11482
|
const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
|
|
@@ -11695,7 +11647,7 @@ async function releaseInputs(dir, flags, r, withRefs = []) {
|
|
|
11695
11647
|
async function cmdRender(argv) {
|
|
11696
11648
|
const { positionals, flags, multi } = parseArgs(
|
|
11697
11649
|
argv,
|
|
11698
|
-
|
|
11650
|
+
BOOLEAN_FLAGS8,
|
|
11699
11651
|
MULTI_FLAGS2
|
|
11700
11652
|
);
|
|
11701
11653
|
const dir = positionals[0];
|
|
@@ -11704,10 +11656,7 @@ async function cmdRender(argv) {
|
|
|
11704
11656
|
"vos render <take> [out] [--width] [--height] [--fps] [--format] [--parallel]"
|
|
11705
11657
|
);
|
|
11706
11658
|
const r = createReporter(flags.json === true);
|
|
11707
|
-
const
|
|
11708
|
-
if (fmtRaw !== "webm" && fmtRaw !== "mp4")
|
|
11709
|
-
throw new UsageError("--format must be webm or mp4");
|
|
11710
|
-
const format = fmtRaw;
|
|
11659
|
+
const format = videoFormat(strFlag(flags, "format"), positionals[1]);
|
|
11711
11660
|
const out = positionals[1] ?? join22(dir, `out.${format}`);
|
|
11712
11661
|
const parallel = numFlag(flags, "parallel", 1);
|
|
11713
11662
|
if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
|
|
@@ -11766,7 +11715,7 @@ async function cmdRender(argv) {
|
|
|
11766
11715
|
async function cmdFrames(argv) {
|
|
11767
11716
|
const { positionals, flags, multi } = parseArgs(
|
|
11768
11717
|
argv,
|
|
11769
|
-
|
|
11718
|
+
BOOLEAN_FLAGS8,
|
|
11770
11719
|
MULTI_FLAGS2
|
|
11771
11720
|
);
|
|
11772
11721
|
const dir = positionals[0];
|
|
@@ -11839,7 +11788,7 @@ async function cmdFrames(argv) {
|
|
|
11839
11788
|
async function cmdDeliver(argv) {
|
|
11840
11789
|
const { positionals, flags, multi } = parseArgs(
|
|
11841
11790
|
argv,
|
|
11842
|
-
|
|
11791
|
+
BOOLEAN_FLAGS8,
|
|
11843
11792
|
MULTI_FLAGS2
|
|
11844
11793
|
);
|
|
11845
11794
|
const dir = positionals[0];
|
|
@@ -11955,15 +11904,15 @@ async function resolveStyleRef(flags) {
|
|
|
11955
11904
|
}
|
|
11956
11905
|
async function resolveDocRef(ref, flags, what) {
|
|
11957
11906
|
const styleRef = ref;
|
|
11958
|
-
const file =
|
|
11907
|
+
const file = existsSync20(styleRef) ? resolve10(
|
|
11959
11908
|
styleRef,
|
|
11960
|
-
|
|
11909
|
+
existsSync20(join22(styleRef, "doc.json")) ? "doc.json" : ""
|
|
11961
11910
|
) : null;
|
|
11962
|
-
if (file &&
|
|
11911
|
+
if (file && existsSync20(file)) {
|
|
11963
11912
|
return {
|
|
11964
11913
|
from: file,
|
|
11965
11914
|
doc: migrateHostedDoc5(
|
|
11966
|
-
JSON.parse(await
|
|
11915
|
+
JSON.parse(await readFile17(file, "utf8"))
|
|
11967
11916
|
)
|
|
11968
11917
|
};
|
|
11969
11918
|
}
|
|
@@ -12005,7 +11954,7 @@ async function officialTemplate(origin, ref) {
|
|
|
12005
11954
|
}
|
|
12006
11955
|
}
|
|
12007
11956
|
async function cmdDigest(argv) {
|
|
12008
|
-
const { positionals, flags } = parseArgs(argv,
|
|
11957
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
12009
11958
|
const dir = positionals[0];
|
|
12010
11959
|
if (!dir)
|
|
12011
11960
|
throw new UsageError(
|
|
@@ -12015,7 +11964,7 @@ async function cmdDigest(argv) {
|
|
|
12015
11964
|
const take = await loadTake(dir);
|
|
12016
11965
|
if (!take.doc) throw new UsageError(`${dir} has no doc.json \u2014 run plan first`);
|
|
12017
11966
|
const transcriptPath = strFlag(flags, "transcript");
|
|
12018
|
-
const transcript = transcriptPath ? parseTranscript(JSON.parse(await
|
|
11967
|
+
const transcript = transcriptPath ? parseTranscript(JSON.parse(await readFile17(transcriptPath, "utf8"))) : null;
|
|
12019
11968
|
const style = await resolveStyleRef(flags);
|
|
12020
11969
|
const noFrames = flags["no-frames"] === true;
|
|
12021
11970
|
let browser = null;
|
|
@@ -12069,12 +12018,12 @@ async function cmdDigest(argv) {
|
|
|
12069
12018
|
}
|
|
12070
12019
|
}
|
|
12071
12020
|
async function cmdOpen(argv) {
|
|
12072
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12021
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
12073
12022
|
const dir = positionals[0];
|
|
12074
12023
|
if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
|
|
12075
12024
|
const r = createReporter(flags.json === true);
|
|
12076
12025
|
const take = await loadTake(dir);
|
|
12077
|
-
if (!
|
|
12026
|
+
if (!existsSync20(take.paths.recording)) {
|
|
12078
12027
|
throw new UsageError(`${dir} has no recording \u2014 re-run record`);
|
|
12079
12028
|
}
|
|
12080
12029
|
const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
|
|
@@ -12107,7 +12056,7 @@ async function cmdOpen(argv) {
|
|
|
12107
12056
|
return EXIT_OK;
|
|
12108
12057
|
}
|
|
12109
12058
|
async function cmdJudge(argv) {
|
|
12110
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12059
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
12111
12060
|
const target = positionals[0];
|
|
12112
12061
|
const against = strFlag(flags, "against");
|
|
12113
12062
|
if (!target || !against)
|
|
@@ -12116,11 +12065,11 @@ async function cmdJudge(argv) {
|
|
|
12116
12065
|
);
|
|
12117
12066
|
const r = createReporter(flags.json === true);
|
|
12118
12067
|
const kitFile = target.endsWith("kit.json") ? target : join22(target, "kit.json");
|
|
12119
|
-
if (!
|
|
12120
|
-
if (!
|
|
12068
|
+
if (!existsSync20(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
|
|
12069
|
+
if (!existsSync20(against))
|
|
12121
12070
|
throw new UsageError(`${against}: no reference manifest`);
|
|
12122
12071
|
const result = await judgeKit(kitFile, against, strFlag(flags, "out"));
|
|
12123
|
-
const verdicts = JSON.parse(await
|
|
12072
|
+
const verdicts = JSON.parse(await readFile17(result.verdictFile, "utf8")).verdicts;
|
|
12124
12073
|
const rate = winRate(verdicts);
|
|
12125
12074
|
const lines = result.sheets.map(
|
|
12126
12075
|
(s) => `${s.asset} vs ${s.reference}: ${s.sheetA}, ${s.sheetB}, ${s.rubric}`
|
|
@@ -12136,7 +12085,7 @@ Win rate so far: ${rate.wins} win(s), ${rate.ties} tie(s) of ${rate.judged} judg
|
|
|
12136
12085
|
return EXIT_OK;
|
|
12137
12086
|
}
|
|
12138
12087
|
async function cmdCallout(argv) {
|
|
12139
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12088
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
12140
12089
|
const dir = positionals[0];
|
|
12141
12090
|
const shape = positionals[1];
|
|
12142
12091
|
if (!dir || !shape || !isCalloutShape(shape))
|
|
@@ -12160,7 +12109,7 @@ async function cmdCallout(argv) {
|
|
|
12160
12109
|
`no register: put a BRAND.md beside the take (vos brand <url> --out BRAND.md; its bgA and accent are the ground and the accent), or pass --ground #hex --accent #hex`
|
|
12161
12110
|
);
|
|
12162
12111
|
const codeFile = strFlag(flags, "code-file");
|
|
12163
|
-
const code = codeFile ? await
|
|
12112
|
+
const code = codeFile ? await readFile17(codeFile, "utf8") : strFlag(flags, "code");
|
|
12164
12113
|
const words = {
|
|
12165
12114
|
kicker: strFlag(flags, "kicker"),
|
|
12166
12115
|
title: strFlag(flags, "title"),
|
|
@@ -12235,11 +12184,11 @@ async function cmdCallout(argv) {
|
|
|
12235
12184
|
return EXIT_OK;
|
|
12236
12185
|
}
|
|
12237
12186
|
async function cmdValidate(argv) {
|
|
12238
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12187
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
12239
12188
|
const target = positionals[0];
|
|
12240
12189
|
if (!target) throw new UsageError("vos validate <actions.json|take>");
|
|
12241
12190
|
const r = createReporter(flags.json === true);
|
|
12242
|
-
const kitTarget = target.endsWith("kit.json") ? target :
|
|
12191
|
+
const kitTarget = target.endsWith("kit.json") ? target : existsSync20(join22(target, "kit.json")) && !isTakeDir(target) ? join22(target, "kit.json") : null;
|
|
12243
12192
|
if (kitTarget) {
|
|
12244
12193
|
const picture = flags.picture === true;
|
|
12245
12194
|
const verdict = await validateKit(kitTarget, { picture });
|
|
@@ -12271,9 +12220,9 @@ async function cmdValidate(argv) {
|
|
|
12271
12220
|
r.done({ valid: true, target }, `${target}: valid actions file`);
|
|
12272
12221
|
return EXIT_OK;
|
|
12273
12222
|
}
|
|
12274
|
-
if (!isTakeDir(target) &&
|
|
12223
|
+
if (!isTakeDir(target) && existsSync20(join22(target, "config.json"))) {
|
|
12275
12224
|
const parsed = JSON.parse(
|
|
12276
|
-
await
|
|
12225
|
+
await readFile17(join22(target, "config.json"), "utf8")
|
|
12277
12226
|
);
|
|
12278
12227
|
const pre = preflightConfig(parsed);
|
|
12279
12228
|
const problems2 = pre.ok ? [] : pre.issues.map((i) => `config.json: ${i}`);
|
|
@@ -12314,7 +12263,7 @@ async function cmdValidate(argv) {
|
|
|
12314
12263
|
const take = await loadTake(target);
|
|
12315
12264
|
const problems = [];
|
|
12316
12265
|
const warnings = [];
|
|
12317
|
-
if (!
|
|
12266
|
+
if (!existsSync20(take.paths.recording)) {
|
|
12318
12267
|
problems.push("missing recording (re-run record)");
|
|
12319
12268
|
} else {
|
|
12320
12269
|
const mismatch = containerMismatch(
|
|
@@ -12397,7 +12346,7 @@ async function cmdValidate(argv) {
|
|
|
12397
12346
|
async function cmdPush3(argv) {
|
|
12398
12347
|
const { positionals, flags, multi } = parseArgs(
|
|
12399
12348
|
argv,
|
|
12400
|
-
|
|
12349
|
+
BOOLEAN_FLAGS8,
|
|
12401
12350
|
MULTI_FLAGS2
|
|
12402
12351
|
);
|
|
12403
12352
|
const dir = positionals[0];
|
|
@@ -12435,16 +12384,33 @@ async function cmdPush3(argv) {
|
|
|
12435
12384
|
origin: strFlag(flags, "origin"),
|
|
12436
12385
|
api: strFlag(flags, "api")
|
|
12437
12386
|
}).replace(/\/+$/, "");
|
|
12387
|
+
const landed = flags.wait === true ? await waitForLanded({
|
|
12388
|
+
origin: pushedTo,
|
|
12389
|
+
key: requireCredential(strFlag(flags, "key")),
|
|
12390
|
+
vosId: result.vosId,
|
|
12391
|
+
versionId: result.versionId ?? null,
|
|
12392
|
+
log: (l) => r.log(l)
|
|
12393
|
+
}) : null;
|
|
12394
|
+
const filed = result.versionNumber === 1 ? `
|
|
12395
|
+
shelf: ${strFlag(flags, "folder") ? `in ${strFlag(flags, "folder")}` : `unfiled, at the root of your shelf (file it: vos folder move ${result.vosId} --to <slug>)`}` : "";
|
|
12438
12396
|
r.done(
|
|
12439
|
-
{
|
|
12397
|
+
{
|
|
12398
|
+
...result,
|
|
12399
|
+
...landed ? {
|
|
12400
|
+
still: landed.still,
|
|
12401
|
+
thumbnailUrl: landed.thumbnailUrl,
|
|
12402
|
+
previewUrl: landed.previewUrl
|
|
12403
|
+
} : {}
|
|
12404
|
+
},
|
|
12440
12405
|
`pushed v${result.versionNumber} \u2192 vos ${result.vosId}
|
|
12441
12406
|
review: ${pushedTo}/vos/${result.vosId}
|
|
12442
|
-
studio: ${pushedTo}/studio?vos=${result.vosId}`
|
|
12407
|
+
studio: ${pushedTo}/studio?vos=${result.vosId}` + filed + (landed ? `
|
|
12408
|
+
${landedLines(landed)}` : "")
|
|
12443
12409
|
);
|
|
12444
12410
|
return EXIT_OK;
|
|
12445
12411
|
}
|
|
12446
12412
|
async function cmdPull2(argv) {
|
|
12447
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12413
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS8, MULTI_FLAGS2);
|
|
12448
12414
|
const dir = positionals[0] ?? ".";
|
|
12449
12415
|
const r = createReporter(flags.json === true);
|
|
12450
12416
|
const result = await pullTake(
|
|
@@ -12555,6 +12521,8 @@ async function run(argv) {
|
|
|
12555
12521
|
const target = rest.find((a) => !a.startsWith("-")) ?? ".";
|
|
12556
12522
|
return isTakeDir(target) ? await cmdPull2(rest) : await cmdPullProgram(rest);
|
|
12557
12523
|
}
|
|
12524
|
+
case "delete":
|
|
12525
|
+
return await cmdDelete(rest);
|
|
12558
12526
|
case "folder":
|
|
12559
12527
|
return await cmdFolder(rest);
|
|
12560
12528
|
case "asset":
|
|
@@ -12601,7 +12569,7 @@ async function run(argv) {
|
|
|
12601
12569
|
}
|
|
12602
12570
|
}
|
|
12603
12571
|
async function cmdSession(argv) {
|
|
12604
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12572
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
12605
12573
|
const [sub, arg] = positionals;
|
|
12606
12574
|
const r = createReporter(flags.json === true);
|
|
12607
12575
|
const usage = "vos session open <url> --name <app> | check <name> [--url <url>] | list | rm <name>";
|
|
@@ -12637,7 +12605,7 @@ async function cmdSession(argv) {
|
|
|
12637
12605
|
if (!arg) throw new UsageError(usage);
|
|
12638
12606
|
const bad = sessionNameVerdict(arg);
|
|
12639
12607
|
if (bad) throw new UsageError(bad);
|
|
12640
|
-
if (!
|
|
12608
|
+
if (!existsSync20(sessionDir(arg)))
|
|
12641
12609
|
throw new UsageError(`no session "${arg}" (vos session list)`);
|
|
12642
12610
|
const summary2 = await summarizeSession(arg);
|
|
12643
12611
|
const url = strFlag(flags, "url");
|
|
@@ -12728,13 +12696,6 @@ export {
|
|
|
12728
12696
|
digestTake,
|
|
12729
12697
|
parseTranscript,
|
|
12730
12698
|
renderTake,
|
|
12731
|
-
platformOrigin,
|
|
12732
|
-
parseVosId,
|
|
12733
|
-
resolveCredential,
|
|
12734
|
-
apiJson,
|
|
12735
|
-
apiError,
|
|
12736
|
-
readSyncState,
|
|
12737
|
-
writeSyncState,
|
|
12738
12699
|
recordTake,
|
|
12739
12700
|
encodeRecording,
|
|
12740
12701
|
planTake,
|
|
@@ -12748,4 +12709,4 @@ export {
|
|
|
12748
12709
|
verbHelp,
|
|
12749
12710
|
run
|
|
12750
12711
|
};
|
|
12751
|
-
//# sourceMappingURL=chunk-
|
|
12712
|
+
//# sourceMappingURL=chunk-F2QCWV4U.js.map
|