@vm0/cli 9.161.7 → 9.161.8
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/{chunk-P3SAJ3Q2.js → chunk-BWCVGYRL.js} +180 -41
- package/{chunk-P3SAJ3Q2.js.map → chunk-BWCVGYRL.js.map} +1 -1
- package/index.js +9 -9
- package/package.json +1 -1
- package/zero.js +328 -12
- package/zero.js.map +1 -1
package/index.js
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
source_default,
|
|
69
69
|
volumeConfigSchema,
|
|
70
70
|
withErrorHandler
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-BWCVGYRL.js";
|
|
72
72
|
import {
|
|
73
73
|
__toESM,
|
|
74
74
|
init_esm_shims
|
|
@@ -387,7 +387,7 @@ function getConfigPath() {
|
|
|
387
387
|
return join(os.homedir(), ".vm0", "config.json");
|
|
388
388
|
}
|
|
389
389
|
var infoCommand = new Command().name("info").description("Display environment and debug information").action(async () => {
|
|
390
|
-
console.log(source_default.bold(`VM0 CLI v${"9.161.
|
|
390
|
+
console.log(source_default.bold(`VM0 CLI v${"9.161.8"}`));
|
|
391
391
|
console.log();
|
|
392
392
|
const config = await loadConfig();
|
|
393
393
|
const hasEnvToken = !!process.env.VM0_TOKEN;
|
|
@@ -4280,7 +4280,7 @@ var composeCommand = new Command().name("compose").description("Create or update
|
|
|
4280
4280
|
options.autoUpdate = false;
|
|
4281
4281
|
}
|
|
4282
4282
|
if (options.autoUpdate !== false) {
|
|
4283
|
-
await startSilentUpgrade("9.161.
|
|
4283
|
+
await startSilentUpgrade("9.161.8");
|
|
4284
4284
|
}
|
|
4285
4285
|
try {
|
|
4286
4286
|
let result;
|
|
@@ -4372,7 +4372,7 @@ var mainRunCommand = new Command().name("run").description("Run an agent").argum
|
|
|
4372
4372
|
withErrorHandler(
|
|
4373
4373
|
async (identifier, prompt, options) => {
|
|
4374
4374
|
if (options.autoUpdate !== false) {
|
|
4375
|
-
await startSilentUpgrade("9.161.
|
|
4375
|
+
await startSilentUpgrade("9.161.8");
|
|
4376
4376
|
}
|
|
4377
4377
|
const { name, version } = parseIdentifier(identifier);
|
|
4378
4378
|
let composeId;
|
|
@@ -6165,13 +6165,13 @@ var upgradeCommand = new Command().name("upgrade").description("Upgrade vm0 CLI
|
|
|
6165
6165
|
if (latestVersion === null) {
|
|
6166
6166
|
throw new Error("Could not check for updates. Please try again later.");
|
|
6167
6167
|
}
|
|
6168
|
-
if (latestVersion === "9.161.
|
|
6169
|
-
console.log(source_default.green(`\u2713 Already up to date (${"9.161.
|
|
6168
|
+
if (latestVersion === "9.161.8") {
|
|
6169
|
+
console.log(source_default.green(`\u2713 Already up to date (${"9.161.8"})`));
|
|
6170
6170
|
return;
|
|
6171
6171
|
}
|
|
6172
6172
|
console.log(
|
|
6173
6173
|
source_default.yellow(
|
|
6174
|
-
`Current version: ${"9.161.
|
|
6174
|
+
`Current version: ${"9.161.8"} -> Latest version: ${latestVersion}`
|
|
6175
6175
|
)
|
|
6176
6176
|
);
|
|
6177
6177
|
console.log();
|
|
@@ -6198,7 +6198,7 @@ var upgradeCommand = new Command().name("upgrade").description("Upgrade vm0 CLI
|
|
|
6198
6198
|
const success = await performUpgrade(packageManager);
|
|
6199
6199
|
if (success) {
|
|
6200
6200
|
console.log(
|
|
6201
|
-
source_default.green(`\u2713 Upgraded from ${"9.161.
|
|
6201
|
+
source_default.green(`\u2713 Upgraded from ${"9.161.8"} to ${latestVersion}`)
|
|
6202
6202
|
);
|
|
6203
6203
|
return;
|
|
6204
6204
|
}
|
|
@@ -6265,7 +6265,7 @@ var whoamiCommand = new Command().name("whoami").description("Show current ident
|
|
|
6265
6265
|
|
|
6266
6266
|
// src/index.ts
|
|
6267
6267
|
var program = new Command();
|
|
6268
|
-
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.161.
|
|
6268
|
+
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.161.8");
|
|
6269
6269
|
program.addCommand(authCommand);
|
|
6270
6270
|
program.addCommand(infoCommand);
|
|
6271
6271
|
program.addCommand(composeCommand);
|
package/package.json
CHANGED
package/zero.js
CHANGED
|
@@ -150,7 +150,7 @@ import {
|
|
|
150
150
|
zeroAgentCustomSkillNameSchema,
|
|
151
151
|
zeroLocalAgentCommand,
|
|
152
152
|
zeroTokenAllowsFeatureSwitch
|
|
153
|
-
} from "./chunk-
|
|
153
|
+
} from "./chunk-BWCVGYRL.js";
|
|
154
154
|
import {
|
|
155
155
|
__commonJS,
|
|
156
156
|
__require,
|
|
@@ -34745,6 +34745,82 @@ init_esm_shims();
|
|
|
34745
34745
|
// src/commands/zero/shared/presentation-generate.ts
|
|
34746
34746
|
init_esm_shims();
|
|
34747
34747
|
import { readFileSync as readFileSync14 } from "fs";
|
|
34748
|
+
|
|
34749
|
+
// src/commands/zero/shared/html-artifact-authoring.ts
|
|
34750
|
+
init_esm_shims();
|
|
34751
|
+
function slugify(value) {
|
|
34752
|
+
const slug = value.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").replace(/-{2,}/gu, "-").slice(0, 48).replace(/-+$/u, "");
|
|
34753
|
+
return slug.length >= 3 ? slug : "html-artifact";
|
|
34754
|
+
}
|
|
34755
|
+
function titleForKind(kind) {
|
|
34756
|
+
return kind === "presentation" ? "HTML presentation" : "hosted website";
|
|
34757
|
+
}
|
|
34758
|
+
function outputDirForSite(site) {
|
|
34759
|
+
return `./opendesign/mockups/${site}`;
|
|
34760
|
+
}
|
|
34761
|
+
function createHtmlArtifactAuthoringPacket(options) {
|
|
34762
|
+
const site = options.site ?? slugify(options.slugSource ?? options.prompt);
|
|
34763
|
+
const outputDir = outputDirForSite(site);
|
|
34764
|
+
const hostCommand = `zero host ${outputDir} --site ${site}${options.kind === "website" ? " --spa" : ""}`;
|
|
34765
|
+
const title = titleForKind(options.kind);
|
|
34766
|
+
const instructions = [
|
|
34767
|
+
`# Zero built-in generate ${options.kind}`,
|
|
34768
|
+
"",
|
|
34769
|
+
`You are the current agent. Author a production-quality ${title} as a static HTML artifact.`,
|
|
34770
|
+
"Zero is not generating this artifact on the server. You are the author.",
|
|
34771
|
+
"",
|
|
34772
|
+
"## User Prompt",
|
|
34773
|
+
options.prompt,
|
|
34774
|
+
"",
|
|
34775
|
+
"## Output Contract",
|
|
34776
|
+
`- Write the artifact under \`${outputDir}/\`.`,
|
|
34777
|
+
`- The entry file must be \`${outputDir}/index.html\`.`,
|
|
34778
|
+
"- Keep every local asset inside the same output directory.",
|
|
34779
|
+
"- Do not reference files from another project path.",
|
|
34780
|
+
"- Use descriptive filenames and canonical HTML: close non-void tags and double-quote attributes.",
|
|
34781
|
+
"- Prefer a single self-contained HTML file unless the artifact genuinely needs separate assets.",
|
|
34782
|
+
"",
|
|
34783
|
+
"## Requested Parameters",
|
|
34784
|
+
...options.details.map((detail) => {
|
|
34785
|
+
return `- ${detail}`;
|
|
34786
|
+
}),
|
|
34787
|
+
"",
|
|
34788
|
+
"## OpenDesign-Style Authoring Rules",
|
|
34789
|
+
"- Read the local codebase, brand assets, and existing design systems before choosing a visual direction.",
|
|
34790
|
+
"- If no design system is available, choose one clear aesthetic direction and hold it across the artifact.",
|
|
34791
|
+
"- Avoid generic AI design defaults: no stock SaaS gradients, no emoji-as-icons, no filler stats, no decorative chrome that does not help the artifact.",
|
|
34792
|
+
"- Build the actual artifact first, not a marketing explanation of the artifact.",
|
|
34793
|
+
"- Make controls and interactions real when they are visible.",
|
|
34794
|
+
"- Keep text readable at desktop and mobile preview sizes.",
|
|
34795
|
+
...options.artifactRules.map((rule) => {
|
|
34796
|
+
return `- ${rule}`;
|
|
34797
|
+
}),
|
|
34798
|
+
"",
|
|
34799
|
+
"## Verification",
|
|
34800
|
+
"- Open the HTML locally and verify it is nonblank.",
|
|
34801
|
+
"- Check that keyboard/click interactions work when present.",
|
|
34802
|
+
"- Check that text does not overflow or overlap at desktop and mobile viewport sizes.",
|
|
34803
|
+
"- Run the final hosting command only after the artifact looks correct.",
|
|
34804
|
+
"",
|
|
34805
|
+
"## Publish",
|
|
34806
|
+
`When everything is OK, publish it with:`,
|
|
34807
|
+
"",
|
|
34808
|
+
"```bash",
|
|
34809
|
+
hostCommand,
|
|
34810
|
+
"```"
|
|
34811
|
+
].join("\n");
|
|
34812
|
+
return {
|
|
34813
|
+
type: "html-artifact-authoring",
|
|
34814
|
+
kind: options.kind,
|
|
34815
|
+
prompt: options.prompt,
|
|
34816
|
+
outputDir,
|
|
34817
|
+
site,
|
|
34818
|
+
hostCommand,
|
|
34819
|
+
instructions
|
|
34820
|
+
};
|
|
34821
|
+
}
|
|
34822
|
+
|
|
34823
|
+
// src/commands/zero/shared/presentation-generate.ts
|
|
34748
34824
|
var PRESENTATION_MAX_IMAGES = 8;
|
|
34749
34825
|
function parseSlideCount(value) {
|
|
34750
34826
|
const slideCount = Number(value);
|
|
@@ -34782,7 +34858,7 @@ function readPrompt2(options, usageCommand) {
|
|
|
34782
34858
|
});
|
|
34783
34859
|
}
|
|
34784
34860
|
function createPresentationGenerateCommand(config) {
|
|
34785
|
-
return new Command().name(config.name).description("Generate
|
|
34861
|
+
return new Command().name(config.name).description("Generate an HTML presentation from a prompt").option(
|
|
34786
34862
|
"--prompt <text>",
|
|
34787
34863
|
"Presentation prompt; can also be piped via stdin"
|
|
34788
34864
|
).option("--style <style>", "Style: editorial or swiss", "editorial").option("--slides <count>", "Slide count: 4-20", parseSlideCount, 8).option(
|
|
@@ -34803,15 +34879,44 @@ Examples:
|
|
|
34803
34879
|
${config.examples}
|
|
34804
34880
|
|
|
34805
34881
|
Output:
|
|
34806
|
-
Prints the generated /f/ HTML presentation URL and metadata
|
|
34882
|
+
Prints the generated /f/ HTML presentation URL and metadata. With openDesignGenerate enabled, prints an OpenDesign-style authoring packet for the current agent instead.
|
|
34807
34883
|
|
|
34808
34884
|
Notes:
|
|
34809
|
-
- Authenticates via ZERO_TOKEN
|
|
34810
|
-
-
|
|
34811
|
-
-
|
|
34885
|
+
- Authenticates via ZERO_TOKEN
|
|
34886
|
+
- Default path charges org credits after successful presentation generation
|
|
34887
|
+
- OpenDesign path is gated by the openDesignGenerate feature switch`
|
|
34812
34888
|
).action(
|
|
34813
34889
|
withErrorHandler(async (options) => {
|
|
34814
34890
|
const prompt = readPrompt2(options, config.usageCommand);
|
|
34891
|
+
if (zeroTokenAllowsFeatureSwitch("openDesignGenerate" /* OpenDesignGenerate */)) {
|
|
34892
|
+
const packet = createHtmlArtifactAuthoringPacket({
|
|
34893
|
+
kind: "presentation",
|
|
34894
|
+
prompt,
|
|
34895
|
+
slugSource: options.title,
|
|
34896
|
+
details: [
|
|
34897
|
+
`Style: ${options.style}`,
|
|
34898
|
+
`Slide count: ${options.slides}`,
|
|
34899
|
+
`Suggested generated visual count: ${options.images}`,
|
|
34900
|
+
`Image model preference if visuals are generated separately: ${options.imageModel ?? "default"}`,
|
|
34901
|
+
`Theme: ${options.theme ?? "agent decides from style"}`,
|
|
34902
|
+
`Audience: ${options.audience ?? "not specified"}`,
|
|
34903
|
+
`Requested deck title: ${options.title ?? "not specified"}`
|
|
34904
|
+
],
|
|
34905
|
+
artifactRules: [
|
|
34906
|
+
"Think like a presentation designer, not a web page designer.",
|
|
34907
|
+
"Use a fixed 1920x1080 slide canvas and scale it uniformly for smaller viewports.",
|
|
34908
|
+
"Use one section per slide and keep repeated elements in consistent positions.",
|
|
34909
|
+
"Make keyboard navigation work with ArrowLeft, ArrowRight, Home, and End.",
|
|
34910
|
+
"Keep slide text readable from across a room; avoid memo-like walls of text."
|
|
34911
|
+
]
|
|
34912
|
+
});
|
|
34913
|
+
if (options.json) {
|
|
34914
|
+
console.log(JSON.stringify(packet));
|
|
34915
|
+
return;
|
|
34916
|
+
}
|
|
34917
|
+
console.log(packet.instructions);
|
|
34918
|
+
return;
|
|
34919
|
+
}
|
|
34815
34920
|
const result = await generateWebPresentation({
|
|
34816
34921
|
prompt,
|
|
34817
34922
|
style: options.style,
|
|
@@ -34860,6 +34965,22 @@ init_esm_shims();
|
|
|
34860
34965
|
// src/commands/zero/shared/video-generate.ts
|
|
34861
34966
|
init_esm_shims();
|
|
34862
34967
|
import { readFileSync as readFileSync15 } from "fs";
|
|
34968
|
+
var FRAME_ASPECT_RATIO_TOLERANCE = 0.02;
|
|
34969
|
+
var JPEG_START_OF_FRAME_MARKERS = /* @__PURE__ */ new Set([
|
|
34970
|
+
192,
|
|
34971
|
+
193,
|
|
34972
|
+
194,
|
|
34973
|
+
195,
|
|
34974
|
+
197,
|
|
34975
|
+
198,
|
|
34976
|
+
199,
|
|
34977
|
+
201,
|
|
34978
|
+
202,
|
|
34979
|
+
203,
|
|
34980
|
+
205,
|
|
34981
|
+
206,
|
|
34982
|
+
207
|
|
34983
|
+
]);
|
|
34863
34984
|
function parseSeed2(value) {
|
|
34864
34985
|
const seed = Number(value);
|
|
34865
34986
|
if (!Number.isInteger(seed) || seed < 0 || !Number.isSafeInteger(seed)) {
|
|
@@ -34870,6 +34991,173 @@ function parseSeed2(value) {
|
|
|
34870
34991
|
function collectUrl(value, previous = []) {
|
|
34871
34992
|
return [...previous, value];
|
|
34872
34993
|
}
|
|
34994
|
+
function parseAspectRatio(value) {
|
|
34995
|
+
const [widthText, heightText] = value.split(":");
|
|
34996
|
+
const width = Number(widthText);
|
|
34997
|
+
const height = Number(heightText);
|
|
34998
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
34999
|
+
throw new Error(`Invalid --aspect-ratio "${value}"`);
|
|
35000
|
+
}
|
|
35001
|
+
return { width, height };
|
|
35002
|
+
}
|
|
35003
|
+
function readPngDimensions(buffer) {
|
|
35004
|
+
if (buffer.length < 24 || buffer.toString("latin1", 0, 8) !== "\x89PNG\r\n\n") {
|
|
35005
|
+
return void 0;
|
|
35006
|
+
}
|
|
35007
|
+
return {
|
|
35008
|
+
width: buffer.readUInt32BE(16),
|
|
35009
|
+
height: buffer.readUInt32BE(20)
|
|
35010
|
+
};
|
|
35011
|
+
}
|
|
35012
|
+
function readGifDimensions(buffer) {
|
|
35013
|
+
if (buffer.length < 10 || !buffer.toString("latin1", 0, 6).startsWith("GIF")) {
|
|
35014
|
+
return void 0;
|
|
35015
|
+
}
|
|
35016
|
+
return {
|
|
35017
|
+
width: buffer.readUInt16LE(6),
|
|
35018
|
+
height: buffer.readUInt16LE(8)
|
|
35019
|
+
};
|
|
35020
|
+
}
|
|
35021
|
+
function readJpegDimensions(buffer) {
|
|
35022
|
+
if (buffer.length < 4 || buffer[0] !== 255 || buffer[1] !== 216) {
|
|
35023
|
+
return void 0;
|
|
35024
|
+
}
|
|
35025
|
+
let offset = 2;
|
|
35026
|
+
while (offset + 9 < buffer.length) {
|
|
35027
|
+
if (buffer[offset] !== 255) {
|
|
35028
|
+
offset += 1;
|
|
35029
|
+
continue;
|
|
35030
|
+
}
|
|
35031
|
+
const marker = buffer[offset + 1];
|
|
35032
|
+
if (marker === 217 || marker === 218) {
|
|
35033
|
+
break;
|
|
35034
|
+
}
|
|
35035
|
+
const segmentLength = buffer.readUInt16BE(offset + 2);
|
|
35036
|
+
if (segmentLength < 2 || offset + 2 + segmentLength > buffer.length) {
|
|
35037
|
+
break;
|
|
35038
|
+
}
|
|
35039
|
+
if (JPEG_START_OF_FRAME_MARKERS.has(marker)) {
|
|
35040
|
+
return {
|
|
35041
|
+
height: buffer.readUInt16BE(offset + 5),
|
|
35042
|
+
width: buffer.readUInt16BE(offset + 7)
|
|
35043
|
+
};
|
|
35044
|
+
}
|
|
35045
|
+
offset += 2 + segmentLength;
|
|
35046
|
+
}
|
|
35047
|
+
return void 0;
|
|
35048
|
+
}
|
|
35049
|
+
function readUnsigned24LE(buffer, offset) {
|
|
35050
|
+
return buffer.readUInt8(offset) + (buffer.readUInt8(offset + 1) << 8) + (buffer.readUInt8(offset + 2) << 16);
|
|
35051
|
+
}
|
|
35052
|
+
function readWebpDimensions(buffer) {
|
|
35053
|
+
if (buffer.length < 30 || buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WEBP") {
|
|
35054
|
+
return void 0;
|
|
35055
|
+
}
|
|
35056
|
+
let offset = 12;
|
|
35057
|
+
while (offset + 8 <= buffer.length) {
|
|
35058
|
+
const chunkType = buffer.toString("ascii", offset, offset + 4);
|
|
35059
|
+
const chunkSize = buffer.readUInt32LE(offset + 4);
|
|
35060
|
+
const payloadOffset = offset + 8;
|
|
35061
|
+
if (payloadOffset + chunkSize > buffer.length) {
|
|
35062
|
+
break;
|
|
35063
|
+
}
|
|
35064
|
+
if (chunkType === "VP8X" && chunkSize >= 10) {
|
|
35065
|
+
return {
|
|
35066
|
+
width: readUnsigned24LE(buffer, payloadOffset + 4) + 1,
|
|
35067
|
+
height: readUnsigned24LE(buffer, payloadOffset + 7) + 1
|
|
35068
|
+
};
|
|
35069
|
+
}
|
|
35070
|
+
if (chunkType === "VP8L" && chunkSize >= 5 && buffer[payloadOffset] === 47) {
|
|
35071
|
+
const byte1 = buffer.readUInt8(payloadOffset + 1);
|
|
35072
|
+
const byte2 = buffer.readUInt8(payloadOffset + 2);
|
|
35073
|
+
const byte3 = buffer.readUInt8(payloadOffset + 3);
|
|
35074
|
+
const byte4 = buffer.readUInt8(payloadOffset + 4);
|
|
35075
|
+
return {
|
|
35076
|
+
width: 1 + byte1 + ((byte2 & 63) << 8),
|
|
35077
|
+
height: 1 + ((byte2 & 192) >> 6) + (byte3 << 2) + ((byte4 & 15) << 10)
|
|
35078
|
+
};
|
|
35079
|
+
}
|
|
35080
|
+
if (chunkType === "VP8 " && chunkSize >= 10 && buffer[payloadOffset + 3] === 157 && buffer[payloadOffset + 4] === 1 && buffer[payloadOffset + 5] === 42) {
|
|
35081
|
+
return {
|
|
35082
|
+
width: buffer.readUInt16LE(payloadOffset + 6) & 16383,
|
|
35083
|
+
height: buffer.readUInt16LE(payloadOffset + 8) & 16383
|
|
35084
|
+
};
|
|
35085
|
+
}
|
|
35086
|
+
offset = payloadOffset + chunkSize + chunkSize % 2;
|
|
35087
|
+
}
|
|
35088
|
+
return void 0;
|
|
35089
|
+
}
|
|
35090
|
+
function readImageDimensions(buffer) {
|
|
35091
|
+
return readPngDimensions(buffer) ?? readJpegDimensions(buffer) ?? readWebpDimensions(buffer) ?? readGifDimensions(buffer);
|
|
35092
|
+
}
|
|
35093
|
+
function formatDimensionsAsRatio({ width, height }) {
|
|
35094
|
+
const divisor = greatestCommonDivisor(width, height);
|
|
35095
|
+
return `${width / divisor}:${height / divisor}`;
|
|
35096
|
+
}
|
|
35097
|
+
function greatestCommonDivisor(left, right) {
|
|
35098
|
+
let a = Math.abs(left);
|
|
35099
|
+
let b = Math.abs(right);
|
|
35100
|
+
while (b !== 0) {
|
|
35101
|
+
const remainder = a % b;
|
|
35102
|
+
a = b;
|
|
35103
|
+
b = remainder;
|
|
35104
|
+
}
|
|
35105
|
+
return a || 1;
|
|
35106
|
+
}
|
|
35107
|
+
function hasMatchingAspectRatio(actual, expected) {
|
|
35108
|
+
const actualRatio = actual.width / actual.height;
|
|
35109
|
+
const expectedRatio = expected.width / expected.height;
|
|
35110
|
+
return Math.abs(actualRatio - expectedRatio) / expectedRatio <= FRAME_ASPECT_RATIO_TOLERANCE;
|
|
35111
|
+
}
|
|
35112
|
+
async function fetchImageDimensions(optionName, imageUrl) {
|
|
35113
|
+
let url;
|
|
35114
|
+
try {
|
|
35115
|
+
url = new URL(imageUrl);
|
|
35116
|
+
} catch {
|
|
35117
|
+
throw new Error(`${optionName} must be an absolute URL`);
|
|
35118
|
+
}
|
|
35119
|
+
const response = await fetch(url);
|
|
35120
|
+
if (!response.ok) {
|
|
35121
|
+
throw new Error(
|
|
35122
|
+
`Could not validate ${optionName}: failed to fetch image (HTTP ${response.status})`
|
|
35123
|
+
);
|
|
35124
|
+
}
|
|
35125
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
35126
|
+
const dimensions = readImageDimensions(buffer);
|
|
35127
|
+
if (!dimensions) {
|
|
35128
|
+
throw new Error(
|
|
35129
|
+
`Could not validate ${optionName}: unsupported image format or missing dimensions`
|
|
35130
|
+
);
|
|
35131
|
+
}
|
|
35132
|
+
return dimensions;
|
|
35133
|
+
}
|
|
35134
|
+
async function validateFrameImageAspectRatio(optionName, imageUrl, aspectRatio) {
|
|
35135
|
+
if (!imageUrl) {
|
|
35136
|
+
return;
|
|
35137
|
+
}
|
|
35138
|
+
const expected = parseAspectRatio(aspectRatio);
|
|
35139
|
+
const actual = await fetchImageDimensions(optionName, imageUrl);
|
|
35140
|
+
if (hasMatchingAspectRatio(actual, expected)) {
|
|
35141
|
+
return;
|
|
35142
|
+
}
|
|
35143
|
+
throw new Error(
|
|
35144
|
+
`${optionName} has aspect ratio ${formatDimensionsAsRatio(actual)} (${actual.width}x${actual.height}), but --aspect-ratio is ${aspectRatio}. Use --aspect-ratio ${formatDimensionsAsRatio(actual)} or provide a frame image with ${aspectRatio} dimensions.`
|
|
35145
|
+
);
|
|
35146
|
+
}
|
|
35147
|
+
async function validateVideoOptions(options) {
|
|
35148
|
+
await Promise.all([
|
|
35149
|
+
validateFrameImageAspectRatio(
|
|
35150
|
+
"--first-frame-image-url",
|
|
35151
|
+
options.firstFrameImageUrl,
|
|
35152
|
+
options.aspectRatio
|
|
35153
|
+
),
|
|
35154
|
+
validateFrameImageAspectRatio(
|
|
35155
|
+
"--last-frame-image-url",
|
|
35156
|
+
options.lastFrameImageUrl,
|
|
35157
|
+
options.aspectRatio
|
|
35158
|
+
)
|
|
35159
|
+
]);
|
|
35160
|
+
}
|
|
34873
35161
|
function readPrompt3(options, usageCommand) {
|
|
34874
35162
|
if (options.prompt?.trim()) {
|
|
34875
35163
|
return options.prompt.trim();
|
|
@@ -34941,6 +35229,7 @@ Models:
|
|
|
34941
35229
|
).action(
|
|
34942
35230
|
withErrorHandler(async (options) => {
|
|
34943
35231
|
const prompt = readPrompt3(options, config.usageCommand);
|
|
35232
|
+
await validateVideoOptions(options);
|
|
34944
35233
|
const result = await generateWebVideo({
|
|
34945
35234
|
prompt,
|
|
34946
35235
|
model: options.model,
|
|
@@ -36066,7 +36355,7 @@ function formatBytes(bytes) {
|
|
|
36066
36355
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
36067
36356
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
36068
36357
|
}
|
|
36069
|
-
var websiteCommand = new Command().name("website").description("Generate
|
|
36358
|
+
var websiteCommand = new Command().name("website").description("Generate a hosted website from a prompt").option("--prompt <text>", "Website prompt; can also be piped via stdin").option(
|
|
36070
36359
|
"--template <template>",
|
|
36071
36360
|
"Template: auto, launch, or profile",
|
|
36072
36361
|
parseTemplate,
|
|
@@ -36089,15 +36378,42 @@ Examples:
|
|
|
36089
36378
|
Pipe prompt: cat brief.txt | zero built-in generate website
|
|
36090
36379
|
|
|
36091
36380
|
Output:
|
|
36092
|
-
|
|
36381
|
+
Generates and publishes a hosted website. With openDesignGenerate enabled, prints an OpenDesign-style authoring packet for the current agent instead.
|
|
36093
36382
|
|
|
36094
36383
|
Notes:
|
|
36095
|
-
- Authenticates via ZERO_TOKEN
|
|
36096
|
-
-
|
|
36097
|
-
-
|
|
36384
|
+
- Authenticates via ZERO_TOKEN
|
|
36385
|
+
- Default path charges org credits for model-generated website content
|
|
36386
|
+
- OpenDesign path is gated by the openDesignGenerate feature switch`
|
|
36098
36387
|
).action(
|
|
36099
36388
|
withErrorHandler(async (options) => {
|
|
36100
36389
|
const prompt = readPrompt4(options);
|
|
36390
|
+
if (zeroTokenAllowsFeatureSwitch("openDesignGenerate" /* OpenDesignGenerate */)) {
|
|
36391
|
+
const packet = createHtmlArtifactAuthoringPacket({
|
|
36392
|
+
kind: "website",
|
|
36393
|
+
prompt,
|
|
36394
|
+
slugSource: options.title,
|
|
36395
|
+
site: options.site,
|
|
36396
|
+
details: [
|
|
36397
|
+
`Template direction: ${options.template}`,
|
|
36398
|
+
`Suggested generated visual count: ${options.images}`,
|
|
36399
|
+
`Image model preference if visuals are generated separately: ${options.imageModel ?? "default"}`,
|
|
36400
|
+
`Requested title/site name: ${options.title ?? "not specified"}`,
|
|
36401
|
+
`Audience: ${options.audience ?? "not specified"}`
|
|
36402
|
+
],
|
|
36403
|
+
artifactRules: [
|
|
36404
|
+
"Build the usable website as the first screen; do not output a landing-page plan.",
|
|
36405
|
+
"If it is a marketing site, make the product or offer visible in the first viewport.",
|
|
36406
|
+
"For app or tool surfaces, prioritize dense, scannable, task-focused UI over decorative sections.",
|
|
36407
|
+
"Use responsive HTML/CSS and verify the page works at mobile and desktop widths."
|
|
36408
|
+
]
|
|
36409
|
+
});
|
|
36410
|
+
if (options.json) {
|
|
36411
|
+
console.log(JSON.stringify(packet));
|
|
36412
|
+
return;
|
|
36413
|
+
}
|
|
36414
|
+
console.log(packet.instructions);
|
|
36415
|
+
return;
|
|
36416
|
+
}
|
|
36101
36417
|
if (!options.json) {
|
|
36102
36418
|
console.log(source_default.dim("Generating website content..."));
|
|
36103
36419
|
}
|
|
@@ -37177,7 +37493,7 @@ function registerZeroCommands(prog, commands) {
|
|
|
37177
37493
|
var program = new Command();
|
|
37178
37494
|
program.name("zero").description(
|
|
37179
37495
|
"Zero CLI \u2014 interact with the zero platform from inside the sandbox"
|
|
37180
|
-
).version("9.161.
|
|
37496
|
+
).version("9.161.8").addHelpText("after", () => {
|
|
37181
37497
|
return buildZeroHelpText();
|
|
37182
37498
|
});
|
|
37183
37499
|
if (process.argv[1]?.endsWith("zero.js") || process.argv[1]?.endsWith("zero.ts") || process.argv[1]?.endsWith("zero")) {
|