@vm0/cli 9.161.6 → 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/index.js CHANGED
@@ -68,7 +68,7 @@ import {
68
68
  source_default,
69
69
  volumeConfigSchema,
70
70
  withErrorHandler
71
- } from "./chunk-DYDX54LP.js";
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.6"}`));
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.6");
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.6");
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.6") {
6169
- console.log(source_default.green(`\u2713 Already up to date (${"9.161.6"})`));
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.6"} -> Latest version: ${latestVersion}`
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.6"} to ${latestVersion}`)
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.6");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "9.161.6",
3
+ "version": "9.161.8",
4
4
  "description": "CLI application",
5
5
  "repository": {
6
6
  "type": "git",
package/zero.js CHANGED
@@ -25,6 +25,7 @@ import {
25
25
  createLocalBrowserWriteCommand,
26
26
  createSkill,
27
27
  createZeroAgent,
28
+ createZeroCreditCheckout,
28
29
  createZeroRun,
29
30
  decodeCliTokenPayload,
30
31
  decodeZeroTokenPayload,
@@ -73,6 +74,7 @@ import {
73
74
  getZeroAgent,
74
75
  getZeroAgentInstructions,
75
76
  getZeroAgentUserConnectors,
77
+ getZeroBillingStatus,
76
78
  getZeroConnector,
77
79
  getZeroOrg,
78
80
  getZeroOrgMembers,
@@ -148,7 +150,7 @@ import {
148
150
  zeroAgentCustomSkillNameSchema,
149
151
  zeroLocalAgentCommand,
150
152
  zeroTokenAllowsFeatureSwitch
151
- } from "./chunk-DYDX54LP.js";
153
+ } from "./chunk-BWCVGYRL.js";
152
154
  import {
153
155
  __commonJS,
154
156
  __require,
@@ -29828,6 +29830,62 @@ var statusCommand2 = new Command().name("status").description("Show detailed sta
29828
29830
  // src/commands/zero/connector/index.ts
29829
29831
  var zeroConnectorCommand = new Command().name("connector").description("Check third-party service connections (GitHub, Slack, etc.)").addCommand(listCommand6).addCommand(searchCommand).addCommand(statusCommand2);
29830
29832
 
29833
+ // src/commands/zero/credit.ts
29834
+ init_esm_shims();
29835
+ function parseCredits(value) {
29836
+ const credits = Number(value.replaceAll(",", ""));
29837
+ if (!Number.isInteger(credits) || credits <= 0) {
29838
+ throw new Error("credits must be a positive integer");
29839
+ }
29840
+ return credits;
29841
+ }
29842
+ var zeroCreditCommand = new Command().name("credit").description("Create a Stripe checkout link to buy credits").argument("<credits>", "Number of credits to buy", parseCredits).addOption(
29843
+ new Option("--auto-recharge", "Enable auto-recharge after checkout")
29844
+ ).option(
29845
+ "--auto-recharge-threshold <credits>",
29846
+ "Recharge when balance is at or below this number of credits",
29847
+ parseCredits
29848
+ ).option(
29849
+ "--auto-recharge-amount <credits>",
29850
+ "Credits to buy for each auto-recharge",
29851
+ parseCredits
29852
+ ).action(
29853
+ withErrorHandler(
29854
+ async (credits, options) => {
29855
+ const members = await getZeroOrgMembers();
29856
+ if (members.role !== "admin") {
29857
+ console.log(
29858
+ source_default.yellow(
29859
+ "Only organization admins can buy credits. Run `zero doctor credit` to see the current credit status and org admins."
29860
+ )
29861
+ );
29862
+ return;
29863
+ }
29864
+ const origin = await getPlatformOrigin();
29865
+ const successUrl = `${origin}/?settings=usage&credit=success`;
29866
+ const cancelUrl = `${origin}/?settings=usage&credit=canceled`;
29867
+ const autoRecharge = options.autoRecharge === void 0 && options.autoRechargeThreshold === void 0 && options.autoRechargeAmount === void 0 ? void 0 : {
29868
+ enabled: options.autoRecharge === true,
29869
+ threshold: options.autoRechargeThreshold,
29870
+ amount: options.autoRechargeAmount
29871
+ };
29872
+ if (autoRecharge?.enabled === true && (autoRecharge.threshold === void 0 || autoRecharge.amount === void 0)) {
29873
+ throw new Error(
29874
+ "--auto-recharge requires --auto-recharge-threshold and --auto-recharge-amount"
29875
+ );
29876
+ }
29877
+ const result = await createZeroCreditCheckout({
29878
+ credits,
29879
+ successUrl,
29880
+ cancelUrl,
29881
+ ...autoRecharge ? { autoRecharge } : {}
29882
+ });
29883
+ console.log(source_default.bold("Credit checkout link:"));
29884
+ console.log(result.url);
29885
+ }
29886
+ )
29887
+ );
29888
+
29831
29889
  // src/commands/zero/doctor/index.ts
29832
29890
  init_esm_shims();
29833
29891
 
@@ -30905,8 +30963,67 @@ Notes:
30905
30963
  )
30906
30964
  );
30907
30965
 
30966
+ // src/commands/zero/doctor/credit.ts
30967
+ init_esm_shims();
30968
+ function memberName(member) {
30969
+ const name = [member.firstName, member.lastName].filter(Boolean).join(" ");
30970
+ return name ? `${name} <${member.email}>` : member.email;
30971
+ }
30972
+ var creditCommand = new Command().name("credit").description("Diagnose current organization credit and purchase access").action(
30973
+ withErrorHandler(async () => {
30974
+ const [org, billing, members] = await Promise.all([
30975
+ getZeroOrg(),
30976
+ getZeroBillingStatus(),
30977
+ getZeroOrgMembers()
30978
+ ]);
30979
+ const admins = members.members.filter((member) => {
30980
+ return member.role === "admin";
30981
+ });
30982
+ const isAdmin = members.role === "admin";
30983
+ const canPurchaseCredits = isAdmin;
30984
+ console.log(source_default.bold("Credit diagnostics:"));
30985
+ console.log(` Org: ${source_default.green(org.slug)}`);
30986
+ console.log(` Tier: ${source_default.cyan(billing.tier)}`);
30987
+ console.log(
30988
+ ` Available credits: ${source_default.cyan(billing.credits.toLocaleString())}`
30989
+ );
30990
+ console.log(` Current user role: ${source_default.cyan(members.role)}`);
30991
+ console.log(
30992
+ ` Can purchase credits: ${canPurchaseCredits ? source_default.green("yes") : source_default.yellow("no")}`
30993
+ );
30994
+ console.log(
30995
+ ` Auto-recharge: ${billing.autoRecharge.enabled ? source_default.green("enabled") : "disabled"}`
30996
+ );
30997
+ if (billing.autoRecharge.enabled) {
30998
+ console.log(
30999
+ ` Threshold: ${billing.autoRecharge.threshold?.toLocaleString() ?? "not set"}`
31000
+ );
31001
+ console.log(
31002
+ ` Amount: ${billing.autoRecharge.amount?.toLocaleString() ?? "not set"}`
31003
+ );
31004
+ }
31005
+ if (!isAdmin) {
31006
+ console.log(source_default.bold("\nOrganization admins:"));
31007
+ for (const admin of admins) {
31008
+ console.log(` - ${memberName(admin)}`);
31009
+ }
31010
+ console.log(
31011
+ source_default.yellow(
31012
+ "\nAsk an organization admin to buy credits or upgrade the plan."
31013
+ )
31014
+ );
31015
+ } else if (billing.tier === "free") {
31016
+ console.log(
31017
+ "\nFree-tier admins can upgrade to Pro from billing or buy credits with `zero credit <credits>`."
31018
+ );
31019
+ } else {
31020
+ console.log("\nUse `zero credit <credits>` to buy more credits.");
31021
+ }
31022
+ })
31023
+ );
31024
+
30908
31025
  // src/commands/zero/doctor/index.ts
30909
- var zeroDoctorCommand = new Command().name("doctor").description("Diagnose runtime issues (connector health, permission denials)").addCommand(checkConnectorCommand).addCommand(generateCommand).addCommand(permissionDenyCommand).addCommand(permissionChangeCommand).addHelpText(
31026
+ var zeroDoctorCommand = new Command().name("doctor").description("Diagnose runtime issues (connector health, permission denials)").addCommand(checkConnectorCommand).addCommand(generateCommand).addCommand(creditCommand).addCommand(permissionDenyCommand).addCommand(permissionChangeCommand).addHelpText(
30910
31027
  "after",
30911
31028
  `
30912
31029
  Examples:
@@ -30914,11 +31031,13 @@ Examples:
30914
31031
  Check a URL? zero doctor check-connector --url https://api.github.com/repos/owner/repo
30915
31032
  Generate with image? zero doctor generate image
30916
31033
  Generate with video? zero doctor generate video
31034
+ Check credits? zero doctor credit
30917
31035
  Check with permission? zero doctor check-connector --env-name SLACK_TOKEN --check-permission chat:write
30918
31036
  Permission denied? zero doctor permission-deny github --method GET --path /repos/owner/repo
30919
31037
  Change a permission? zero doctor permission-change github --permission contents:read --enable
30920
31038
 
30921
31039
  Notes:
31040
+ - Use zero doctor credit when a run or generation fails because the org has insufficient credits, when a user asks how to recharge, or before trying to buy credits
30922
31041
  - Use this when your task fails due to a missing environment variable or permission denial
30923
31042
  - The doctor will identify the issue and give the user a link to resolve it`
30924
31043
  );
@@ -34626,6 +34745,82 @@ init_esm_shims();
34626
34745
  // src/commands/zero/shared/presentation-generate.ts
34627
34746
  init_esm_shims();
34628
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
34629
34824
  var PRESENTATION_MAX_IMAGES = 8;
34630
34825
  function parseSlideCount(value) {
34631
34826
  const slideCount = Number(value);
@@ -34663,7 +34858,7 @@ function readPrompt2(options, usageCommand) {
34663
34858
  });
34664
34859
  }
34665
34860
  function createPresentationGenerateCommand(config) {
34666
- return new Command().name(config.name).description("Generate a billed HTML presentation from a prompt").option(
34861
+ return new Command().name(config.name).description("Generate an HTML presentation from a prompt").option(
34667
34862
  "--prompt <text>",
34668
34863
  "Presentation prompt; can also be piped via stdin"
34669
34864
  ).option("--style <style>", "Style: editorial or swiss", "editorial").option("--slides <count>", "Slide count: 4-20", parseSlideCount, 8).option(
@@ -34684,15 +34879,44 @@ Examples:
34684
34879
  ${config.examples}
34685
34880
 
34686
34881
  Output:
34687
- 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.
34688
34883
 
34689
34884
  Notes:
34690
- - Authenticates via ZERO_TOKEN (requires file:write capability)
34691
- - Charges org credits after successful presentation generation
34692
- - Uses OpenAI gpt-5.5 for deck text and fal.ai for generated visuals`
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`
34693
34888
  ).action(
34694
34889
  withErrorHandler(async (options) => {
34695
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
+ }
34696
34920
  const result = await generateWebPresentation({
34697
34921
  prompt,
34698
34922
  style: options.style,
@@ -34741,6 +34965,22 @@ init_esm_shims();
34741
34965
  // src/commands/zero/shared/video-generate.ts
34742
34966
  init_esm_shims();
34743
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
+ ]);
34744
34984
  function parseSeed2(value) {
34745
34985
  const seed = Number(value);
34746
34986
  if (!Number.isInteger(seed) || seed < 0 || !Number.isSafeInteger(seed)) {
@@ -34751,6 +34991,173 @@ function parseSeed2(value) {
34751
34991
  function collectUrl(value, previous = []) {
34752
34992
  return [...previous, value];
34753
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
+ }
34754
35161
  function readPrompt3(options, usageCommand) {
34755
35162
  if (options.prompt?.trim()) {
34756
35163
  return options.prompt.trim();
@@ -34822,6 +35229,7 @@ Models:
34822
35229
  ).action(
34823
35230
  withErrorHandler(async (options) => {
34824
35231
  const prompt = readPrompt3(options, config.usageCommand);
35232
+ await validateVideoOptions(options);
34825
35233
  const result = await generateWebVideo({
34826
35234
  prompt,
34827
35235
  model: options.model,
@@ -35947,7 +36355,7 @@ function formatBytes(bytes) {
35947
36355
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
35948
36356
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
35949
36357
  }
35950
- var websiteCommand = new Command().name("website").description("Generate, build, and publish a hosted website from a prompt").option("--prompt <text>", "Website prompt; can also be piped via stdin").option(
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(
35951
36359
  "--template <template>",
35952
36360
  "Template: auto, launch, or profile",
35953
36361
  parseTemplate,
@@ -35970,15 +36378,42 @@ Examples:
35970
36378
  Pipe prompt: cat brief.txt | zero built-in generate website
35971
36379
 
35972
36380
  Output:
35973
- Builds a React template into a static website, publishes it with zero host, and prints the hosted URL
36381
+ Generates and publishes a hosted website. With openDesignGenerate enabled, prints an OpenDesign-style authoring packet for the current agent instead.
35974
36382
 
35975
36383
  Notes:
35976
- - Authenticates via ZERO_TOKEN (requires host:write capability)
35977
- - Charges org credits for model-generated website content
35978
- - Uses OpenAI gpt-5.5 for website content and fal.ai for generated visuals`
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`
35979
36387
  ).action(
35980
36388
  withErrorHandler(async (options) => {
35981
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
+ }
35982
36417
  if (!options.json) {
35983
36418
  console.log(source_default.dim("Generating website content..."));
35984
36419
  }
@@ -36947,6 +37382,7 @@ var COMMAND_CAPABILITY_MAP = {
36947
37382
  run: "agent-run:write",
36948
37383
  schedule: "schedule:read",
36949
37384
  doctor: null,
37385
+ credit: null,
36950
37386
  model: null,
36951
37387
  "model-provider": null,
36952
37388
  logs: "agent-run:read",
@@ -36972,6 +37408,7 @@ var DEFAULT_COMMANDS = [
36972
37408
  zeroModelProviderCommand,
36973
37409
  zeroAgentCommand,
36974
37410
  zeroConnectorCommand,
37411
+ zeroCreditCommand,
36975
37412
  zeroDoctorCommand,
36976
37413
  zeroPreferenceCommand,
36977
37414
  zeroRunCommand,
@@ -37014,6 +37451,8 @@ function shouldHideCommand(name, payload) {
37014
37451
  function buildZeroHelpText(payload = decodeZeroTokenPayload()) {
37015
37452
  const examples = [
37016
37453
  " Check a connector? zero doctor check-connector --env-name <ENV_NAME>",
37454
+ " Check credits? zero doctor credit",
37455
+ " Buy credits? zero credit 20000",
37017
37456
  " Send a Slack message? zero slack message send --help",
37018
37457
  " Upload GitHub? zero github upload-file --help",
37019
37458
  " Download GitHub? zero github download-file --help",
@@ -37054,7 +37493,7 @@ function registerZeroCommands(prog, commands) {
37054
37493
  var program = new Command();
37055
37494
  program.name("zero").description(
37056
37495
  "Zero CLI \u2014 interact with the zero platform from inside the sandbox"
37057
- ).version("9.161.6").addHelpText("after", () => {
37496
+ ).version("9.161.8").addHelpText("after", () => {
37058
37497
  return buildZeroHelpText();
37059
37498
  });
37060
37499
  if (process.argv[1]?.endsWith("zero.js") || process.argv[1]?.endsWith("zero.ts") || process.argv[1]?.endsWith("zero")) {