@vm0/cli 9.150.11 → 9.152.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/zero.js CHANGED
@@ -10,11 +10,14 @@ import {
10
10
  InvalidArgumentError,
11
11
  MODEL_PROVIDER_TYPES,
12
12
  Option,
13
+ completeHostedSite,
13
14
  completePhoneFileUpload,
14
15
  completeSlackFileUpload,
15
16
  completeTelegramFileUpload,
16
17
  configureGlobalProxyFromEnv,
17
18
  connectorTypeSchema,
19
+ createLocalBrowserReadCommand,
20
+ createLocalBrowserWriteCommand,
18
21
  createSkill,
19
22
  createZeroAgent,
20
23
  createZeroRun,
@@ -51,6 +54,7 @@ import {
51
54
  getConnectorFirewall,
52
55
  getConnectorTypeForSecretName,
53
56
  getDefaultAuthMethod,
57
+ getLocalBrowserReadCommand,
54
58
  getScopeDiff,
55
59
  getSecretsForAuthMethod,
56
60
  getSelectableProviderTypes,
@@ -91,6 +95,7 @@ import {
91
95
  paginate,
92
96
  parseEvent,
93
97
  parseTime,
98
+ prepareHostedSite,
94
99
  promptConfirm,
95
100
  promptPassword,
96
101
  promptSelect,
@@ -130,7 +135,7 @@ import {
130
135
  withErrorHandler,
131
136
  zeroAgentCustomSkillNameSchema,
132
137
  zeroRemoteAgentCommand
133
- } from "./chunk-2ZSJAVJT.js";
138
+ } from "./chunk-T32CMT7F.js";
134
139
  import {
135
140
  __toESM,
136
141
  init_esm_shims
@@ -1885,8 +1890,8 @@ function formatDateTime(dateStr) {
1885
1890
  const hours = String(date.getHours()).padStart(2, "0");
1886
1891
  const minutes = String(date.getMinutes()).padStart(2, "0");
1887
1892
  const formatted = `${year}-${month}-${day} ${hours}:${minutes}`;
1888
- const relative = formatRelativeTime(dateStr);
1889
- return `${formatted} (${relative})`;
1893
+ const relative2 = formatRelativeTime(dateStr);
1894
+ return `${formatted} (${relative2})`;
1890
1895
  }
1891
1896
  function generateCronExpression(frequency, time, day) {
1892
1897
  const [hourStr, minuteStr] = time.split(":");
@@ -3281,8 +3286,8 @@ function renderTerminalRunResult(runId, runResponse) {
3281
3286
  return { succeeded: false, runId };
3282
3287
  }
3283
3288
  function sleep(ms) {
3284
- return new Promise((resolve) => {
3285
- return setTimeout(resolve, ms);
3289
+ return new Promise((resolve2) => {
3290
+ return setTimeout(resolve2, ms);
3286
3291
  });
3287
3292
  }
3288
3293
  async function pollZeroEvents(runId, options) {
@@ -4559,8 +4564,8 @@ Notes:
4559
4564
  async (options) => {
4560
4565
  let fileSize;
4561
4566
  try {
4562
- const stat = statSync(options.file);
4563
- fileSize = stat.size;
4567
+ const stat2 = statSync(options.file);
4568
+ fileSize = stat2.size;
4564
4569
  } catch {
4565
4570
  throw new Error(`File not found: ${options.file}`);
4566
4571
  }
@@ -4894,11 +4899,11 @@ Notes:
4894
4899
  async (options) => {
4895
4900
  let fileSize;
4896
4901
  try {
4897
- const stat = statSync2(options.file);
4898
- if (!stat.isFile()) {
4902
+ const stat2 = statSync2(options.file);
4903
+ if (!stat2.isFile()) {
4899
4904
  throw new Error(`Not a regular file: ${options.file}`);
4900
4905
  }
4901
- fileSize = stat.size;
4906
+ fileSize = stat2.size;
4902
4907
  } catch (error) {
4903
4908
  if (error instanceof Error && error.message.startsWith("Not ")) {
4904
4909
  throw error;
@@ -5072,11 +5077,11 @@ Output:
5072
5077
  async (options) => {
5073
5078
  let fileSize;
5074
5079
  try {
5075
- const stat = statSync3(options.file);
5076
- if (!stat.isFile()) {
5080
+ const stat2 = statSync3(options.file);
5081
+ if (!stat2.isFile()) {
5077
5082
  throw new Error(`Not a regular file: ${options.file}`);
5078
5083
  }
5079
- fileSize = stat.size;
5084
+ fileSize = stat2.size;
5080
5085
  } catch (error) {
5081
5086
  if (error instanceof Error && error.message.startsWith("Not ")) {
5082
5087
  throw error;
@@ -5378,13 +5383,13 @@ import { join as join4 } from "path";
5378
5383
  var IGNORED_NAMES = /* @__PURE__ */ new Set(["node_modules", ".git", ".DS_Store"]);
5379
5384
  function readSkillDirectory(dirPath) {
5380
5385
  const files = [];
5381
- function walk(dir, prefix) {
5386
+ function walk2(dir, prefix) {
5382
5387
  const entries = readdirSync(dir, { withFileTypes: true });
5383
5388
  for (const entry of entries) {
5384
5389
  if (entry.name.startsWith(".") || IGNORED_NAMES.has(entry.name)) continue;
5385
5390
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
5386
5391
  if (entry.isDirectory()) {
5387
- walk(join4(dir, entry.name), relPath);
5392
+ walk2(join4(dir, entry.name), relPath);
5388
5393
  } else {
5389
5394
  files.push({
5390
5395
  path: relPath,
@@ -5393,7 +5398,7 @@ function readSkillDirectory(dirPath) {
5393
5398
  }
5394
5399
  }
5395
5400
  }
5396
- walk(dirPath, "");
5401
+ walk2(dirPath, "");
5397
5402
  if (!files.some((f) => {
5398
5403
  return f.path === "SKILL.md";
5399
5404
  })) {
@@ -6459,12 +6464,12 @@ async function readClipboard() {
6459
6464
  return stdout;
6460
6465
  }
6461
6466
  async function writeClipboard(text) {
6462
- return new Promise((resolve, reject) => {
6467
+ return new Promise((resolve2, reject) => {
6463
6468
  const proc = spawn("pbcopy", { stdio: ["pipe", "ignore", "ignore"] });
6464
6469
  proc.on("error", reject);
6465
6470
  proc.on("close", (code) => {
6466
6471
  if (code === 0) {
6467
- resolve();
6472
+ resolve2();
6468
6473
  } else {
6469
6474
  reject(new Error(`pbcopy exited with code ${code}`));
6470
6475
  }
@@ -6486,13 +6491,13 @@ async function openApplication(nameOrBundleId) {
6486
6491
 
6487
6492
  // src/lib/computer-use/desktop-server.ts
6488
6493
  function readBody(req) {
6489
- return new Promise((resolve, reject) => {
6494
+ return new Promise((resolve2, reject) => {
6490
6495
  const chunks = [];
6491
6496
  req.on("data", (chunk) => {
6492
6497
  chunks.push(chunk);
6493
6498
  });
6494
6499
  req.on("end", () => {
6495
- resolve(Buffer.concat(chunks).toString());
6500
+ resolve2(Buffer.concat(chunks).toString());
6496
6501
  });
6497
6502
  req.on("error", reject);
6498
6503
  });
@@ -6532,7 +6537,7 @@ async function handleZoom(searchParams, res) {
6532
6537
  res.end(JSON.stringify(result));
6533
6538
  }
6534
6539
  function parseJsonBody(req) {
6535
- return new Promise((resolve, reject) => {
6540
+ return new Promise((resolve2, reject) => {
6536
6541
  const chunks = [];
6537
6542
  let size = 0;
6538
6543
  req.on("data", (chunk) => {
@@ -6546,7 +6551,7 @@ function parseJsonBody(req) {
6546
6551
  });
6547
6552
  req.on("end", () => {
6548
6553
  try {
6549
- resolve(JSON.parse(Buffer.concat(chunks).toString()));
6554
+ resolve2(JSON.parse(Buffer.concat(chunks).toString()));
6550
6555
  } catch {
6551
6556
  reject(new Error("Invalid JSON body"));
6552
6557
  }
@@ -6688,12 +6693,12 @@ async function handleOpenApplication(req, res) {
6688
6693
  res.end(JSON.stringify({ ok: true }));
6689
6694
  }
6690
6695
  async function getRandomPort() {
6691
- return new Promise((resolve, reject) => {
6696
+ return new Promise((resolve2, reject) => {
6692
6697
  const server = createNetServer();
6693
6698
  server.listen(0, "127.0.0.1", () => {
6694
6699
  const { port } = server.address();
6695
6700
  server.close(() => {
6696
- resolve(port);
6701
+ resolve2(port);
6697
6702
  });
6698
6703
  });
6699
6704
  server.on("error", reject);
@@ -6760,7 +6765,7 @@ async function handleRequest(token, req, res) {
6760
6765
  }
6761
6766
  }
6762
6767
  function startDesktopServer(token, port) {
6763
- return new Promise((resolve, reject) => {
6768
+ return new Promise((resolve2, reject) => {
6764
6769
  const server = createServer((req, res) => {
6765
6770
  handleRequest(token, req, res).catch(() => {
6766
6771
  if (!res.headersSent) {
@@ -6771,7 +6776,7 @@ function startDesktopServer(token, port) {
6771
6776
  });
6772
6777
  server.on("error", reject);
6773
6778
  server.listen(port, "127.0.0.1", () => {
6774
- resolve(server);
6779
+ resolve2(server);
6775
6780
  });
6776
6781
  });
6777
6782
  }
@@ -6835,13 +6840,13 @@ var hostStartCommand = new Command().name("start").description("Start the comput
6835
6840
  console.log(source_default.dim("Press ^C twice to disconnect"));
6836
6841
  console.log();
6837
6842
  let sigintCount = 0;
6838
- await new Promise((resolve) => {
6843
+ await new Promise((resolve2) => {
6839
6844
  const keepAlive = setInterval(() => {
6840
6845
  }, 6e4);
6841
6846
  const done = () => {
6842
6847
  clearInterval(keepAlive);
6843
6848
  process.removeListener("SIGINT", onSigint);
6844
- resolve();
6849
+ resolve2();
6845
6850
  };
6846
6851
  const onSigint = () => {
6847
6852
  sigintCount++;
@@ -7681,6 +7686,522 @@ Examples:
7681
7686
  Download a file: zero web download-file <file-id> -o /tmp/out.pdf`
7682
7687
  );
7683
7688
 
7689
+ // src/commands/zero/local-browser/index.ts
7690
+ init_esm_shims();
7691
+ function sleep3(ms) {
7692
+ return new Promise((resolve2) => {
7693
+ setTimeout(resolve2, ms);
7694
+ });
7695
+ }
7696
+ function parseTimeoutSeconds(value) {
7697
+ if (!value) return 30;
7698
+ const seconds = Number.parseInt(value, 10);
7699
+ if (!Number.isFinite(seconds) || seconds <= 0) {
7700
+ throw new Error("Timeout must be a positive number of seconds");
7701
+ }
7702
+ return seconds;
7703
+ }
7704
+ function parseOptionalNonNegativeInteger(value, label) {
7705
+ if (value === void 0) return void 0;
7706
+ const parsed = Number.parseInt(value, 10);
7707
+ if (!Number.isFinite(parsed) || parsed < 0) {
7708
+ throw new Error(`${label} must be a non-negative integer`);
7709
+ }
7710
+ return parsed;
7711
+ }
7712
+ function parsePositiveInteger2(value, label) {
7713
+ if (value === void 0) {
7714
+ throw new Error(`${label} is required`);
7715
+ }
7716
+ const parsed = Number.parseInt(value, 10);
7717
+ if (!Number.isFinite(parsed) || parsed <= 0) {
7718
+ throw new Error(`${label} must be a positive integer`);
7719
+ }
7720
+ return parsed;
7721
+ }
7722
+ function resultText(command) {
7723
+ if (!command.result) {
7724
+ return "";
7725
+ }
7726
+ return JSON.stringify(command.result, null, 2);
7727
+ }
7728
+ async function runReadCommand(kind, options) {
7729
+ const timeoutSeconds = parseTimeoutSeconds(options.timeout);
7730
+ const created = await createLocalBrowserReadCommand({
7731
+ kind,
7732
+ timeoutMs: timeoutSeconds * 1e3,
7733
+ ...options.tabId ? { tabId: options.tabId } : {},
7734
+ ...options.host ? { hostName: options.host } : {},
7735
+ ...options.hostId ? { hostId: options.hostId } : {}
7736
+ });
7737
+ const deadline = Date.now() + timeoutSeconds * 1e3;
7738
+ while (Date.now() <= deadline) {
7739
+ const command = await getLocalBrowserReadCommand(created.commandId);
7740
+ if (command.status === "queued" || command.status === "running") {
7741
+ if (process.stdout.isTTY) {
7742
+ process.stdout.write(".");
7743
+ }
7744
+ await sleep3(1e3);
7745
+ continue;
7746
+ }
7747
+ if (process.stdout.isTTY) {
7748
+ process.stdout.write("\n");
7749
+ }
7750
+ if (command.status === "failed") {
7751
+ throw new Error(
7752
+ command.error ? `${command.error.code}: ${command.error.message}` : "Local-browser command failed"
7753
+ );
7754
+ }
7755
+ const text = resultText(command);
7756
+ if (text) {
7757
+ console.log(text);
7758
+ }
7759
+ return;
7760
+ }
7761
+ throw new Error(`Local-browser command timed out: ${created.commandId}`);
7762
+ }
7763
+ async function waitForCommand(commandId, timeoutSeconds) {
7764
+ const deadline = Date.now() + timeoutSeconds * 1e3;
7765
+ while (Date.now() <= deadline) {
7766
+ const command = await getLocalBrowserReadCommand(commandId);
7767
+ if (command.status === "pending_approval" || command.status === "queued" || command.status === "running") {
7768
+ if (process.stdout.isTTY) {
7769
+ process.stdout.write(".");
7770
+ }
7771
+ await sleep3(1e3);
7772
+ continue;
7773
+ }
7774
+ if (process.stdout.isTTY) {
7775
+ process.stdout.write("\n");
7776
+ }
7777
+ if (command.status === "failed") {
7778
+ throw new Error(
7779
+ command.error ? `${command.error.code}: ${command.error.message}` : "Local-browser command failed"
7780
+ );
7781
+ }
7782
+ const text = resultText(command);
7783
+ if (text) {
7784
+ console.log(text);
7785
+ }
7786
+ return;
7787
+ }
7788
+ throw new Error(`Local-browser command timed out: ${commandId}`);
7789
+ }
7790
+ async function runWriteCommand(kind, options, payload) {
7791
+ const timeoutSeconds = parseTimeoutSeconds(options.timeout);
7792
+ const created = await createLocalBrowserWriteCommand({
7793
+ kind,
7794
+ timeoutMs: timeoutSeconds * 1e3,
7795
+ ...options.tabId ? { tabId: options.tabId } : {},
7796
+ ...options.host ? { hostName: options.host } : {},
7797
+ ...options.hostId ? { hostId: options.hostId } : {},
7798
+ ...payload
7799
+ });
7800
+ await waitForCommand(created.commandId, timeoutSeconds);
7801
+ }
7802
+ function addReadOptions(command) {
7803
+ return command.option("--host <name>", "Run on a named local-browser host").option("--host-id <id>", "Run on a specific local-browser host id").option("--tab-id <id>", "Target a specific browser tab").option("--timeout <seconds>", "Maximum time to wait", "30");
7804
+ }
7805
+ function addWriteOptions(command, options = {}) {
7806
+ const withHostOptions = command.option("--host <name>", "Run on a named local-browser host").option("--host-id <id>", "Run on a specific local-browser host id").option("--timeout <seconds>", "Maximum time to wait", "30");
7807
+ return options.tabId === false ? withHostOptions : withHostOptions.option("--tab-id <id>", "Target a specific browser tab");
7808
+ }
7809
+ function readCommand(name, kind) {
7810
+ return addReadOptions(
7811
+ new Command().name(name).description(`Run ${kind}`).action(
7812
+ withErrorHandler(async (options) => {
7813
+ await runReadCommand(kind, options);
7814
+ })
7815
+ )
7816
+ );
7817
+ }
7818
+ var tabsCommand = new Command().name("tabs").description("Read and control browser tabs").addCommand(readCommand("list", "tabs.list")).addCommand(readCommand("current", "tabs.current")).addCommand(
7819
+ addWriteOptions(
7820
+ new Command().name("activate").description("Run tabs.activate").requiredOption("--tab-id <id>", "Tab to activate").action(
7821
+ withErrorHandler(async (options) => {
7822
+ await runWriteCommand("tabs.activate", options, {});
7823
+ })
7824
+ ),
7825
+ { tabId: false }
7826
+ )
7827
+ ).addCommand(
7828
+ addWriteOptions(
7829
+ new Command().name("open").description("Run tabs.open").requiredOption("--url <url>", "URL to open").action(
7830
+ withErrorHandler(async (options) => {
7831
+ await runWriteCommand("tabs.open", options, { url: options.url });
7832
+ })
7833
+ )
7834
+ )
7835
+ ).addCommand(
7836
+ addWriteOptions(
7837
+ new Command().name("close").description("Run tabs.close").requiredOption("--tab-id <id>", "Tab to close").action(
7838
+ withErrorHandler(async (options) => {
7839
+ await runWriteCommand("tabs.close", options, {});
7840
+ })
7841
+ ),
7842
+ { tabId: false }
7843
+ )
7844
+ );
7845
+ var pageCommand = new Command().name("page").description("Read and control the active browser page").addCommand(readCommand("snapshot", "page.snapshot")).addCommand(readCommand("screenshot", "page.screenshot")).addCommand(readCommand("selection", "page.selection")).addCommand(readCommand("metadata", "page.metadata")).addCommand(
7846
+ addWriteOptions(
7847
+ new Command().name("click").description("Run page.click").option("--selector <selector>", "CSS selector to click").option("--x <pixels>", "X coordinate to click").option("--y <pixels>", "Y coordinate to click").action(
7848
+ withErrorHandler(async (options) => {
7849
+ await runWriteCommand("page.click", options, {
7850
+ ...options.selector ? { selector: options.selector } : {},
7851
+ ...options.x !== void 0 ? { x: parseOptionalNonNegativeInteger(options.x, "x") } : {},
7852
+ ...options.y !== void 0 ? { y: parseOptionalNonNegativeInteger(options.y, "y") } : {}
7853
+ });
7854
+ })
7855
+ )
7856
+ )
7857
+ ).addCommand(
7858
+ addWriteOptions(
7859
+ new Command().name("type").description("Run page.type").requiredOption("--selector <selector>", "CSS selector to type into").requiredOption("--text <text>", "Text to type").action(
7860
+ withErrorHandler(async (options) => {
7861
+ await runWriteCommand("page.type", options, {
7862
+ selector: options.selector,
7863
+ text: options.text
7864
+ });
7865
+ })
7866
+ )
7867
+ )
7868
+ ).addCommand(
7869
+ addWriteOptions(
7870
+ new Command().name("scroll").description("Run page.scroll").option(
7871
+ "--direction <direction>",
7872
+ "Scroll direction: up or down",
7873
+ "down"
7874
+ ).option("--amount <pixels>", "Scroll amount in pixels", "600").action(
7875
+ withErrorHandler(async (options) => {
7876
+ if (options.direction !== "up" && options.direction !== "down") {
7877
+ throw new Error("direction must be up or down");
7878
+ }
7879
+ await runWriteCommand("page.scroll", options, {
7880
+ direction: options.direction,
7881
+ amount: parsePositiveInteger2(options.amount, "amount")
7882
+ });
7883
+ })
7884
+ )
7885
+ )
7886
+ ).addCommand(
7887
+ addWriteOptions(
7888
+ new Command().name("navigate").description("Run page.navigate").requiredOption("--url <url>", "URL to navigate to").action(
7889
+ withErrorHandler(async (options) => {
7890
+ await runWriteCommand("page.navigate", options, {
7891
+ url: options.url
7892
+ });
7893
+ })
7894
+ )
7895
+ )
7896
+ );
7897
+ var zeroLocalBrowserCommand = new Command().name("local-browser").description("Read authorized browser context").addHelpText(
7898
+ "after",
7899
+ `
7900
+ Examples:
7901
+ List tabs? zero local-browser tabs list
7902
+ Current tab? zero local-browser tabs current
7903
+ Click page? zero local-browser page click --selector button
7904
+ Open tab? zero local-browser tabs open --url https://example.com`
7905
+ ).addCommand(tabsCommand).addCommand(pageCommand);
7906
+
7907
+ // src/commands/zero/host/index.ts
7908
+ init_esm_shims();
7909
+ import { readFile as readFile3 } from "fs/promises";
7910
+
7911
+ // src/lib/host/static-site.ts
7912
+ init_esm_shims();
7913
+ import { createHash } from "crypto";
7914
+ import { readdir, readFile as readFile2, stat } from "fs/promises";
7915
+ import { extname as extname3, relative, resolve, sep, dirname, posix } from "path";
7916
+ var MIME_BY_EXTENSION3 = {
7917
+ ".html": "text/html; charset=utf-8",
7918
+ ".htm": "text/html; charset=utf-8",
7919
+ ".css": "text/css; charset=utf-8",
7920
+ ".js": "application/javascript; charset=utf-8",
7921
+ ".mjs": "application/javascript; charset=utf-8",
7922
+ ".json": "application/json; charset=utf-8",
7923
+ ".map": "application/json; charset=utf-8",
7924
+ ".txt": "text/plain; charset=utf-8",
7925
+ ".xml": "application/xml; charset=utf-8",
7926
+ ".svg": "image/svg+xml; charset=utf-8",
7927
+ ".png": "image/png",
7928
+ ".jpg": "image/jpeg",
7929
+ ".jpeg": "image/jpeg",
7930
+ ".gif": "image/gif",
7931
+ ".webp": "image/webp",
7932
+ ".avif": "image/avif",
7933
+ ".ico": "image/x-icon",
7934
+ ".wasm": "application/wasm",
7935
+ ".woff": "font/woff",
7936
+ ".woff2": "font/woff2",
7937
+ ".ttf": "font/ttf",
7938
+ ".otf": "font/otf",
7939
+ ".eot": "application/vnd.ms-fontobject",
7940
+ ".mp4": "video/mp4",
7941
+ ".webm": "video/webm",
7942
+ ".mp3": "audio/mpeg",
7943
+ ".wav": "audio/wav"
7944
+ };
7945
+ var HTML_REFERENCE_RE = /<(?:script|link|img|source|video|audio|embed|object)\b[^>]*\s(?:src|href|poster|data)=["']([^"']+)["'][^>]*>/giu;
7946
+ var SRCSET_RE = /\s(?:srcset)=["']([^"']+)["']/giu;
7947
+ var CSS_URL_RE = /url\(\s*["']?([^"')]+)["']?\s*\)/giu;
7948
+ var CSS_IMPORT_RE = /@import\s+(?:url\()?["']([^"']+)["']\)?/giu;
7949
+ function inferContentType3(path) {
7950
+ return MIME_BY_EXTENSION3[extname3(path).toLowerCase()] ?? "application/octet-stream";
7951
+ }
7952
+ function looksImmutable(path) {
7953
+ if (path.startsWith("/assets/")) {
7954
+ return true;
7955
+ }
7956
+ return /(?:[-.])[A-Za-z0-9_-]{8,}\.[A-Za-z0-9]+$/u.test(path);
7957
+ }
7958
+ function toSitePath(root, absolutePath) {
7959
+ const rel = relative(root, absolutePath).split(sep).join("/");
7960
+ return `/${rel}`;
7961
+ }
7962
+ function isSafeSitePath(path) {
7963
+ if (!path.startsWith("/") || path.startsWith("//")) {
7964
+ return false;
7965
+ }
7966
+ if (path.includes("\\") || path.includes("\0")) {
7967
+ return false;
7968
+ }
7969
+ const segments = path.split("/").filter(Boolean);
7970
+ return !segments.some((segment) => {
7971
+ return segment === "." || segment === "..";
7972
+ });
7973
+ }
7974
+ function isExternalReference(value) {
7975
+ return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/iu.test(value);
7976
+ }
7977
+ function stripQueryAndHash(value) {
7978
+ const hashIndex = value.indexOf("#");
7979
+ const withoutHash = hashIndex >= 0 ? value.slice(0, hashIndex) : value;
7980
+ const queryIndex = withoutHash.indexOf("?");
7981
+ return queryIndex >= 0 ? withoutHash.slice(0, queryIndex) : withoutHash;
7982
+ }
7983
+ function normalizeReference(fromPath, raw) {
7984
+ const trimmed = raw.trim();
7985
+ if (!trimmed || isExternalReference(trimmed)) {
7986
+ return null;
7987
+ }
7988
+ const stripped = stripQueryAndHash(trimmed);
7989
+ if (!stripped || stripped.endsWith("/")) {
7990
+ return null;
7991
+ }
7992
+ const resolved = stripped.startsWith("/") ? posix.normalize(stripped) : posix.normalize(posix.join(dirname(fromPath), stripped));
7993
+ const path = resolved.startsWith("/") ? resolved : `/${resolved}`;
7994
+ if (!isSafeSitePath(path)) {
7995
+ throw new Error(`Invalid asset reference in ${fromPath}: ${raw}`);
7996
+ }
7997
+ return path;
7998
+ }
7999
+ function shouldRequireHtmlReference(path) {
8000
+ return extname3(path).length > 0 || path.startsWith("/assets/");
8001
+ }
8002
+ function isHtmlExtension(ext) {
8003
+ return ext === ".html" || ext === ".htm";
8004
+ }
8005
+ function shouldValidateReferences(ext) {
8006
+ return isHtmlExtension(ext) || ext === ".css";
8007
+ }
8008
+ function collectSrcsetReferences(value) {
8009
+ return value.split(",").map((entry) => {
8010
+ return entry.trim().split(/\s+/u)[0] ?? "";
8011
+ }).filter(Boolean);
8012
+ }
8013
+ function collectHtmlReferences(text) {
8014
+ const references = [];
8015
+ for (const match of text.matchAll(HTML_REFERENCE_RE)) {
8016
+ if (match[1]) references.push(match[1]);
8017
+ }
8018
+ for (const match of text.matchAll(SRCSET_RE)) {
8019
+ if (!match[1]) continue;
8020
+ references.push(...collectSrcsetReferences(match[1]));
8021
+ }
8022
+ return references;
8023
+ }
8024
+ function collectCssReferences(text) {
8025
+ const references = [];
8026
+ for (const match of text.matchAll(CSS_URL_RE)) {
8027
+ if (match[1]) references.push(match[1]);
8028
+ }
8029
+ for (const match of text.matchAll(CSS_IMPORT_RE)) {
8030
+ if (match[1]) references.push(match[1]);
8031
+ }
8032
+ return references;
8033
+ }
8034
+ function collectReferences(ext, text) {
8035
+ return isHtmlExtension(ext) ? collectHtmlReferences(text) : collectCssReferences(text);
8036
+ }
8037
+ async function hashFile(path) {
8038
+ const bytes = await readFile2(path);
8039
+ return createHash("sha256").update(bytes).digest("hex");
8040
+ }
8041
+ async function walk(root, dir, files) {
8042
+ const entries = await readdir(dir, { withFileTypes: true });
8043
+ for (const entry of entries) {
8044
+ const fullPath = resolve(dir, entry.name);
8045
+ if (entry.isDirectory()) {
8046
+ await walk(root, fullPath, files);
8047
+ continue;
8048
+ }
8049
+ if (!entry.isFile()) {
8050
+ throw new Error(`Unsupported file type in hosted site: ${fullPath}`);
8051
+ }
8052
+ const fileStat = await stat(fullPath);
8053
+ const path = toSitePath(root, fullPath);
8054
+ if (!isSafeSitePath(path)) {
8055
+ throw new Error(`Invalid hosted-site path: ${path}`);
8056
+ }
8057
+ files.push({
8058
+ absolutePath: fullPath,
8059
+ path,
8060
+ size: fileStat.size,
8061
+ sha256: await hashFile(fullPath),
8062
+ contentType: inferContentType3(path),
8063
+ immutable: looksImmutable(path) || void 0
8064
+ });
8065
+ }
8066
+ }
8067
+ async function assertReferencesExist(files) {
8068
+ const byPath = new Map(
8069
+ files.map((file) => {
8070
+ return [file.path, file];
8071
+ })
8072
+ );
8073
+ for (const file of files) {
8074
+ const ext = extname3(file.path).toLowerCase();
8075
+ if (!shouldValidateReferences(ext)) {
8076
+ continue;
8077
+ }
8078
+ const text = await readFile2(file.absolutePath, "utf8");
8079
+ const references = collectReferences(ext, text);
8080
+ for (const reference of references) {
8081
+ const normalized = normalizeReference(file.path, reference);
8082
+ if (!normalized) {
8083
+ continue;
8084
+ }
8085
+ if (isHtmlExtension(ext) && !shouldRequireHtmlReference(normalized)) {
8086
+ continue;
8087
+ }
8088
+ if (!byPath.has(normalized)) {
8089
+ throw new Error(
8090
+ `Missing asset referenced by ${file.path}: ${reference}`
8091
+ );
8092
+ }
8093
+ }
8094
+ }
8095
+ }
8096
+ async function scanStaticSite(rootPath) {
8097
+ const root = resolve(rootPath);
8098
+ const rootStat = await stat(root);
8099
+ if (!rootStat.isDirectory()) {
8100
+ throw new Error(`Hosted site path must be a directory: ${rootPath}`);
8101
+ }
8102
+ const files = [];
8103
+ await walk(root, root, files);
8104
+ if (!files.some((file) => {
8105
+ return file.path === "/index.html";
8106
+ })) {
8107
+ throw new Error("Hosted site directory must include index.html");
8108
+ }
8109
+ await assertReferencesExist(files);
8110
+ return {
8111
+ root,
8112
+ files: files.sort((a, b) => {
8113
+ return a.path.localeCompare(b.path);
8114
+ })
8115
+ };
8116
+ }
8117
+
8118
+ // src/commands/zero/host/index.ts
8119
+ function formatBytes(bytes) {
8120
+ if (bytes < 1024) return `${bytes} B`;
8121
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
8122
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
8123
+ }
8124
+ var zeroHostCommand = new Command().name("host").description("Publish a built static site and print its public URL").argument("<dir>", "Static build directory, for example ./dist").requiredOption("--site <slug>", "Public site slug, e.g. my-product-demo").option("--spa", "Serve unknown HTML navigation paths from index.html").option("--json", "Output only the final result as JSON").addHelpText(
8125
+ "after",
8126
+ `
8127
+ Examples:
8128
+ Publish a Vite build: zero host ./dist --site my-product-demo --spa
8129
+ Machine readable: zero host ./dist --site my-product-demo --spa --json
8130
+
8131
+ Notes:
8132
+ - Authenticates via ZERO_TOKEN (requires host:write capability)
8133
+ - The directory must include index.html
8134
+ - Local HTML/CSS asset references must point at files inside the directory`
8135
+ ).action(
8136
+ withErrorHandler(async (dir, options) => {
8137
+ const scan = await scanStaticSite(dir);
8138
+ const totalSize = scan.files.reduce((sum, file) => {
8139
+ return sum + file.size;
8140
+ }, 0);
8141
+ if (!options.json) {
8142
+ console.log(source_default.dim(`Preparing ${scan.files.length} files...`));
8143
+ }
8144
+ const prepared = await prepareHostedSite({
8145
+ site: options.site,
8146
+ spaFallback: Boolean(options.spa),
8147
+ files: scan.files.map((file) => {
8148
+ return {
8149
+ path: file.path,
8150
+ size: file.size,
8151
+ sha256: file.sha256,
8152
+ contentType: file.contentType,
8153
+ immutable: file.immutable
8154
+ };
8155
+ })
8156
+ });
8157
+ const uploadByPath = new Map(
8158
+ prepared.uploads.map((upload) => {
8159
+ return [upload.path, upload.uploadUrl];
8160
+ })
8161
+ );
8162
+ for (const file of scan.files) {
8163
+ const uploadUrl = uploadByPath.get(file.path);
8164
+ if (!uploadUrl) {
8165
+ throw new Error(`Missing upload URL for ${file.path}`);
8166
+ }
8167
+ if (!options.json) {
8168
+ console.log(source_default.dim(`Uploading ${file.path}`));
8169
+ }
8170
+ const bytes = await readFile3(file.absolutePath);
8171
+ const response = await fetch(uploadUrl, {
8172
+ method: "PUT",
8173
+ headers: { "Content-Type": file.contentType },
8174
+ body: new Uint8Array(bytes)
8175
+ });
8176
+ if (!response.ok) {
8177
+ throw new Error(
8178
+ `Failed to upload ${file.path} (HTTP ${response.status})`
8179
+ );
8180
+ }
8181
+ }
8182
+ const completed = await completeHostedSite(prepared.deploymentId);
8183
+ if (options.json) {
8184
+ console.log(
8185
+ JSON.stringify({
8186
+ siteId: completed.siteId,
8187
+ deploymentId: completed.deploymentId,
8188
+ publicSlug: completed.publicSlug,
8189
+ url: completed.url,
8190
+ fileCount: scan.files.length,
8191
+ size: totalSize
8192
+ })
8193
+ );
8194
+ return;
8195
+ }
8196
+ console.log(source_default.green("\u2713 Hosted site ready"));
8197
+ console.log(source_default.dim(` Site: ${completed.publicSlug}`));
8198
+ console.log(source_default.dim(` Deployment: ${completed.deploymentId}`));
8199
+ console.log(source_default.dim(` Files: ${scan.files.length.toLocaleString()}`));
8200
+ console.log(source_default.dim(` Size: ${formatBytes(totalSize)}`));
8201
+ console.log(` URL: ${completed.url}`);
8202
+ })
8203
+ );
8204
+
7684
8205
  // src/zero.ts
7685
8206
  var COMMAND_CAPABILITY_MAP = {
7686
8207
  agent: "agent:read",
@@ -7700,7 +8221,9 @@ var COMMAND_CAPABILITY_MAP = {
7700
8221
  "computer-use": "computer-use:write",
7701
8222
  "built-in": "file:write",
7702
8223
  web: null,
7703
- "remote-agent": ["remote-agent:read", "remote-agent:write"]
8224
+ host: "host:write",
8225
+ "remote-agent": ["remote-agent:read", "remote-agent:write"],
8226
+ "local-browser": ["local-browser:read", "local-browser:write"]
7704
8227
  };
7705
8228
  var DEFAULT_COMMANDS = [
7706
8229
  zeroOrgCommand,
@@ -7724,7 +8247,9 @@ var DEFAULT_COMMANDS = [
7724
8247
  zeroComputerUseCommand,
7725
8248
  zeroBuiltInCommand,
7726
8249
  zeroWebCommand,
7727
- zeroRemoteAgentCommand
8250
+ zeroHostCommand,
8251
+ zeroRemoteAgentCommand,
8252
+ zeroLocalBrowserCommand
7728
8253
  ];
7729
8254
  function shouldHideCommand(name, payload) {
7730
8255
  if (!payload) return false;
@@ -7738,6 +8263,30 @@ function shouldHideCommand(name, payload) {
7738
8263
  }
7739
8264
  return !payload.capabilities.includes(requiredCap);
7740
8265
  }
8266
+ function buildZeroHelpText(payload = decodeZeroTokenPayload()) {
8267
+ const examples = [
8268
+ " Check a connector? zero doctor check-connector --env-name <ENV_NAME>",
8269
+ " Send a Slack message? zero slack message send --help",
8270
+ " List Telegram bots? zero telegram bot list",
8271
+ " Send Telegram? zero telegram message send --help",
8272
+ " Upload Telegram? zero telegram upload-file --help",
8273
+ " Download Telegram? zero telegram download-file --help",
8274
+ " Send AgentPhone? zero phone message --help",
8275
+ " Upload AgentPhone? zero phone upload-file --help",
8276
+ " Download AgentPhone? zero phone download-file --help",
8277
+ " Set up a schedule? zero schedule setup --help",
8278
+ " Update yourself? zero agent --help",
8279
+ " Manage custom skills? zero skill --help",
8280
+ " Generate image? zero built-in generate image --help",
8281
+ " Generate voice? zero built-in generate voice --help",
8282
+ ...shouldHideCommand("local-browser", payload) ? [] : [" Read browser context? zero local-browser --help"],
8283
+ ...shouldHideCommand("host", payload) ? [] : [" Host a static site? zero host ./dist --site my-site --spa"],
8284
+ " Check your identity? zero whoami"
8285
+ ];
8286
+ return `
8287
+ Examples:
8288
+ ${examples.join("\n")}`;
8289
+ }
7741
8290
  function registerZeroCommands(prog, commands) {
7742
8291
  const token = process.env.ZERO_TOKEN;
7743
8292
  const payload = token ? decodeZeroTokenPayload(token) : void 0;
@@ -7749,32 +8298,16 @@ function registerZeroCommands(prog, commands) {
7749
8298
  var program = new Command();
7750
8299
  program.name("zero").description(
7751
8300
  "Zero CLI \u2014 interact with the zero platform from inside the sandbox"
7752
- ).version("9.150.11").addHelpText(
7753
- "after",
7754
- `
7755
- Examples:
7756
- Check a connector? zero doctor check-connector --env-name <ENV_NAME>
7757
- Send a Slack message? zero slack message send --help
7758
- List Telegram bots? zero telegram bot list
7759
- Send Telegram? zero telegram message send --help
7760
- Upload Telegram? zero telegram upload-file --help
7761
- Download Telegram? zero telegram download-file --help
7762
- Send AgentPhone? zero phone message --help
7763
- Upload AgentPhone? zero phone upload-file --help
7764
- Download AgentPhone? zero phone download-file --help
7765
- Set up a schedule? zero schedule setup --help
7766
- Update yourself? zero agent --help
7767
- Manage custom skills? zero skill --help
7768
- Generate image? zero built-in generate image --help
7769
- Generate voice? zero built-in generate voice --help
7770
- Check your identity? zero whoami`
7771
- );
8301
+ ).version("9.152.0").addHelpText("after", () => {
8302
+ return buildZeroHelpText();
8303
+ });
7772
8304
  if (process.argv[1]?.endsWith("zero.js") || process.argv[1]?.endsWith("zero.ts") || process.argv[1]?.endsWith("zero")) {
7773
8305
  configureGlobalProxyFromEnv();
7774
8306
  registerZeroCommands(program);
7775
8307
  program.parse();
7776
8308
  }
7777
8309
  export {
8310
+ buildZeroHelpText,
7778
8311
  program,
7779
8312
  registerZeroCommands
7780
8313
  };