@vosjs/cli 0.61.0 → 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-NS64PLY5.js → assetManifest-OMN33M2A.js} +2 -2
- package/dist/{chunk-C4P6MZJG.js → chunk-AKIPDSHE.js} +605 -135
- 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-EZW6ZCEH.js → chunk-PT73CRXX.js} +21 -10
- 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-IZ72SDZC.js → run-OGG3NP2N.js} +3 -3
- package/package.json +3 -3
- 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-C4P6MZJG.js.map +0 -1
- package/dist/chunk-EZW6ZCEH.js.map +0 -1
- package/dist/manifest-Q4QMVIYR.js +0 -8
- /package/dist/{assetManifest-NS64PLY5.js.map → assetManifest-OMN33M2A.js.map} +0 -0
- /package/dist/{manifest-Q4QMVIYR.js.map → manifest-ZBK6IERM.js.map} +0 -0
- /package/dist/{run-IZ72SDZC.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) {
|
|
@@ -7473,10 +7482,10 @@ async function pullTake(dir, flags, r) {
|
|
|
7473
7482
|
}
|
|
7474
7483
|
|
|
7475
7484
|
// src/plugin/program.ts
|
|
7476
|
-
import { existsSync as
|
|
7485
|
+
import { existsSync as existsSync14 } from "fs";
|
|
7477
7486
|
import { mkdir as mkdir7, readFile as readFile13, writeFile as writeFile9 } from "fs/promises";
|
|
7478
7487
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
7479
|
-
import { basename as
|
|
7488
|
+
import { basename as basename3, dirname as dirname3, join as join15 } from "path";
|
|
7480
7489
|
import {
|
|
7481
7490
|
CURRENT_CONFIG_VERSION,
|
|
7482
7491
|
migrateConfig,
|
|
@@ -7608,6 +7617,112 @@ async function handoffLogin(url, r, opts = {}) {
|
|
|
7608
7617
|
throw new Error(`${said}${hint}`);
|
|
7609
7618
|
}
|
|
7610
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
|
+
|
|
7611
7726
|
// src/plugin/program.ts
|
|
7612
7727
|
var BOOLEAN_FLAGS2 = /* @__PURE__ */ new Set([
|
|
7613
7728
|
"json",
|
|
@@ -7616,6 +7731,7 @@ var BOOLEAN_FLAGS2 = /* @__PURE__ */ new Set([
|
|
|
7616
7731
|
"check",
|
|
7617
7732
|
"yes",
|
|
7618
7733
|
"claimable",
|
|
7734
|
+
"share",
|
|
7619
7735
|
"no-browser",
|
|
7620
7736
|
"wait"
|
|
7621
7737
|
]);
|
|
@@ -7683,7 +7799,7 @@ function preflightConfig(parsed) {
|
|
|
7683
7799
|
function resolveConfigPath(target) {
|
|
7684
7800
|
if (target.endsWith(".json")) return target;
|
|
7685
7801
|
const inDir = join15(target, "config.json");
|
|
7686
|
-
if (
|
|
7802
|
+
if (existsSync14(inDir)) return inDir;
|
|
7687
7803
|
throw new UsageError(
|
|
7688
7804
|
`${target} has no config.json (and is not a take \u2014 no doc.json)`
|
|
7689
7805
|
);
|
|
@@ -7833,6 +7949,7 @@ async function createProgramVos(opts) {
|
|
|
7833
7949
|
client: clientId()
|
|
7834
7950
|
};
|
|
7835
7951
|
if (opts.description) body.description = opts.description;
|
|
7952
|
+
if (opts.prompt) body.prompt = opts.prompt;
|
|
7836
7953
|
if (opts.tags?.length) body.tags = opts.tags;
|
|
7837
7954
|
if (opts.folderId) body.folderId = opts.folderId;
|
|
7838
7955
|
if (opts.remixOfId) body.remixOfId = opts.remixOfId;
|
|
@@ -7867,7 +7984,7 @@ async function cmdPushProgram(argv) {
|
|
|
7867
7984
|
const target = positionals[0];
|
|
7868
7985
|
if (!target) {
|
|
7869
7986
|
throw new UsageError(
|
|
7870
|
-
|
|
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]]'
|
|
7871
7988
|
);
|
|
7872
7989
|
}
|
|
7873
7990
|
const source = resolveConfigPath(target);
|
|
@@ -7887,6 +8004,12 @@ async function cmdPushProgram(argv) {
|
|
|
7887
8004
|
}
|
|
7888
8005
|
const config = pre.config;
|
|
7889
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
|
+
}
|
|
7890
8013
|
const vosFlag = strFlag(flags, "vos");
|
|
7891
8014
|
const explicitVosId = vosFlag ? parseVosId(vosFlag) : null;
|
|
7892
8015
|
const stillFlag = strFlag(flags, "still");
|
|
@@ -7897,7 +8020,8 @@ async function cmdPushProgram(argv) {
|
|
|
7897
8020
|
await setProgramStill(dir, t);
|
|
7898
8021
|
r.log(`cover: ${t}s (doc.json "still")`);
|
|
7899
8022
|
}
|
|
7900
|
-
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);
|
|
7901
8025
|
const programDoc = await readProgramDoc(dir, config);
|
|
7902
8026
|
if (programDoc) {
|
|
7903
8027
|
const lint = lintDoc(programDoc);
|
|
@@ -7905,7 +8029,7 @@ async function cmdPushProgram(argv) {
|
|
|
7905
8029
|
throw new Error(`doc.json fails lint:
|
|
7906
8030
|
${lint.problems.join("\n ")}`);
|
|
7907
8031
|
}
|
|
7908
|
-
for (const w of lint.warnings) r.
|
|
8032
|
+
for (const w of lint.warnings) r.warn(`doc.json: ${w}`);
|
|
7909
8033
|
}
|
|
7910
8034
|
if (flags.claimable === true) {
|
|
7911
8035
|
if (explicitVosId) {
|
|
@@ -7913,43 +8037,50 @@ async function cmdPushProgram(argv) {
|
|
|
7913
8037
|
"--claimable creates a NEW claimable vos and cannot iterate (--vos). After the human claims it, iterate with their key: vos push --vos <id>"
|
|
7914
8038
|
);
|
|
7915
8039
|
}
|
|
7916
|
-
const fallback = state?.title ? `${state.title} remix` :
|
|
8040
|
+
const fallback = state?.title ? `${state.title} remix` : basename3(source).replace(/\.json$/i, "") || "vos program";
|
|
7917
8041
|
const title2 = (strFlag(flags, "title") ?? fallback).slice(0, 100);
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
const
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
audio: (sound.audio ?? []).filter((a) => !dropped.has(a.key))
|
|
7927
|
-
};
|
|
7928
|
-
r.log(
|
|
7929
|
-
`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"}`
|
|
7930
|
-
);
|
|
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));
|
|
7931
8050
|
}
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
throw new UsageError(
|
|
7936
|
-
`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`
|
|
7937
8054
|
);
|
|
8055
|
+
await files2.upload({
|
|
8056
|
+
origin,
|
|
8057
|
+
key: uploadToken,
|
|
8058
|
+
door: "/api/claim/uploads/files"
|
|
8059
|
+
});
|
|
7938
8060
|
}
|
|
7939
8061
|
const body = {
|
|
7940
8062
|
title: title2,
|
|
7941
|
-
config: storedProgramConfig(config,
|
|
8063
|
+
config: storedProgramConfig(config, programDoc)
|
|
7942
8064
|
};
|
|
8065
|
+
if (uploadToken) body.uploadToken = uploadToken;
|
|
8066
|
+
if (prompt) body.prompt = prompt;
|
|
8067
|
+
if (flags.share === true) body.share = true;
|
|
7943
8068
|
const slug = strFlag(flags, "slug");
|
|
7944
8069
|
if (slug) body.slug = slug;
|
|
7945
8070
|
const res = await apiJson(origin, "/api/claim", { method: "POST", body });
|
|
7946
8071
|
if (res.status !== 201) throw new Error(apiError("claimable push", res));
|
|
7947
|
-
serverWarnings(res.body, (
|
|
8072
|
+
serverWarnings(res.body, say(r));
|
|
7948
8073
|
const claimUrl = String(res.body.claimUrl ?? "");
|
|
7949
8074
|
const expiresAt = String(res.body.expiresAt ?? "");
|
|
7950
8075
|
const created2 = res.body.vos ?? {};
|
|
7951
8076
|
r.done(
|
|
7952
|
-
{
|
|
8077
|
+
{
|
|
8078
|
+
id: created2.id ?? null,
|
|
8079
|
+
title: title2,
|
|
8080
|
+
claimUrl,
|
|
8081
|
+
expiresAt,
|
|
8082
|
+
...uploadToken ? { files: files2.count } : {}
|
|
8083
|
+
},
|
|
7953
8084
|
`Claimable push created (${title2})
|
|
7954
8085
|
claim: ${claimUrl}
|
|
7955
8086
|
expires: ${expiresAt} \u2014 unclaimed work is deleted after 72h
|
|
@@ -7961,15 +8092,8 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
7961
8092
|
}
|
|
7962
8093
|
const key = requireCredential(strFlag(flags, "key"));
|
|
7963
8094
|
void flags.yes;
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
docLayerRefs(layersOf(programDoc)),
|
|
7967
|
-
dir,
|
|
7968
|
-
{ origin, key },
|
|
7969
|
-
(l) => r.log(l)
|
|
7970
|
-
);
|
|
7971
|
-
}
|
|
7972
|
-
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 });
|
|
7973
8097
|
const overrides = [
|
|
7974
8098
|
...multi.override ?? [],
|
|
7975
8099
|
...strFlag(flags, "overrides")?.split(",") ?? []
|
|
@@ -8033,7 +8157,15 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8033
8157
|
}
|
|
8034
8158
|
if (res.status !== 201)
|
|
8035
8159
|
throw new Error(apiError(`push version to ${vosId}`, res));
|
|
8036
|
-
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
|
+
}
|
|
8037
8169
|
const version = res.body.version ?? {};
|
|
8038
8170
|
if (typeof version.id === "string") {
|
|
8039
8171
|
writeSyncState(dir, { vosId, versionId: version.id });
|
|
@@ -8045,7 +8177,7 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
|
|
|
8045
8177
|
key,
|
|
8046
8178
|
vosId,
|
|
8047
8179
|
versionId: typeof version.id === "string" ? version.id : null,
|
|
8048
|
-
log: (
|
|
8180
|
+
log: say(r)
|
|
8049
8181
|
}) : null;
|
|
8050
8182
|
r.done(
|
|
8051
8183
|
{
|
|
@@ -8069,7 +8201,7 @@ ${landedLines(landed2)}` : "")
|
|
|
8069
8201
|
return EXIT_OK;
|
|
8070
8202
|
}
|
|
8071
8203
|
const remixOfId = pushTarget.kind === "create" ? pushTarget.remixOfId : void 0;
|
|
8072
|
-
const fallbackTitle = state?.title ? `${state.title} remix` :
|
|
8204
|
+
const fallbackTitle = state?.title ? `${state.title} remix` : basename3(source).replace(/\.json$/i, "") || "vos remix";
|
|
8073
8205
|
const title = (strFlag(flags, "title") ?? fallbackTitle).slice(0, 100);
|
|
8074
8206
|
const folderId = folderRef ? resolveFolder(await listFolders(origin, key), folderRef).id : void 0;
|
|
8075
8207
|
const created = await createProgramVos({
|
|
@@ -8081,6 +8213,7 @@ ${landedLines(landed2)}` : "")
|
|
|
8081
8213
|
// (found seeding one-layer members: `--with` then said the vos carried
|
|
8082
8214
|
// no doc).
|
|
8083
8215
|
doc: programDoc ?? void 0,
|
|
8216
|
+
prompt,
|
|
8084
8217
|
title,
|
|
8085
8218
|
slug: strFlag(flags, "slug"),
|
|
8086
8219
|
description: desc,
|
|
@@ -8105,7 +8238,7 @@ ${landedLines(landed2)}` : "")
|
|
|
8105
8238
|
key,
|
|
8106
8239
|
vosId: created.id,
|
|
8107
8240
|
versionId: created.currentVersionId,
|
|
8108
|
-
log: (
|
|
8241
|
+
log: say(r)
|
|
8109
8242
|
}) : null;
|
|
8110
8243
|
r.done(
|
|
8111
8244
|
{
|
|
@@ -8203,7 +8336,7 @@ async function cmdPullProgram(argv) {
|
|
|
8203
8336
|
throw new Error(apiError(`fetch head config for ${vosId}`, cfg));
|
|
8204
8337
|
const configPath = join15(dir, "config.json");
|
|
8205
8338
|
let backedUp = false;
|
|
8206
|
-
if (
|
|
8339
|
+
if (existsSync14(configPath)) {
|
|
8207
8340
|
await writeFile9(join15(dir, "config.backup.json"), await readFile13(configPath));
|
|
8208
8341
|
backedUp = true;
|
|
8209
8342
|
}
|
|
@@ -8308,6 +8441,27 @@ async function storeKey(key, origin, r) {
|
|
|
8308
8441
|
function isTakeDir(target) {
|
|
8309
8442
|
return directoryKind(target) === "take";
|
|
8310
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
|
+
}
|
|
8311
8465
|
function soundOf(doc) {
|
|
8312
8466
|
return { audio: Array.isArray(doc.audio) ? doc.audio : [] };
|
|
8313
8467
|
}
|
|
@@ -8325,7 +8479,7 @@ function storedProgramConfig(config, programDoc) {
|
|
|
8325
8479
|
}
|
|
8326
8480
|
async function readProgramDoc(dir, config) {
|
|
8327
8481
|
const docPath = join15(dir, "doc.json");
|
|
8328
|
-
if (!
|
|
8482
|
+
if (!existsSync14(docPath)) return null;
|
|
8329
8483
|
const parsed = JSON.parse(await readFile13(docPath, "utf8"));
|
|
8330
8484
|
if (typeof parsed !== "object" || parsed === null || "source" in parsed)
|
|
8331
8485
|
return null;
|
|
@@ -8336,7 +8490,7 @@ async function readProgramDoc(dir, config) {
|
|
|
8336
8490
|
async function setProgramStill(dir, t) {
|
|
8337
8491
|
const docPath = join15(dir, "doc.json");
|
|
8338
8492
|
let doc = { program: {}, audio: [] };
|
|
8339
|
-
if (
|
|
8493
|
+
if (existsSync14(docPath)) {
|
|
8340
8494
|
const parsed = JSON.parse(await readFile13(docPath, "utf8"));
|
|
8341
8495
|
if (!parsed || typeof parsed !== "object" || "source" in parsed)
|
|
8342
8496
|
throw new UsageError(
|
|
@@ -8359,7 +8513,107 @@ async function writeProgramDoc(dir, hosted) {
|
|
|
8359
8513
|
// src/plugin/asset.ts
|
|
8360
8514
|
import { createHash as createHash2 } from "crypto";
|
|
8361
8515
|
import { readFileSync as readFileSync3 } from "fs";
|
|
8362
|
-
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
|
|
8363
8617
|
var BOOLEAN_FLAGS3 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
8364
8618
|
async function cmdRename(argv) {
|
|
8365
8619
|
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS3);
|
|
@@ -8400,7 +8654,7 @@ async function cmdPush(argv) {
|
|
|
8400
8654
|
const folder = folderRef ? resolveFolder(await listFolders(origin, key), folderRef) : null;
|
|
8401
8655
|
const uploaded = [];
|
|
8402
8656
|
for (const file of positionals) {
|
|
8403
|
-
const name =
|
|
8657
|
+
const name = basename4(file);
|
|
8404
8658
|
const body = readFileSync3(file);
|
|
8405
8659
|
const media = resolveMediaType({
|
|
8406
8660
|
head: body.subarray(0, MEDIA_HEAD_BYTES),
|
|
@@ -8429,12 +8683,27 @@ async function cmdPush(argv) {
|
|
|
8429
8683
|
filename: stored,
|
|
8430
8684
|
kind: put.kind,
|
|
8431
8685
|
url: put.url,
|
|
8686
|
+
ref: put.ref,
|
|
8687
|
+
size: put.size,
|
|
8688
|
+
...put.duration ? { duration: put.duration } : {},
|
|
8689
|
+
...put.metadata ? { metadata: put.metadata } : {},
|
|
8432
8690
|
...put.reused ? { reused: true } : {},
|
|
8433
8691
|
...put.notes.length ? { notes: put.notes } : {}
|
|
8434
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
|
+
};
|
|
8435
8701
|
r.log(
|
|
8436
|
-
`uploaded ${stored}
|
|
8702
|
+
`uploaded ${stored}${put.reused ? " \u2014 already yours, nothing sent" : ""}`
|
|
8437
8703
|
);
|
|
8704
|
+
r.log(` ${probeLine(hosted)}, ${bytesWords(put.size)}`);
|
|
8705
|
+
r.log(` ${put.ref} ${put.url}`);
|
|
8706
|
+
r.log(` ${manifestLine(hosted)}`);
|
|
8438
8707
|
for (const note of put.notes) r.log(` note: ${note}`);
|
|
8439
8708
|
}
|
|
8440
8709
|
r.done(
|
|
@@ -8447,6 +8716,181 @@ async function cmdPush(argv) {
|
|
|
8447
8716
|
);
|
|
8448
8717
|
return EXIT_OK;
|
|
8449
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
|
+
}
|
|
8450
8894
|
async function cmdAsset(argv) {
|
|
8451
8895
|
const sub = argv[0];
|
|
8452
8896
|
const rest = argv.slice(1);
|
|
@@ -8455,15 +8899,24 @@ async function cmdAsset(argv) {
|
|
|
8455
8899
|
return cmdRename(rest);
|
|
8456
8900
|
case "push":
|
|
8457
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);
|
|
8458
8911
|
default:
|
|
8459
8912
|
throw new UsageError(
|
|
8460
|
-
"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>])"
|
|
8461
8914
|
);
|
|
8462
8915
|
}
|
|
8463
8916
|
}
|
|
8464
8917
|
|
|
8465
8918
|
// src/plugin/remove.ts
|
|
8466
|
-
import { existsSync as
|
|
8919
|
+
import { existsSync as existsSync15, rmSync as rmSync2, statSync as statSync3 } from "fs";
|
|
8467
8920
|
import { join as join16 } from "path";
|
|
8468
8921
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
8469
8922
|
|
|
@@ -8643,7 +9096,7 @@ async function cmdDelete(argv) {
|
|
|
8643
9096
|
api: strFlag(flags, "api")
|
|
8644
9097
|
}).replace(/\/+$/, "");
|
|
8645
9098
|
const key = requireCredential(strFlag(flags, "key"));
|
|
8646
|
-
const dir =
|
|
9099
|
+
const dir = existsSync15(target) && statSync3(target).isDirectory() ? target : null;
|
|
8647
9100
|
const vosId = dir ? readSyncState(dir)?.vosId : parseVosId(target);
|
|
8648
9101
|
if (!vosId)
|
|
8649
9102
|
throw new UsageError(
|
|
@@ -8711,7 +9164,7 @@ async function cmdDelete(argv) {
|
|
|
8711
9164
|
|
|
8712
9165
|
// src/plugin/buildProgram.ts
|
|
8713
9166
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
8714
|
-
import { basename as
|
|
9167
|
+
import { basename as basename5, dirname as dirname4, join as join17, resolve as resolve7 } from "path";
|
|
8715
9168
|
import { pathToFileURL } from "url";
|
|
8716
9169
|
import { parse } from "acorn";
|
|
8717
9170
|
import { fullAncestor } from "acorn-walk";
|
|
@@ -8919,7 +9372,7 @@ async function cmdBuild(argv) {
|
|
|
8919
9372
|
throw new UsageError(
|
|
8920
9373
|
`${source} exports no program: \`export default { version: 2, duration, camera, createContent, createTimeline, \u2026 }\``
|
|
8921
9374
|
);
|
|
8922
|
-
const built = buildProgram(program,
|
|
9375
|
+
const built = buildProgram(program, basename5(file), text);
|
|
8923
9376
|
for (const p of built.problems) r.log(`error ${p.fn}: ${p.message}`);
|
|
8924
9377
|
if (built.problems.length) {
|
|
8925
9378
|
r.done(
|
|
@@ -8946,7 +9399,7 @@ async function cmdBuild(argv) {
|
|
|
8946
9399
|
|
|
8947
9400
|
// src/plugin/compare.ts
|
|
8948
9401
|
import { spawnSync } from "child_process";
|
|
8949
|
-
import { existsSync as
|
|
9402
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
8950
9403
|
import { dirname as dirname5, join as join18, resolve as resolve8 } from "path";
|
|
8951
9404
|
var BOOLEAN_FLAGS7 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
8952
9405
|
function sampleTimes(duration, every) {
|
|
@@ -9014,7 +9467,7 @@ async function cmdCompare(argv) {
|
|
|
9014
9467
|
const config = loaded.config;
|
|
9015
9468
|
const duration = configDuration(config) ?? 5;
|
|
9016
9469
|
const assets = await programAssets(config, {
|
|
9017
|
-
baseDir: /^https?:\/\//.test(source) ? null :
|
|
9470
|
+
baseDir: /^https?:\/\//.test(source) ? null : existsSync16(source) && statSync4(source).isDirectory() ? source : dirname5(source),
|
|
9018
9471
|
origin: platformOrigin({}),
|
|
9019
9472
|
key: resolveCredential(),
|
|
9020
9473
|
log: (line) => r.log(line)
|
|
@@ -9133,28 +9586,28 @@ async function cmdCompare(argv) {
|
|
|
9133
9586
|
}
|
|
9134
9587
|
|
|
9135
9588
|
// src/plugin/port.ts
|
|
9136
|
-
import { existsSync as
|
|
9589
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
9137
9590
|
import { dirname as dirname8, join as join22, resolve as resolve11 } from "path";
|
|
9138
9591
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
9139
9592
|
|
|
9140
9593
|
// src/plugin/portInventory.ts
|
|
9141
9594
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9142
9595
|
import {
|
|
9143
|
-
existsSync as
|
|
9596
|
+
existsSync as existsSync18,
|
|
9144
9597
|
mkdirSync as mkdirSync3,
|
|
9145
9598
|
readFileSync as readFileSync6,
|
|
9146
9599
|
readdirSync as readdirSync4,
|
|
9147
9600
|
statSync as statSync6
|
|
9148
9601
|
} from "fs";
|
|
9149
|
-
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";
|
|
9150
9603
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
9151
9604
|
import { findFontFamily as findFontFamily3 } from "@vosjs/shared";
|
|
9152
9605
|
|
|
9153
9606
|
// src/plugin/portRemotion.ts
|
|
9154
|
-
import { existsSync as
|
|
9607
|
+
import { existsSync as existsSync17, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
|
|
9155
9608
|
import { createServer as createServer2 } from "http";
|
|
9156
9609
|
import { createRequire } from "module";
|
|
9157
|
-
import { basename as
|
|
9610
|
+
import { basename as basename6, extname as extname3, join as join19 } from "path";
|
|
9158
9611
|
function themeColors(sources) {
|
|
9159
9612
|
const out = {};
|
|
9160
9613
|
const re = /\bconst\s+([A-Za-z_$][\w$]*)\s*(?::\s*string\s*)?=\s*['"](#[0-9a-fA-F]{3,8})['"]/g;
|
|
@@ -9227,7 +9680,7 @@ function sourceTexts(dir) {
|
|
|
9227
9680
|
}
|
|
9228
9681
|
};
|
|
9229
9682
|
const src = join19(dir, "src");
|
|
9230
|
-
walk(
|
|
9683
|
+
walk(existsSync17(src) ? src : dir, 0);
|
|
9231
9684
|
return out;
|
|
9232
9685
|
}
|
|
9233
9686
|
function entryPoint(dir) {
|
|
@@ -9239,7 +9692,7 @@ function entryPoint(dir) {
|
|
|
9239
9692
|
"remotion/index.ts",
|
|
9240
9693
|
"remotion/index.tsx"
|
|
9241
9694
|
])
|
|
9242
|
-
if (
|
|
9695
|
+
if (existsSync17(join19(dir, p))) return join19(dir, p);
|
|
9243
9696
|
return null;
|
|
9244
9697
|
}
|
|
9245
9698
|
var TYPES = {
|
|
@@ -9332,10 +9785,10 @@ async function readRemotion(dir, log) {
|
|
|
9332
9785
|
const server = createServer2((rq, rs) => {
|
|
9333
9786
|
const path = decodeURIComponent(new URL(rq.url ?? "/", "http://x").pathname);
|
|
9334
9787
|
let f = join19(out, path);
|
|
9335
|
-
if (!f.startsWith(out) || !
|
|
9788
|
+
if (!f.startsWith(out) || !existsSync17(f) || statSync5(f).isDirectory())
|
|
9336
9789
|
f = join19(out, "index.html");
|
|
9337
9790
|
rs.writeHead(200, {
|
|
9338
|
-
"content-type": TYPES[
|
|
9791
|
+
"content-type": TYPES[extname3(f)] ?? "application/octet-stream"
|
|
9339
9792
|
});
|
|
9340
9793
|
rs.end(readFileSync5(f));
|
|
9341
9794
|
}).listen(0, "127.0.0.1");
|
|
@@ -9434,8 +9887,8 @@ async function readRemotion(dir, log) {
|
|
|
9434
9887
|
const sources = sourceTexts(dir);
|
|
9435
9888
|
const publicDir = join19(dir, "public");
|
|
9436
9889
|
const media = tree.audio.map((a) => {
|
|
9437
|
-
const name =
|
|
9438
|
-
const local =
|
|
9890
|
+
const name = basename6(a.src.split("?")[0]);
|
|
9891
|
+
const local = existsSync17(join19(publicDir, name)) ? `public/${name}` : a.src;
|
|
9439
9892
|
return {
|
|
9440
9893
|
kind: "audio",
|
|
9441
9894
|
src: local,
|
|
@@ -9476,9 +9929,9 @@ async function readRemotion(dir, log) {
|
|
|
9476
9929
|
|
|
9477
9930
|
// src/plugin/portInventory.ts
|
|
9478
9931
|
function detectEngine(dir) {
|
|
9479
|
-
if (
|
|
9932
|
+
if (existsSync18(join20(dir, "hyperframes.json"))) return "hyperframes";
|
|
9480
9933
|
const pkg = join20(dir, "package.json");
|
|
9481
|
-
if (
|
|
9934
|
+
if (existsSync18(pkg)) {
|
|
9482
9935
|
try {
|
|
9483
9936
|
const p = JSON.parse(readFileSync6(pkg, "utf8"));
|
|
9484
9937
|
const deps = { ...p.dependencies ?? {}, ...p.devDependencies ?? {} };
|
|
@@ -9492,13 +9945,13 @@ function detectEngine(dir) {
|
|
|
9492
9945
|
return "page";
|
|
9493
9946
|
}
|
|
9494
9947
|
function sourcePage(source) {
|
|
9495
|
-
if (!
|
|
9948
|
+
if (!existsSync18(source)) return null;
|
|
9496
9949
|
if (statSync6(source).isFile()) return /\.html?$/i.test(source) ? source : null;
|
|
9497
9950
|
const index = join20(source, "index.html");
|
|
9498
|
-
return
|
|
9951
|
+
return existsSync18(index) ? index : null;
|
|
9499
9952
|
}
|
|
9500
9953
|
function findRender(dir) {
|
|
9501
|
-
const videos = (d) =>
|
|
9954
|
+
const videos = (d) => existsSync18(d) ? readdirSync4(d).filter((f) => /\.(mp4|webm|mov)$/i.test(f)).map((f) => join20(d, f)) : [];
|
|
9502
9955
|
for (const d of [join20(dir, "renders"), join20(dir, "out"), dir]) {
|
|
9503
9956
|
const found = videos(d);
|
|
9504
9957
|
if (found.length) return found[0];
|
|
@@ -9770,7 +10223,7 @@ var SEEK_AND_READ = `((args) => {
|
|
|
9770
10223
|
})`;
|
|
9771
10224
|
async function readInventory(source, opts) {
|
|
9772
10225
|
const abs = resolve9(source);
|
|
9773
|
-
const dir =
|
|
10226
|
+
const dir = existsSync18(abs) && statSync6(abs).isDirectory() ? abs : dirname6(abs);
|
|
9774
10227
|
const engine = detectEngine(dir);
|
|
9775
10228
|
const page = sourcePage(abs);
|
|
9776
10229
|
const render = opts.render === void 0 ? findRender(dir) : opts.render;
|
|
@@ -9900,7 +10353,7 @@ async function readInventory(source, opts) {
|
|
|
9900
10353
|
if (ok)
|
|
9901
10354
|
stills.push({
|
|
9902
10355
|
t: Number(t.toFixed(2)),
|
|
9903
|
-
file:
|
|
10356
|
+
file: relative3(opts.outDir, file)
|
|
9904
10357
|
});
|
|
9905
10358
|
}
|
|
9906
10359
|
} else {
|
|
@@ -9911,7 +10364,7 @@ async function readInventory(source, opts) {
|
|
|
9911
10364
|
}
|
|
9912
10365
|
const scriptStrings = scriptStringsOf(read.scriptText);
|
|
9913
10366
|
return {
|
|
9914
|
-
source:
|
|
10367
|
+
source: relative3(process.cwd(), abs) || ".",
|
|
9915
10368
|
root: abs.endsWith(".html") ? dirname6(abs) : abs,
|
|
9916
10369
|
engine,
|
|
9917
10370
|
width: read.width,
|
|
@@ -9929,15 +10382,15 @@ async function readInventory(source, opts) {
|
|
|
9929
10382
|
variables: read.variables,
|
|
9930
10383
|
scriptStrings,
|
|
9931
10384
|
uses: read.uses ?? sourceUses(read.scriptText ?? ""),
|
|
9932
|
-
render: render ?
|
|
10385
|
+
render: render ? relative3(process.cwd(), render) : null,
|
|
9933
10386
|
stills
|
|
9934
10387
|
};
|
|
9935
10388
|
}
|
|
9936
10389
|
|
|
9937
10390
|
// src/plugin/portScaffold.ts
|
|
9938
10391
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
9939
|
-
import { copyFileSync, existsSync as
|
|
9940
|
-
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";
|
|
9941
10394
|
import { findFontFamily as findFontFamily4, fontFaceUrl, nearestFontWeight } from "@vosjs/shared";
|
|
9942
10395
|
|
|
9943
10396
|
// src/plugin/painterLib.ts
|
|
@@ -10347,8 +10800,8 @@ function planScaffold(inv, opts) {
|
|
|
10347
10800
|
const copies = [];
|
|
10348
10801
|
inv.media.filter((m) => m.kind === "audio" && m.src).forEach((m, i) => {
|
|
10349
10802
|
const from = resolve10(opts.sourceDir, m.src);
|
|
10350
|
-
const name =
|
|
10351
|
-
if (!
|
|
10803
|
+
const name = basename7(from);
|
|
10804
|
+
if (!existsSync19(from)) {
|
|
10352
10805
|
report.push(
|
|
10353
10806
|
`- the audio ${m.src} is not in the source folder; add it to doc.json by hand`
|
|
10354
10807
|
);
|
|
@@ -10491,7 +10944,7 @@ ${sceneTodos}${springHint}
|
|
|
10491
10944
|
}
|
|
10492
10945
|
function reportText(inv, r) {
|
|
10493
10946
|
const lines = [
|
|
10494
|
-
`# Port report: ${inv.source === "." && inv.root ?
|
|
10947
|
+
`# Port report: ${inv.source === "." && inv.root ? basename7(inv.root) : inv.source}`,
|
|
10495
10948
|
"",
|
|
10496
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}\`.`,
|
|
10497
10950
|
"",
|
|
@@ -10619,7 +11072,7 @@ async function scaffold(argv) {
|
|
|
10619
11072
|
const { flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
10620
11073
|
const r = createReporter(flags.json === true);
|
|
10621
11074
|
const from = resolve11(strFlag(flags, "from") ?? join22("port", "inventory.json"));
|
|
10622
|
-
if (!
|
|
11075
|
+
if (!existsSync20(from))
|
|
10623
11076
|
throw new UsageError(
|
|
10624
11077
|
`no inventory at ${from}: run npx vos port inventory <source> first`
|
|
10625
11078
|
);
|
|
@@ -10655,11 +11108,11 @@ function scaffoldSourceDir(inv) {
|
|
|
10655
11108
|
// src/plugin/recipe.ts
|
|
10656
11109
|
import { createHash as createHash3 } from "crypto";
|
|
10657
11110
|
import { readFileSync as readFileSync8 } from "fs";
|
|
10658
|
-
import { basename as
|
|
11111
|
+
import { basename as basename8 } from "path";
|
|
10659
11112
|
var BOOLEAN_FLAGS9 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
10660
11113
|
var USAGE = "vos recipe push <file.md> (--folder <folderId|slug> | --asset <assetId>)";
|
|
10661
11114
|
function recipeFilename(file) {
|
|
10662
|
-
const name =
|
|
11115
|
+
const name = basename8(file);
|
|
10663
11116
|
if (!/\.md$/i.test(name)) {
|
|
10664
11117
|
throw new UsageError(`a recipe is a markdown file \u2014 ${name} is not .md`);
|
|
10665
11118
|
}
|
|
@@ -11311,8 +11764,8 @@ var quoteAttr = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
|
11311
11764
|
function hrefSelector(href) {
|
|
11312
11765
|
try {
|
|
11313
11766
|
const u = new URL(href);
|
|
11314
|
-
const
|
|
11315
|
-
return `a[href=${quoteAttr(
|
|
11767
|
+
const relative4 = `${u.pathname}${u.search}${u.hash}`;
|
|
11768
|
+
return `a[href=${quoteAttr(relative4)}], a[href=${quoteAttr(href)}]`;
|
|
11316
11769
|
} catch {
|
|
11317
11770
|
return `a[href=${quoteAttr(href)}]`;
|
|
11318
11771
|
}
|
|
@@ -11739,9 +12192,9 @@ async function cmdActionsScript(input, flags) {
|
|
|
11739
12192
|
}
|
|
11740
12193
|
|
|
11741
12194
|
// src/plugin/ingest.ts
|
|
11742
|
-
import { existsSync as
|
|
12195
|
+
import { existsSync as existsSync21 } from "fs";
|
|
11743
12196
|
import { copyFile, mkdir as mkdir8, readFile as readFile14, stat as stat3 } from "fs/promises";
|
|
11744
|
-
import { basename as
|
|
12197
|
+
import { basename as basename9, join as join23, resolve as resolve14 } from "path";
|
|
11745
12198
|
import { inflateRawSync } from "zlib";
|
|
11746
12199
|
import {
|
|
11747
12200
|
cursorFromPlaywrightTrace,
|
|
@@ -11822,7 +12275,7 @@ async function copyRecording(src, dest, ext) {
|
|
|
11822
12275
|
}
|
|
11823
12276
|
}
|
|
11824
12277
|
async function readTraceFile(path, offsetMs) {
|
|
11825
|
-
const kind = traceKindOf(
|
|
12278
|
+
const kind = traceKindOf(basename9(path));
|
|
11826
12279
|
if (!kind) {
|
|
11827
12280
|
throw new UsageError(
|
|
11828
12281
|
`${path}: not a trace the recorder reads (trace.zip, .trace, .jsonl, .json or .csv)`
|
|
@@ -11843,7 +12296,7 @@ async function readTraceFile(path, offsetMs) {
|
|
|
11843
12296
|
return cursorFromPlaywrightTrace(text2, { offsetMs });
|
|
11844
12297
|
}
|
|
11845
12298
|
const text = await readFile14(path, "utf8");
|
|
11846
|
-
return cursorFromTrace(
|
|
12299
|
+
return cursorFromTrace(basename9(path), text, { offsetMs });
|
|
11847
12300
|
}
|
|
11848
12301
|
function parseViewport(v) {
|
|
11849
12302
|
if (!v) return null;
|
|
@@ -11859,7 +12312,7 @@ async function cmdIngest(argv) {
|
|
|
11859
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]"
|
|
11860
12313
|
);
|
|
11861
12314
|
const videoPath = resolve14(source);
|
|
11862
|
-
if (!
|
|
12315
|
+
if (!existsSync21(videoPath)) throw new UsageError(`${source}: no such file`);
|
|
11863
12316
|
const r = createReporter(flags.json === true);
|
|
11864
12317
|
const outDir = resolve14(strFlag(flags, "out") ?? "take");
|
|
11865
12318
|
const offsetMs = numFlag(flags, "offset", 0);
|
|
@@ -11874,7 +12327,7 @@ async function cmdIngest(argv) {
|
|
|
11874
12327
|
"note: finished footage opens bare, so --background is not used (pass --as take for the screen-recording card and its ground)"
|
|
11875
12328
|
);
|
|
11876
12329
|
const backdrop = footage ? null : await takeBackdrop(flags, r);
|
|
11877
|
-
r.log(`probing ${
|
|
12330
|
+
r.log(`probing ${basename9(videoPath)}\u2026`);
|
|
11878
12331
|
const probed = await probeVideoFile(videoPath);
|
|
11879
12332
|
if (!(probed.durationMs > 0)) {
|
|
11880
12333
|
throw new UsageError(
|
|
@@ -11886,7 +12339,7 @@ async function cmdIngest(argv) {
|
|
|
11886
12339
|
trace = await readTraceFile(resolve14(cursorPath), offsetMs);
|
|
11887
12340
|
for (const n of trace.notes) r.log(`note: ${n}`);
|
|
11888
12341
|
}
|
|
11889
|
-
if (
|
|
12342
|
+
if (existsSync21(join23(outDir, "meta.json"))) {
|
|
11890
12343
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
11891
12344
|
r.log(
|
|
11892
12345
|
`note: re-ingesting into ${outDir}; kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? `; apply the previous cut with: vos plan ${outDir} --reuse` : "")
|
|
@@ -11923,13 +12376,13 @@ async function cmdIngest(argv) {
|
|
|
11923
12376
|
const clicks = events.filter((e) => e.type === "down").length;
|
|
11924
12377
|
const seconds = (probed.durationMs / 1e3).toFixed(1);
|
|
11925
12378
|
const facts = `${seconds}s, ${probed.width}\xD7${probed.height} @ ${meta.fps} fps${probed.hasAudio ? ", audio" : ", no audio"}${remuxed ? "" : ", copied as is"}`;
|
|
11926
|
-
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";
|
|
11927
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)`;
|
|
11928
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`;
|
|
11929
12382
|
r.done(
|
|
11930
12383
|
{
|
|
11931
12384
|
out: outDir,
|
|
11932
|
-
recording:
|
|
12385
|
+
recording: basename9(recording),
|
|
11933
12386
|
durationMs: probed.durationMs,
|
|
11934
12387
|
width: probed.width,
|
|
11935
12388
|
height: probed.height,
|
|
@@ -11938,7 +12391,7 @@ async function cmdIngest(argv) {
|
|
|
11938
12391
|
hasAudio: probed.hasAudio,
|
|
11939
12392
|
remuxed,
|
|
11940
12393
|
cursor: trace ? {
|
|
11941
|
-
file:
|
|
12394
|
+
file: basename9(cursorPath),
|
|
11942
12395
|
kind: trace.kind,
|
|
11943
12396
|
events: events.length,
|
|
11944
12397
|
clicks,
|
|
@@ -11950,7 +12403,7 @@ async function cmdIngest(argv) {
|
|
|
11950
12403
|
backdrop: plan.backdrop ?? null,
|
|
11951
12404
|
as: footage ? "footage" : "take"
|
|
11952
12405
|
},
|
|
11953
|
-
`Ingested ${
|
|
12406
|
+
`Ingested ${basename9(videoPath)} \u2192 ${outDir}/${basename9(recording)} (${facts})
|
|
11954
12407
|
${opened}
|
|
11955
12408
|
${cursorLine}
|
|
11956
12409
|
${next}`
|
|
@@ -11962,7 +12415,7 @@ async function cmdIngest(argv) {
|
|
|
11962
12415
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
11963
12416
|
import {
|
|
11964
12417
|
cpSync,
|
|
11965
|
-
existsSync as
|
|
12418
|
+
existsSync as existsSync23,
|
|
11966
12419
|
mkdirSync as mkdirSync6,
|
|
11967
12420
|
readFileSync as readFileSync10,
|
|
11968
12421
|
rmSync as rmSync3,
|
|
@@ -11973,7 +12426,7 @@ import { join as join25 } from "path";
|
|
|
11973
12426
|
import { chromium as chromium2 } from "playwright";
|
|
11974
12427
|
|
|
11975
12428
|
// src/plugin/machine.ts
|
|
11976
|
-
import { existsSync as
|
|
12429
|
+
import { existsSync as existsSync22, readFileSync as readFileSync9, readdirSync as readdirSync5, statSync as statSync7 } from "fs";
|
|
11977
12430
|
import { dirname as dirname9, join as join24 } from "path";
|
|
11978
12431
|
var AGENT_NAMES = [
|
|
11979
12432
|
"claude",
|
|
@@ -11994,7 +12447,7 @@ var HOME_DIRS = {
|
|
|
11994
12447
|
copilot: [".copilot"]
|
|
11995
12448
|
};
|
|
11996
12449
|
function detectAgents(root, opts = {}) {
|
|
11997
|
-
const exists = opts.exists ??
|
|
12450
|
+
const exists = opts.exists ?? existsSync22;
|
|
11998
12451
|
const out = [];
|
|
11999
12452
|
for (const agent of AGENT_NAMES) {
|
|
12000
12453
|
for (const rel of PROJECT_DIRS[agent]) {
|
|
@@ -12041,7 +12494,7 @@ function skillsDirFor(home) {
|
|
|
12041
12494
|
function fallbackSkillsHome(root) {
|
|
12042
12495
|
return { agent: "codex", dir: join24(root, ".agents"), scope: "project" };
|
|
12043
12496
|
}
|
|
12044
|
-
function installedSkills(dir, exists =
|
|
12497
|
+
function installedSkills(dir, exists = existsSync22, list = (p) => {
|
|
12045
12498
|
try {
|
|
12046
12499
|
return readdirSync5(p);
|
|
12047
12500
|
} catch {
|
|
@@ -12082,7 +12535,7 @@ ${RULES_BLOCK}
|
|
|
12082
12535
|
}
|
|
12083
12536
|
return { text, changed: text !== current };
|
|
12084
12537
|
}
|
|
12085
|
-
function rulesFileFor(root, exists =
|
|
12538
|
+
function rulesFileFor(root, exists = existsSync22) {
|
|
12086
12539
|
const agents = join24(root, "AGENTS.md");
|
|
12087
12540
|
if (exists(agents)) return agents;
|
|
12088
12541
|
const claude = join24(root, "CLAUDE.md");
|
|
@@ -12189,12 +12642,12 @@ function bundledSkillsDir(from) {
|
|
|
12189
12642
|
let dir = dirname9(from);
|
|
12190
12643
|
for (let i = 0; i < 6; i++) {
|
|
12191
12644
|
const pkg = join24(dir, "package.json");
|
|
12192
|
-
if (
|
|
12645
|
+
if (existsSync22(pkg)) {
|
|
12193
12646
|
try {
|
|
12194
12647
|
const name = JSON.parse(readFileSync9(pkg, "utf8")).name;
|
|
12195
12648
|
if (name === "@vosjs/cli") {
|
|
12196
12649
|
const skills = join24(dir, "skills");
|
|
12197
|
-
return
|
|
12650
|
+
return existsSync22(skills) && statSync7(skills).isDirectory() ? skills : null;
|
|
12198
12651
|
}
|
|
12199
12652
|
} catch {
|
|
12200
12653
|
}
|
|
@@ -12225,7 +12678,7 @@ function npx() {
|
|
|
12225
12678
|
function bundledChromium() {
|
|
12226
12679
|
try {
|
|
12227
12680
|
const p = chromium2.executablePath();
|
|
12228
|
-
return p &&
|
|
12681
|
+
return p && existsSync23(p) ? p : null;
|
|
12229
12682
|
} catch {
|
|
12230
12683
|
return null;
|
|
12231
12684
|
}
|
|
@@ -12234,7 +12687,7 @@ function probe2() {
|
|
|
12234
12687
|
return probeBrowser({
|
|
12235
12688
|
platform: process.platform,
|
|
12236
12689
|
env: process.env,
|
|
12237
|
-
exists:
|
|
12690
|
+
exists: existsSync23,
|
|
12238
12691
|
bundled: bundledChromium
|
|
12239
12692
|
});
|
|
12240
12693
|
}
|
|
@@ -12373,7 +12826,7 @@ async function cmdSetup(argv) {
|
|
|
12373
12826
|
let rules = null;
|
|
12374
12827
|
if (!noRules) {
|
|
12375
12828
|
const file = rulesFileFor(root);
|
|
12376
|
-
const existing =
|
|
12829
|
+
const existing = existsSync23(file) ? readFileSync10(file, "utf8") : null;
|
|
12377
12830
|
const { text, changed } = applyRulesBlock(existing);
|
|
12378
12831
|
if (changed) writeFileSync6(file, text);
|
|
12379
12832
|
rules = { file, changed };
|
|
@@ -12487,7 +12940,7 @@ async function cmdWhoami(argv) {
|
|
|
12487
12940
|
function cmdLogout(argv) {
|
|
12488
12941
|
const { flags } = parseArgs(argv, BOOLEAN_FLAGS11);
|
|
12489
12942
|
const r = reporterFor(flags);
|
|
12490
|
-
const had =
|
|
12943
|
+
const had = existsSync23(CREDENTIALS_PATH);
|
|
12491
12944
|
rmSync3(CREDENTIALS_PATH, { force: true });
|
|
12492
12945
|
const envKey = Boolean(process.env.VOS_API_KEY?.trim());
|
|
12493
12946
|
r.done(
|
|
@@ -12649,10 +13102,26 @@ Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
|
|
|
12649
13102
|
vos asset push <file...> [--folder <folderId|slug>]
|
|
12650
13103
|
add files to your library: models, pictures and SVGs, fonts,
|
|
12651
13104
|
HDR maps, sound, video, captions and recipes. The platform
|
|
12652
|
-
reads each file and answers what it is
|
|
12653
|
-
|
|
12654
|
-
the url a doc.json key takes
|
|
12655
|
-
|
|
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
|
|
12656
13125
|
vos recipe push <file.md> --folder <folderId|slug>
|
|
12657
13126
|
vos recipe push <file.md> --asset <assetId>
|
|
12658
13127
|
put a recipe (a plain .md) on the shelf: --folder creates it
|
|
@@ -12904,7 +13373,7 @@ function takeStorageState(flags) {
|
|
|
12904
13373
|
const raw = strFlag(flags, "storage-state");
|
|
12905
13374
|
if (!raw) return void 0;
|
|
12906
13375
|
const path = resolve15(raw);
|
|
12907
|
-
if (!
|
|
13376
|
+
if (!existsSync24(path))
|
|
12908
13377
|
throw new UsageError(`--storage-state: no such file: ${path}`);
|
|
12909
13378
|
return path;
|
|
12910
13379
|
}
|
|
@@ -12917,7 +13386,7 @@ function takeSessionName(flags, storageState) {
|
|
|
12917
13386
|
throw new UsageError(
|
|
12918
13387
|
"--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
|
|
12919
13388
|
);
|
|
12920
|
-
if (!
|
|
13389
|
+
if (!existsSync24(sessionDir(name)))
|
|
12921
13390
|
throw new UsageError(
|
|
12922
13391
|
`no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
|
|
12923
13392
|
);
|
|
@@ -13140,7 +13609,7 @@ ${lines.join("\n")}
|
|
|
13140
13609
|
}
|
|
13141
13610
|
const paths = takePaths(outDir);
|
|
13142
13611
|
const prepare = async () => {
|
|
13143
|
-
if (
|
|
13612
|
+
if (existsSync24(join26(outDir, "meta.json"))) {
|
|
13144
13613
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
13145
13614
|
r.log(
|
|
13146
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" : "")
|
|
@@ -13244,7 +13713,7 @@ async function cmdCreate2(argv) {
|
|
|
13244
13713
|
const headers = takeHeaders(multi);
|
|
13245
13714
|
const paths = takePaths(outDir);
|
|
13246
13715
|
const prepare = async () => {
|
|
13247
|
-
if (
|
|
13716
|
+
if (existsSync24(join26(outDir, "meta.json"))) {
|
|
13248
13717
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
13249
13718
|
r.log(
|
|
13250
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" : "")
|
|
@@ -13357,7 +13826,7 @@ async function cmdPlan(argv) {
|
|
|
13357
13826
|
let reuse;
|
|
13358
13827
|
if (flags.reuse === true) {
|
|
13359
13828
|
const from = resolve15(strFlag(flags, "from") ?? join26(dir, PREV_DOC_NAME));
|
|
13360
|
-
if (!
|
|
13829
|
+
if (!existsSync24(from)) {
|
|
13361
13830
|
throw new UsageError(
|
|
13362
13831
|
`--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
|
|
13363
13832
|
);
|
|
@@ -13368,7 +13837,7 @@ async function cmdPlan(argv) {
|
|
|
13368
13837
|
};
|
|
13369
13838
|
}
|
|
13370
13839
|
const style = await resolveStyleRef(flags);
|
|
13371
|
-
const hasDoc =
|
|
13840
|
+
const hasDoc = existsSync24(join26(dir, "doc.json"));
|
|
13372
13841
|
const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
|
|
13373
13842
|
const release = await releaseInputs(dir, flags, r, multi.with ?? []);
|
|
13374
13843
|
const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
|
|
@@ -13796,11 +14265,11 @@ async function resolveStyleRef(flags) {
|
|
|
13796
14265
|
}
|
|
13797
14266
|
async function resolveDocRef(ref, flags, what) {
|
|
13798
14267
|
const styleRef = ref;
|
|
13799
|
-
const file =
|
|
14268
|
+
const file = existsSync24(styleRef) ? resolve15(
|
|
13800
14269
|
styleRef,
|
|
13801
|
-
|
|
14270
|
+
existsSync24(join26(styleRef, "doc.json")) ? "doc.json" : ""
|
|
13802
14271
|
) : null;
|
|
13803
|
-
if (file &&
|
|
14272
|
+
if (file && existsSync24(file)) {
|
|
13804
14273
|
return {
|
|
13805
14274
|
from: file,
|
|
13806
14275
|
doc: migrateHostedDoc5(
|
|
@@ -13915,7 +14384,7 @@ async function cmdOpen(argv) {
|
|
|
13915
14384
|
if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
|
|
13916
14385
|
const r = createReporter(flags.json === true);
|
|
13917
14386
|
const take = await loadTake(dir);
|
|
13918
|
-
if (!
|
|
14387
|
+
if (!existsSync24(take.paths.recording)) {
|
|
13919
14388
|
throw new UsageError(`${dir} has no recording \u2014 re-run record`);
|
|
13920
14389
|
}
|
|
13921
14390
|
const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
|
|
@@ -13957,8 +14426,8 @@ async function cmdJudge(argv) {
|
|
|
13957
14426
|
);
|
|
13958
14427
|
const r = createReporter(flags.json === true);
|
|
13959
14428
|
const kitFile = target.endsWith("kit.json") ? target : join26(target, "kit.json");
|
|
13960
|
-
if (!
|
|
13961
|
-
if (!
|
|
14429
|
+
if (!existsSync24(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
|
|
14430
|
+
if (!existsSync24(against))
|
|
13962
14431
|
throw new UsageError(`${against}: no reference manifest`);
|
|
13963
14432
|
const result = await judgeKit(kitFile, against, strFlag(flags, "out"));
|
|
13964
14433
|
const verdicts = JSON.parse(await readFile15(result.verdictFile, "utf8")).verdicts;
|
|
@@ -14080,7 +14549,7 @@ async function cmdValidate(argv) {
|
|
|
14080
14549
|
const target = positionals[0];
|
|
14081
14550
|
if (!target) throw new UsageError("vos validate <actions.json|take>");
|
|
14082
14551
|
const r = createReporter(flags.json === true);
|
|
14083
|
-
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;
|
|
14084
14553
|
if (kitTarget) {
|
|
14085
14554
|
const picture = flags.picture === true;
|
|
14086
14555
|
const verdict = await validateKit(kitTarget, { picture });
|
|
@@ -14112,7 +14581,7 @@ async function cmdValidate(argv) {
|
|
|
14112
14581
|
r.done({ valid: true, target }, `${target}: valid actions file`);
|
|
14113
14582
|
return EXIT_OK;
|
|
14114
14583
|
}
|
|
14115
|
-
if (!isTakeDir(target) &&
|
|
14584
|
+
if (!isTakeDir(target) && existsSync24(join26(target, "config.json"))) {
|
|
14116
14585
|
const parsed = JSON.parse(
|
|
14117
14586
|
await readFile15(join26(target, "config.json"), "utf8")
|
|
14118
14587
|
);
|
|
@@ -14155,7 +14624,7 @@ async function cmdValidate(argv) {
|
|
|
14155
14624
|
const take = await loadTake(target);
|
|
14156
14625
|
const problems = [];
|
|
14157
14626
|
const warnings = [];
|
|
14158
|
-
if (!
|
|
14627
|
+
if (!existsSync24(take.paths.recording)) {
|
|
14159
14628
|
problems.push("missing recording (re-run record)");
|
|
14160
14629
|
} else {
|
|
14161
14630
|
const mismatch = containerMismatch(
|
|
@@ -14428,6 +14897,7 @@ async function run(argv) {
|
|
|
14428
14897
|
case "folder":
|
|
14429
14898
|
return await cmdFolder(rest);
|
|
14430
14899
|
case "asset":
|
|
14900
|
+
case "assets":
|
|
14431
14901
|
return await cmdAsset(rest);
|
|
14432
14902
|
case "recipe":
|
|
14433
14903
|
return await cmdRecipe(rest);
|
|
@@ -14507,7 +14977,7 @@ async function cmdSession(argv) {
|
|
|
14507
14977
|
if (!arg) throw new UsageError(usage);
|
|
14508
14978
|
const bad = sessionNameVerdict(arg);
|
|
14509
14979
|
if (bad) throw new UsageError(bad);
|
|
14510
|
-
if (!
|
|
14980
|
+
if (!existsSync24(sessionDir(arg)))
|
|
14511
14981
|
throw new UsageError(`no session "${arg}" (vos session list)`);
|
|
14512
14982
|
const summary3 = await summarizeSession(arg);
|
|
14513
14983
|
const url = strFlag(flags, "url");
|
|
@@ -14607,4 +15077,4 @@ export {
|
|
|
14607
15077
|
verbHelp,
|
|
14608
15078
|
run
|
|
14609
15079
|
};
|
|
14610
|
-
//# sourceMappingURL=chunk-
|
|
15080
|
+
//# sourceMappingURL=chunk-AKIPDSHE.js.map
|