@vm0/cli 9.161.7 → 9.161.9

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/zero.js CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  connectorTypeSchema,
22
22
  createComputerUseReadCommand,
23
23
  createComputerUseWriteCommand,
24
+ createGithubLabelListener,
24
25
  createLocalBrowserReadCommand,
25
26
  createLocalBrowserWriteCommand,
26
27
  createSkill,
@@ -29,6 +30,7 @@ import {
29
30
  createZeroRun,
30
31
  decodeCliTokenPayload,
31
32
  decodeZeroTokenPayload,
33
+ deleteGithubLabelListener,
32
34
  deleteLocalBrowserHost,
33
35
  deleteSkill,
34
36
  deleteZeroAgent,
@@ -64,6 +66,7 @@ import {
64
66
  getConnectorGenerationTypes,
65
67
  getConnectorTypeForSecretName,
66
68
  getDefaultAuthMethod,
69
+ getGithubInstallation,
67
70
  getLocalBrowserReadCommand,
68
71
  getScopeDiff,
69
72
  getSecretsForAuthMethod,
@@ -139,6 +142,7 @@ import {
139
142
  source_default,
140
143
  submitDeveloperSupport,
141
144
  switchZeroOrg,
145
+ updateGithubLabelListener,
142
146
  updateSkill,
143
147
  updateZeroAgent,
144
148
  updateZeroAgentInstructions,
@@ -150,7 +154,7 @@ import {
150
154
  zeroAgentCustomSkillNameSchema,
151
155
  zeroLocalAgentCommand,
152
156
  zeroTokenAllowsFeatureSwitch
153
- } from "./chunk-P3SAJ3Q2.js";
157
+ } from "./chunk-JXO3PHAE.js";
154
158
  import {
155
159
  __commonJS,
156
160
  __require,
@@ -32432,6 +32436,160 @@ Notes:
32432
32436
  )
32433
32437
  );
32434
32438
 
32439
+ // src/commands/zero/github/label-listener/index.ts
32440
+ init_esm_shims();
32441
+ function parseTriggerMode(value) {
32442
+ if (value === "created_by_me" || value === "anyone") {
32443
+ return value;
32444
+ }
32445
+ throw new Error("trigger-mode must be one of: created_by_me, anyone");
32446
+ }
32447
+ function enabledLabel(listener) {
32448
+ return listener.enabled ? source_default.green("yes") : source_default.dim("no");
32449
+ }
32450
+ function manageableLabel(listener) {
32451
+ return listener.canManage ? source_default.green("yes") : source_default.dim("no");
32452
+ }
32453
+ function printListeners(listeners) {
32454
+ if (listeners.length === 0) {
32455
+ console.log(source_default.dim("No GitHub label listeners found"));
32456
+ return;
32457
+ }
32458
+ const idWidth = Math.max(
32459
+ 2,
32460
+ ...listeners.map((listener) => {
32461
+ return listener.id.length;
32462
+ })
32463
+ );
32464
+ const labelWidth = Math.max(
32465
+ 5,
32466
+ ...listeners.map((listener) => {
32467
+ return listener.labelName.length;
32468
+ })
32469
+ );
32470
+ const agentWidth = Math.max(
32471
+ 5,
32472
+ ...listeners.map((listener) => {
32473
+ return (listener.agent?.name ?? "-").length;
32474
+ })
32475
+ );
32476
+ console.log(
32477
+ source_default.dim(
32478
+ [
32479
+ "ID".padEnd(idWidth),
32480
+ "LABEL".padEnd(labelWidth),
32481
+ "AGENT".padEnd(agentWidth),
32482
+ "TRIGGER".padEnd(13),
32483
+ "ENABLED",
32484
+ "CAN MANAGE"
32485
+ ].join(" ")
32486
+ )
32487
+ );
32488
+ for (const listener of listeners) {
32489
+ console.log(
32490
+ [
32491
+ listener.id.padEnd(idWidth),
32492
+ listener.labelName.padEnd(labelWidth),
32493
+ (listener.agent?.name ?? "-").padEnd(agentWidth),
32494
+ listener.triggerMode.padEnd(13),
32495
+ enabledLabel(listener).padEnd(7),
32496
+ manageableLabel(listener)
32497
+ ].join(" ")
32498
+ );
32499
+ }
32500
+ }
32501
+ var listCommand9 = new Command().name("list").alias("ls").description("List GitHub label listeners for the active organization").option("--json", "Print raw JSON").action(
32502
+ withErrorHandler(async (options) => {
32503
+ const installation = await getGithubInstallation();
32504
+ if (options.json) {
32505
+ console.log(JSON.stringify(installation.labelListeners));
32506
+ return;
32507
+ }
32508
+ printListeners(installation.labelListeners);
32509
+ })
32510
+ );
32511
+ var createCommand2 = new Command().name("create").description("Create a GitHub label listener").requiredOption("--label <name>", "GitHub label name to watch").requiredOption(
32512
+ "--agent-id <id>",
32513
+ "Agent ID to run when the label is applied"
32514
+ ).requiredOption("--prompt <text>", "Prompt to pass to the agent").option(
32515
+ "--trigger-mode <mode>",
32516
+ "Who can trigger the listener: anyone | created_by_me",
32517
+ "anyone"
32518
+ ).option("--disabled", "Create the listener disabled").option("--json", "Print raw JSON").action(
32519
+ withErrorHandler(
32520
+ async (options) => {
32521
+ const result = await createGithubLabelListener({
32522
+ labelName: options.label,
32523
+ agentId: options.agentId,
32524
+ prompt: options.prompt,
32525
+ triggerMode: parseTriggerMode(options.triggerMode),
32526
+ enabled: options.disabled ? false : void 0
32527
+ });
32528
+ if (options.json) {
32529
+ console.log(JSON.stringify(result.listener));
32530
+ return;
32531
+ }
32532
+ console.log(`Created GitHub label listener ${result.listener.id}`);
32533
+ }
32534
+ )
32535
+ );
32536
+ var updateCommand = new Command().name("update").alias("edit").description("Update a GitHub label listener").argument("<listener-id>", "GitHub label listener ID").option("--label <name>", "New GitHub label name").option("--agent-id <id>", "New agent ID").option("--prompt <text>", "New prompt").option(
32537
+ "--trigger-mode <mode>",
32538
+ "Who can trigger the listener: anyone | created_by_me"
32539
+ ).option("--enable", "Enable the listener").option("--disable", "Disable the listener").option("--json", "Print raw JSON").action(
32540
+ withErrorHandler(
32541
+ async (listenerId, options) => {
32542
+ if (options.enable && options.disable) {
32543
+ throw new Error("Use only one of --enable or --disable");
32544
+ }
32545
+ const body = {};
32546
+ if (options.label !== void 0) body.labelName = options.label;
32547
+ if (options.agentId !== void 0) body.agentId = options.agentId;
32548
+ if (options.prompt !== void 0) body.prompt = options.prompt;
32549
+ if (options.triggerMode !== void 0) {
32550
+ body.triggerMode = parseTriggerMode(options.triggerMode);
32551
+ }
32552
+ if (options.enable) body.enabled = true;
32553
+ if (options.disable) body.enabled = false;
32554
+ if (Object.keys(body).length === 0) {
32555
+ throw new Error(
32556
+ "Provide at least one change: --label, --agent-id, --prompt, --trigger-mode, --enable, or --disable"
32557
+ );
32558
+ }
32559
+ const result = await updateGithubLabelListener(listenerId, body);
32560
+ if (options.json) {
32561
+ console.log(JSON.stringify(result.listener));
32562
+ return;
32563
+ }
32564
+ console.log(`Updated GitHub label listener ${result.listener.id}`);
32565
+ }
32566
+ )
32567
+ );
32568
+ var deleteCommand5 = new Command().name("delete").alias("rm").description("Delete a GitHub label listener").argument("<listener-id>", "GitHub label listener ID").option("--json", "Print raw JSON").action(
32569
+ withErrorHandler(
32570
+ async (listenerId, options) => {
32571
+ const result = await deleteGithubLabelListener(listenerId);
32572
+ if (options.json) {
32573
+ console.log(JSON.stringify(result));
32574
+ return;
32575
+ }
32576
+ console.log(`Deleted GitHub label listener ${listenerId}`);
32577
+ }
32578
+ )
32579
+ );
32580
+ var labelListenerCommand = new Command().name("label-listener").alias("label-listeners").alias("labels").description("Manage GitHub label listeners").addCommand(listCommand9).addCommand(createCommand2).addCommand(updateCommand).addCommand(deleteCommand5).addHelpText(
32581
+ "after",
32582
+ `
32583
+ Examples:
32584
+ List listeners: zero github label-listener list
32585
+ Create listener: zero github label-listener create --label zero --agent-id <agent-id> --prompt "Handle this issue"
32586
+ Edit listener: zero github label-listener update <listener-id> --disable
32587
+ Delete listener: zero github label-listener delete <listener-id>
32588
+
32589
+ Notes:
32590
+ - Updating or deleting a listener is allowed only for the listener owner or an org admin.`
32591
+ );
32592
+
32435
32593
  // src/commands/zero/github/upload-file.ts
32436
32594
  init_esm_shims();
32437
32595
  import { readFileSync as readFileSync5, statSync } from "fs";
@@ -32531,12 +32689,13 @@ Notes:
32531
32689
  );
32532
32690
 
32533
32691
  // src/commands/zero/github/index.ts
32534
- var zeroGithubCommand = new Command().name("github").description("Upload files to GitHub issues and download GitHub files").addCommand(downloadFileCommand).addCommand(uploadFileCommand).addHelpText(
32692
+ var zeroGithubCommand = new Command().name("github").description("Manage GitHub integration files and label listeners").addCommand(downloadFileCommand).addCommand(labelListenerCommand).addCommand(uploadFileCommand).addHelpText(
32535
32693
  "after",
32536
32694
  `
32537
32695
  Examples:
32538
32696
  Upload a file: zero github upload-file -f /tmp/report.pdf -r vm0-ai/vm0 -i 42
32539
- Download a file: zero github download-file https://github.com/user-attachments/assets/abc123 -o /tmp/out.png`
32697
+ Download a file: zero github download-file https://github.com/user-attachments/assets/abc123 -o /tmp/out.png
32698
+ List labels: zero github label-listener list`
32540
32699
  );
32541
32700
 
32542
32701
  // src/commands/zero/slack/index.ts
@@ -32748,7 +32907,7 @@ function statusLabel(bot) {
32748
32907
  if (bot.tokenStatus === "invalid") return source_default.red("invalid");
32749
32908
  return source_default.yellow("unknown");
32750
32909
  }
32751
- var listCommand9 = new Command().name("list").alias("ls").description("List Telegram bots available in the active organization").addHelpText(
32910
+ var listCommand10 = new Command().name("list").alias("ls").description("List Telegram bots available in the active organization").addHelpText(
32752
32911
  "after",
32753
32912
  `
32754
32913
  Examples:
@@ -32807,7 +32966,7 @@ Notes:
32807
32966
  );
32808
32967
 
32809
32968
  // src/commands/zero/telegram/bot/index.ts
32810
- var zeroTelegramBotCommand = new Command().name("bot").description("Inspect Telegram bots").addCommand(listCommand9).addHelpText(
32969
+ var zeroTelegramBotCommand = new Command().name("bot").description("Inspect Telegram bots").addCommand(listCommand10).addHelpText(
32811
32970
  "after",
32812
32971
  `
32813
32972
  Examples:
@@ -33221,7 +33380,7 @@ function truncateValue2(value, maxLength = 60) {
33221
33380
  }
33222
33381
  return value.slice(0, maxLength - 15) + "... [truncated]";
33223
33382
  }
33224
- var listCommand10 = new Command().name("list").alias("ls").description("List all variables").action(
33383
+ var listCommand11 = new Command().name("list").alias("ls").description("List all variables").action(
33225
33384
  withErrorHandler(async () => {
33226
33385
  const result = await listZeroVariables();
33227
33386
  if (result.variables.length === 0) {
@@ -33277,7 +33436,7 @@ var setCommand5 = new Command().name("set").description("Create or update a vari
33277
33436
 
33278
33437
  // src/commands/zero/variable/delete.ts
33279
33438
  init_esm_shims();
33280
- var deleteCommand5 = new Command().name("delete").description("Delete a variable").argument("<name>", "Variable name to delete").option("-y, --yes", "Skip confirmation prompt").action(
33439
+ var deleteCommand6 = new Command().name("delete").description("Delete a variable").argument("<name>", "Variable name to delete").option("-y, --yes", "Skip confirmation prompt").action(
33281
33440
  withErrorHandler(async (name, options) => {
33282
33441
  if (!options.yes) {
33283
33442
  if (!isInteractive()) {
@@ -33298,7 +33457,7 @@ var deleteCommand5 = new Command().name("delete").description("Delete a variable
33298
33457
  );
33299
33458
 
33300
33459
  // src/commands/zero/variable/index.ts
33301
- var zeroVariableCommand = new Command().name("variable").description("Read or write non-sensitive configuration values").addCommand(listCommand10).addCommand(setCommand5).addCommand(deleteCommand5);
33460
+ var zeroVariableCommand = new Command().name("variable").description("Read or write non-sensitive configuration values").addCommand(listCommand11).addCommand(setCommand5).addCommand(deleteCommand6);
33302
33461
 
33303
33462
  // src/commands/zero/whoami.ts
33304
33463
  init_esm_shims();
@@ -33487,7 +33646,7 @@ function readSkillDirectory(dirPath) {
33487
33646
  }
33488
33647
 
33489
33648
  // src/commands/zero/skill/create.ts
33490
- var createCommand2 = new Command().name("create").description("Create a custom skill in the organization").argument("<name>", "Skill name (lowercase alphanumeric with hyphens)").requiredOption("--dir <path>", "Path to directory containing SKILL.md").option("--display-name <name>", "Skill display name").option("--description <text>", "Skill description").addHelpText(
33649
+ var createCommand3 = new Command().name("create").description("Create a custom skill in the organization").argument("<name>", "Skill name (lowercase alphanumeric with hyphens)").requiredOption("--dir <path>", "Path to directory containing SKILL.md").option("--display-name <name>", "Skill display name").option("--description <text>", "Skill description").addHelpText(
33491
33650
  "after",
33492
33651
  `
33493
33652
  Examples:
@@ -33577,7 +33736,7 @@ Examples:
33577
33736
 
33578
33737
  // src/commands/zero/skill/list.ts
33579
33738
  init_esm_shims();
33580
- var listCommand11 = new Command().name("list").alias("ls").description("List custom skills in the organization").addHelpText(
33739
+ var listCommand12 = new Command().name("list").alias("ls").description("List custom skills in the organization").addHelpText(
33581
33740
  "after",
33582
33741
  `
33583
33742
  Examples:
@@ -33623,7 +33782,7 @@ Examples:
33623
33782
 
33624
33783
  // src/commands/zero/skill/delete.ts
33625
33784
  init_esm_shims();
33626
- var deleteCommand6 = new Command().name("delete").alias("rm").description("Delete a custom skill from the organization").argument("<name>", "Skill name").option("-y, --yes", "Skip confirmation prompt").addHelpText(
33785
+ var deleteCommand7 = new Command().name("delete").alias("rm").description("Delete a custom skill from the organization").argument("<name>", "Skill name").option("-y, --yes", "Skip confirmation prompt").addHelpText(
33627
33786
  "after",
33628
33787
  `
33629
33788
  Examples:
@@ -33655,7 +33814,7 @@ Notes:
33655
33814
  );
33656
33815
 
33657
33816
  // src/commands/zero/skill/index.ts
33658
- var zeroSkillCommand = new Command("skill").description("Manage custom skills").addCommand(createCommand2).addCommand(editCommand2).addCommand(viewCommand2).addCommand(listCommand11).addCommand(deleteCommand6).addHelpText(
33817
+ var zeroSkillCommand = new Command("skill").description("Manage custom skills").addCommand(createCommand3).addCommand(editCommand2).addCommand(viewCommand2).addCommand(listCommand12).addCommand(deleteCommand7).addHelpText(
33659
33818
  "after",
33660
33819
  `
33661
33820
  Examples:
@@ -33696,7 +33855,7 @@ function formatStatus(status) {
33696
33855
  function formatTime(iso) {
33697
33856
  return new Date(iso).toISOString().replace(/\.\d{3}Z$/, "Z");
33698
33857
  }
33699
- var listCommand12 = new Command().name("list").alias("ls").description("List agent run logs").option("--agent <id>", "Filter by Zero agent ID").option(
33858
+ var listCommand13 = new Command().name("list").alias("ls").description("List agent run logs").option("--agent <id>", "Filter by Zero agent ID").option(
33700
33859
  "--status <status>",
33701
33860
  "Filter by status (queued|pending|running|completed|failed|timeout|cancelled)"
33702
33861
  ).option(
@@ -33959,7 +34118,7 @@ async function showAgentEvents(runId, options) {
33959
34118
  renderer.render(parsed);
33960
34119
  }
33961
34120
  }
33962
- var zeroLogsCommand = new Command().name("logs").description("View and search agent run logs").argument("[runId]", "Run ID to view agent events for").addCommand(listCommand12).addCommand(searchCommand2).option(
34121
+ var zeroLogsCommand = new Command().name("logs").description("View and search agent run logs").argument("[runId]", "Run ID to view agent events for").addCommand(listCommand13).addCommand(searchCommand2).option(
33963
34122
  "--since <time>",
33964
34123
  "Show logs since timestamp (e.g., 5m, 2h, 1d, 2024-01-15T10:30:00Z)"
33965
34124
  ).option("--tail <n>", "Show last N entries (default: 5)").option("--head <n>", "Show first N entries").option("--all", "Fetch all log entries").addHelpText(
@@ -34165,13 +34324,13 @@ var zeroSearchCommand = new Command().name("search").description("Search logs, c
34165
34324
  if (sources.length > 1) {
34166
34325
  throw new Error("Only one --source is allowed.");
34167
34326
  }
34168
- const source = sources[0];
34169
- if (!SUPPORTED_SOURCES.includes(source)) {
34327
+ const source2 = sources[0];
34328
+ if (!SUPPORTED_SOURCES.includes(source2)) {
34170
34329
  throw new Error(
34171
- `Unknown --source "${source}". Expected one of: ${SUPPORTED_SOURCES.join(", ")}`
34330
+ `Unknown --source "${source2}". Expected one of: ${SUPPORTED_SOURCES.join(", ")}`
34172
34331
  );
34173
34332
  }
34174
- switch (source) {
34333
+ switch (source2) {
34175
34334
  case "logs":
34176
34335
  await runLogsSource(query, options);
34177
34336
  return;
@@ -34745,6 +34904,457 @@ init_esm_shims();
34745
34904
  // src/commands/zero/shared/presentation-generate.ts
34746
34905
  init_esm_shims();
34747
34906
  import { readFileSync as readFileSync14 } from "fs";
34907
+
34908
+ // src/commands/zero/shared/html-artifact-authoring.ts
34909
+ init_esm_shims();
34910
+
34911
+ // src/commands/zero/shared/open-design-registry.ts
34912
+ init_esm_shims();
34913
+ var OPEN_DESIGN_REPO = "vm0-ai/open-design";
34914
+ var OPEN_DESIGN_COMMIT = "d021b04720ace133f1d6133d1487326f5fc28f07";
34915
+ var OPEN_DESIGN_REGISTRY_VERSION = `${OPEN_DESIGN_REPO}@${OPEN_DESIGN_COMMIT}`;
34916
+ function source(path) {
34917
+ return {
34918
+ repo: OPEN_DESIGN_REPO,
34919
+ commit: OPEN_DESIGN_COMMIT,
34920
+ path
34921
+ };
34922
+ }
34923
+ var OPEN_DESIGN_REGISTRY = [
34924
+ {
34925
+ id: "od:skill:data-report",
34926
+ kind: "skill",
34927
+ name: "Data Report",
34928
+ description: "Turns source-backed data, rankings, metrics, or lists into a concise analytical report.",
34929
+ source: source("skills/data-report/SKILL.md"),
34930
+ targets: ["presentation", "website", "dashboard", "report", "docs"],
34931
+ tags: ["analysis", "data", "report", "ranking", "sources", "table"],
34932
+ triggers: ["report", "top 10", "ranking", "metrics", "analysis"],
34933
+ bestFor: ["source-backed reports", "ranked lists", "data summaries"],
34934
+ status: "curated",
34935
+ priority: 40
34936
+ },
34937
+ {
34938
+ id: "od:skill:article-magazine",
34939
+ kind: "skill",
34940
+ name: "Article Magazine",
34941
+ description: "Shapes research or editorial material into a magazine-like narrative with strong hierarchy.",
34942
+ source: source("skills/article-magazine/SKILL.md"),
34943
+ targets: ["presentation", "website", "poster", "report", "docs"],
34944
+ tags: ["editorial", "magazine", "article", "narrative", "research"],
34945
+ triggers: ["magazine", "editorial", "story", "essay", "briefing"],
34946
+ bestFor: [
34947
+ "editorial reports",
34948
+ "narrative explainers",
34949
+ "research synthesis"
34950
+ ],
34951
+ status: "curated",
34952
+ priority: 28
34953
+ },
34954
+ {
34955
+ id: "od:skill:design-brief",
34956
+ kind: "skill",
34957
+ name: "Design Brief",
34958
+ description: "Converts a product, brand, or feature request into a structured design brief.",
34959
+ source: source("skills/design-brief/SKILL.md"),
34960
+ targets: ["presentation", "website", "mobile-app", "docs"],
34961
+ tags: ["design", "brief", "product", "brand", "requirements"],
34962
+ triggers: ["design brief", "brand", "product direction", "requirements"],
34963
+ bestFor: ["product design briefs", "brand-driven websites"],
34964
+ status: "curated",
34965
+ priority: 16
34966
+ },
34967
+ {
34968
+ id: "od:template:dashboard",
34969
+ kind: "template",
34970
+ name: "Dashboard",
34971
+ description: "Dense operational dashboard layout for KPIs, lists, filters, and repeated scanning.",
34972
+ source: source("design-templates/dashboard"),
34973
+ targets: ["website", "dashboard", "report"],
34974
+ tags: ["dashboard", "analytics", "kpi", "metrics", "operations", "table"],
34975
+ triggers: ["dashboard", "analytics", "monitoring", "metrics", "ops"],
34976
+ bestFor: ["metric-heavy pages", "status surfaces", "operational summaries"],
34977
+ compatibleWith: ["od:skill:data-report", "od:design-system:dashboard"],
34978
+ status: "curated",
34979
+ priority: 36
34980
+ },
34981
+ {
34982
+ id: "od:template:finance-report",
34983
+ kind: "template",
34984
+ name: "Finance Report",
34985
+ description: "Executive report layout with tables, callouts, trend blocks, and source notes.",
34986
+ source: source("design-templates/finance-report"),
34987
+ targets: ["presentation", "website", "report"],
34988
+ tags: ["report", "finance", "executive", "table", "analysis", "sources"],
34989
+ triggers: ["report", "brief", "analysis", "top 10", "finance"],
34990
+ bestFor: ["source-backed reports", "executive summaries", "ranked lists"],
34991
+ compatibleWith: ["od:skill:data-report", "od:design-system:dashboard"],
34992
+ status: "curated",
34993
+ priority: 34
34994
+ },
34995
+ {
34996
+ id: "od:template:docs-page",
34997
+ kind: "template",
34998
+ name: "Docs Page",
34999
+ description: "Documentation-style page layout for structured explanations, navigation, and examples.",
35000
+ source: source("design-templates/docs-page"),
35001
+ targets: ["website", "docs", "report"],
35002
+ tags: ["docs", "explanation", "guide", "structured", "reference"],
35003
+ triggers: ["docs", "documentation", "guide", "explain", "how to"],
35004
+ bestFor: ["technical explainers", "product docs", "implementation notes"],
35005
+ compatibleWith: ["od:skill:design-brief", "od:design-system:mono"],
35006
+ status: "curated",
35007
+ priority: 26
35008
+ },
35009
+ {
35010
+ id: "od:template:html-ppt-graphify-dark-graph",
35011
+ kind: "template",
35012
+ name: "Graphify Dark Graph",
35013
+ description: "Dark graph-heavy HTML presentation template for data stories and technical briefings.",
35014
+ source: source("design-templates/html-ppt-graphify-dark-graph"),
35015
+ targets: ["presentation", "report"],
35016
+ tags: ["presentation", "dark", "graph", "data", "technical", "metrics"],
35017
+ triggers: ["deck", "presentation", "graph", "dark", "data story"],
35018
+ bestFor: ["data presentations", "technical executive briefings"],
35019
+ compatibleWith: [
35020
+ "od:skill:data-report",
35021
+ "od:design-system:trading-terminal"
35022
+ ],
35023
+ status: "curated",
35024
+ priority: 32
35025
+ },
35026
+ {
35027
+ id: "od:template:html-ppt-zhangzara-retro-zine",
35028
+ kind: "template",
35029
+ name: "Zhangzara Retro Zine",
35030
+ description: "Expressive retro editorial HTML presentation template with zine-like composition.",
35031
+ source: source("design-templates/html-ppt-zhangzara-retro-zine"),
35032
+ targets: ["presentation", "poster", "report"],
35033
+ tags: ["presentation", "retro", "zine", "editorial", "expressive"],
35034
+ triggers: ["retro", "zine", "editorial", "magazine", "bold"],
35035
+ bestFor: [
35036
+ "editorial decks",
35037
+ "culture reports",
35038
+ "visually distinctive summaries"
35039
+ ],
35040
+ compatibleWith: [
35041
+ "od:skill:article-magazine",
35042
+ "od:design-system:warm-editorial"
35043
+ ],
35044
+ status: "curated",
35045
+ priority: 30
35046
+ },
35047
+ {
35048
+ id: "od:template:weekly-update",
35049
+ kind: "template",
35050
+ name: "Weekly Update",
35051
+ description: "Compact update deck/page structure for highlights, risks, next steps, and metrics.",
35052
+ source: source("design-templates/weekly-update"),
35053
+ targets: ["presentation", "report", "docs"],
35054
+ tags: ["update", "status", "briefing", "report", "metrics"],
35055
+ triggers: ["weekly", "status", "update", "briefing"],
35056
+ bestFor: ["team updates", "status reports", "progress summaries"],
35057
+ compatibleWith: ["od:skill:data-report", "od:design-system:dashboard"],
35058
+ status: "curated",
35059
+ priority: 24
35060
+ },
35061
+ {
35062
+ id: "od:template:web-prototype-taste-editorial",
35063
+ kind: "template",
35064
+ name: "Taste Editorial Web Prototype",
35065
+ description: "Editorial website prototype direction with image-led sections and strong copy hierarchy.",
35066
+ source: source("design-templates/web-prototype-taste-editorial"),
35067
+ targets: ["website", "poster"],
35068
+ tags: ["website", "editorial", "brand", "visual", "prototype"],
35069
+ triggers: ["landing", "site", "brand", "editorial", "launch"],
35070
+ bestFor: ["brand websites", "launch pages", "editorial product pages"],
35071
+ compatibleWith: ["od:skill:article-magazine", "od:design-system:editorial"],
35072
+ status: "curated",
35073
+ priority: 30
35074
+ },
35075
+ {
35076
+ id: "od:design-system:dashboard",
35077
+ kind: "design-system",
35078
+ name: "Dashboard",
35079
+ description: "Quiet, dense interface system for dashboards, tables, filters, and repeat workflows.",
35080
+ source: source("design-systems/dashboard"),
35081
+ targets: ["website", "dashboard", "report"],
35082
+ tags: ["dashboard", "neutral", "dense", "table", "operations", "charts"],
35083
+ triggers: ["dashboard", "analytics", "metrics", "ops", "report"],
35084
+ bestFor: ["operational UIs", "data reports", "admin surfaces"],
35085
+ status: "curated",
35086
+ priority: 36
35087
+ },
35088
+ {
35089
+ id: "od:design-system:trading-terminal",
35090
+ kind: "design-system",
35091
+ name: "Trading Terminal",
35092
+ description: "Dark dense market-terminal aesthetic for charts, feeds, tables, and high information density.",
35093
+ source: source("design-systems/trading-terminal"),
35094
+ targets: ["presentation", "website", "dashboard", "report"],
35095
+ tags: ["dark", "terminal", "finance", "data", "charts", "dense"],
35096
+ triggers: ["dark", "terminal", "trading", "chart", "graph"],
35097
+ bestFor: ["dark analytical reports", "graph-heavy dashboards"],
35098
+ status: "curated",
35099
+ priority: 32
35100
+ },
35101
+ {
35102
+ id: "od:design-system:warm-editorial",
35103
+ kind: "design-system",
35104
+ name: "Warm Editorial",
35105
+ description: "Warm editorial design system for readable narrative pages, zines, and reports.",
35106
+ source: source("design-systems/warm-editorial"),
35107
+ targets: ["presentation", "website", "poster", "report", "docs"],
35108
+ tags: ["warm", "editorial", "magazine", "narrative", "readable"],
35109
+ triggers: ["editorial", "magazine", "zine", "warm", "story"],
35110
+ bestFor: ["narrative reports", "editorial decks", "long-form pages"],
35111
+ status: "curated",
35112
+ priority: 30
35113
+ },
35114
+ {
35115
+ id: "od:design-system:editorial",
35116
+ kind: "design-system",
35117
+ name: "Editorial",
35118
+ description: "Clean editorial design system with strong typography, media framing, and section rhythm.",
35119
+ source: source("design-systems/editorial"),
35120
+ targets: ["presentation", "website", "poster", "report", "docs"],
35121
+ tags: ["editorial", "typography", "media", "brand", "article"],
35122
+ triggers: ["editorial", "article", "brand", "landing", "magazine"],
35123
+ bestFor: ["brand sites", "article-style reports", "visual narratives"],
35124
+ status: "curated",
35125
+ priority: 28
35126
+ },
35127
+ {
35128
+ id: "od:design-system:mono",
35129
+ kind: "design-system",
35130
+ name: "Mono",
35131
+ description: "Minimal monospace-oriented system for documentation, technical pages, and precise reports.",
35132
+ source: source("design-systems/mono"),
35133
+ targets: ["website", "docs", "report"],
35134
+ tags: ["mono", "docs", "technical", "minimal", "structured"],
35135
+ triggers: ["docs", "technical", "reference", "minimal", "api"],
35136
+ bestFor: ["technical documentation", "implementation reports"],
35137
+ status: "curated",
35138
+ priority: 20
35139
+ }
35140
+ ];
35141
+ function normalizeText(value) {
35142
+ return value.toLowerCase();
35143
+ }
35144
+ function tokenize(value) {
35145
+ return normalizeText(value).split(/[^a-z0-9]+/u).filter((token) => {
35146
+ return token.length >= 2;
35147
+ });
35148
+ }
35149
+ function phraseScore(values, prompt, weight) {
35150
+ if (!values) {
35151
+ return 0;
35152
+ }
35153
+ return values.reduce((score, value) => {
35154
+ return prompt.includes(normalizeText(value)) ? score + weight : score;
35155
+ }, 0);
35156
+ }
35157
+ function tokenScore(values, promptTokens, weight) {
35158
+ if (!values) {
35159
+ return 0;
35160
+ }
35161
+ return values.reduce((score, value) => {
35162
+ const matches = tokenize(value).filter((token) => {
35163
+ return promptTokens.has(token);
35164
+ });
35165
+ return score + matches.length * weight;
35166
+ }, 0);
35167
+ }
35168
+ function scoreEntry(entry, target, prompt) {
35169
+ const normalizedPrompt = normalizeText(prompt);
35170
+ const promptTokens = new Set(tokenize(prompt));
35171
+ const targetScore = entry.targets.includes(target) ? 100 : 0;
35172
+ const curationScore = entry.status === "curated" ? 20 : entry.status === "experimental" ? -20 : -1e3;
35173
+ return targetScore + curationScore + (entry.priority ?? 0) + phraseScore(entry.triggers, normalizedPrompt, 40) + phraseScore(entry.bestFor, normalizedPrompt, 15) + tokenScore(entry.tags, promptTokens, 10) + tokenScore(entry.description.split(" "), promptTokens, 2);
35174
+ }
35175
+ function selectByKind(kind, target, prompt, limit) {
35176
+ return OPEN_DESIGN_REGISTRY.filter((entry) => {
35177
+ return entry.kind === kind && entry.status !== "hidden";
35178
+ }).map((entry) => {
35179
+ return { entry, score: scoreEntry(entry, target, prompt) };
35180
+ }).filter(({ score }) => {
35181
+ return score > 0;
35182
+ }).sort((left, right) => {
35183
+ if (right.score !== left.score) {
35184
+ return right.score - left.score;
35185
+ }
35186
+ return left.entry.id.localeCompare(right.entry.id);
35187
+ }).slice(0, limit).map(({ entry }) => {
35188
+ return entry;
35189
+ });
35190
+ }
35191
+ function selectOpenDesignCandidates(options) {
35192
+ const limitPerKind = options.limitPerKind ?? 8;
35193
+ return {
35194
+ registryVersion: OPEN_DESIGN_REGISTRY_VERSION,
35195
+ source: {
35196
+ repo: OPEN_DESIGN_REPO,
35197
+ commit: OPEN_DESIGN_COMMIT
35198
+ },
35199
+ candidates: {
35200
+ skills: selectByKind(
35201
+ "skill",
35202
+ options.target,
35203
+ options.prompt,
35204
+ limitPerKind
35205
+ ),
35206
+ templates: selectByKind(
35207
+ "template",
35208
+ options.target,
35209
+ options.prompt,
35210
+ limitPerKind
35211
+ ),
35212
+ designSystems: selectByKind(
35213
+ "design-system",
35214
+ options.target,
35215
+ options.prompt,
35216
+ limitPerKind
35217
+ )
35218
+ }
35219
+ };
35220
+ }
35221
+
35222
+ // src/commands/zero/shared/html-artifact-authoring.ts
35223
+ function slugify(value) {
35224
+ const slug = value.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").replace(/-{2,}/gu, "-").slice(0, 48).replace(/-+$/u, "");
35225
+ return slug.length >= 3 ? slug : "html-artifact";
35226
+ }
35227
+ function titleForKind(kind) {
35228
+ const titles = {
35229
+ presentation: "HTML presentation",
35230
+ website: "hosted website",
35231
+ dashboard: "dashboard",
35232
+ "mobile-app": "mobile app prototype",
35233
+ poster: "poster",
35234
+ "intro-video": "intro video storyboard",
35235
+ report: "report",
35236
+ docs: "documentation site"
35237
+ };
35238
+ return titles[kind];
35239
+ }
35240
+ function outputDirForSite(site) {
35241
+ return `./opendesign/mockups/${site}`;
35242
+ }
35243
+ function createHtmlArtifactAuthoringPacket(options) {
35244
+ const site = options.site ?? slugify(options.slugSource ?? options.prompt);
35245
+ const outputDir = outputDirForSite(site);
35246
+ const hostCommand = `zero host ${outputDir} --site ${site}${options.kind === "website" ? " --spa" : ""}`;
35247
+ const title = titleForKind(options.kind);
35248
+ const candidateSlice = selectOpenDesignCandidates({
35249
+ target: options.kind,
35250
+ prompt: [
35251
+ options.prompt,
35252
+ options.slugSource ?? "",
35253
+ ...options.details,
35254
+ ...options.artifactRules
35255
+ ].join("\n")
35256
+ });
35257
+ const selectionSchema = {
35258
+ skills: "string[]",
35259
+ template: "string",
35260
+ designSystem: "string | null",
35261
+ rationale: "string"
35262
+ };
35263
+ const instructions = [
35264
+ `# Zero built-in generate ${options.kind}`,
35265
+ "",
35266
+ "This is an Open Design resource-selection packet for the current agent.",
35267
+ `Zero is not generating this ${title} on the server. You select resources, resolve them, and author the artifact.`,
35268
+ "",
35269
+ "## User Prompt",
35270
+ options.prompt,
35271
+ "",
35272
+ "## Stage 1: Resource Selection",
35273
+ "- Choose the Open Design resources from the bundled registry slice below.",
35274
+ "- Select one template, one or more skills, and zero or one design system.",
35275
+ "- Choose only IDs present in this packet; do not invent registry IDs.",
35276
+ "- Prefer compatible resources, but the user prompt is the highest-priority signal.",
35277
+ "- Treat the selection JSON as internal working state, then continue to authoring.",
35278
+ "",
35279
+ "## Selection Output Schema",
35280
+ "```json",
35281
+ JSON.stringify(selectionSchema, null, 2),
35282
+ "```",
35283
+ "",
35284
+ "## Candidate Registry Slice",
35285
+ `Registry: \`${candidateSlice.registryVersion}\``,
35286
+ "",
35287
+ "```json",
35288
+ JSON.stringify(candidateSlice.candidates, null, 2),
35289
+ "```",
35290
+ "",
35291
+ "## Stage 2: Resolve Selected Resources",
35292
+ "- For every selected resource, fetch or read the referenced Open Design source before authoring.",
35293
+ "- Source refs are pinned as `repo@commit:path`; use the commit in the packet for reproducibility.",
35294
+ "- For directory refs, inspect the most relevant files such as `SKILL.md`, `DESIGN.md`, `README.md`, tokens, examples, and templates.",
35295
+ "- If a source file cannot be fetched, state that limitation and fall back to the registry metadata for that resource.",
35296
+ "",
35297
+ "## Stage 3: Author Artifact",
35298
+ `Author a production-quality ${title} as a static HTML artifact using the selected Open Design resources.`,
35299
+ "",
35300
+ "## Output Contract",
35301
+ `- Write the artifact under \`${outputDir}/\`.`,
35302
+ `- The entry file must be \`${outputDir}/index.html\`.`,
35303
+ "- Keep every local asset inside the same output directory.",
35304
+ "- Do not reference files from another project path.",
35305
+ "- Use descriptive filenames and canonical HTML: close non-void tags and double-quote attributes.",
35306
+ "- Prefer a single self-contained HTML file unless the artifact genuinely needs separate assets.",
35307
+ "",
35308
+ "## Requested Parameters",
35309
+ ...options.details.map((detail) => {
35310
+ return `- ${detail}`;
35311
+ }),
35312
+ "",
35313
+ "## Open Design Authoring Rules",
35314
+ "- Let the selected template define structure, the selected design system define visual language, and the selected skills define process.",
35315
+ "- Read the local codebase, brand assets, and existing design systems when the prompt depends on this repository.",
35316
+ "- 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.",
35317
+ "- Build the actual artifact first, not a marketing explanation of the artifact.",
35318
+ "- Make controls and interactions real when they are visible.",
35319
+ "- Keep text readable at desktop and mobile preview sizes.",
35320
+ ...options.artifactRules.map((rule) => {
35321
+ return `- ${rule}`;
35322
+ }),
35323
+ "",
35324
+ "## Verification",
35325
+ "- Open the HTML locally and verify it is nonblank.",
35326
+ "- Check that keyboard/click interactions work when present.",
35327
+ "- Check that text does not overflow or overlap at desktop and mobile viewport sizes.",
35328
+ "- Run the final hosting command only after the artifact looks correct.",
35329
+ "",
35330
+ "## Publish",
35331
+ `When everything is OK, publish it with:`,
35332
+ "",
35333
+ "```bash",
35334
+ hostCommand,
35335
+ "```"
35336
+ ].join("\n");
35337
+ return {
35338
+ type: "open-design-resource-selection",
35339
+ kind: options.kind,
35340
+ prompt: options.prompt,
35341
+ registryVersion: candidateSlice.registryVersion,
35342
+ selection: {
35343
+ candidates: candidateSlice.candidates,
35344
+ outputSchema: selectionSchema
35345
+ },
35346
+ authoring: {
35347
+ details: options.details,
35348
+ artifactRules: options.artifactRules
35349
+ },
35350
+ outputDir,
35351
+ site,
35352
+ hostCommand,
35353
+ instructions
35354
+ };
35355
+ }
35356
+
35357
+ // src/commands/zero/shared/presentation-generate.ts
34748
35358
  var PRESENTATION_MAX_IMAGES = 8;
34749
35359
  function parseSlideCount(value) {
34750
35360
  const slideCount = Number(value);
@@ -34782,7 +35392,7 @@ function readPrompt2(options, usageCommand) {
34782
35392
  });
34783
35393
  }
34784
35394
  function createPresentationGenerateCommand(config) {
34785
- return new Command().name(config.name).description("Generate a billed HTML presentation from a prompt").option(
35395
+ return new Command().name(config.name).description("Generate an HTML presentation from a prompt").option(
34786
35396
  "--prompt <text>",
34787
35397
  "Presentation prompt; can also be piped via stdin"
34788
35398
  ).option("--style <style>", "Style: editorial or swiss", "editorial").option("--slides <count>", "Slide count: 4-20", parseSlideCount, 8).option(
@@ -34803,15 +35413,44 @@ Examples:
34803
35413
  ${config.examples}
34804
35414
 
34805
35415
  Output:
34806
- Prints the generated /f/ HTML presentation URL and metadata
35416
+ Prints the generated /f/ HTML presentation URL and metadata. With openDesignGenerate enabled, prints an Open Design registry-selection packet for the current agent instead.
34807
35417
 
34808
35418
  Notes:
34809
- - Authenticates via ZERO_TOKEN (requires file:write capability)
34810
- - Charges org credits after successful presentation generation
34811
- - Uses OpenAI gpt-5.5 for deck text and fal.ai for generated visuals`
35419
+ - Authenticates via ZERO_TOKEN
35420
+ - Default path charges org credits after successful presentation generation
35421
+ - OpenDesign path is gated by the openDesignGenerate feature switch`
34812
35422
  ).action(
34813
35423
  withErrorHandler(async (options) => {
34814
35424
  const prompt = readPrompt2(options, config.usageCommand);
35425
+ if (zeroTokenAllowsFeatureSwitch("openDesignGenerate" /* OpenDesignGenerate */)) {
35426
+ const packet = createHtmlArtifactAuthoringPacket({
35427
+ kind: "presentation",
35428
+ prompt,
35429
+ slugSource: options.title,
35430
+ details: [
35431
+ `Style: ${options.style}`,
35432
+ `Slide count: ${options.slides}`,
35433
+ `Suggested generated visual count: ${options.images}`,
35434
+ `Image model preference if visuals are generated separately: ${options.imageModel ?? "default"}`,
35435
+ `Theme: ${options.theme ?? "agent decides from style"}`,
35436
+ `Audience: ${options.audience ?? "not specified"}`,
35437
+ `Requested deck title: ${options.title ?? "not specified"}`
35438
+ ],
35439
+ artifactRules: [
35440
+ "Think like a presentation designer, not a web page designer.",
35441
+ "Use a fixed 1920x1080 slide canvas and scale it uniformly for smaller viewports.",
35442
+ "Use one section per slide and keep repeated elements in consistent positions.",
35443
+ "Make keyboard navigation work with ArrowLeft, ArrowRight, Home, and End.",
35444
+ "Keep slide text readable from across a room; avoid memo-like walls of text."
35445
+ ]
35446
+ });
35447
+ if (options.json) {
35448
+ console.log(JSON.stringify(packet));
35449
+ return;
35450
+ }
35451
+ console.log(packet.instructions);
35452
+ return;
35453
+ }
34815
35454
  const result = await generateWebPresentation({
34816
35455
  prompt,
34817
35456
  style: options.style,
@@ -34860,6 +35499,22 @@ init_esm_shims();
34860
35499
  // src/commands/zero/shared/video-generate.ts
34861
35500
  init_esm_shims();
34862
35501
  import { readFileSync as readFileSync15 } from "fs";
35502
+ var FRAME_ASPECT_RATIO_TOLERANCE = 0.02;
35503
+ var JPEG_START_OF_FRAME_MARKERS = /* @__PURE__ */ new Set([
35504
+ 192,
35505
+ 193,
35506
+ 194,
35507
+ 195,
35508
+ 197,
35509
+ 198,
35510
+ 199,
35511
+ 201,
35512
+ 202,
35513
+ 203,
35514
+ 205,
35515
+ 206,
35516
+ 207
35517
+ ]);
34863
35518
  function parseSeed2(value) {
34864
35519
  const seed = Number(value);
34865
35520
  if (!Number.isInteger(seed) || seed < 0 || !Number.isSafeInteger(seed)) {
@@ -34870,6 +35525,173 @@ function parseSeed2(value) {
34870
35525
  function collectUrl(value, previous = []) {
34871
35526
  return [...previous, value];
34872
35527
  }
35528
+ function parseAspectRatio(value) {
35529
+ const [widthText, heightText] = value.split(":");
35530
+ const width = Number(widthText);
35531
+ const height = Number(heightText);
35532
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
35533
+ throw new Error(`Invalid --aspect-ratio "${value}"`);
35534
+ }
35535
+ return { width, height };
35536
+ }
35537
+ function readPngDimensions(buffer) {
35538
+ if (buffer.length < 24 || buffer.toString("latin1", 0, 8) !== "\x89PNG\r\n\n") {
35539
+ return void 0;
35540
+ }
35541
+ return {
35542
+ width: buffer.readUInt32BE(16),
35543
+ height: buffer.readUInt32BE(20)
35544
+ };
35545
+ }
35546
+ function readGifDimensions(buffer) {
35547
+ if (buffer.length < 10 || !buffer.toString("latin1", 0, 6).startsWith("GIF")) {
35548
+ return void 0;
35549
+ }
35550
+ return {
35551
+ width: buffer.readUInt16LE(6),
35552
+ height: buffer.readUInt16LE(8)
35553
+ };
35554
+ }
35555
+ function readJpegDimensions(buffer) {
35556
+ if (buffer.length < 4 || buffer[0] !== 255 || buffer[1] !== 216) {
35557
+ return void 0;
35558
+ }
35559
+ let offset = 2;
35560
+ while (offset + 9 < buffer.length) {
35561
+ if (buffer[offset] !== 255) {
35562
+ offset += 1;
35563
+ continue;
35564
+ }
35565
+ const marker = buffer[offset + 1];
35566
+ if (marker === 217 || marker === 218) {
35567
+ break;
35568
+ }
35569
+ const segmentLength = buffer.readUInt16BE(offset + 2);
35570
+ if (segmentLength < 2 || offset + 2 + segmentLength > buffer.length) {
35571
+ break;
35572
+ }
35573
+ if (JPEG_START_OF_FRAME_MARKERS.has(marker)) {
35574
+ return {
35575
+ height: buffer.readUInt16BE(offset + 5),
35576
+ width: buffer.readUInt16BE(offset + 7)
35577
+ };
35578
+ }
35579
+ offset += 2 + segmentLength;
35580
+ }
35581
+ return void 0;
35582
+ }
35583
+ function readUnsigned24LE(buffer, offset) {
35584
+ return buffer.readUInt8(offset) + (buffer.readUInt8(offset + 1) << 8) + (buffer.readUInt8(offset + 2) << 16);
35585
+ }
35586
+ function readWebpDimensions(buffer) {
35587
+ if (buffer.length < 30 || buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WEBP") {
35588
+ return void 0;
35589
+ }
35590
+ let offset = 12;
35591
+ while (offset + 8 <= buffer.length) {
35592
+ const chunkType = buffer.toString("ascii", offset, offset + 4);
35593
+ const chunkSize = buffer.readUInt32LE(offset + 4);
35594
+ const payloadOffset = offset + 8;
35595
+ if (payloadOffset + chunkSize > buffer.length) {
35596
+ break;
35597
+ }
35598
+ if (chunkType === "VP8X" && chunkSize >= 10) {
35599
+ return {
35600
+ width: readUnsigned24LE(buffer, payloadOffset + 4) + 1,
35601
+ height: readUnsigned24LE(buffer, payloadOffset + 7) + 1
35602
+ };
35603
+ }
35604
+ if (chunkType === "VP8L" && chunkSize >= 5 && buffer[payloadOffset] === 47) {
35605
+ const byte1 = buffer.readUInt8(payloadOffset + 1);
35606
+ const byte2 = buffer.readUInt8(payloadOffset + 2);
35607
+ const byte3 = buffer.readUInt8(payloadOffset + 3);
35608
+ const byte4 = buffer.readUInt8(payloadOffset + 4);
35609
+ return {
35610
+ width: 1 + byte1 + ((byte2 & 63) << 8),
35611
+ height: 1 + ((byte2 & 192) >> 6) + (byte3 << 2) + ((byte4 & 15) << 10)
35612
+ };
35613
+ }
35614
+ if (chunkType === "VP8 " && chunkSize >= 10 && buffer[payloadOffset + 3] === 157 && buffer[payloadOffset + 4] === 1 && buffer[payloadOffset + 5] === 42) {
35615
+ return {
35616
+ width: buffer.readUInt16LE(payloadOffset + 6) & 16383,
35617
+ height: buffer.readUInt16LE(payloadOffset + 8) & 16383
35618
+ };
35619
+ }
35620
+ offset = payloadOffset + chunkSize + chunkSize % 2;
35621
+ }
35622
+ return void 0;
35623
+ }
35624
+ function readImageDimensions(buffer) {
35625
+ return readPngDimensions(buffer) ?? readJpegDimensions(buffer) ?? readWebpDimensions(buffer) ?? readGifDimensions(buffer);
35626
+ }
35627
+ function formatDimensionsAsRatio({ width, height }) {
35628
+ const divisor = greatestCommonDivisor(width, height);
35629
+ return `${width / divisor}:${height / divisor}`;
35630
+ }
35631
+ function greatestCommonDivisor(left, right) {
35632
+ let a = Math.abs(left);
35633
+ let b = Math.abs(right);
35634
+ while (b !== 0) {
35635
+ const remainder = a % b;
35636
+ a = b;
35637
+ b = remainder;
35638
+ }
35639
+ return a || 1;
35640
+ }
35641
+ function hasMatchingAspectRatio(actual, expected) {
35642
+ const actualRatio = actual.width / actual.height;
35643
+ const expectedRatio = expected.width / expected.height;
35644
+ return Math.abs(actualRatio - expectedRatio) / expectedRatio <= FRAME_ASPECT_RATIO_TOLERANCE;
35645
+ }
35646
+ async function fetchImageDimensions(optionName, imageUrl) {
35647
+ let url;
35648
+ try {
35649
+ url = new URL(imageUrl);
35650
+ } catch {
35651
+ throw new Error(`${optionName} must be an absolute URL`);
35652
+ }
35653
+ const response = await fetch(url);
35654
+ if (!response.ok) {
35655
+ throw new Error(
35656
+ `Could not validate ${optionName}: failed to fetch image (HTTP ${response.status})`
35657
+ );
35658
+ }
35659
+ const buffer = Buffer.from(await response.arrayBuffer());
35660
+ const dimensions = readImageDimensions(buffer);
35661
+ if (!dimensions) {
35662
+ throw new Error(
35663
+ `Could not validate ${optionName}: unsupported image format or missing dimensions`
35664
+ );
35665
+ }
35666
+ return dimensions;
35667
+ }
35668
+ async function validateFrameImageAspectRatio(optionName, imageUrl, aspectRatio) {
35669
+ if (!imageUrl) {
35670
+ return;
35671
+ }
35672
+ const expected = parseAspectRatio(aspectRatio);
35673
+ const actual = await fetchImageDimensions(optionName, imageUrl);
35674
+ if (hasMatchingAspectRatio(actual, expected)) {
35675
+ return;
35676
+ }
35677
+ throw new Error(
35678
+ `${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.`
35679
+ );
35680
+ }
35681
+ async function validateVideoOptions(options) {
35682
+ await Promise.all([
35683
+ validateFrameImageAspectRatio(
35684
+ "--first-frame-image-url",
35685
+ options.firstFrameImageUrl,
35686
+ options.aspectRatio
35687
+ ),
35688
+ validateFrameImageAspectRatio(
35689
+ "--last-frame-image-url",
35690
+ options.lastFrameImageUrl,
35691
+ options.aspectRatio
35692
+ )
35693
+ ]);
35694
+ }
34873
35695
  function readPrompt3(options, usageCommand) {
34874
35696
  if (options.prompt?.trim()) {
34875
35697
  return options.prompt.trim();
@@ -34941,6 +35763,7 @@ Models:
34941
35763
  ).action(
34942
35764
  withErrorHandler(async (options) => {
34943
35765
  const prompt = readPrompt3(options, config.usageCommand);
35766
+ await validateVideoOptions(options);
34944
35767
  const result = await generateWebVideo({
34945
35768
  prompt,
34946
35769
  model: options.model,
@@ -36066,7 +36889,7 @@ function formatBytes(bytes) {
36066
36889
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
36067
36890
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
36068
36891
  }
36069
- 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(
36892
+ 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
36893
  "--template <template>",
36071
36894
  "Template: auto, launch, or profile",
36072
36895
  parseTemplate,
@@ -36089,15 +36912,42 @@ Examples:
36089
36912
  Pipe prompt: cat brief.txt | zero built-in generate website
36090
36913
 
36091
36914
  Output:
36092
- Builds a React template into a static website, publishes it with zero host, and prints the hosted URL
36915
+ Generates and publishes a hosted website. With openDesignGenerate enabled, prints an Open Design registry-selection packet for the current agent instead.
36093
36916
 
36094
36917
  Notes:
36095
- - Authenticates via ZERO_TOKEN (requires host:write capability)
36096
- - Charges org credits for model-generated website content
36097
- - Uses OpenAI gpt-5.5 for website content and fal.ai for generated visuals`
36918
+ - Authenticates via ZERO_TOKEN
36919
+ - Default path charges org credits for model-generated website content
36920
+ - OpenDesign path is gated by the openDesignGenerate feature switch`
36098
36921
  ).action(
36099
36922
  withErrorHandler(async (options) => {
36100
36923
  const prompt = readPrompt4(options);
36924
+ if (zeroTokenAllowsFeatureSwitch("openDesignGenerate" /* OpenDesignGenerate */)) {
36925
+ const packet = createHtmlArtifactAuthoringPacket({
36926
+ kind: "website",
36927
+ prompt,
36928
+ slugSource: options.title,
36929
+ site: options.site,
36930
+ details: [
36931
+ `Template direction: ${options.template}`,
36932
+ `Suggested generated visual count: ${options.images}`,
36933
+ `Image model preference if visuals are generated separately: ${options.imageModel ?? "default"}`,
36934
+ `Requested title/site name: ${options.title ?? "not specified"}`,
36935
+ `Audience: ${options.audience ?? "not specified"}`
36936
+ ],
36937
+ artifactRules: [
36938
+ "Build the usable website as the first screen; do not output a landing-page plan.",
36939
+ "If it is a marketing site, make the product or offer visible in the first viewport.",
36940
+ "For app or tool surfaces, prioritize dense, scannable, task-focused UI over decorative sections.",
36941
+ "Use responsive HTML/CSS and verify the page works at mobile and desktop widths."
36942
+ ]
36943
+ });
36944
+ if (options.json) {
36945
+ console.log(JSON.stringify(packet));
36946
+ return;
36947
+ }
36948
+ console.log(packet.instructions);
36949
+ return;
36950
+ }
36101
36951
  if (!options.json) {
36102
36952
  console.log(source_default.dim("Generating website content..."));
36103
36953
  }
@@ -36974,7 +37824,7 @@ function getModelSwitchGuidance(integration = getCurrentIntegration()) {
36974
37824
  }
36975
37825
  return "Open https://app.vm0.ai and switch models from the model selector next to the input box.";
36976
37826
  }
36977
- var listCommand13 = new Command().name("list").alias("ls").description("List models allowed by the current organization").action(
37827
+ var listCommand14 = new Command().name("list").alias("ls").description("List models allowed by the current organization").action(
36978
37828
  withErrorHandler(async () => {
36979
37829
  const result = await listZeroModelPolicies();
36980
37830
  if (result.policies.length === 0) {
@@ -37010,7 +37860,7 @@ var listCommand13 = new Command().name("list").alias("ls").description("List mod
37010
37860
  var switchCommand = new Command().name("switch").description("Show how to switch models in the current environment").action(() => {
37011
37861
  console.log(getModelSwitchGuidance());
37012
37862
  });
37013
- var zeroModelCommand = new Command().name("model").description("List available models and model-switching guidance").addCommand(listCommand13).addCommand(switchCommand);
37863
+ var zeroModelCommand = new Command().name("model").description("List available models and model-switching guidance").addCommand(listCommand14).addCommand(switchCommand);
37014
37864
 
37015
37865
  // src/commands/zero/model-provider/index.ts
37016
37866
  init_esm_shims();
@@ -37021,7 +37871,7 @@ var MODEL_PROVIDER_SET_GUIDANCE = [
37021
37871
  "",
37022
37872
  "If an organization admin sets a model provider to subscription, members must use the bottom-left user menu, choose Preferences / Personal Models, and connect their personal subscription."
37023
37873
  ].join("\n");
37024
- var listCommand14 = new Command().name("list").alias("ls").description(
37874
+ var listCommand15 = new Command().name("list").alias("ls").description(
37025
37875
  "List provider routing for each model allowed by the organization"
37026
37876
  ).action(
37027
37877
  withErrorHandler(async () => {
@@ -37056,7 +37906,7 @@ var setCommand6 = new Command().name("set").description("Show where to adjust mo
37056
37906
  ${MODEL_PROVIDER_SET_GUIDANCE}`).action(() => {
37057
37907
  console.log(MODEL_PROVIDER_SET_GUIDANCE);
37058
37908
  });
37059
- var zeroModelProviderCommand = new Command().name("model-provider").description("Inspect model provider routing").addCommand(listCommand14).addCommand(setCommand6);
37909
+ var zeroModelProviderCommand = new Command().name("model-provider").description("Inspect model provider routing").addCommand(listCommand15).addCommand(setCommand6);
37060
37910
 
37061
37911
  // src/zero.ts
37062
37912
  var COMMAND_CAPABILITY_MAP = {
@@ -37177,7 +38027,7 @@ function registerZeroCommands(prog, commands) {
37177
38027
  var program = new Command();
37178
38028
  program.name("zero").description(
37179
38029
  "Zero CLI \u2014 interact with the zero platform from inside the sandbox"
37180
- ).version("9.161.7").addHelpText("after", () => {
38030
+ ).version("9.161.9").addHelpText("after", () => {
37181
38031
  return buildZeroHelpText();
37182
38032
  });
37183
38033
  if (process.argv[1]?.endsWith("zero.js") || process.argv[1]?.endsWith("zero.ts") || process.argv[1]?.endsWith("zero")) {