@vosjs/cli 0.60.1 → 0.63.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 +5 -3
- package/dist/{assetManifest-Y5FYTZW6.js → assetManifest-OMN33M2A.js} +2 -2
- package/dist/{chunk-QL25OPZB.js → chunk-AKIPDSHE.js} +656 -222
- package/dist/chunk-AKIPDSHE.js.map +1 -0
- package/dist/{chunk-WZGCEAXB.js → chunk-AUFY3M22.js} +2 -2
- package/dist/{chunk-WZGCEAXB.js.map → chunk-AUFY3M22.js.map} +1 -1
- package/dist/{chunk-YMVRTZ4L.js → chunk-PT73CRXX.js} +48 -77
- package/dist/chunk-PT73CRXX.js.map +1 -0
- package/dist/cli.js +5 -5
- package/dist/index.js +3 -3
- package/dist/manifest-ZBK6IERM.js +8 -0
- package/dist/{run-BJ77A4G6.js → run-OGG3NP2N.js} +3 -3
- package/package.json +4 -4
- package/schema/doc.schema.json +1 -1
- package/skills/VERSION +1 -1
- package/skills/vos-authoring/references/schema-reference.md +2 -0
- package/skills/vos-remix/references/3d-recipe.md +16 -10
- package/dist/chunk-QL25OPZB.js.map +0 -1
- package/dist/chunk-YMVRTZ4L.js.map +0 -1
- package/dist/manifest-Q4QMVIYR.js +0 -8
- /package/dist/{assetManifest-Y5FYTZW6.js.map → assetManifest-OMN33M2A.js.map} +0 -0
- /package/dist/{manifest-Q4QMVIYR.js.map → manifest-ZBK6IERM.js.map} +0 -0
- /package/dist/{run-BJ77A4G6.js.map → run-OGG3NP2N.js.map} +0 -0
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
PREV_DOC_NAME,
|
|
15
15
|
RECORDING_NAME,
|
|
16
|
-
docAudioRefs,
|
|
17
16
|
docLayerRefs,
|
|
18
17
|
docMediaRefs,
|
|
19
18
|
dropScreencastFrames,
|
|
@@ -34,7 +33,7 @@ import {
|
|
|
34
33
|
uploadDocRefs,
|
|
35
34
|
uploadManifest,
|
|
36
35
|
writeJson
|
|
37
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-PT73CRXX.js";
|
|
38
37
|
import {
|
|
39
38
|
CREDENTIALS_PATH,
|
|
40
39
|
MEDIA_HEAD_BYTES,
|
|
@@ -48,6 +47,7 @@ import {
|
|
|
48
47
|
formatChanges,
|
|
49
48
|
hasFlag,
|
|
50
49
|
hostedAssetId,
|
|
50
|
+
localFile,
|
|
51
51
|
manifestRefs,
|
|
52
52
|
nameForType,
|
|
53
53
|
numFlag2 as numFlag,
|
|
@@ -127,7 +127,7 @@ function landedLines(l) {
|
|
|
127
127
|
|
|
128
128
|
// src/plugin/run.ts
|
|
129
129
|
import { mkdir as mkdir9, readFile as readFile15, rm as rm3 } from "fs/promises";
|
|
130
|
-
import { existsSync as
|
|
130
|
+
import { existsSync as existsSync24 } from "fs";
|
|
131
131
|
import { join as join26, resolve as resolve15 } from "path";
|
|
132
132
|
import {
|
|
133
133
|
END_CARD_FROM as END_CARD_FROM2,
|
|
@@ -146,6 +146,15 @@ function createReporter(json) {
|
|
|
146
146
|
json,
|
|
147
147
|
log: (msg) => {
|
|
148
148
|
if (!json) process.stderr.write(`${msg}
|
|
149
|
+
`);
|
|
150
|
+
},
|
|
151
|
+
warn: (msg) => {
|
|
152
|
+
if (json) {
|
|
153
|
+
process.stdout.write(
|
|
154
|
+
`${JSON.stringify({ event: "warning", message: msg })}
|
|
155
|
+
`
|
|
156
|
+
);
|
|
157
|
+
} else process.stderr.write(`warning ${msg}
|
|
149
158
|
`);
|
|
150
159
|
},
|
|
151
160
|
event: (obj) => {
|
|
@@ -2526,9 +2535,9 @@ function parsePath(path) {
|
|
|
2526
2535
|
if (expectKey) bad();
|
|
2527
2536
|
const j = path.indexOf("]", i);
|
|
2528
2537
|
if (j < 0) bad();
|
|
2529
|
-
const
|
|
2530
|
-
if (!/^\d+$/.test(
|
|
2531
|
-
segs.push({ key: Number(
|
|
2538
|
+
const num2 = path.slice(i + 1, j);
|
|
2539
|
+
if (!/^\d+$/.test(num2)) bad();
|
|
2540
|
+
segs.push({ key: Number(num2) });
|
|
2532
2541
|
i = j + 1;
|
|
2533
2542
|
} else {
|
|
2534
2543
|
let j = i;
|
|
@@ -3911,11 +3920,11 @@ var DUPLICATE_BITS = 6;
|
|
|
3911
3920
|
var POPULATED_INK = 0.12;
|
|
3912
3921
|
function pickMoments(measured, opts = {}) {
|
|
3913
3922
|
const minInk = opts.minInk ?? 0.06;
|
|
3914
|
-
const
|
|
3923
|
+
const relative4 = opts.relativeInk ?? 0.4;
|
|
3915
3924
|
const bits = opts.duplicateBits ?? DUPLICATE_BITS;
|
|
3916
3925
|
const inks = measured.map((m) => m.ink).sort((a, b) => a - b);
|
|
3917
3926
|
const median2 = inks[inks.length >> 1] ?? 0;
|
|
3918
|
-
const floor = Math.max(minInk, Math.min(POPULATED_INK, median2 *
|
|
3927
|
+
const floor = Math.max(minInk, Math.min(POPULATED_INK, median2 * relative4));
|
|
3919
3928
|
const kept = [];
|
|
3920
3929
|
const dropped = [];
|
|
3921
3930
|
for (const m of measured) {
|
|
@@ -7255,6 +7264,7 @@ async function pushTake(dir, flags, r) {
|
|
|
7255
7264
|
`recording upload failed: ${e instanceof Error ? e.message : String(e)}`
|
|
7256
7265
|
);
|
|
7257
7266
|
}
|
|
7267
|
+
for (const note of upload.notes) r.log(` note: ${note}`);
|
|
7258
7268
|
const assetId = upload.id;
|
|
7259
7269
|
const assetUrl = upload.url;
|
|
7260
7270
|
r.event({ event: "recording", assetId, reused: upload.reused });
|
|
@@ -7472,10 +7482,10 @@ async function pullTake(dir, flags, r) {
|
|
|
7472
7482
|
}
|
|
7473
7483
|
|
|
7474
7484
|
// src/plugin/program.ts
|
|
7475
|
-
import { existsSync as
|
|
7485
|
+
import { existsSync as existsSync14 } from "fs";
|
|
7476
7486
|
import { mkdir as mkdir7, readFile as readFile13, writeFile as writeFile9 } from "fs/promises";
|
|
7477
7487
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
7478
|
-
import { basename as
|
|
7488
|
+
import { basename as basename3, dirname as dirname3, join as join15 } from "path";
|
|
7479
7489
|
import {
|
|
7480
7490
|
CURRENT_CONFIG_VERSION,
|
|
7481
7491
|
migrateConfig,
|
|
@@ -7607,6 +7617,112 @@ async function handoffLogin(url, r, opts = {}) {
|
|
|
7607
7617
|
throw new Error(`${said}${hint}`);
|
|
7608
7618
|
}
|
|
7609
7619
|
|
|
7620
|
+
// src/plugin/programFiles.ts
|
|
7621
|
+
import { basename as basename2, extname as extname2, relative as relative2 } from "path";
|
|
7622
|
+
import { existsSync as existsSync13, realpathSync } from "fs";
|
|
7623
|
+
function isLocalName(value) {
|
|
7624
|
+
if (/^(https?:|data:|blob:|asset:|media:)/.test(value)) return false;
|
|
7625
|
+
if (value.startsWith("$")) return false;
|
|
7626
|
+
if (value.startsWith("/")) return existsSync13(value);
|
|
7627
|
+
return true;
|
|
7628
|
+
}
|
|
7629
|
+
var FILE_EXT = /\.(png|jpe?g|webp|gif|avif|svg|mp4|webm|mov|mp3|m4a|ogg|wav|flac|aac|glb|gltf|woff2?|ttf|otf|hdr|exr)$/i;
|
|
7630
|
+
function manifestName(file, taken) {
|
|
7631
|
+
const stem = basename2(file, extname2(file)).replace(/[^A-Za-z0-9_]/g, "_").replace(/^(?=[0-9])/, "_") || "file";
|
|
7632
|
+
let name = stem;
|
|
7633
|
+
for (let n = 2; taken.has(name); n++) name = `${stem}_${n}`;
|
|
7634
|
+
taken.add(name);
|
|
7635
|
+
return name;
|
|
7636
|
+
}
|
|
7637
|
+
function liftNamedFiles(config, dir) {
|
|
7638
|
+
const manifest = config.assets ?? {};
|
|
7639
|
+
const taken = new Set(Object.keys(manifest));
|
|
7640
|
+
const lifted = [];
|
|
7641
|
+
const lift = (where, value, kind) => {
|
|
7642
|
+
if (typeof value !== "string" || !value || !isLocalName(value)) return null;
|
|
7643
|
+
const found = localFile(value, dir);
|
|
7644
|
+
if ("refused" in found) {
|
|
7645
|
+
if (found.refused.startsWith("which is outside")) {
|
|
7646
|
+
throw new Error(`${where} names ${value}, ${found.refused}`);
|
|
7647
|
+
}
|
|
7648
|
+
return null;
|
|
7649
|
+
}
|
|
7650
|
+
const name = manifestName(value, taken);
|
|
7651
|
+
manifest[name] = kind ? { ref: value, kind } : { ref: value };
|
|
7652
|
+
lifted.push(`${where} \u2192 assets.${name}`);
|
|
7653
|
+
return `$assets.${name}`;
|
|
7654
|
+
};
|
|
7655
|
+
const elements = Array.isArray(config.elements) ? config.elements : [];
|
|
7656
|
+
elements.forEach((el, i) => {
|
|
7657
|
+
const e = el;
|
|
7658
|
+
if (!e || e.type === "text") return;
|
|
7659
|
+
const next = lift(`elements[${i}].src`, e.src);
|
|
7660
|
+
if (next) e.src = next;
|
|
7661
|
+
});
|
|
7662
|
+
const fonts = Array.isArray(config.fonts) ? config.fonts : [];
|
|
7663
|
+
fonts.forEach((f, i) => {
|
|
7664
|
+
const font = f;
|
|
7665
|
+
const next = lift(`fonts[${i}].url`, font?.url, "font");
|
|
7666
|
+
if (next) font.url = next;
|
|
7667
|
+
});
|
|
7668
|
+
if (lifted.length) config.assets = manifest;
|
|
7669
|
+
return lifted;
|
|
7670
|
+
}
|
|
7671
|
+
function localDataRefs(config, dir) {
|
|
7672
|
+
const refs = [];
|
|
7673
|
+
const root = realpathSync(dir);
|
|
7674
|
+
const walk = (value, where, set) => {
|
|
7675
|
+
if (typeof value === "string") {
|
|
7676
|
+
if (!isLocalName(value) || !FILE_EXT.test(value)) return;
|
|
7677
|
+
const found = localFile(value, dir);
|
|
7678
|
+
if ("refused" in found) return;
|
|
7679
|
+
refs.push({ where, key: relative2(root, found.file), set });
|
|
7680
|
+
return;
|
|
7681
|
+
}
|
|
7682
|
+
if (Array.isArray(value)) {
|
|
7683
|
+
value.forEach(
|
|
7684
|
+
(v, i) => walk(v, `${where}[${i}]`, (next) => {
|
|
7685
|
+
value[i] = next;
|
|
7686
|
+
})
|
|
7687
|
+
);
|
|
7688
|
+
return;
|
|
7689
|
+
}
|
|
7690
|
+
if (value && typeof value === "object") {
|
|
7691
|
+
const obj = value;
|
|
7692
|
+
for (const k of Object.keys(obj)) {
|
|
7693
|
+
walk(obj[k], `${where}.${k}`, (next) => {
|
|
7694
|
+
obj[k] = next;
|
|
7695
|
+
});
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7698
|
+
};
|
|
7699
|
+
walk(config.data, "data", () => {
|
|
7700
|
+
});
|
|
7701
|
+
return refs;
|
|
7702
|
+
}
|
|
7703
|
+
var FUNCTION_FIELDS = ["setup", "createContent", "createTimeline", "onFrame"];
|
|
7704
|
+
var QUOTED = /(['"`])([^'"`\n]{1,200}?)\1/g;
|
|
7705
|
+
function codeFileWarnings(config, dir) {
|
|
7706
|
+
const out = [];
|
|
7707
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7708
|
+
const scan = (label, src) => {
|
|
7709
|
+
if (typeof src !== "string") return;
|
|
7710
|
+
for (const m of src.matchAll(QUOTED)) {
|
|
7711
|
+
const value = m[2];
|
|
7712
|
+
if (seen.has(value) || !isLocalName(value) || !FILE_EXT.test(value)) {
|
|
7713
|
+
continue;
|
|
7714
|
+
}
|
|
7715
|
+
if ("refused" in localFile(value, dir)) continue;
|
|
7716
|
+
seen.add(value);
|
|
7717
|
+
out.push(
|
|
7718
|
+
`${label} names ${value} in code, which no push can carry. Declare it: "assets": { "name": { "ref": "${value}" } }, and read ctx.assets.name`
|
|
7719
|
+
);
|
|
7720
|
+
}
|
|
7721
|
+
};
|
|
7722
|
+
for (const field of FUNCTION_FIELDS) scan(field, config[field]);
|
|
7723
|
+
return out;
|
|
7724
|
+
}
|
|
7725
|
+
|
|
7610
7726
|
// src/plugin/program.ts
|
|
7611
7727
|
var BOOLEAN_FLAGS2 = /* @__PURE__ */ new Set([
|
|
7612
7728
|
"json",
|
|
@@ -7615,6 +7731,7 @@ var BOOLEAN_FLAGS2 = /* @__PURE__ */ new Set([
|
|
|
7615
7731
|
"check",
|
|
7616
7732
|
"yes",
|
|
7617
7733
|
"claimable",
|
|
7734
|
+
"share",
|
|
7618
7735
|
"no-browser",
|
|
7619
7736
|
"wait"
|
|
7620
7737
|
]);
|
|
@@ -7682,7 +7799,7 @@ function preflightConfig(parsed) {
|
|
|
7682
7799
|
function resolveConfigPath(target) {
|
|
7683
7800
|
if (target.endsWith(".json")) return target;
|
|
7684
7801
|
const inDir = join15(target, "config.json");
|
|
7685
|
-
if (
|
|
7802
|
+
if (existsSync14(inDir)) return inDir;
|
|
7686
7803
|
throw new UsageError(
|
|
7687
7804
|
`${target} has no config.json (and is not a take \u2014 no doc.json)`
|
|
7688
7805
|
);
|
|
@@ -7832,6 +7949,7 @@ async function createProgramVos(opts) {
|
|
|
7832
7949
|
client: clientId()
|
|
7833
7950
|
};
|
|
7834
7951
|
if (opts.description) body.description = opts.description;
|
|
7952
|
+
if (opts.prompt) body.prompt = opts.prompt;
|
|
7835
7953
|
if (opts.tags?.length) body.tags = opts.tags;
|
|
7836
7954
|
if (opts.folderId) body.folderId = opts.folderId;
|
|
7837
7955
|
if (opts.remixOfId) body.remixOfId = opts.remixOfId;
|
|
@@ -7866,7 +7984,7 @@ async function cmdPushProgram(argv) {
|
|
|
7866
7984
|
const target = positionals[0];
|
|
7867
7985
|
if (!target) {
|
|
7868
7986
|
throw new UsageError(
|
|
7869
|
-
|
|
7987
|
+
'vos push <config.json|take> [--vos id] [--title t] [--slug s] [--desc d] [--tags a,b] [--folder <id|slug>] [--remix-of id] [--note n] [--label l] [--base versionId] [--override id]... [--prompt "<the prompt that made it>"] [--claimable [--share]]'
|
|
7870
7988
|
);
|
|
7871
7989
|
}
|
|
7872
7990
|
const source = resolveConfigPath(target);
|
|
@@ -7886,6 +8004,12 @@ async function cmdPushProgram(argv) {
|
|
|
7886
8004
|
}
|
|
7887
8005
|
const config = pre.config;
|
|
7888
8006
|
const state = readSyncState(dir);
|
|
8007
|
+
const prompt = strFlag(flags, "prompt");
|
|
8008
|
+
if (flags.share === true && flags.claimable !== true) {
|
|
8009
|
+
throw new UsageError(
|
|
8010
|
+
'--share asks a claimable push to lead its claim page with "Claim and share". A key can never publish: open the watch page and set it to Unlisted or Public there'
|
|
8011
|
+
);
|
|
8012
|
+
}
|
|
7889
8013
|
const vosFlag = strFlag(flags, "vos");
|
|
7890
8014
|
const explicitVosId = vosFlag ? parseVosId(vosFlag) : null;
|
|
7891
8015
|
const stillFlag = strFlag(flags, "still");
|
|
@@ -7896,7 +8020,8 @@ async function cmdPushProgram(argv) {
|
|
|
7896
8020
|
await setProgramStill(dir, t);
|
|
7897
8021
|
r.log(`cover: ${t}s (doc.json "still")`);
|
|
7898
8022
|
}
|
|
7899
|
-
for (const w of hostedLiteralWarnings(config)) r.
|
|
8023
|
+
for (const w of hostedLiteralWarnings(config)) r.warn(w);
|
|
8024
|
+
for (const w of codeFileWarnings(config, dir)) r.warn(w);
|
|
7900
8025
|
const programDoc = await readProgramDoc(dir, config);
|
|
7901
8026
|
if (programDoc) {
|
|
7902
8027
|
const lint = lintDoc(programDoc);
|
|
@@ -7904,7 +8029,7 @@ async function cmdPushProgram(argv) {
|
|
|
7904
8029
|
throw new Error(`doc.json fails lint:
|
|
7905
8030
|
${lint.problems.join("\n ")}`);
|
|
7906
8031
|
}
|
|
7907
|
-
for (const w of lint.warnings) r.
|
|
8032
|
+
for (const w of lint.warnings) r.warn(`doc.json: ${w}`);
|
|
7908
8033
|
}
|
|
7909
8034
|
if (flags.claimable === true) {
|
|
7910
8035
|
if (explicitVosId) {
|
|
@@ -7912,43 +8037,50 @@ async function cmdPushProgram(argv) {
|
|
|
7912
8037
|
"--claimable creates a NEW claimable vos and cannot iterate (--vos). After the human claims it, iterate with their key: vos push --vos <id>"
|
|
7913
8038
|
);
|
|
7914
8039
|
}
|
|
7915
|
-
const fallback = state?.title ? `${state.title} remix` :
|
|
8040
|
+
const fallback = state?.title ? `${state.title} remix` : basename3(source).replace(/\.json$/i, "") || "vos program";
|
|
7916
8041
|
const title2 = (strFlag(flags, "title") ?? fallback).slice(0, 100);
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
const
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
audio: (sound.audio ?? []).filter((a) => !dropped.has(a.key))
|
|
7926
|
-
};
|
|
7927
|
-
r.log(
|
|
7928
|
-
`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"}`
|
|
7929
|
-
);
|
|
8042
|
+
const files2 = await collectFiles(config, programDoc, dir, r);
|
|
8043
|
+
let uploadToken;
|
|
8044
|
+
if (files2.count > 0) {
|
|
8045
|
+
const opened = await apiJson(origin, "/api/claim/uploads", {
|
|
8046
|
+
method: "POST"
|
|
8047
|
+
});
|
|
8048
|
+
if (opened.status !== 201) {
|
|
8049
|
+
throw new Error(apiError("open a claim upload session", opened));
|
|
7930
8050
|
}
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
throw new UsageError(
|
|
7935
|
-
`a claimable push carries no files, and this program declares ${stay.length} local file${stay.length === 1 ? "" : "s"} (${stay.join(", ")}). Push with a key instead (vos login, then vos push ${source}), which uploads ${stay.length === 1 ? "it" : "them"}`
|
|
8051
|
+
uploadToken = String(opened.body.uploadToken);
|
|
8052
|
+
r.log(
|
|
8053
|
+
`uploading ${files2.count} file${files2.count === 1 ? "" : "s"} with the claim`
|
|
7936
8054
|
);
|
|
8055
|
+
await files2.upload({
|
|
8056
|
+
origin,
|
|
8057
|
+
key: uploadToken,
|
|
8058
|
+
door: "/api/claim/uploads/files"
|
|
8059
|
+
});
|
|
7937
8060
|
}
|
|
7938
8061
|
const body = {
|
|
7939
8062
|
title: title2,
|
|
7940
|
-
config: storedProgramConfig(config,
|
|
8063
|
+
config: storedProgramConfig(config, programDoc)
|
|
7941
8064
|
};
|
|
8065
|
+
if (uploadToken) body.uploadToken = uploadToken;
|
|
8066
|
+
if (prompt) body.prompt = prompt;
|
|
8067
|
+
if (flags.share === true) body.share = true;
|
|
7942
8068
|
const slug = strFlag(flags, "slug");
|
|
7943
8069
|
if (slug) body.slug = slug;
|
|
7944
8070
|
const res = await apiJson(origin, "/api/claim", { method: "POST", body });
|
|
7945
8071
|
if (res.status !== 201) throw new Error(apiError("claimable push", res));
|
|
7946
|
-
serverWarnings(res.body, (
|
|
8072
|
+
serverWarnings(res.body, say(r));
|
|
7947
8073
|
const claimUrl = String(res.body.claimUrl ?? "");
|
|
7948
8074
|
const expiresAt = String(res.body.expiresAt ?? "");
|
|
7949
8075
|
const created2 = res.body.vos ?? {};
|
|
7950
8076
|
r.done(
|
|
7951
|
-
{
|
|
8077
|
+
{
|
|
8078
|
+
id: created2.id ?? null,
|
|
8079
|
+
title: title2,
|
|
8080
|
+
claimUrl,
|
|
8081
|
+
expiresAt,
|
|
8082
|
+
...uploadToken ? { files: files2.count } : {}
|
|
8083
|
+
},
|
|
7952
8084
|
`Claimable push created (${title2})
|
|
7953
8085
|
claim: ${claimUrl}
|
|
7954
8086
|
expires: ${expiresAt} \u2014 unclaimed work is deleted after 72h
|
|
@@ -7960,15 +8092,8 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
7960
8092
|
}
|
|
7961
8093
|
const key = requireCredential(strFlag(flags, "key"));
|
|
7962
8094
|
void flags.yes;
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
docLayerRefs(layersOf(programDoc)),
|
|
7966
|
-
dir,
|
|
7967
|
-
{ origin, key },
|
|
7968
|
-
(l) => r.log(l)
|
|
7969
|
-
);
|
|
7970
|
-
}
|
|
7971
|
-
await uploadManifest(config, dir, { origin, key }, (l) => r.log(l));
|
|
8095
|
+
const files = await collectFiles(config, programDoc, dir, r);
|
|
8096
|
+
await files.upload({ origin, key });
|
|
7972
8097
|
const overrides = [
|
|
7973
8098
|
...multi.override ?? [],
|
|
7974
8099
|
...strFlag(flags, "overrides")?.split(",") ?? []
|
|
@@ -8032,7 +8157,15 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8032
8157
|
}
|
|
8033
8158
|
if (res.status !== 201)
|
|
8034
8159
|
throw new Error(apiError(`push version to ${vosId}`, res));
|
|
8035
|
-
serverWarnings(res.body, (
|
|
8160
|
+
serverWarnings(res.body, say(r));
|
|
8161
|
+
if (prompt) {
|
|
8162
|
+
const set = await apiJson(origin, `/api/vos/${vosId}`, {
|
|
8163
|
+
method: "PATCH",
|
|
8164
|
+
key,
|
|
8165
|
+
body: { prompt }
|
|
8166
|
+
});
|
|
8167
|
+
if (set.status !== 200) r.warn(apiError("set the prompt", set));
|
|
8168
|
+
}
|
|
8036
8169
|
const version = res.body.version ?? {};
|
|
8037
8170
|
if (typeof version.id === "string") {
|
|
8038
8171
|
writeSyncState(dir, { vosId, versionId: version.id });
|
|
@@ -8044,7 +8177,7 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8044
8177
|
key,
|
|
8045
8178
|
vosId,
|
|
8046
8179
|
versionId: typeof version.id === "string" ? version.id : null,
|
|
8047
|
-
log: (
|
|
8180
|
+
log: say(r)
|
|
8048
8181
|
}) : null;
|
|
8049
8182
|
r.done(
|
|
8050
8183
|
{
|
|
@@ -8068,7 +8201,7 @@ ${landedLines(landed2)}` : "")
|
|
|
8068
8201
|
return EXIT_OK;
|
|
8069
8202
|
}
|
|
8070
8203
|
const remixOfId = pushTarget.kind === "create" ? pushTarget.remixOfId : void 0;
|
|
8071
|
-
const fallbackTitle = state?.title ? `${state.title} remix` :
|
|
8204
|
+
const fallbackTitle = state?.title ? `${state.title} remix` : basename3(source).replace(/\.json$/i, "") || "vos remix";
|
|
8072
8205
|
const title = (strFlag(flags, "title") ?? fallbackTitle).slice(0, 100);
|
|
8073
8206
|
const folderId = folderRef ? resolveFolder(await listFolders(origin, key), folderRef).id : void 0;
|
|
8074
8207
|
const created = await createProgramVos({
|
|
@@ -8080,6 +8213,7 @@ ${landedLines(landed2)}` : "")
|
|
|
8080
8213
|
// (found seeding one-layer members: `--with` then said the vos carried
|
|
8081
8214
|
// no doc).
|
|
8082
8215
|
doc: programDoc ?? void 0,
|
|
8216
|
+
prompt,
|
|
8083
8217
|
title,
|
|
8084
8218
|
slug: strFlag(flags, "slug"),
|
|
8085
8219
|
description: desc,
|
|
@@ -8104,7 +8238,7 @@ ${landedLines(landed2)}` : "")
|
|
|
8104
8238
|
key,
|
|
8105
8239
|
vosId: created.id,
|
|
8106
8240
|
versionId: created.currentVersionId,
|
|
8107
|
-
log: (
|
|
8241
|
+
log: say(r)
|
|
8108
8242
|
}) : null;
|
|
8109
8243
|
r.done(
|
|
8110
8244
|
{
|
|
@@ -8202,7 +8336,7 @@ async function cmdPullProgram(argv) {
|
|
|
8202
8336
|
throw new Error(apiError(`fetch head config for ${vosId}`, cfg));
|
|
8203
8337
|
const configPath = join15(dir, "config.json");
|
|
8204
8338
|
let backedUp = false;
|
|
8205
|
-
if (
|
|
8339
|
+
if (existsSync14(configPath)) {
|
|
8206
8340
|
await writeFile9(join15(dir, "config.backup.json"), await readFile13(configPath));
|
|
8207
8341
|
backedUp = true;
|
|
8208
8342
|
}
|
|
@@ -8307,6 +8441,27 @@ async function storeKey(key, origin, r) {
|
|
|
8307
8441
|
function isTakeDir(target) {
|
|
8308
8442
|
return directoryKind(target) === "take";
|
|
8309
8443
|
}
|
|
8444
|
+
function say(r) {
|
|
8445
|
+
return (line) => {
|
|
8446
|
+
const m = /^\s*warning\s+(.*)$/s.exec(line);
|
|
8447
|
+
if (m) r.warn(m[1]);
|
|
8448
|
+
else r.log(line);
|
|
8449
|
+
};
|
|
8450
|
+
}
|
|
8451
|
+
async function collectFiles(config, programDoc, dir, r) {
|
|
8452
|
+
for (const line of liftNamedFiles(config, dir)) r.log(` ${line}`);
|
|
8453
|
+
const docRefs = programDoc ? docLayerRefs(layersOf(programDoc)) : [];
|
|
8454
|
+
const dataRefs = localDataRefs(config, dir);
|
|
8455
|
+
const count = docRefs.length + localManifestRefs(config).length + dataRefs.length;
|
|
8456
|
+
return {
|
|
8457
|
+
count,
|
|
8458
|
+
upload: async (target) => {
|
|
8459
|
+
await uploadDocRefs(docRefs, dir, target, say(r));
|
|
8460
|
+
await uploadManifest(config, dir, target, say(r));
|
|
8461
|
+
await uploadDocRefs(dataRefs, dir, target, say(r));
|
|
8462
|
+
}
|
|
8463
|
+
};
|
|
8464
|
+
}
|
|
8310
8465
|
function soundOf(doc) {
|
|
8311
8466
|
return { audio: Array.isArray(doc.audio) ? doc.audio : [] };
|
|
8312
8467
|
}
|
|
@@ -8324,7 +8479,7 @@ function storedProgramConfig(config, programDoc) {
|
|
|
8324
8479
|
}
|
|
8325
8480
|
async function readProgramDoc(dir, config) {
|
|
8326
8481
|
const docPath = join15(dir, "doc.json");
|
|
8327
|
-
if (!
|
|
8482
|
+
if (!existsSync14(docPath)) return null;
|
|
8328
8483
|
const parsed = JSON.parse(await readFile13(docPath, "utf8"));
|
|
8329
8484
|
if (typeof parsed !== "object" || parsed === null || "source" in parsed)
|
|
8330
8485
|
return null;
|
|
@@ -8335,7 +8490,7 @@ async function readProgramDoc(dir, config) {
|
|
|
8335
8490
|
async function setProgramStill(dir, t) {
|
|
8336
8491
|
const docPath = join15(dir, "doc.json");
|
|
8337
8492
|
let doc = { program: {}, audio: [] };
|
|
8338
|
-
if (
|
|
8493
|
+
if (existsSync14(docPath)) {
|
|
8339
8494
|
const parsed = JSON.parse(await readFile13(docPath, "utf8"));
|
|
8340
8495
|
if (!parsed || typeof parsed !== "object" || "source" in parsed)
|
|
8341
8496
|
throw new UsageError(
|
|
@@ -8358,18 +8513,108 @@ async function writeProgramDoc(dir, hosted) {
|
|
|
8358
8513
|
// src/plugin/asset.ts
|
|
8359
8514
|
import { createHash as createHash2 } from "crypto";
|
|
8360
8515
|
import { readFileSync as readFileSync3 } from "fs";
|
|
8361
|
-
import { basename as
|
|
8516
|
+
import { basename as basename4 } from "path";
|
|
8517
|
+
|
|
8518
|
+
// src/plugin/assetWords.ts
|
|
8519
|
+
function hostedFile(raw) {
|
|
8520
|
+
const row = raw && typeof raw === "object" ? raw : {};
|
|
8521
|
+
const text = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
8522
|
+
return {
|
|
8523
|
+
...row,
|
|
8524
|
+
id: text(row.id),
|
|
8525
|
+
kind: text(row.kind) || text(row.category, "file"),
|
|
8526
|
+
filename: text(row.filename, "unnamed"),
|
|
8527
|
+
size: typeof row.size === "number" ? row.size : 0
|
|
8528
|
+
};
|
|
8529
|
+
}
|
|
8530
|
+
function bytesWords(bytes) {
|
|
8531
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "0 B";
|
|
8532
|
+
if (bytes < 1024) return `${Math.round(bytes)} B`;
|
|
8533
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
8534
|
+
let value = bytes / 1024;
|
|
8535
|
+
let unit = 0;
|
|
8536
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
8537
|
+
value /= 1024;
|
|
8538
|
+
unit++;
|
|
8539
|
+
}
|
|
8540
|
+
return `${value >= 100 ? Math.round(value) : value.toFixed(1)} ${units[unit]}`;
|
|
8541
|
+
}
|
|
8542
|
+
var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
8543
|
+
function probeLine(file) {
|
|
8544
|
+
const meta = file.metadata ?? {};
|
|
8545
|
+
const parts = [file.kind];
|
|
8546
|
+
const width = num(meta.width);
|
|
8547
|
+
const height = num(meta.height);
|
|
8548
|
+
if (width && height) parts.push(`${width}\xD7${height}`);
|
|
8549
|
+
if (typeof meta.videoCodec === "string") parts.push(meta.videoCodec);
|
|
8550
|
+
else if (typeof meta.audioCodec === "string" && file.kind === "audio") {
|
|
8551
|
+
parts.push(meta.audioCodec);
|
|
8552
|
+
}
|
|
8553
|
+
const seconds = num(file.duration);
|
|
8554
|
+
if (seconds && seconds > 0) {
|
|
8555
|
+
parts.push(`${seconds >= 10 ? seconds.toFixed(1) : seconds.toFixed(2)} s`);
|
|
8556
|
+
}
|
|
8557
|
+
let line = parts.join(" ");
|
|
8558
|
+
if (file.kind === "video" && meta.hasAudio === true) line += ", audio";
|
|
8559
|
+
if (file.kind === "model") {
|
|
8560
|
+
const meshes = num(meta.meshCount);
|
|
8561
|
+
if (meshes !== null) line += ` ${meshes} mesh${meshes === 1 ? "" : "es"}`;
|
|
8562
|
+
}
|
|
8563
|
+
return line;
|
|
8564
|
+
}
|
|
8565
|
+
function manifestName2(filename) {
|
|
8566
|
+
const stem = filename.replace(/\.[^.]+$/, "");
|
|
8567
|
+
const name = stem.replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").replace(/^(\d)/, "_$1");
|
|
8568
|
+
return name || "file";
|
|
8569
|
+
}
|
|
8570
|
+
var MANIFEST_KINDS = /* @__PURE__ */ new Set([
|
|
8571
|
+
"image",
|
|
8572
|
+
"video",
|
|
8573
|
+
"audio",
|
|
8574
|
+
"model",
|
|
8575
|
+
"font",
|
|
8576
|
+
"hdr"
|
|
8577
|
+
]);
|
|
8578
|
+
function manifestLine(file) {
|
|
8579
|
+
const kind = file.kind === "vector" ? "image" : file.kind;
|
|
8580
|
+
const hint = MANIFEST_KINDS.has(kind) ? `, "kind": "${kind}"` : "";
|
|
8581
|
+
return `"${manifestName2(file.filename)}": { "ref": "asset:${file.id}"${hint} }`;
|
|
8582
|
+
}
|
|
8583
|
+
function listRow(file) {
|
|
8584
|
+
return [
|
|
8585
|
+
file.id,
|
|
8586
|
+
file.kind,
|
|
8587
|
+
bytesWords(file.size),
|
|
8588
|
+
file.kind === "recipe" ? "" : file.uses ? `used in ${file.uses}` : "not used",
|
|
8589
|
+
file.intent === "attached" ? "attached" : "library",
|
|
8590
|
+
file.filename
|
|
8591
|
+
];
|
|
8592
|
+
}
|
|
8593
|
+
function alignRows(rows) {
|
|
8594
|
+
if (rows.length === 0) return [];
|
|
8595
|
+
const widths = rows[0].map(
|
|
8596
|
+
(_, col) => Math.max(...rows.map((row) => (row[col] ?? "").length))
|
|
8597
|
+
);
|
|
8598
|
+
return rows.map(
|
|
8599
|
+
(row) => row.map(
|
|
8600
|
+
(cell, col) => col === row.length - 1 ? cell : cell.padEnd(widths[col])
|
|
8601
|
+
).join(" ").trimEnd()
|
|
8602
|
+
);
|
|
8603
|
+
}
|
|
8604
|
+
function nameFromUrl(url) {
|
|
8605
|
+
try {
|
|
8606
|
+
const last = decodeURIComponent(
|
|
8607
|
+
new URL(url).pathname.split("/").filter(Boolean).pop() ?? ""
|
|
8608
|
+
);
|
|
8609
|
+
const clean = last.replace(/[^\w.\- ]+/g, "_").slice(0, 120);
|
|
8610
|
+
return clean || "download";
|
|
8611
|
+
} catch {
|
|
8612
|
+
return "download";
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
|
|
8616
|
+
// src/plugin/asset.ts
|
|
8362
8617
|
var BOOLEAN_FLAGS3 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
8363
|
-
var MIME_BY_EXT = {
|
|
8364
|
-
".png": "image/png",
|
|
8365
|
-
".jpg": "image/jpeg",
|
|
8366
|
-
".jpeg": "image/jpeg",
|
|
8367
|
-
".webp": "image/webp",
|
|
8368
|
-
".gif": "image/gif",
|
|
8369
|
-
".glb": "model/gltf-binary",
|
|
8370
|
-
".gltf": "model/gltf+json",
|
|
8371
|
-
".md": "text/markdown"
|
|
8372
|
-
};
|
|
8373
8618
|
async function cmdRename(argv) {
|
|
8374
8619
|
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS3);
|
|
8375
8620
|
const [assetId, filename] = positionals;
|
|
@@ -8408,64 +8653,58 @@ async function cmdPush(argv) {
|
|
|
8408
8653
|
const folderRef = strFlag(flags, "folder");
|
|
8409
8654
|
const folder = folderRef ? resolveFolder(await listFolders(origin, key), folderRef) : null;
|
|
8410
8655
|
const uploaded = [];
|
|
8411
|
-
let unfiledMedia = 0;
|
|
8412
8656
|
for (const file of positionals) {
|
|
8413
|
-
const name =
|
|
8657
|
+
const name = basename4(file);
|
|
8414
8658
|
const body = readFileSync3(file);
|
|
8415
8659
|
const media = resolveMediaType({
|
|
8416
8660
|
head: body.subarray(0, MEDIA_HEAD_BYTES),
|
|
8417
8661
|
filename: name
|
|
8418
8662
|
});
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
try {
|
|
8423
|
-
put = await uploadAsset({ origin, key }, new Uint8Array(body), {
|
|
8424
|
-
filename: sent,
|
|
8425
|
-
contentType: media.type,
|
|
8426
|
-
contentHash: createHash2("sha256").update(body).digest("hex"),
|
|
8427
|
-
...folder ? { folderId: folder.id } : {}
|
|
8428
|
-
});
|
|
8429
|
-
} catch (e) {
|
|
8430
|
-
const landed = uploaded.length ? ` (${uploaded.length} of ${positionals.length} landed before it)` : "";
|
|
8431
|
-
throw new Error(
|
|
8432
|
-
`push ${name}${landed}: ${e instanceof Error ? e.message : String(e)}`
|
|
8433
|
-
);
|
|
8434
|
-
}
|
|
8435
|
-
if (!folder && !put.reused) unfiledMedia += 1;
|
|
8436
|
-
uploaded.push({ id: put.id, filename: sent });
|
|
8437
|
-
r.log(
|
|
8438
|
-
`uploaded ${sent} (${put.id})${put.reused ? " \u2014 already on your shelf" : ""} ${put.url}`
|
|
8439
|
-
);
|
|
8440
|
-
continue;
|
|
8441
|
-
}
|
|
8442
|
-
const mime = MIME_BY_EXT[extname2(name).toLowerCase()] ?? "application/octet-stream";
|
|
8443
|
-
const form = new FormData();
|
|
8444
|
-
form.set("file", new Blob([body], { type: mime }), name);
|
|
8445
|
-
if (folder) form.set("folderId", folder.id);
|
|
8446
|
-
const res = await fetch(`${origin}/api/assets/upload`, {
|
|
8447
|
-
method: "POST",
|
|
8448
|
-
headers: { accept: "application/json", authorization: `Bearer ${key}` },
|
|
8449
|
-
body: form
|
|
8450
|
-
});
|
|
8451
|
-
let payload = {};
|
|
8663
|
+
const isMedia = /^(audio|video)\//.test(media.type);
|
|
8664
|
+
const sent = isMedia ? nameForType(name, media.type) : name;
|
|
8665
|
+
let put;
|
|
8452
8666
|
try {
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8667
|
+
put = await uploadAsset({ origin, key }, new Uint8Array(body), {
|
|
8668
|
+
filename: sent,
|
|
8669
|
+
...isMedia ? { contentType: media.type } : {},
|
|
8670
|
+
contentHash: createHash2("sha256").update(body).digest("hex"),
|
|
8671
|
+
intent: "library",
|
|
8672
|
+
...folder ? { folderId: folder.id } : {}
|
|
8673
|
+
});
|
|
8674
|
+
} catch (e) {
|
|
8457
8675
|
const landed = uploaded.length ? ` (${uploaded.length} of ${positionals.length} landed before it)` : "";
|
|
8458
8676
|
throw new Error(
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8677
|
+
`push ${name}${landed}: ${e instanceof Error ? e.message : String(e)}`
|
|
8678
|
+
);
|
|
8679
|
+
}
|
|
8680
|
+
const stored = put.filename || sent;
|
|
8681
|
+
uploaded.push({
|
|
8682
|
+
id: put.id,
|
|
8683
|
+
filename: stored,
|
|
8684
|
+
kind: put.kind,
|
|
8685
|
+
url: put.url,
|
|
8686
|
+
ref: put.ref,
|
|
8687
|
+
size: put.size,
|
|
8688
|
+
...put.duration ? { duration: put.duration } : {},
|
|
8689
|
+
...put.metadata ? { metadata: put.metadata } : {},
|
|
8690
|
+
...put.reused ? { reused: true } : {},
|
|
8691
|
+
...put.notes.length ? { notes: put.notes } : {}
|
|
8692
|
+
});
|
|
8693
|
+
const hosted = {
|
|
8694
|
+
id: put.id,
|
|
8695
|
+
kind: put.kind,
|
|
8696
|
+
filename: stored,
|
|
8697
|
+
size: put.size,
|
|
8698
|
+
duration: put.duration,
|
|
8699
|
+
metadata: put.metadata
|
|
8700
|
+
};
|
|
8466
8701
|
r.log(
|
|
8467
|
-
`uploaded ${
|
|
8702
|
+
`uploaded ${stored}${put.reused ? " \u2014 already yours, nothing sent" : ""}`
|
|
8468
8703
|
);
|
|
8704
|
+
r.log(` ${probeLine(hosted)}, ${bytesWords(put.size)}`);
|
|
8705
|
+
r.log(` ${put.ref} ${put.url}`);
|
|
8706
|
+
r.log(` ${manifestLine(hosted)}`);
|
|
8707
|
+
for (const note of put.notes) r.log(` note: ${note}`);
|
|
8469
8708
|
}
|
|
8470
8709
|
r.done(
|
|
8471
8710
|
{
|
|
@@ -8473,11 +8712,185 @@ async function cmdPush(argv) {
|
|
|
8473
8712
|
count: uploaded.length,
|
|
8474
8713
|
folder: folder?.slug ?? null
|
|
8475
8714
|
},
|
|
8476
|
-
`pushed ${uploaded.length} asset${uploaded.length === 1 ? "" : "s"}${folder ? ` into ${folder.slug}` : ""}`
|
|
8477
|
-
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>` : "")
|
|
8715
|
+
`pushed ${uploaded.length} asset${uploaded.length === 1 ? "" : "s"}${folder ? ` into ${folder.slug}` : ""}`
|
|
8478
8716
|
);
|
|
8479
8717
|
return EXIT_OK;
|
|
8480
8718
|
}
|
|
8719
|
+
var IMPORT_MAX_BYTES = 512 * 1024 * 1024;
|
|
8720
|
+
async function cmdImport(argv) {
|
|
8721
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS3);
|
|
8722
|
+
const [url] = positionals;
|
|
8723
|
+
if (!url || !/^https?:\/\//i.test(url)) {
|
|
8724
|
+
throw new UsageError(
|
|
8725
|
+
"vos asset import <http(s) url> [--name <file>] [--folder <slug>] [--license <text>] [--attribution <text>]"
|
|
8726
|
+
);
|
|
8727
|
+
}
|
|
8728
|
+
const r = createReporter(flags.json === true);
|
|
8729
|
+
const origin = platformOrigin({
|
|
8730
|
+
origin: strFlag(flags, "origin"),
|
|
8731
|
+
api: strFlag(flags, "api")
|
|
8732
|
+
});
|
|
8733
|
+
const key = requireCredential(strFlag(flags, "key"));
|
|
8734
|
+
const folderRef = strFlag(flags, "folder");
|
|
8735
|
+
const folder = folderRef ? resolveFolder(await listFolders(origin, key), folderRef) : null;
|
|
8736
|
+
r.log(`fetching ${url}`);
|
|
8737
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
8738
|
+
if (!res.ok)
|
|
8739
|
+
throw new Error(`import ${url}: the address answered ${res.status}`);
|
|
8740
|
+
const declared = Number(res.headers.get("content-length") ?? 0);
|
|
8741
|
+
if (declared > IMPORT_MAX_BYTES) {
|
|
8742
|
+
throw new Error(
|
|
8743
|
+
`import ${url}: ${bytesWords(declared)} is over the ${bytesWords(IMPORT_MAX_BYTES)} an import carries. Download it and use vos asset push`
|
|
8744
|
+
);
|
|
8745
|
+
}
|
|
8746
|
+
const body = new Uint8Array(await res.arrayBuffer());
|
|
8747
|
+
if (body.length > IMPORT_MAX_BYTES) {
|
|
8748
|
+
throw new Error(
|
|
8749
|
+
`import ${url}: ${bytesWords(body.length)} is over the ${bytesWords(IMPORT_MAX_BYTES)} an import carries. Download it and use vos asset push`
|
|
8750
|
+
);
|
|
8751
|
+
}
|
|
8752
|
+
const name = strFlag(flags, "name") ?? nameFromUrl(res.url || url);
|
|
8753
|
+
const license = strFlag(flags, "license");
|
|
8754
|
+
const attribution = strFlag(flags, "attribution");
|
|
8755
|
+
let put;
|
|
8756
|
+
try {
|
|
8757
|
+
put = await uploadAsset({ origin, key }, body, {
|
|
8758
|
+
filename: name,
|
|
8759
|
+
contentHash: createHash2("sha256").update(body).digest("hex"),
|
|
8760
|
+
intent: "library",
|
|
8761
|
+
...folder ? { folderId: folder.id } : {},
|
|
8762
|
+
provenance: {
|
|
8763
|
+
sourceUrl: res.url || url,
|
|
8764
|
+
...license ? { license } : {},
|
|
8765
|
+
...attribution ? { attribution } : {}
|
|
8766
|
+
}
|
|
8767
|
+
});
|
|
8768
|
+
} catch (e) {
|
|
8769
|
+
throw new Error(
|
|
8770
|
+
`import ${name}: ${e instanceof Error ? e.message : String(e)}`
|
|
8771
|
+
);
|
|
8772
|
+
}
|
|
8773
|
+
const hosted = {
|
|
8774
|
+
id: put.id,
|
|
8775
|
+
kind: put.kind,
|
|
8776
|
+
filename: put.filename || name,
|
|
8777
|
+
size: put.size,
|
|
8778
|
+
duration: put.duration,
|
|
8779
|
+
metadata: put.metadata
|
|
8780
|
+
};
|
|
8781
|
+
if (!license) {
|
|
8782
|
+
r.log(
|
|
8783
|
+
"note: no --license was given. Say what lets you use this file, so the next person can tell"
|
|
8784
|
+
);
|
|
8785
|
+
}
|
|
8786
|
+
r.done(
|
|
8787
|
+
{
|
|
8788
|
+
id: put.id,
|
|
8789
|
+
filename: hosted.filename,
|
|
8790
|
+
kind: put.kind,
|
|
8791
|
+
url: put.url,
|
|
8792
|
+
ref: put.ref,
|
|
8793
|
+
size: put.size,
|
|
8794
|
+
sourceUrl: res.url || url,
|
|
8795
|
+
...license ? { license } : {},
|
|
8796
|
+
...attribution ? { attribution } : {},
|
|
8797
|
+
...put.reused ? { reused: true } : {},
|
|
8798
|
+
...put.notes.length ? { notes: put.notes } : {},
|
|
8799
|
+
folder: folder?.slug ?? null
|
|
8800
|
+
},
|
|
8801
|
+
`imported ${hosted.filename}${put.reused ? " \u2014 already yours, nothing sent" : ""}
|
|
8802
|
+
${probeLine(hosted)}, ${bytesWords(put.size)}
|
|
8803
|
+
${put.ref} ${put.url}
|
|
8804
|
+
${manifestLine(hosted)}`
|
|
8805
|
+
);
|
|
8806
|
+
return EXIT_OK;
|
|
8807
|
+
}
|
|
8808
|
+
async function cmdList2(argv) {
|
|
8809
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS3);
|
|
8810
|
+
const r = createReporter(flags.json === true);
|
|
8811
|
+
const origin = platformOrigin({
|
|
8812
|
+
origin: strFlag(flags, "origin"),
|
|
8813
|
+
api: strFlag(flags, "api")
|
|
8814
|
+
});
|
|
8815
|
+
const key = requireCredential(strFlag(flags, "key"));
|
|
8816
|
+
const params = new URLSearchParams({
|
|
8817
|
+
limit: String(Math.min(200, Math.max(1, numFlag(flags, "limit", 50))))
|
|
8818
|
+
});
|
|
8819
|
+
for (const name of ["kind", "q", "sort", "scope"]) {
|
|
8820
|
+
const value = strFlag(flags, name);
|
|
8821
|
+
if (value) params.set(name, value);
|
|
8822
|
+
}
|
|
8823
|
+
const folderRef = strFlag(flags, "folder");
|
|
8824
|
+
if (folderRef) {
|
|
8825
|
+
params.set(
|
|
8826
|
+
"folder",
|
|
8827
|
+
folderRef === "none" ? "none" : resolveFolder(await listFolders(origin, key), folderRef).id
|
|
8828
|
+
);
|
|
8829
|
+
}
|
|
8830
|
+
const res = await apiJson(origin, `/api/assets?${params}`, { key });
|
|
8831
|
+
if (res.status !== 200) throw new Error(apiError("list assets", res));
|
|
8832
|
+
const assets = (res.body.assets ?? []).map(hostedFile);
|
|
8833
|
+
const total = typeof res.body.total === "number" ? res.body.total : assets.length;
|
|
8834
|
+
r.done(
|
|
8835
|
+
{ assets, total, kinds: res.body.kinds ?? {} },
|
|
8836
|
+
assets.length === 0 ? "no files" : `${alignRows(assets.map(listRow)).join("\n")}
|
|
8837
|
+
${assets.length} of ${total} file${total === 1 ? "" : "s"}${total > assets.length ? " (--limit for more, --q to search)" : ""}`
|
|
8838
|
+
);
|
|
8839
|
+
return EXIT_OK;
|
|
8840
|
+
}
|
|
8841
|
+
async function cmdUsage(argv) {
|
|
8842
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS3);
|
|
8843
|
+
const r = createReporter(flags.json === true);
|
|
8844
|
+
const origin = platformOrigin({
|
|
8845
|
+
origin: strFlag(flags, "origin"),
|
|
8846
|
+
api: strFlag(flags, "api")
|
|
8847
|
+
});
|
|
8848
|
+
const key = requireCredential(strFlag(flags, "key"));
|
|
8849
|
+
const res = await apiJson(origin, "/api/user/usage", { key });
|
|
8850
|
+
if (res.status !== 200) throw new Error(apiError("read usage", res));
|
|
8851
|
+
const usage = res.body.usage ?? {};
|
|
8852
|
+
const limits = res.body.limits ?? {};
|
|
8853
|
+
const held = Number(usage.assetBytes ?? 0);
|
|
8854
|
+
const trash = Number(usage.trashBytes ?? 0);
|
|
8855
|
+
const limit = Number(limits.storageBytes ?? 0);
|
|
8856
|
+
const today = usage.uploadsToday ?? {};
|
|
8857
|
+
const perKind = limits.uploadKinds ?? {};
|
|
8858
|
+
const rates = Object.entries(today).filter(([kind]) => perKind[kind]?.perDay).map(([kind, n]) => `${kind} ${n} of ${perKind[kind].perDay}`);
|
|
8859
|
+
r.done(
|
|
8860
|
+
{ plan: res.body.plan, usage, limits },
|
|
8861
|
+
`${bytesWords(held)} of ${bytesWords(limit)} used` + (trash > 0 ? `, ${bytesWords(trash)} of it in Trash (a person empties Trash at vos.so/app/trash; deleting more frees nothing)` : "") + (rates.length ? `
|
|
8862
|
+
uploaded in the last 24 h: ${rates.join(", ")}` : "")
|
|
8863
|
+
);
|
|
8864
|
+
return EXIT_OK;
|
|
8865
|
+
}
|
|
8866
|
+
async function cmdWhy(argv) {
|
|
8867
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS3);
|
|
8868
|
+
const [assetId] = positionals;
|
|
8869
|
+
if (!assetId) throw new UsageError("vos assets why <assetId>");
|
|
8870
|
+
const r = createReporter(flags.json === true);
|
|
8871
|
+
const origin = platformOrigin({
|
|
8872
|
+
origin: strFlag(flags, "origin"),
|
|
8873
|
+
api: strFlag(flags, "api")
|
|
8874
|
+
});
|
|
8875
|
+
const key = requireCredential(strFlag(flags, "key"));
|
|
8876
|
+
const res = await apiJson(origin, `/api/assets/${assetId}`, { key });
|
|
8877
|
+
if (res.status !== 200) throw new Error(apiError(`read ${assetId}`, res));
|
|
8878
|
+
const asset = hostedFile(res.body.asset);
|
|
8879
|
+
const usedIn = res.body.usedIn ?? [];
|
|
8880
|
+
const how = (u) => [
|
|
8881
|
+
u.head ? "in what plays now" : "",
|
|
8882
|
+
u.versions ? `${u.versions} version${u.versions === 1 ? "" : "s"}` : "",
|
|
8883
|
+
u.draft ? "an unsaved draft" : "",
|
|
8884
|
+
u.trashed ? "in Trash" : ""
|
|
8885
|
+
].filter(Boolean).join(", ");
|
|
8886
|
+
const lines = [
|
|
8887
|
+
`${asset.filename} ${probeLine(asset)}, ${bytesWords(asset.size)}`,
|
|
8888
|
+
asset.removedAt ? `removed from vos.so: ${asset.removedReason ?? "no reason given"}` : usedIn.length === 0 ? asset.intent === "attached" ? "nothing uses it: an attached file that stays unused is collected after 7 days" : "nothing uses it: it stays because it is in your library" : `used by ${usedIn.length}:`,
|
|
8889
|
+
...usedIn.map((u) => ` ${u.vosId} ${u.title || "Untitled"} (${how(u)})`)
|
|
8890
|
+
];
|
|
8891
|
+
r.done({ asset, usedIn }, lines.join("\n"));
|
|
8892
|
+
return EXIT_OK;
|
|
8893
|
+
}
|
|
8481
8894
|
async function cmdAsset(argv) {
|
|
8482
8895
|
const sub = argv[0];
|
|
8483
8896
|
const rest = argv.slice(1);
|
|
@@ -8486,15 +8899,24 @@ async function cmdAsset(argv) {
|
|
|
8486
8899
|
return cmdRename(rest);
|
|
8487
8900
|
case "push":
|
|
8488
8901
|
return cmdPush(rest);
|
|
8902
|
+
case "import":
|
|
8903
|
+
return cmdImport(rest);
|
|
8904
|
+
case "ls":
|
|
8905
|
+
case "list":
|
|
8906
|
+
return cmdList2(rest);
|
|
8907
|
+
case "usage":
|
|
8908
|
+
return cmdUsage(rest);
|
|
8909
|
+
case "why":
|
|
8910
|
+
return cmdWhy(rest);
|
|
8489
8911
|
default:
|
|
8490
8912
|
throw new UsageError(
|
|
8491
|
-
"vos asset <rename|
|
|
8913
|
+
"vos asset <push|import|rename> | vos assets <ls|usage|why> \u2014 add files to your library, or see what is in it (vos asset push <file...> [--folder <slug>])"
|
|
8492
8914
|
);
|
|
8493
8915
|
}
|
|
8494
8916
|
}
|
|
8495
8917
|
|
|
8496
8918
|
// src/plugin/remove.ts
|
|
8497
|
-
import { existsSync as
|
|
8919
|
+
import { existsSync as existsSync15, rmSync as rmSync2, statSync as statSync3 } from "fs";
|
|
8498
8920
|
import { join as join16 } from "path";
|
|
8499
8921
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
8500
8922
|
|
|
@@ -8674,7 +9096,7 @@ async function cmdDelete(argv) {
|
|
|
8674
9096
|
api: strFlag(flags, "api")
|
|
8675
9097
|
}).replace(/\/+$/, "");
|
|
8676
9098
|
const key = requireCredential(strFlag(flags, "key"));
|
|
8677
|
-
const dir =
|
|
9099
|
+
const dir = existsSync15(target) && statSync3(target).isDirectory() ? target : null;
|
|
8678
9100
|
const vosId = dir ? readSyncState(dir)?.vosId : parseVosId(target);
|
|
8679
9101
|
if (!vosId)
|
|
8680
9102
|
throw new UsageError(
|
|
@@ -8742,7 +9164,7 @@ async function cmdDelete(argv) {
|
|
|
8742
9164
|
|
|
8743
9165
|
// src/plugin/buildProgram.ts
|
|
8744
9166
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
8745
|
-
import { basename as
|
|
9167
|
+
import { basename as basename5, dirname as dirname4, join as join17, resolve as resolve7 } from "path";
|
|
8746
9168
|
import { pathToFileURL } from "url";
|
|
8747
9169
|
import { parse } from "acorn";
|
|
8748
9170
|
import { fullAncestor } from "acorn-walk";
|
|
@@ -8950,7 +9372,7 @@ async function cmdBuild(argv) {
|
|
|
8950
9372
|
throw new UsageError(
|
|
8951
9373
|
`${source} exports no program: \`export default { version: 2, duration, camera, createContent, createTimeline, \u2026 }\``
|
|
8952
9374
|
);
|
|
8953
|
-
const built = buildProgram(program,
|
|
9375
|
+
const built = buildProgram(program, basename5(file), text);
|
|
8954
9376
|
for (const p of built.problems) r.log(`error ${p.fn}: ${p.message}`);
|
|
8955
9377
|
if (built.problems.length) {
|
|
8956
9378
|
r.done(
|
|
@@ -8977,7 +9399,7 @@ async function cmdBuild(argv) {
|
|
|
8977
9399
|
|
|
8978
9400
|
// src/plugin/compare.ts
|
|
8979
9401
|
import { spawnSync } from "child_process";
|
|
8980
|
-
import { existsSync as
|
|
9402
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
8981
9403
|
import { dirname as dirname5, join as join18, resolve as resolve8 } from "path";
|
|
8982
9404
|
var BOOLEAN_FLAGS7 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
8983
9405
|
function sampleTimes(duration, every) {
|
|
@@ -9045,7 +9467,7 @@ async function cmdCompare(argv) {
|
|
|
9045
9467
|
const config = loaded.config;
|
|
9046
9468
|
const duration = configDuration(config) ?? 5;
|
|
9047
9469
|
const assets = await programAssets(config, {
|
|
9048
|
-
baseDir: /^https?:\/\//.test(source) ? null :
|
|
9470
|
+
baseDir: /^https?:\/\//.test(source) ? null : existsSync16(source) && statSync4(source).isDirectory() ? source : dirname5(source),
|
|
9049
9471
|
origin: platformOrigin({}),
|
|
9050
9472
|
key: resolveCredential(),
|
|
9051
9473
|
log: (line) => r.log(line)
|
|
@@ -9164,28 +9586,28 @@ async function cmdCompare(argv) {
|
|
|
9164
9586
|
}
|
|
9165
9587
|
|
|
9166
9588
|
// src/plugin/port.ts
|
|
9167
|
-
import { existsSync as
|
|
9589
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
9168
9590
|
import { dirname as dirname8, join as join22, resolve as resolve11 } from "path";
|
|
9169
9591
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
9170
9592
|
|
|
9171
9593
|
// src/plugin/portInventory.ts
|
|
9172
9594
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9173
9595
|
import {
|
|
9174
|
-
existsSync as
|
|
9596
|
+
existsSync as existsSync18,
|
|
9175
9597
|
mkdirSync as mkdirSync3,
|
|
9176
9598
|
readFileSync as readFileSync6,
|
|
9177
9599
|
readdirSync as readdirSync4,
|
|
9178
9600
|
statSync as statSync6
|
|
9179
9601
|
} from "fs";
|
|
9180
|
-
import { dirname as dirname6, join as join20, relative as
|
|
9602
|
+
import { dirname as dirname6, join as join20, relative as relative3, resolve as resolve9 } from "path";
|
|
9181
9603
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
9182
9604
|
import { findFontFamily as findFontFamily3 } from "@vosjs/shared";
|
|
9183
9605
|
|
|
9184
9606
|
// src/plugin/portRemotion.ts
|
|
9185
|
-
import { existsSync as
|
|
9607
|
+
import { existsSync as existsSync17, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
|
|
9186
9608
|
import { createServer as createServer2 } from "http";
|
|
9187
9609
|
import { createRequire } from "module";
|
|
9188
|
-
import { basename as
|
|
9610
|
+
import { basename as basename6, extname as extname3, join as join19 } from "path";
|
|
9189
9611
|
function themeColors(sources) {
|
|
9190
9612
|
const out = {};
|
|
9191
9613
|
const re = /\bconst\s+([A-Za-z_$][\w$]*)\s*(?::\s*string\s*)?=\s*['"](#[0-9a-fA-F]{3,8})['"]/g;
|
|
@@ -9258,7 +9680,7 @@ function sourceTexts(dir) {
|
|
|
9258
9680
|
}
|
|
9259
9681
|
};
|
|
9260
9682
|
const src = join19(dir, "src");
|
|
9261
|
-
walk(
|
|
9683
|
+
walk(existsSync17(src) ? src : dir, 0);
|
|
9262
9684
|
return out;
|
|
9263
9685
|
}
|
|
9264
9686
|
function entryPoint(dir) {
|
|
@@ -9270,7 +9692,7 @@ function entryPoint(dir) {
|
|
|
9270
9692
|
"remotion/index.ts",
|
|
9271
9693
|
"remotion/index.tsx"
|
|
9272
9694
|
])
|
|
9273
|
-
if (
|
|
9695
|
+
if (existsSync17(join19(dir, p))) return join19(dir, p);
|
|
9274
9696
|
return null;
|
|
9275
9697
|
}
|
|
9276
9698
|
var TYPES = {
|
|
@@ -9363,7 +9785,7 @@ async function readRemotion(dir, log) {
|
|
|
9363
9785
|
const server = createServer2((rq, rs) => {
|
|
9364
9786
|
const path = decodeURIComponent(new URL(rq.url ?? "/", "http://x").pathname);
|
|
9365
9787
|
let f = join19(out, path);
|
|
9366
|
-
if (!f.startsWith(out) || !
|
|
9788
|
+
if (!f.startsWith(out) || !existsSync17(f) || statSync5(f).isDirectory())
|
|
9367
9789
|
f = join19(out, "index.html");
|
|
9368
9790
|
rs.writeHead(200, {
|
|
9369
9791
|
"content-type": TYPES[extname3(f)] ?? "application/octet-stream"
|
|
@@ -9465,8 +9887,8 @@ async function readRemotion(dir, log) {
|
|
|
9465
9887
|
const sources = sourceTexts(dir);
|
|
9466
9888
|
const publicDir = join19(dir, "public");
|
|
9467
9889
|
const media = tree.audio.map((a) => {
|
|
9468
|
-
const name =
|
|
9469
|
-
const local =
|
|
9890
|
+
const name = basename6(a.src.split("?")[0]);
|
|
9891
|
+
const local = existsSync17(join19(publicDir, name)) ? `public/${name}` : a.src;
|
|
9470
9892
|
return {
|
|
9471
9893
|
kind: "audio",
|
|
9472
9894
|
src: local,
|
|
@@ -9507,9 +9929,9 @@ async function readRemotion(dir, log) {
|
|
|
9507
9929
|
|
|
9508
9930
|
// src/plugin/portInventory.ts
|
|
9509
9931
|
function detectEngine(dir) {
|
|
9510
|
-
if (
|
|
9932
|
+
if (existsSync18(join20(dir, "hyperframes.json"))) return "hyperframes";
|
|
9511
9933
|
const pkg = join20(dir, "package.json");
|
|
9512
|
-
if (
|
|
9934
|
+
if (existsSync18(pkg)) {
|
|
9513
9935
|
try {
|
|
9514
9936
|
const p = JSON.parse(readFileSync6(pkg, "utf8"));
|
|
9515
9937
|
const deps = { ...p.dependencies ?? {}, ...p.devDependencies ?? {} };
|
|
@@ -9523,13 +9945,13 @@ function detectEngine(dir) {
|
|
|
9523
9945
|
return "page";
|
|
9524
9946
|
}
|
|
9525
9947
|
function sourcePage(source) {
|
|
9526
|
-
if (!
|
|
9948
|
+
if (!existsSync18(source)) return null;
|
|
9527
9949
|
if (statSync6(source).isFile()) return /\.html?$/i.test(source) ? source : null;
|
|
9528
9950
|
const index = join20(source, "index.html");
|
|
9529
|
-
return
|
|
9951
|
+
return existsSync18(index) ? index : null;
|
|
9530
9952
|
}
|
|
9531
9953
|
function findRender(dir) {
|
|
9532
|
-
const videos = (d) =>
|
|
9954
|
+
const videos = (d) => existsSync18(d) ? readdirSync4(d).filter((f) => /\.(mp4|webm|mov)$/i.test(f)).map((f) => join20(d, f)) : [];
|
|
9533
9955
|
for (const d of [join20(dir, "renders"), join20(dir, "out"), dir]) {
|
|
9534
9956
|
const found = videos(d);
|
|
9535
9957
|
if (found.length) return found[0];
|
|
@@ -9801,7 +10223,7 @@ var SEEK_AND_READ = `((args) => {
|
|
|
9801
10223
|
})`;
|
|
9802
10224
|
async function readInventory(source, opts) {
|
|
9803
10225
|
const abs = resolve9(source);
|
|
9804
|
-
const dir =
|
|
10226
|
+
const dir = existsSync18(abs) && statSync6(abs).isDirectory() ? abs : dirname6(abs);
|
|
9805
10227
|
const engine = detectEngine(dir);
|
|
9806
10228
|
const page = sourcePage(abs);
|
|
9807
10229
|
const render = opts.render === void 0 ? findRender(dir) : opts.render;
|
|
@@ -9931,7 +10353,7 @@ async function readInventory(source, opts) {
|
|
|
9931
10353
|
if (ok)
|
|
9932
10354
|
stills.push({
|
|
9933
10355
|
t: Number(t.toFixed(2)),
|
|
9934
|
-
file:
|
|
10356
|
+
file: relative3(opts.outDir, file)
|
|
9935
10357
|
});
|
|
9936
10358
|
}
|
|
9937
10359
|
} else {
|
|
@@ -9942,7 +10364,7 @@ async function readInventory(source, opts) {
|
|
|
9942
10364
|
}
|
|
9943
10365
|
const scriptStrings = scriptStringsOf(read.scriptText);
|
|
9944
10366
|
return {
|
|
9945
|
-
source:
|
|
10367
|
+
source: relative3(process.cwd(), abs) || ".",
|
|
9946
10368
|
root: abs.endsWith(".html") ? dirname6(abs) : abs,
|
|
9947
10369
|
engine,
|
|
9948
10370
|
width: read.width,
|
|
@@ -9960,15 +10382,15 @@ async function readInventory(source, opts) {
|
|
|
9960
10382
|
variables: read.variables,
|
|
9961
10383
|
scriptStrings,
|
|
9962
10384
|
uses: read.uses ?? sourceUses(read.scriptText ?? ""),
|
|
9963
|
-
render: render ?
|
|
10385
|
+
render: render ? relative3(process.cwd(), render) : null,
|
|
9964
10386
|
stills
|
|
9965
10387
|
};
|
|
9966
10388
|
}
|
|
9967
10389
|
|
|
9968
10390
|
// src/plugin/portScaffold.ts
|
|
9969
10391
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
9970
|
-
import { copyFileSync, existsSync as
|
|
9971
|
-
import { basename as
|
|
10392
|
+
import { copyFileSync, existsSync as existsSync19, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
10393
|
+
import { basename as basename7, dirname as dirname7, join as join21, resolve as resolve10 } from "path";
|
|
9972
10394
|
import { findFontFamily as findFontFamily4, fontFaceUrl, nearestFontWeight } from "@vosjs/shared";
|
|
9973
10395
|
|
|
9974
10396
|
// src/plugin/painterLib.ts
|
|
@@ -10378,8 +10800,8 @@ function planScaffold(inv, opts) {
|
|
|
10378
10800
|
const copies = [];
|
|
10379
10801
|
inv.media.filter((m) => m.kind === "audio" && m.src).forEach((m, i) => {
|
|
10380
10802
|
const from = resolve10(opts.sourceDir, m.src);
|
|
10381
|
-
const name =
|
|
10382
|
-
if (!
|
|
10803
|
+
const name = basename7(from);
|
|
10804
|
+
if (!existsSync19(from)) {
|
|
10383
10805
|
report.push(
|
|
10384
10806
|
`- the audio ${m.src} is not in the source folder; add it to doc.json by hand`
|
|
10385
10807
|
);
|
|
@@ -10522,7 +10944,7 @@ ${sceneTodos}${springHint}
|
|
|
10522
10944
|
}
|
|
10523
10945
|
function reportText(inv, r) {
|
|
10524
10946
|
const lines = [
|
|
10525
|
-
`# Port report: ${inv.source === "." && inv.root ?
|
|
10947
|
+
`# Port report: ${inv.source === "." && inv.root ? basename7(inv.root) : inv.source}`,
|
|
10526
10948
|
"",
|
|
10527
10949
|
`${inv.engine} piece, ${inv.width}\xD7${inv.height}${inv.fps ? ` at ${inv.fps} fps` : ""}, ${inv.duration ?? "?"} s. A program has no size of its own: render it with \`--width ${inv.width} --height ${inv.height}\`.`,
|
|
10528
10950
|
"",
|
|
@@ -10650,7 +11072,7 @@ async function scaffold(argv) {
|
|
|
10650
11072
|
const { flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
10651
11073
|
const r = createReporter(flags.json === true);
|
|
10652
11074
|
const from = resolve11(strFlag(flags, "from") ?? join22("port", "inventory.json"));
|
|
10653
|
-
if (!
|
|
11075
|
+
if (!existsSync20(from))
|
|
10654
11076
|
throw new UsageError(
|
|
10655
11077
|
`no inventory at ${from}: run npx vos port inventory <source> first`
|
|
10656
11078
|
);
|
|
@@ -10684,12 +11106,13 @@ function scaffoldSourceDir(inv) {
|
|
|
10684
11106
|
}
|
|
10685
11107
|
|
|
10686
11108
|
// src/plugin/recipe.ts
|
|
11109
|
+
import { createHash as createHash3 } from "crypto";
|
|
10687
11110
|
import { readFileSync as readFileSync8 } from "fs";
|
|
10688
|
-
import { basename as
|
|
11111
|
+
import { basename as basename8 } from "path";
|
|
10689
11112
|
var BOOLEAN_FLAGS9 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
10690
11113
|
var USAGE = "vos recipe push <file.md> (--folder <folderId|slug> | --asset <assetId>)";
|
|
10691
11114
|
function recipeFilename(file) {
|
|
10692
|
-
const name =
|
|
11115
|
+
const name = basename8(file);
|
|
10693
11116
|
if (!/\.md$/i.test(name)) {
|
|
10694
11117
|
throw new UsageError(`a recipe is a markdown file \u2014 ${name} is not .md`);
|
|
10695
11118
|
}
|
|
@@ -10712,55 +11135,49 @@ async function cmdPush2(argv) {
|
|
|
10712
11135
|
});
|
|
10713
11136
|
const key = requireCredential(strFlag(flags, "key"));
|
|
10714
11137
|
if (assetId) {
|
|
10715
|
-
const
|
|
11138
|
+
const res = await apiJson(origin, `/api/assets/${assetId}/file`, {
|
|
10716
11139
|
method: "PUT",
|
|
10717
11140
|
key,
|
|
10718
11141
|
raw: new Uint8Array(body),
|
|
10719
11142
|
headers: { "content-type": "text/markdown" }
|
|
10720
11143
|
});
|
|
10721
|
-
if (
|
|
10722
|
-
throw new Error(apiError(`replace recipe ${assetId}`,
|
|
11144
|
+
if (res.status !== 200) {
|
|
11145
|
+
throw new Error(apiError(`replace recipe ${assetId}`, res));
|
|
10723
11146
|
}
|
|
10724
11147
|
r.done(
|
|
10725
|
-
{ id: assetId, filename:
|
|
10726
|
-
`replaced recipe ${assetId} (${String(
|
|
11148
|
+
{ id: assetId, filename: res.body.filename, replaced: true },
|
|
11149
|
+
`replaced recipe ${assetId} (${String(res.body.filename)}) in place \u2014 the prior body is kept, restore it from the recipe page`
|
|
10727
11150
|
);
|
|
10728
11151
|
return EXIT_OK;
|
|
10729
11152
|
}
|
|
10730
11153
|
const folder = resolveFolder(await listFolders(origin, key), folderRef);
|
|
10731
|
-
|
|
10732
|
-
form.set("file", new Blob([body], { type: "text/markdown" }), filename);
|
|
10733
|
-
form.set("folderId", folder.id);
|
|
10734
|
-
const res = await fetch(`${origin}/api/assets/upload`, {
|
|
10735
|
-
method: "POST",
|
|
10736
|
-
headers: { accept: "application/json", authorization: `Bearer ${key}` },
|
|
10737
|
-
body: form
|
|
10738
|
-
});
|
|
10739
|
-
let payload = {};
|
|
11154
|
+
let put;
|
|
10740
11155
|
try {
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
11156
|
+
put = await uploadAsset({ origin, key }, new Uint8Array(body), {
|
|
11157
|
+
filename,
|
|
11158
|
+
contentType: "text/markdown",
|
|
11159
|
+
contentHash: createHash3("sha256").update(body).digest("hex"),
|
|
11160
|
+
intent: "library",
|
|
11161
|
+
folderId: folder.id
|
|
11162
|
+
});
|
|
11163
|
+
} catch (e) {
|
|
10745
11164
|
throw new Error(
|
|
10746
|
-
|
|
10747
|
-
status: res.status,
|
|
10748
|
-
body: payload
|
|
10749
|
-
})
|
|
11165
|
+
`push recipe ${filename}: ${e instanceof Error ? e.message : String(e)}`
|
|
10750
11166
|
);
|
|
10751
11167
|
}
|
|
10752
|
-
const meta =
|
|
10753
|
-
const stored =
|
|
11168
|
+
const meta = put.metadata ?? {};
|
|
11169
|
+
const stored = put.filename || filename;
|
|
10754
11170
|
r.done(
|
|
10755
11171
|
{
|
|
10756
|
-
id:
|
|
10757
|
-
filename:
|
|
11172
|
+
id: put.id,
|
|
11173
|
+
filename: stored,
|
|
10758
11174
|
folder: folder.slug,
|
|
10759
11175
|
name: meta.name,
|
|
10760
|
-
description: meta.description
|
|
11176
|
+
description: meta.description,
|
|
11177
|
+
...put.notes.length ? { notes: put.notes } : {}
|
|
10761
11178
|
},
|
|
10762
|
-
`pushed ${stored} into ${folder.slug} (${
|
|
10763
|
-
Replace it later with: vos recipe push ${file} --asset ${
|
|
11179
|
+
`pushed ${stored} into ${folder.slug} (${put.id})${typeof meta.name === "string" ? ` \u2014 ${meta.name}` : ""}
|
|
11180
|
+
Replace it later with: vos recipe push ${file} --asset ${put.id}`
|
|
10764
11181
|
);
|
|
10765
11182
|
return EXIT_OK;
|
|
10766
11183
|
}
|
|
@@ -11347,8 +11764,8 @@ var quoteAttr = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
|
11347
11764
|
function hrefSelector(href) {
|
|
11348
11765
|
try {
|
|
11349
11766
|
const u = new URL(href);
|
|
11350
|
-
const
|
|
11351
|
-
return `a[href=${quoteAttr(
|
|
11767
|
+
const relative4 = `${u.pathname}${u.search}${u.hash}`;
|
|
11768
|
+
return `a[href=${quoteAttr(relative4)}], a[href=${quoteAttr(href)}]`;
|
|
11352
11769
|
} catch {
|
|
11353
11770
|
return `a[href=${quoteAttr(href)}]`;
|
|
11354
11771
|
}
|
|
@@ -11775,9 +12192,9 @@ async function cmdActionsScript(input, flags) {
|
|
|
11775
12192
|
}
|
|
11776
12193
|
|
|
11777
12194
|
// src/plugin/ingest.ts
|
|
11778
|
-
import { existsSync as
|
|
12195
|
+
import { existsSync as existsSync21 } from "fs";
|
|
11779
12196
|
import { copyFile, mkdir as mkdir8, readFile as readFile14, stat as stat3 } from "fs/promises";
|
|
11780
|
-
import { basename as
|
|
12197
|
+
import { basename as basename9, join as join23, resolve as resolve14 } from "path";
|
|
11781
12198
|
import { inflateRawSync } from "zlib";
|
|
11782
12199
|
import {
|
|
11783
12200
|
cursorFromPlaywrightTrace,
|
|
@@ -11858,7 +12275,7 @@ async function copyRecording(src, dest, ext) {
|
|
|
11858
12275
|
}
|
|
11859
12276
|
}
|
|
11860
12277
|
async function readTraceFile(path, offsetMs) {
|
|
11861
|
-
const kind = traceKindOf(
|
|
12278
|
+
const kind = traceKindOf(basename9(path));
|
|
11862
12279
|
if (!kind) {
|
|
11863
12280
|
throw new UsageError(
|
|
11864
12281
|
`${path}: not a trace the recorder reads (trace.zip, .trace, .jsonl, .json or .csv)`
|
|
@@ -11879,7 +12296,7 @@ async function readTraceFile(path, offsetMs) {
|
|
|
11879
12296
|
return cursorFromPlaywrightTrace(text2, { offsetMs });
|
|
11880
12297
|
}
|
|
11881
12298
|
const text = await readFile14(path, "utf8");
|
|
11882
|
-
return cursorFromTrace(
|
|
12299
|
+
return cursorFromTrace(basename9(path), text, { offsetMs });
|
|
11883
12300
|
}
|
|
11884
12301
|
function parseViewport(v) {
|
|
11885
12302
|
if (!v) return null;
|
|
@@ -11895,7 +12312,7 @@ async function cmdIngest(argv) {
|
|
|
11895
12312
|
"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]"
|
|
11896
12313
|
);
|
|
11897
12314
|
const videoPath = resolve14(source);
|
|
11898
|
-
if (!
|
|
12315
|
+
if (!existsSync21(videoPath)) throw new UsageError(`${source}: no such file`);
|
|
11899
12316
|
const r = createReporter(flags.json === true);
|
|
11900
12317
|
const outDir = resolve14(strFlag(flags, "out") ?? "take");
|
|
11901
12318
|
const offsetMs = numFlag(flags, "offset", 0);
|
|
@@ -11910,7 +12327,7 @@ async function cmdIngest(argv) {
|
|
|
11910
12327
|
"note: finished footage opens bare, so --background is not used (pass --as take for the screen-recording card and its ground)"
|
|
11911
12328
|
);
|
|
11912
12329
|
const backdrop = footage ? null : await takeBackdrop(flags, r);
|
|
11913
|
-
r.log(`probing ${
|
|
12330
|
+
r.log(`probing ${basename9(videoPath)}\u2026`);
|
|
11914
12331
|
const probed = await probeVideoFile(videoPath);
|
|
11915
12332
|
if (!(probed.durationMs > 0)) {
|
|
11916
12333
|
throw new UsageError(
|
|
@@ -11922,7 +12339,7 @@ async function cmdIngest(argv) {
|
|
|
11922
12339
|
trace = await readTraceFile(resolve14(cursorPath), offsetMs);
|
|
11923
12340
|
for (const n of trace.notes) r.log(`note: ${n}`);
|
|
11924
12341
|
}
|
|
11925
|
-
if (
|
|
12342
|
+
if (existsSync21(join23(outDir, "meta.json"))) {
|
|
11926
12343
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
11927
12344
|
r.log(
|
|
11928
12345
|
`note: re-ingesting into ${outDir}; kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? `; apply the previous cut with: vos plan ${outDir} --reuse` : "")
|
|
@@ -11959,13 +12376,13 @@ async function cmdIngest(argv) {
|
|
|
11959
12376
|
const clicks = events.filter((e) => e.type === "down").length;
|
|
11960
12377
|
const seconds = (probed.durationMs / 1e3).toFixed(1);
|
|
11961
12378
|
const facts = `${seconds}s, ${probed.width}\xD7${probed.height} @ ${meta.fps} fps${probed.hasAudio ? ", audio" : ", no audio"}${remuxed ? "" : ", copied as is"}`;
|
|
11962
|
-
const cursorLine = trace ? `${events.length} cursor events (${clicks} clicks) from ${
|
|
12379
|
+
const cursorLine = trace ? `${events.length} cursor events (${clicks} clicks) from ${basename9(cursorPath)}${trace.dropped ? `, ${trace.dropped} not placed` : ""}; ${plan.zoomAuto} zoom${plan.zoomAuto === 1 ? "" : "s"} planned` : "no cursor track, so nothing was planned";
|
|
11963
12380
|
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)`;
|
|
11964
12381
|
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`;
|
|
11965
12382
|
r.done(
|
|
11966
12383
|
{
|
|
11967
12384
|
out: outDir,
|
|
11968
|
-
recording:
|
|
12385
|
+
recording: basename9(recording),
|
|
11969
12386
|
durationMs: probed.durationMs,
|
|
11970
12387
|
width: probed.width,
|
|
11971
12388
|
height: probed.height,
|
|
@@ -11974,7 +12391,7 @@ async function cmdIngest(argv) {
|
|
|
11974
12391
|
hasAudio: probed.hasAudio,
|
|
11975
12392
|
remuxed,
|
|
11976
12393
|
cursor: trace ? {
|
|
11977
|
-
file:
|
|
12394
|
+
file: basename9(cursorPath),
|
|
11978
12395
|
kind: trace.kind,
|
|
11979
12396
|
events: events.length,
|
|
11980
12397
|
clicks,
|
|
@@ -11986,7 +12403,7 @@ async function cmdIngest(argv) {
|
|
|
11986
12403
|
backdrop: plan.backdrop ?? null,
|
|
11987
12404
|
as: footage ? "footage" : "take"
|
|
11988
12405
|
},
|
|
11989
|
-
`Ingested ${
|
|
12406
|
+
`Ingested ${basename9(videoPath)} \u2192 ${outDir}/${basename9(recording)} (${facts})
|
|
11990
12407
|
${opened}
|
|
11991
12408
|
${cursorLine}
|
|
11992
12409
|
${next}`
|
|
@@ -11998,7 +12415,7 @@ async function cmdIngest(argv) {
|
|
|
11998
12415
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
11999
12416
|
import {
|
|
12000
12417
|
cpSync,
|
|
12001
|
-
existsSync as
|
|
12418
|
+
existsSync as existsSync23,
|
|
12002
12419
|
mkdirSync as mkdirSync6,
|
|
12003
12420
|
readFileSync as readFileSync10,
|
|
12004
12421
|
rmSync as rmSync3,
|
|
@@ -12009,7 +12426,7 @@ import { join as join25 } from "path";
|
|
|
12009
12426
|
import { chromium as chromium2 } from "playwright";
|
|
12010
12427
|
|
|
12011
12428
|
// src/plugin/machine.ts
|
|
12012
|
-
import { existsSync as
|
|
12429
|
+
import { existsSync as existsSync22, readFileSync as readFileSync9, readdirSync as readdirSync5, statSync as statSync7 } from "fs";
|
|
12013
12430
|
import { dirname as dirname9, join as join24 } from "path";
|
|
12014
12431
|
var AGENT_NAMES = [
|
|
12015
12432
|
"claude",
|
|
@@ -12030,7 +12447,7 @@ var HOME_DIRS = {
|
|
|
12030
12447
|
copilot: [".copilot"]
|
|
12031
12448
|
};
|
|
12032
12449
|
function detectAgents(root, opts = {}) {
|
|
12033
|
-
const exists = opts.exists ??
|
|
12450
|
+
const exists = opts.exists ?? existsSync22;
|
|
12034
12451
|
const out = [];
|
|
12035
12452
|
for (const agent of AGENT_NAMES) {
|
|
12036
12453
|
for (const rel of PROJECT_DIRS[agent]) {
|
|
@@ -12077,7 +12494,7 @@ function skillsDirFor(home) {
|
|
|
12077
12494
|
function fallbackSkillsHome(root) {
|
|
12078
12495
|
return { agent: "codex", dir: join24(root, ".agents"), scope: "project" };
|
|
12079
12496
|
}
|
|
12080
|
-
function installedSkills(dir, exists =
|
|
12497
|
+
function installedSkills(dir, exists = existsSync22, list = (p) => {
|
|
12081
12498
|
try {
|
|
12082
12499
|
return readdirSync5(p);
|
|
12083
12500
|
} catch {
|
|
@@ -12118,7 +12535,7 @@ ${RULES_BLOCK}
|
|
|
12118
12535
|
}
|
|
12119
12536
|
return { text, changed: text !== current };
|
|
12120
12537
|
}
|
|
12121
|
-
function rulesFileFor(root, exists =
|
|
12538
|
+
function rulesFileFor(root, exists = existsSync22) {
|
|
12122
12539
|
const agents = join24(root, "AGENTS.md");
|
|
12123
12540
|
if (exists(agents)) return agents;
|
|
12124
12541
|
const claude = join24(root, "CLAUDE.md");
|
|
@@ -12225,12 +12642,12 @@ function bundledSkillsDir(from) {
|
|
|
12225
12642
|
let dir = dirname9(from);
|
|
12226
12643
|
for (let i = 0; i < 6; i++) {
|
|
12227
12644
|
const pkg = join24(dir, "package.json");
|
|
12228
|
-
if (
|
|
12645
|
+
if (existsSync22(pkg)) {
|
|
12229
12646
|
try {
|
|
12230
12647
|
const name = JSON.parse(readFileSync9(pkg, "utf8")).name;
|
|
12231
12648
|
if (name === "@vosjs/cli") {
|
|
12232
12649
|
const skills = join24(dir, "skills");
|
|
12233
|
-
return
|
|
12650
|
+
return existsSync22(skills) && statSync7(skills).isDirectory() ? skills : null;
|
|
12234
12651
|
}
|
|
12235
12652
|
} catch {
|
|
12236
12653
|
}
|
|
@@ -12261,7 +12678,7 @@ function npx() {
|
|
|
12261
12678
|
function bundledChromium() {
|
|
12262
12679
|
try {
|
|
12263
12680
|
const p = chromium2.executablePath();
|
|
12264
|
-
return p &&
|
|
12681
|
+
return p && existsSync23(p) ? p : null;
|
|
12265
12682
|
} catch {
|
|
12266
12683
|
return null;
|
|
12267
12684
|
}
|
|
@@ -12270,7 +12687,7 @@ function probe2() {
|
|
|
12270
12687
|
return probeBrowser({
|
|
12271
12688
|
platform: process.platform,
|
|
12272
12689
|
env: process.env,
|
|
12273
|
-
exists:
|
|
12690
|
+
exists: existsSync23,
|
|
12274
12691
|
bundled: bundledChromium
|
|
12275
12692
|
});
|
|
12276
12693
|
}
|
|
@@ -12409,7 +12826,7 @@ async function cmdSetup(argv) {
|
|
|
12409
12826
|
let rules = null;
|
|
12410
12827
|
if (!noRules) {
|
|
12411
12828
|
const file = rulesFileFor(root);
|
|
12412
|
-
const existing =
|
|
12829
|
+
const existing = existsSync23(file) ? readFileSync10(file, "utf8") : null;
|
|
12413
12830
|
const { text, changed } = applyRulesBlock(existing);
|
|
12414
12831
|
if (changed) writeFileSync6(file, text);
|
|
12415
12832
|
rules = { file, changed };
|
|
@@ -12523,7 +12940,7 @@ async function cmdWhoami(argv) {
|
|
|
12523
12940
|
function cmdLogout(argv) {
|
|
12524
12941
|
const { flags } = parseArgs(argv, BOOLEAN_FLAGS11);
|
|
12525
12942
|
const r = reporterFor(flags);
|
|
12526
|
-
const had =
|
|
12943
|
+
const had = existsSync23(CREDENTIALS_PATH);
|
|
12527
12944
|
rmSync3(CREDENTIALS_PATH, { force: true });
|
|
12528
12945
|
const envKey = Boolean(process.env.VOS_API_KEY?.trim());
|
|
12529
12946
|
r.done(
|
|
@@ -12683,12 +13100,28 @@ Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
|
|
|
12683
13100
|
and fileUrl untouched; the extension must keep matching the
|
|
12684
13101
|
asset's kind (a recipe stays .md)
|
|
12685
13102
|
vos asset push <file...> [--folder <folderId|slug>]
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
the url a doc.json
|
|
12691
|
-
|
|
13103
|
+
add files to your library: models, pictures and SVGs, fonts,
|
|
13104
|
+
HDR maps, sound, video, captions and recipes. The platform
|
|
13105
|
+
reads each file and answers what it is; the same bytes twice
|
|
13106
|
+
are the same file. Prints, per file, what was read, its
|
|
13107
|
+
asset:<id>, the url a doc.json key takes, and the line that
|
|
13108
|
+
declares it in a program's "assets"
|
|
13109
|
+
vos asset import <url> [--name <file>] [--folder <slug>]
|
|
13110
|
+
[--license <text>] [--attribution <text>]
|
|
13111
|
+
fetch a file from an address and add it, with where it came
|
|
13112
|
+
from kept beside it. Say the license: it is what lets the
|
|
13113
|
+
next person use the file
|
|
13114
|
+
vos assets ls [--kind <kind>] [--folder <slug>|none] [--q <text>]
|
|
13115
|
+
[--sort recent|size|name] [--limit <n>]
|
|
13116
|
+
what is in your library: id, kind, size, whether a program
|
|
13117
|
+
uses it, and whether it is a library file or attached to a
|
|
13118
|
+
document
|
|
13119
|
+
vos assets usage
|
|
13120
|
+
storage held, of how much, and the part in Trash; uploads in
|
|
13121
|
+
the last 24 hours per kind against each daily rate
|
|
13122
|
+
vos assets why <assetId>
|
|
13123
|
+
every program that uses a file, and how (in what plays now,
|
|
13124
|
+
in history, in a draft): why it is kept
|
|
12692
13125
|
vos recipe push <file.md> --folder <folderId|slug>
|
|
12693
13126
|
vos recipe push <file.md> --asset <assetId>
|
|
12694
13127
|
put a recipe (a plain .md) on the shelf: --folder creates it
|
|
@@ -12940,7 +13373,7 @@ function takeStorageState(flags) {
|
|
|
12940
13373
|
const raw = strFlag(flags, "storage-state");
|
|
12941
13374
|
if (!raw) return void 0;
|
|
12942
13375
|
const path = resolve15(raw);
|
|
12943
|
-
if (!
|
|
13376
|
+
if (!existsSync24(path))
|
|
12944
13377
|
throw new UsageError(`--storage-state: no such file: ${path}`);
|
|
12945
13378
|
return path;
|
|
12946
13379
|
}
|
|
@@ -12953,7 +13386,7 @@ function takeSessionName(flags, storageState) {
|
|
|
12953
13386
|
throw new UsageError(
|
|
12954
13387
|
"--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
|
|
12955
13388
|
);
|
|
12956
|
-
if (!
|
|
13389
|
+
if (!existsSync24(sessionDir(name)))
|
|
12957
13390
|
throw new UsageError(
|
|
12958
13391
|
`no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
|
|
12959
13392
|
);
|
|
@@ -13176,7 +13609,7 @@ ${lines.join("\n")}
|
|
|
13176
13609
|
}
|
|
13177
13610
|
const paths = takePaths(outDir);
|
|
13178
13611
|
const prepare = async () => {
|
|
13179
|
-
if (
|
|
13612
|
+
if (existsSync24(join26(outDir, "meta.json"))) {
|
|
13180
13613
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
13181
13614
|
r.log(
|
|
13182
13615
|
`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" : "")
|
|
@@ -13280,7 +13713,7 @@ async function cmdCreate2(argv) {
|
|
|
13280
13713
|
const headers = takeHeaders(multi);
|
|
13281
13714
|
const paths = takePaths(outDir);
|
|
13282
13715
|
const prepare = async () => {
|
|
13283
|
-
if (
|
|
13716
|
+
if (existsSync24(join26(outDir, "meta.json"))) {
|
|
13284
13717
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
13285
13718
|
r.log(
|
|
13286
13719
|
`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" : "")
|
|
@@ -13393,7 +13826,7 @@ async function cmdPlan(argv) {
|
|
|
13393
13826
|
let reuse;
|
|
13394
13827
|
if (flags.reuse === true) {
|
|
13395
13828
|
const from = resolve15(strFlag(flags, "from") ?? join26(dir, PREV_DOC_NAME));
|
|
13396
|
-
if (!
|
|
13829
|
+
if (!existsSync24(from)) {
|
|
13397
13830
|
throw new UsageError(
|
|
13398
13831
|
`--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
|
|
13399
13832
|
);
|
|
@@ -13404,7 +13837,7 @@ async function cmdPlan(argv) {
|
|
|
13404
13837
|
};
|
|
13405
13838
|
}
|
|
13406
13839
|
const style = await resolveStyleRef(flags);
|
|
13407
|
-
const hasDoc =
|
|
13840
|
+
const hasDoc = existsSync24(join26(dir, "doc.json"));
|
|
13408
13841
|
const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
|
|
13409
13842
|
const release = await releaseInputs(dir, flags, r, multi.with ?? []);
|
|
13410
13843
|
const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
|
|
@@ -13832,11 +14265,11 @@ async function resolveStyleRef(flags) {
|
|
|
13832
14265
|
}
|
|
13833
14266
|
async function resolveDocRef(ref, flags, what) {
|
|
13834
14267
|
const styleRef = ref;
|
|
13835
|
-
const file =
|
|
14268
|
+
const file = existsSync24(styleRef) ? resolve15(
|
|
13836
14269
|
styleRef,
|
|
13837
|
-
|
|
14270
|
+
existsSync24(join26(styleRef, "doc.json")) ? "doc.json" : ""
|
|
13838
14271
|
) : null;
|
|
13839
|
-
if (file &&
|
|
14272
|
+
if (file && existsSync24(file)) {
|
|
13840
14273
|
return {
|
|
13841
14274
|
from: file,
|
|
13842
14275
|
doc: migrateHostedDoc5(
|
|
@@ -13951,7 +14384,7 @@ async function cmdOpen(argv) {
|
|
|
13951
14384
|
if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
|
|
13952
14385
|
const r = createReporter(flags.json === true);
|
|
13953
14386
|
const take = await loadTake(dir);
|
|
13954
|
-
if (!
|
|
14387
|
+
if (!existsSync24(take.paths.recording)) {
|
|
13955
14388
|
throw new UsageError(`${dir} has no recording \u2014 re-run record`);
|
|
13956
14389
|
}
|
|
13957
14390
|
const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
|
|
@@ -13993,8 +14426,8 @@ async function cmdJudge(argv) {
|
|
|
13993
14426
|
);
|
|
13994
14427
|
const r = createReporter(flags.json === true);
|
|
13995
14428
|
const kitFile = target.endsWith("kit.json") ? target : join26(target, "kit.json");
|
|
13996
|
-
if (!
|
|
13997
|
-
if (!
|
|
14429
|
+
if (!existsSync24(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
|
|
14430
|
+
if (!existsSync24(against))
|
|
13998
14431
|
throw new UsageError(`${against}: no reference manifest`);
|
|
13999
14432
|
const result = await judgeKit(kitFile, against, strFlag(flags, "out"));
|
|
14000
14433
|
const verdicts = JSON.parse(await readFile15(result.verdictFile, "utf8")).verdicts;
|
|
@@ -14116,7 +14549,7 @@ async function cmdValidate(argv) {
|
|
|
14116
14549
|
const target = positionals[0];
|
|
14117
14550
|
if (!target) throw new UsageError("vos validate <actions.json|take>");
|
|
14118
14551
|
const r = createReporter(flags.json === true);
|
|
14119
|
-
const kitTarget = target.endsWith("kit.json") ? target :
|
|
14552
|
+
const kitTarget = target.endsWith("kit.json") ? target : existsSync24(join26(target, "kit.json")) && !isTakeDir(target) ? join26(target, "kit.json") : null;
|
|
14120
14553
|
if (kitTarget) {
|
|
14121
14554
|
const picture = flags.picture === true;
|
|
14122
14555
|
const verdict = await validateKit(kitTarget, { picture });
|
|
@@ -14148,7 +14581,7 @@ async function cmdValidate(argv) {
|
|
|
14148
14581
|
r.done({ valid: true, target }, `${target}: valid actions file`);
|
|
14149
14582
|
return EXIT_OK;
|
|
14150
14583
|
}
|
|
14151
|
-
if (!isTakeDir(target) &&
|
|
14584
|
+
if (!isTakeDir(target) && existsSync24(join26(target, "config.json"))) {
|
|
14152
14585
|
const parsed = JSON.parse(
|
|
14153
14586
|
await readFile15(join26(target, "config.json"), "utf8")
|
|
14154
14587
|
);
|
|
@@ -14191,7 +14624,7 @@ async function cmdValidate(argv) {
|
|
|
14191
14624
|
const take = await loadTake(target);
|
|
14192
14625
|
const problems = [];
|
|
14193
14626
|
const warnings = [];
|
|
14194
|
-
if (!
|
|
14627
|
+
if (!existsSync24(take.paths.recording)) {
|
|
14195
14628
|
problems.push("missing recording (re-run record)");
|
|
14196
14629
|
} else {
|
|
14197
14630
|
const mismatch = containerMismatch(
|
|
@@ -14464,6 +14897,7 @@ async function run(argv) {
|
|
|
14464
14897
|
case "folder":
|
|
14465
14898
|
return await cmdFolder(rest);
|
|
14466
14899
|
case "asset":
|
|
14900
|
+
case "assets":
|
|
14467
14901
|
return await cmdAsset(rest);
|
|
14468
14902
|
case "recipe":
|
|
14469
14903
|
return await cmdRecipe(rest);
|
|
@@ -14543,7 +14977,7 @@ async function cmdSession(argv) {
|
|
|
14543
14977
|
if (!arg) throw new UsageError(usage);
|
|
14544
14978
|
const bad = sessionNameVerdict(arg);
|
|
14545
14979
|
if (bad) throw new UsageError(bad);
|
|
14546
|
-
if (!
|
|
14980
|
+
if (!existsSync24(sessionDir(arg)))
|
|
14547
14981
|
throw new UsageError(`no session "${arg}" (vos session list)`);
|
|
14548
14982
|
const summary3 = await summarizeSession(arg);
|
|
14549
14983
|
const url = strFlag(flags, "url");
|
|
@@ -14643,4 +15077,4 @@ export {
|
|
|
14643
15077
|
verbHelp,
|
|
14644
15078
|
run
|
|
14645
15079
|
};
|
|
14646
|
-
//# sourceMappingURL=chunk-
|
|
15080
|
+
//# sourceMappingURL=chunk-AKIPDSHE.js.map
|