@zeroxyz/cli 0.0.13 → 0.0.15

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/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/app.ts
4
- import { Command as Command8 } from "commander";
4
+ import { Command as Command9 } from "commander";
5
5
 
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@zeroxyz/cli",
9
- version: "0.0.13",
9
+ version: "0.0.15",
10
10
  type: "module",
11
11
  bin: {
12
12
  zero: "dist/index.js",
@@ -22,6 +22,7 @@ var package_default = {
22
22
  },
23
23
  scripts: {
24
24
  build: "tsup src/index.ts --format esm --out-dir dist --clean",
25
+ "build:binary": "tsup --config tsup.binary.ts && cp -r skills hooks dist/pkg/ && pkg dist/pkg/index.cjs --config pkg.json --targets node24-macos-arm64,node24-macos-x64,node24-linux-x64 --output dist/bin/zero",
25
26
  prepublishOnly: "pnpm run build",
26
27
  dev: "tsx src/index.ts",
27
28
  cli: "ZERO_API_URL=http://localhost:1111 tsx src/index.ts",
@@ -49,6 +50,7 @@ var package_default = {
49
50
  "@hono/node-server": "^1.19.13",
50
51
  "@types/node": "^25.0.7",
51
52
  "@x402/hono": "^2.9.0",
53
+ "@yao-pkg/pkg": "^6.15.0",
52
54
  hono: "^4.12.12",
53
55
  tsup: "^8.5.1",
54
56
  tsx: "^4.21.0",
@@ -128,7 +130,7 @@ var detectPaymentRequirement = (headers, status) => {
128
130
  }
129
131
  return { protocol: "unknown", raw: {} };
130
132
  };
131
- var fetchCommand = (appContext2) => new Command2("fetch").description("Fetch a capability URL with automatic payment handling").argument("<url>", "URL to fetch").option("-d, --data <body>", "Request body (JSON string)").option("-H, --header <header...>", "Headers in Key:Value format").option("--max-pay <amount>", "Maximum amount willing to pay (USDC)").action(
133
+ var fetchCommand = (appContext) => new Command2("fetch").description("Fetch a capability URL with automatic payment handling").argument("<url>", "URL to fetch").option("-d, --data <body>", "Request body (JSON string)").option("-H, --header <header...>", "Headers in Key:Value format").option("--max-pay <amount>", "Maximum amount willing to pay (USDC)").action(
132
134
  async (url, options) => {
133
135
  try {
134
136
  const {
@@ -137,7 +139,7 @@ var fetchCommand = (appContext2) => new Command2("fetch").description("Fetch a c
137
139
  paymentService,
138
140
  stateService,
139
141
  walletService
140
- } = appContext2.services;
142
+ } = appContext.services;
141
143
  const startTime = Date.now();
142
144
  const headers = {};
143
145
  if (options.header) {
@@ -148,7 +150,10 @@ var fetchCommand = (appContext2) => new Command2("fetch").description("Fetch a c
148
150
  }
149
151
  }
150
152
  }
151
- if (options.data && !headers["content-type"]) {
153
+ const hasContentType = Object.keys(headers).some(
154
+ (k) => k.toLowerCase() === "content-type"
155
+ );
156
+ if (options.data && !hasContentType) {
152
157
  headers["content-type"] = "application/json";
153
158
  }
154
159
  const log = (msg) => console.error(` ${msg}`);
@@ -267,14 +272,14 @@ var fetchCommand = (appContext2) => new Command2("fetch").description("Fetch a c
267
272
 
268
273
  // src/commands/get-command.ts
269
274
  import { Command as Command3 } from "commander";
270
- var getCommand = (appContext2) => new Command3("get").description(
275
+ var getCommand = (appContext) => new Command3("get").description(
271
276
  "Get details for a capability by position from last search, or by slug"
272
277
  ).argument(
273
278
  "<identifier>",
274
279
  "Position number from search results, or a capability slug"
275
280
  ).action(async (identifier) => {
276
281
  try {
277
- const { analyticsService, apiService, stateService } = appContext2.services;
282
+ const { analyticsService, apiService, stateService } = appContext.services;
278
283
  const position = Number.parseInt(identifier, 10);
279
284
  const isPosition = !Number.isNaN(position) && position >= 1;
280
285
  let capabilityId;
@@ -339,7 +344,12 @@ var AGENT_TOOLS = [
339
344
  { name: "Cursor", configDir: ".cursor" }
340
345
  ];
341
346
  var getPackageRoot = () => {
342
- let dir = dirname(fileURLToPath(import.meta.url));
347
+ let dir;
348
+ if (import.meta.url) {
349
+ dir = dirname(fileURLToPath(import.meta.url));
350
+ } else {
351
+ dir = __dirname;
352
+ }
343
353
  while (!existsSync2(join2(dir, "package.json"))) {
344
354
  const parent = dirname(dir);
345
355
  if (parent === dir) break;
@@ -417,6 +427,22 @@ var installHook = (home) => {
417
427
  } else {
418
428
  preToolUse.push(zeroHookEntry);
419
429
  }
430
+ if (!settings.sandbox || typeof settings.sandbox !== "object") {
431
+ settings.sandbox = {};
432
+ }
433
+ const sandbox = settings.sandbox;
434
+ if (!sandbox.network || typeof sandbox.network !== "object") {
435
+ sandbox.network = {};
436
+ }
437
+ const network = sandbox.network;
438
+ if (!Array.isArray(network.allowedDomains)) {
439
+ network.allowedDomains = [];
440
+ }
441
+ const allowedDomains = network.allowedDomains;
442
+ const zeroDomain = "*.zero.xyz";
443
+ if (!allowedDomains.includes(zeroDomain)) {
444
+ allowedDomains.push(zeroDomain);
445
+ }
420
446
  writeFileSync2(settingsPath, `${JSON.stringify(settings, null, 2)}
421
447
  `);
422
448
  return true;
@@ -450,7 +476,7 @@ var installSkills = (home) => {
450
476
  }
451
477
  return installed;
452
478
  };
453
- var initCommand = (appContext2) => new Command4("init").description("Initialize Zero CLI for usage").option("--force", "Overwrite existing configuration").action(async (options) => {
479
+ var initCommand = (appContext) => new Command4("init").description("Initialize Zero CLI for usage").option("--force", "Overwrite existing configuration").action(async (options) => {
454
480
  const home = homedir2();
455
481
  const zeroDir = join2(home, ".zero");
456
482
  const configPath = join2(zeroDir, "config.json");
@@ -509,7 +535,7 @@ var initCommand = (appContext2) => new Command4("init").description("Initialize
509
535
  console.error(
510
536
  'Zero is ready! Run `zero search` to find capabilities.\n\nTry:\n zero search "translate text to Spanish"\n zero search "generate an image"\n zero search "weather forecast"'
511
537
  );
512
- appContext2.services.analyticsService.capture("wallet_initialized", {
538
+ appContext.services.analyticsService.capture("wallet_initialized", {
513
539
  // biome-ignore lint/style/useNamingConvention: snake_case for analytics
514
540
  agents_detected: agentsDetected,
515
541
  // biome-ignore lint/style/useNamingConvention: snake_case for analytics
@@ -530,7 +556,7 @@ var initCommand = (appContext2) => new Command4("init").description("Initialize
530
556
 
531
557
  // src/commands/review-command.ts
532
558
  import { Command as Command5 } from "commander";
533
- var reviewCommand = (appContext2) => new Command5("review").description("Submit a review for a capability run").addHelpText(
559
+ var reviewCommand = (appContext) => new Command5("review").description("Submit a review for a capability run").addHelpText(
534
560
  "after",
535
561
  `
536
562
  Tips for a great review:
@@ -542,14 +568,50 @@ Tips for a great review:
542
568
 
543
569
  Example:
544
570
  zero review run_abc123 --success --accuracy 5 --value 4 --reliability 5 --content "Fast, accurate translation"`
545
- ).argument("<runId>", "Run ID to review").option("--success", "The capability succeeded").option("--no-success", "The capability failed").requiredOption("--accuracy <n>", "Accuracy rating (1-5)", Number.parseInt).requiredOption("--value <n>", "Value rating (1-5)", Number.parseInt).requiredOption(
571
+ ).argument(
572
+ "[runId]",
573
+ "Run ID to review (omit when using --capability to auto-resolve)"
574
+ ).option(
575
+ "--capability <id>",
576
+ "Review by capability uid or slug (auto-picks your most recent un-reviewed run)"
577
+ ).option("--success", "The capability succeeded").option("--no-success", "The capability failed").requiredOption("--accuracy <n>", "Accuracy rating (1-5)", Number.parseInt).requiredOption("--value <n>", "Value rating (1-5)", Number.parseInt).requiredOption(
546
578
  "--reliability <n>",
547
579
  "Reliability rating (1-5)",
548
580
  Number.parseInt
549
581
  ).option("--content <text>", "Optional review text").action(
550
582
  async (runId, options) => {
551
583
  try {
552
- const { analyticsService, apiService } = appContext2.services;
584
+ const { analyticsService, apiService } = appContext.services;
585
+ if (!runId) {
586
+ if (!options.capability) {
587
+ console.error(
588
+ "Provide a <runId> or --capability <slug> to resolve one."
589
+ );
590
+ process.exitCode = 1;
591
+ return;
592
+ }
593
+ const list = await apiService.listRuns({
594
+ capabilityId: options.capability,
595
+ unreviewed: true,
596
+ limit: 2
597
+ });
598
+ if (list.runs.length === 0) {
599
+ console.error(
600
+ `No un-reviewed runs found for capability "${options.capability}".`
601
+ );
602
+ process.exitCode = 1;
603
+ return;
604
+ }
605
+ if (list.runs.length > 1) {
606
+ console.error(
607
+ `Multiple un-reviewed runs for "${options.capability}". Run "zero runs --capability ${options.capability} --unreviewed" and pass the runId explicitly.`
608
+ );
609
+ process.exitCode = 1;
610
+ return;
611
+ }
612
+ runId = list.runs[0].uid;
613
+ console.log(`Resolved to run ${runId}`);
614
+ }
553
615
  for (const [field, val] of [
554
616
  ["accuracy", options.accuracy],
555
617
  ["value", options.value],
@@ -581,53 +643,140 @@ Example:
581
643
  }
582
644
  );
583
645
 
584
- // src/commands/search-command.ts
646
+ // src/commands/runs-command.ts
585
647
  import { Command as Command6 } from "commander";
648
+ var runsCommand = (appContext) => new Command6("runs").description("List your recent capability runs").addHelpText(
649
+ "after",
650
+ `
651
+ View your recent capability runs \u2014 status, latency, cost, and payment info.
652
+ A leading [\u2713] means the run already has a review; [ ] means it does not.
653
+
654
+ Examples:
655
+ zero runs # most recent runs
656
+ zero runs --unreviewed # filter to runs without a review
657
+ zero runs --capability translate-en # filter to one capability (uid or slug)`
658
+ ).option("--capability <id>", "Filter by capability uid or slug").option("--unreviewed", "Only show runs without a review").option(
659
+ "--limit <n>",
660
+ "Max rows (1-100, default 25)",
661
+ (v) => Number.parseInt(v, 10)
662
+ ).action(
663
+ async (options) => {
664
+ try {
665
+ const { apiService } = appContext.services;
666
+ const result = await apiService.listRuns({
667
+ capabilityId: options.capability,
668
+ unreviewed: options.unreviewed,
669
+ limit: options.limit
670
+ });
671
+ if (result.runs.length === 0) {
672
+ console.log("No runs found.");
673
+ return;
674
+ }
675
+ for (const r of result.runs) {
676
+ const when = r.createdAt.toISOString().replace("T", " ").slice(0, 19);
677
+ const mark = r.reviewed ? "\u2713" : " ";
678
+ const status = r.status ?? "\u2014";
679
+ const latency = r.latencyMs != null ? `${r.latencyMs}ms` : "\u2014";
680
+ console.log(
681
+ `[${mark}] ${r.uid} ${r.capabilitySlug} status=${status} ${latency} ${when}`
682
+ );
683
+ }
684
+ if (result.nextCursor) {
685
+ console.log(`
686
+ (more available \u2014 use --limit to see more)`);
687
+ }
688
+ } catch (err) {
689
+ console.error(err instanceof Error ? err.message : "Failed");
690
+ process.exitCode = 1;
691
+ }
692
+ }
693
+ );
694
+
695
+ // src/commands/search-command.ts
696
+ import { Command as Command7 } from "commander";
586
697
  var formatReviewCount = (count) => {
587
698
  if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
588
699
  return count.toString();
589
700
  };
701
+ var formatRatingBadge = (rating) => {
702
+ if (rating.state === "unrated") return "\u2014 unrated";
703
+ const successPct = `${Math.round(Number.parseFloat(rating.successRate) * 100)}%`;
704
+ const reviews = formatReviewCount(rating.reviews);
705
+ if (rating.stars) {
706
+ return `\u2605 ${rating.stars}/5 (${successPct} success, ${reviews} reviews)`;
707
+ }
708
+ return `${successPct} success \xB7 ${reviews} reviews`;
709
+ };
590
710
  var formatSearchResults = (results) => {
591
711
  if (results.length === 0) return "No capabilities found.";
592
712
  return results.map((r) => {
593
- const successPct = `${Math.round(Number.parseFloat(r.rating.successRate) * 100)}%`;
594
- const reviews = formatReviewCount(r.rating.reviews);
595
- const displayName = r.canonicalName ?? r.name;
596
- return ` ${r.position}. ${displayName} \u2014 $${r.cost.amount}/call \u2014 \u2605 ${Number.parseFloat(r.rating.score).toFixed(1)} (${successPct} success, ${reviews} reviews)
597
- "${r.description}"`;
713
+ const baseName = r.canonicalName ?? r.name;
714
+ const displayName = r.brandName ? `${r.brandName} ${baseName}` : baseName;
715
+ const displayDescription = r.whatItDoes ?? r.description;
716
+ return ` ${r.position}. ${displayName} \u2014 $${r.cost.amount}/call \u2014 ${formatRatingBadge(r.rating)}
717
+ "${displayDescription}"`;
598
718
  }).join("\n");
599
719
  };
600
- var searchCommand = (appContext2) => new Command6("search").description("Search for capabilities").argument("<query>", "Search query").action(async (query) => {
601
- try {
602
- const { analyticsService, apiService, stateService } = appContext2.services;
603
- const result = await apiService.search(query);
604
- stateService.saveLastSearch({
605
- searchId: result.searchId,
606
- capabilities: result.capabilities.map((c) => ({
607
- position: c.position,
608
- id: c.id,
609
- url: c.url
610
- }))
611
- });
612
- analyticsService.capture("search_executed", {
613
- query,
614
- resultCount: result.capabilities.length
615
- });
616
- console.log(formatSearchResults(result.capabilities));
617
- if (result.capabilities.length > 0) {
618
- console.error("\n Run `zero get {NUMBER}` to view a capability.\n");
720
+ var searchCommand = (appContext) => new Command7("search").description("Search for capabilities").argument("<query>", "Search query").option("--json", "Output raw JSON to stdout").option("--offset <n>", "Pagination offset", Number).option("--limit <n>", "Results per page", Number).option("--free", "Only show free capabilities").option("--max-cost <amount>", "Maximum cost per call").option("--min-rating <stars>", "Minimum star rating (1-5)", Number).option("--protocol <protocol>", "Payment protocol (x402 or mpp)").action(
721
+ async (query, options) => {
722
+ try {
723
+ const { analyticsService, apiService, stateService } = appContext.services;
724
+ if (options.protocol && !["x402", "mpp"].includes(options.protocol)) {
725
+ console.error(
726
+ `Invalid protocol "${options.protocol}". Must be x402 or mpp.`
727
+ );
728
+ process.exitCode = 1;
729
+ return;
730
+ }
731
+ const result = await apiService.search({
732
+ query,
733
+ offset: options.offset,
734
+ limit: options.limit,
735
+ freeOnly: options.free,
736
+ maxCost: options.maxCost,
737
+ minRating: options.minRating,
738
+ protocol: options.protocol
739
+ });
740
+ analyticsService.capture("search_executed", {
741
+ query,
742
+ resultCount: result.capabilities.length
743
+ });
744
+ if (options.json) {
745
+ console.log(JSON.stringify(result, null, 2));
746
+ return;
747
+ }
748
+ stateService.saveLastSearch({
749
+ searchId: result.searchId,
750
+ capabilities: result.capabilities.map((c) => ({
751
+ position: c.position,
752
+ id: c.id,
753
+ url: c.url
754
+ }))
755
+ });
756
+ console.log(formatSearchResults(result.capabilities));
757
+ if (result.hasMore) {
758
+ console.error(
759
+ `
760
+ Showing ${result.capabilities.length} of ${result.total} results. Use --offset ${result.offset + result.capabilities.length} to see more.`
761
+ );
762
+ }
763
+ if (result.capabilities.length > 0) {
764
+ console.error(
765
+ "\n Run `zero get {NUMBER}` to view a capability.\n"
766
+ );
767
+ }
768
+ } catch (err) {
769
+ console.error(err instanceof Error ? err.message : "Search failed");
770
+ process.exitCode = 1;
619
771
  }
620
- } catch (err) {
621
- console.error(err instanceof Error ? err.message : "Search failed");
622
- process.exitCode = 1;
623
772
  }
624
- });
773
+ );
625
774
 
626
775
  // src/commands/wallet-command.ts
627
- import { Command as Command7 } from "commander";
776
+ import { Command as Command8 } from "commander";
628
777
  import open from "open";
629
- var walletBalanceCommand = (appContext2) => new Command7("balance").description("Show wallet balance").action(async () => {
630
- const { walletService } = appContext2.services;
778
+ var walletBalanceCommand = (appContext) => new Command8("balance").description("Show wallet balance").action(async () => {
779
+ const { walletService } = appContext.services;
631
780
  const balance = await walletService.getBalance();
632
781
  if (balance === null) {
633
782
  console.error("No wallet configured. Run `zero init` first.");
@@ -641,9 +790,9 @@ var walletBalanceCommand = (appContext2) => new Command7("balance").description(
641
790
  }
642
791
  console.log(`${balance.amount} ${balance.asset}`);
643
792
  });
644
- var walletFundCommand = (appContext2) => new Command7("fund").description("Fund your wallet").argument("[amount]", "Amount to fund in USDC").option("--manual", "Show wallet address for manual transfer").action(
793
+ var walletFundCommand = (appContext) => new Command8("fund").description("Fund your wallet").argument("[amount]", "Amount to fund in USDC").option("--manual", "Show wallet address for manual transfer").action(
645
794
  async (amount, options) => {
646
- const { analyticsService, walletService } = appContext2.services;
795
+ const { analyticsService, walletService } = appContext.services;
647
796
  const address = walletService.getAddress();
648
797
  if (!address) {
649
798
  console.error("No wallet configured. Run `zero init` first.");
@@ -659,7 +808,7 @@ ${address}`);
659
808
  });
660
809
  return;
661
810
  }
662
- const url = await appContext2.services.apiService.getFundingUrl(amount);
811
+ const url = await appContext.services.apiService.getFundingUrl(amount);
663
812
  if (url) {
664
813
  await open(url);
665
814
  console.log("Opened funding page in your browser.");
@@ -678,8 +827,8 @@ ${address}`);
678
827
  }
679
828
  }
680
829
  );
681
- var walletAddressCommand = (appContext2) => new Command7("address").description("Show wallet address").action(() => {
682
- const { walletService } = appContext2.services;
830
+ var walletAddressCommand = (appContext) => new Command8("address").description("Show wallet address").action(() => {
831
+ const { walletService } = appContext.services;
683
832
  const address = walletService.getAddress();
684
833
  if (!address) {
685
834
  console.error("No wallet configured. Run `zero init` first.");
@@ -688,29 +837,30 @@ var walletAddressCommand = (appContext2) => new Command7("address").description(
688
837
  }
689
838
  console.log(address);
690
839
  });
691
- var walletCommand = (appContext2) => {
692
- const cmd = new Command7("wallet").description("Manage your wallet");
693
- cmd.addCommand(walletBalanceCommand(appContext2));
694
- cmd.addCommand(walletFundCommand(appContext2));
695
- cmd.addCommand(walletAddressCommand(appContext2));
840
+ var walletCommand = (appContext) => {
841
+ const cmd = new Command8("wallet").description("Manage your wallet");
842
+ cmd.addCommand(walletBalanceCommand(appContext));
843
+ cmd.addCommand(walletFundCommand(appContext));
844
+ cmd.addCommand(walletAddressCommand(appContext));
696
845
  return cmd;
697
846
  };
698
847
 
699
848
  // src/app.ts
700
- var createApp = (appContext2) => {
701
- const { analyticsService } = appContext2.services;
702
- const program = new Command8().name("zero").description("Zero CLI \u2014 Search engine and payment platform for AI agents").version(package_default.version, "-v, --version").exitOverride().hook("preAction", (_thisCommand, actionCommand) => {
849
+ var createApp = (appContext) => {
850
+ const { analyticsService } = appContext.services;
851
+ const program = new Command9().name("zero").description("Zero CLI \u2014 Search engine and payment platform for AI agents").version(package_default.version, "-v, --version").exitOverride().hook("preAction", (_thisCommand, actionCommand) => {
703
852
  analyticsService.capture("command_executed", {
704
853
  command: actionCommand.name()
705
854
  });
706
855
  });
707
- program.addCommand(initCommand(appContext2));
708
- program.addCommand(searchCommand(appContext2));
709
- program.addCommand(getCommand(appContext2));
710
- program.addCommand(fetchCommand(appContext2));
711
- program.addCommand(reviewCommand(appContext2));
712
- program.addCommand(walletCommand(appContext2));
713
- program.addCommand(configCommand(appContext2));
856
+ program.addCommand(initCommand(appContext));
857
+ program.addCommand(searchCommand(appContext));
858
+ program.addCommand(getCommand(appContext));
859
+ program.addCommand(fetchCommand(appContext));
860
+ program.addCommand(reviewCommand(appContext));
861
+ program.addCommand(runsCommand(appContext));
862
+ program.addCommand(walletCommand(appContext));
863
+ program.addCommand(configCommand(appContext));
714
864
  return program;
715
865
  };
716
866
 
@@ -834,16 +984,22 @@ var searchResultSchema = z2.object({
834
984
  name: z2.string(),
835
985
  canonicalName: z2.string().nullable().optional(),
836
986
  description: z2.string(),
987
+ whatItDoes: z2.string().nullable().optional(),
837
988
  url: z2.string(),
838
989
  cost: z2.object({ amount: z2.string(), asset: z2.string() }),
839
990
  rating: z2.object({
840
991
  score: z2.string(),
841
992
  successRate: z2.string(),
842
- reviews: z2.number()
993
+ reviews: z2.number(),
994
+ stars: z2.string().nullable().optional(),
995
+ state: z2.enum(["unrated", "rated"]).optional()
843
996
  })
844
997
  });
845
998
  var searchResponseSchema = z2.object({
846
999
  searchId: z2.string(),
1000
+ total: z2.number().optional().default(0),
1001
+ offset: z2.number().optional().default(0),
1002
+ hasMore: z2.boolean().optional().default(false),
847
1003
  capabilities: z2.array(searchResultSchema)
848
1004
  });
849
1005
  var capabilityResponseSchema = z2.object({
@@ -863,7 +1019,9 @@ var capabilityResponseSchema = z2.object({
863
1019
  rating: z2.object({
864
1020
  score: z2.string(),
865
1021
  successRate: z2.string(),
866
- reviews: z2.number()
1022
+ reviews: z2.number(),
1023
+ stars: z2.string().nullable().optional(),
1024
+ state: z2.enum(["unrated", "rated"]).optional()
867
1025
  }),
868
1026
  paymentMethods: z2.array(
869
1027
  z2.object({
@@ -885,6 +1043,27 @@ var createReviewResponseSchema = z2.object({
885
1043
  reviewId: z2.string(),
886
1044
  recorded: z2.boolean()
887
1045
  });
1046
+ var runListItemSchema = z2.object({
1047
+ uid: z2.string(),
1048
+ capabilityUid: z2.string(),
1049
+ capabilitySlug: z2.string(),
1050
+ capabilityName: z2.string(),
1051
+ status: z2.number().nullable(),
1052
+ latencyMs: z2.number().nullable(),
1053
+ cost: z2.object({ amount: z2.string(), asset: z2.string().nullable() }).nullable(),
1054
+ payment: z2.object({
1055
+ protocol: z2.string(),
1056
+ chain: z2.string().nullable(),
1057
+ txHash: z2.string().nullable(),
1058
+ mode: z2.string().nullable()
1059
+ }).nullable(),
1060
+ createdAt: z2.coerce.date(),
1061
+ reviewed: z2.boolean()
1062
+ });
1063
+ var listRunsResponseSchema = z2.object({
1064
+ runs: z2.array(runListItemSchema),
1065
+ nextCursor: z2.string().nullable()
1066
+ });
888
1067
  var buildCanonicalMessage = (method, path, body, timestamp, nonce) => {
889
1068
  const bodyHash = createHash2("sha256").update(body ?? "").digest("hex");
890
1069
  return `${method}:${path}:${bodyHash}:${timestamp}:${nonce}`;
@@ -933,8 +1112,8 @@ var ApiService = class {
933
1112
  }
934
1113
  return response.json();
935
1114
  };
936
- search = async (query) => {
937
- const json = await this.request("POST", "/v1/search", { query });
1115
+ search = async (options) => {
1116
+ const json = await this.request("POST", "/v1/search", options);
938
1117
  return searchResponseSchema.parse(json);
939
1118
  };
940
1119
  getCapability = async (id, searchId) => {
@@ -949,6 +1128,16 @@ var ApiService = class {
949
1128
  const json = await this.request("POST", "/v1/runs", data);
950
1129
  return createRunResponseSchema.parse(json);
951
1130
  };
1131
+ listRuns = async (params = {}) => {
1132
+ const qs = new URLSearchParams();
1133
+ if (params.capabilityId) qs.set("capabilityId", params.capabilityId);
1134
+ if (params.unreviewed) qs.set("unreviewed", "true");
1135
+ if (params.limit) qs.set("limit", String(params.limit));
1136
+ if (params.cursor) qs.set("cursor", params.cursor);
1137
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
1138
+ const json = await this.request("GET", `/v1/runs${suffix}`);
1139
+ return listRunsResponseSchema.parse(json);
1140
+ };
952
1141
  createReview = async (data) => {
953
1142
  const json = await this.request("POST", "/v1/reviews", data);
954
1143
  return createReviewResponseSchema.parse(json);
@@ -1386,20 +1575,23 @@ var createAppContext = () => {
1386
1575
  };
1387
1576
 
1388
1577
  // src/index.ts
1389
- var appContext = createAppContext();
1390
- if (!appContext) {
1391
- console.error("Failed to create app context");
1392
- process.exit(1);
1393
- }
1394
- var app = createApp(appContext);
1395
- try {
1396
- await app.parseAsync(process.argv);
1397
- } catch (err) {
1398
- const isCommanderExit = err instanceof Error && "exitCode" in err && err.exitCode === 0;
1399
- if (!isCommanderExit) {
1400
- console.error(err instanceof Error ? err.message : "Unexpected error");
1401
- process.exitCode = 1;
1578
+ var main = async () => {
1579
+ const appContext = createAppContext();
1580
+ if (!appContext) {
1581
+ console.error("Failed to create app context");
1582
+ process.exit(1);
1583
+ }
1584
+ const app = createApp(appContext);
1585
+ try {
1586
+ await app.parseAsync(process.argv);
1587
+ } catch (err) {
1588
+ const isCommanderExit = err instanceof Error && "exitCode" in err && err.exitCode === 0;
1589
+ if (!isCommanderExit) {
1590
+ console.error(err instanceof Error ? err.message : "Unexpected error");
1591
+ process.exitCode = 1;
1592
+ }
1593
+ } finally {
1594
+ await appContext.services.analyticsService.shutdown();
1402
1595
  }
1403
- } finally {
1404
- await appContext.services.analyticsService.shutdown();
1405
- }
1596
+ };
1597
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxyz/cli",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zero": "dist/index.js",
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "scripts": {
18
18
  "build": "tsup src/index.ts --format esm --out-dir dist --clean",
19
+ "build:binary": "tsup --config tsup.binary.ts && cp -r skills hooks dist/pkg/ && pkg dist/pkg/index.cjs --config pkg.json --targets node24-macos-arm64,node24-macos-x64,node24-linux-x64 --output dist/bin/zero",
19
20
  "prepublishOnly": "pnpm run build",
20
21
  "dev": "tsx src/index.ts",
21
22
  "cli": "ZERO_API_URL=http://localhost:1111 tsx src/index.ts",
@@ -43,6 +44,7 @@
43
44
  "@hono/node-server": "^1.19.13",
44
45
  "@types/node": "^25.0.7",
45
46
  "@x402/hono": "^2.9.0",
47
+ "@yao-pkg/pkg": "^6.15.0",
46
48
  "hono": "^4.12.12",
47
49
  "tsup": "^8.5.1",
48
50
  "tsx": "^4.21.0",
@@ -50,7 +50,9 @@ Starter prompts should be user-facing tasks, not command templates:
50
50
  zero search "<query>"
51
51
  zero get <position>
52
52
  zero fetch <url> [-d '<json>'] [-H "Key:Value"] [--max-pay <amount>]
53
+ zero runs [--capability <slug>] [--unreviewed]
53
54
  zero review <runId> --accuracy <1-5> --value <1-5> --reliability <1-5>
55
+ zero review --capability <slug> --success --accuracy <1-5> --value <1-5> --reliability <1-5>
54
56
  ```
55
57
 
56
58
  ### Workflow
@@ -58,7 +60,8 @@ zero review <runId> --accuracy <1-5> --value <1-5> --reliability <1-5>
58
60
  1. **Search** — `zero search "weather forecast"` finds matching capabilities. Results show name, cost, rating, and success rate.
59
61
  2. **Inspect** — `zero get 1` returns full details for result #1: URL, method, headers, body schema, examples, and pricing.
60
62
  3. **Call** — `zero fetch <url>` makes the request. If the server returns 402, payment is handled automatically (x402 and MPP protocols, including cross-chain bridging from Base to Tempo).
61
- 4. **Review** — `zero review <runId>` submits a quality review. Run IDs are printed after a successful fetch.
63
+ 4. **Review** — `zero review <runId>` submits a quality review. Run IDs are printed after a successful fetch. **Always review after a paid call** — reviews bias future search rankings, so this is how you tune future results to services that actually work for you.
64
+ 5. **Retroactive review** — if you lost a runId, run `zero runs --unreviewed` (or `zero runs --capability <slug> --unreviewed`) to find it. `zero review --capability <slug> ...` will auto-resolve to your most recent un-reviewed run for that capability.
62
65
 
63
66
  ### Request Templates
64
67
 
@@ -85,7 +88,8 @@ zero fetch https://api.example.com/expensive --max-pay 0.50
85
88
 
86
89
  - Always discover capabilities with `zero search` + `zero get` before calling; never guess endpoint URLs or schemas.
87
90
  - Use `--max-pay` before potentially expensive requests.
88
- - Review capabilities after use — reviews improve search quality for all agents.
91
+ - Review capabilities after every paid call. The review signal feeds search ranking skipping reviews means your next search is less informed.
92
+ - Before ending a multi-call task, run `zero runs --unreviewed` and review anything you missed.
89
93
 
90
94
  ## Configuration
91
95