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