@zeroxyz/cli 1.13.0 → 1.14.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.
Files changed (2) hide show
  1. package/dist/index.js +104 -103
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -78576,7 +78576,7 @@ init_esm_shims();
78576
78576
 
78577
78577
  // package.json
78578
78578
  var package_default = {
78579
- version: "1.13.0"};
78579
+ version: "1.14.0"};
78580
78580
 
78581
78581
  // src/app.ts
78582
78582
  init_esm_shims();
@@ -79584,7 +79584,7 @@ init_esm_shims();
79584
79584
  // ../sdk/dist/index.js
79585
79585
  init_esm_shims();
79586
79586
 
79587
- // ../sdk/dist/chunk-T6JT5NV5.js
79587
+ // ../sdk/dist/chunk-AAAVBKT3.js
79588
79588
  init_esm_shims();
79589
79589
 
79590
79590
  // ../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/index.js
@@ -144966,7 +144966,7 @@ config(en_default2());
144966
144966
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/index.js
144967
144967
  var zod_default = external_exports3;
144968
144968
 
144969
- // ../sdk/dist/chunk-T6JT5NV5.js
144969
+ // ../sdk/dist/chunk-AAAVBKT3.js
144970
144970
  var createManagedAccount = (client, address2) => toAccount({
144971
144971
  address: address2,
144972
144972
  async signMessage({ message: message2 }) {
@@ -147884,6 +147884,10 @@ var searchResultSchema = external_exports3.object({
147884
147884
  url: external_exports3.string(),
147885
147885
  urlTemplate: external_exports3.string().nullable().optional(),
147886
147886
  cost: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }),
147887
+ // ZERO-199 — the server-resolved price. Prefer `pricing.summary` over
147888
+ // deriving a label from `cost.amount` (which flattens usage-priced caps).
147889
+ // Optional: absent on older API deploys.
147890
+ pricing: external_exports3.object({ kind: external_exports3.string(), summary: external_exports3.string() }).optional(),
147887
147891
  // Payment protocol (x402 | mpp) for this result. The server emits it; the SDK
147888
147892
  // previously stripped it, leaving an agent unable to tell which handshake to
147889
147893
  // engage from a search row. Optional/nullable so older deploys still parse
@@ -149229,12 +149233,12 @@ var formatCost = (capability) => {
149229
149233
  const median = observed.medianCents ? centsToDollars(observed.medianCents) : null;
149230
149234
  const detail = median ? `median $${median}, n=${observed.sampleCount}` : `n=${observed.sampleCount}`;
149231
149235
  lines.push(` Cost: $${min}\u2013$${max}/call (${detail})`);
149232
- } else if (capability.priceDynamic && capability.priceHint) {
149236
+ } else if (capability.priceDynamic && capability.priceHint && /\$\d/.test(capability.priceHint)) {
149233
149237
  const ceiling = observed?.maxCents ? centsToDollars(observed.maxCents) : null;
149234
149238
  lines.push(
149235
149239
  ` Cost: ${capability.priceHint} (dynamic${ceiling ? `, up to ~$${ceiling}/call` : ""})`
149236
149240
  );
149237
- } else if (capability.priceStatus === "unknown_unparsed") {
149241
+ } else if (capability.priceStatus === "unknown_unparsed" && !(Number(capability.displayCostAmount) > 0) && !paidRailAmount(capability)) {
149238
149242
  lines.push(` Cost: price unknown`);
149239
149243
  } else if (capability.priceDynamic) {
149240
149244
  lines.push(
@@ -149652,16 +149656,20 @@ var agentStandaloneMcpSchema = zod_default.looseObject({
149652
149656
  var agentStandaloneHooksSchema = zod_default.looseObject({
149653
149657
  // Config file to merge the hook declarations into (relative to $HOME).
149654
149658
  file: zod_default.string(),
149655
- // Names the config schema. The CLI's generic JSON merger handles the shared
149656
- // `{ hooks: { Event: [...] } }` shape (claude-settings, codex-hooks); other
149657
- // formats (e.g. kiro-agent) need their own writer and are skipped until one
149658
- // exists.
149659
- format: zod_default.string(),
149659
+ // Informational name of the config schema. Registration is driven by `merge`
149660
+ // alone; `format` stays in the matrix because pre-1.14 CLIs require it.
149661
+ format: zod_default.string().optional(),
149660
149662
  sessionStart: zod_default.boolean().optional(),
149661
149663
  // Domains to ensure on this host's sandbox network allowlist
149662
149664
  // (sandbox.network.allowedDomains) after merging — e.g. ["*.zero.xyz"] for
149663
149665
  // Claude. Omitted hosts get no allowlist entry.
149664
- network: zod_default.array(zod_default.string()).optional()
149666
+ network: zod_default.array(zod_default.string()).optional(),
149667
+ // The literal hook config fragment to deep-merge into `file`, with
149668
+ // ${ZERO_HOOKS_DIR} standing in for the staged ~/.zero/hooks dir. The CLI
149669
+ // derives which hook scripts to fetch+stage by scanning this fragment for
149670
+ // ${ZERO_HOOKS_DIR}/<name> references. Hosts without a fragment get no hook
149671
+ // registration.
149672
+ merge: zod_default.record(zod_default.string(), zod_default.unknown()).optional()
149665
149673
  });
149666
149674
  var agentStandaloneSchema = zod_default.looseObject({
149667
149675
  // Skill install dirs (relative to $HOME) for the standalone fallback.
@@ -149746,6 +149754,57 @@ var loadAgents = async (webBase, cliVersion) => {
149746
149754
  return { entries: filterMatrix(matrix, cliVersion) };
149747
149755
  };
149748
149756
 
149757
+ // src/util/hook-fragment.ts
149758
+ init_esm_shims();
149759
+ var ZERO_HOOKS_DIR_PLACEHOLDER = "${ZERO_HOOKS_DIR}";
149760
+ var SCRIPT_REF_PATTERN = /\$\{ZERO_HOOKS_DIR\}\/([a-z0-9][a-z0-9-]*\.sh)/g;
149761
+ var isPlainObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
149762
+ var fragmentScriptNames = (fragment) => {
149763
+ const names = /* @__PURE__ */ new Set();
149764
+ for (const match of JSON.stringify(fragment).matchAll(SCRIPT_REF_PATTERN)) {
149765
+ if (match[1]) names.add(match[1]);
149766
+ }
149767
+ return [...names];
149768
+ };
149769
+ var substituteHooksDir = (value, hooksDir) => {
149770
+ if (typeof value === "string") {
149771
+ return value.replaceAll(ZERO_HOOKS_DIR_PLACEHOLDER, hooksDir);
149772
+ }
149773
+ if (Array.isArray(value)) {
149774
+ return value.map((item) => substituteHooksDir(item, hooksDir));
149775
+ }
149776
+ if (isPlainObject3(value)) {
149777
+ return Object.fromEntries(
149778
+ Object.entries(value).map(([key, item]) => [
149779
+ key,
149780
+ substituteHooksDir(item, hooksDir)
149781
+ ])
149782
+ );
149783
+ }
149784
+ return value;
149785
+ };
149786
+ var referencesAnyScript = (element, scriptNames) => {
149787
+ const json3 = JSON.stringify(element) ?? "";
149788
+ return scriptNames.some((name) => json3.includes(name));
149789
+ };
149790
+ var mergeHookFragment = (target, fragment, scriptNames) => {
149791
+ for (const [key, incoming] of Object.entries(fragment)) {
149792
+ const existing = target[key];
149793
+ if (Array.isArray(incoming)) {
149794
+ const kept = Array.isArray(existing) ? existing.filter(
149795
+ (element) => !referencesAnyScript(element, scriptNames)
149796
+ ) : [];
149797
+ target[key] = [...kept, ...incoming];
149798
+ } else if (isPlainObject3(incoming)) {
149799
+ const base2 = isPlainObject3(existing) ? existing : {};
149800
+ target[key] = base2;
149801
+ mergeHookFragment(base2, incoming, scriptNames);
149802
+ } else {
149803
+ target[key] = incoming;
149804
+ }
149805
+ }
149806
+ };
149807
+
149749
149808
  // src/util/install-banner.ts
149750
149809
  init_esm_shims();
149751
149810
  var colorsEnabled = () => {
@@ -149913,8 +149972,6 @@ var fallbackReason = (selection, detections, results) => {
149913
149972
  }
149914
149973
  return "no plugin install succeeded";
149915
149974
  };
149916
- var PLUGIN_HOOKS_ROOT = "${CLAUDE_PLUGIN_ROOT}/hooks";
149917
- var SCRIPT_NAME_PATTERN = /\$\{CLAUDE_PLUGIN_ROOT\}\/hooks\/([a-z0-9][a-z0-9-]*\.sh)/;
149918
149975
  var fetchText = async (url4) => {
149919
149976
  const res = await fetch(url4);
149920
149977
  if (!res.ok) {
@@ -149922,15 +149979,6 @@ var fetchText = async (url4) => {
149922
149979
  }
149923
149980
  return await res.text();
149924
149981
  };
149925
- var entryScriptNames = (entry) => {
149926
- const names = [];
149927
- for (const hook of entry.hooks ?? []) {
149928
- if (typeof hook.command !== "string") continue;
149929
- const match = hook.command.match(SCRIPT_NAME_PATTERN);
149930
- if (match?.[1]) names.push(match[1]);
149931
- }
149932
- return names;
149933
- };
149934
149982
  var expandHome = (path3, home) => {
149935
149983
  if (path3 === "~") return home;
149936
149984
  if (path3.startsWith("~/")) return join(home, path3.slice(2));
@@ -149954,20 +150002,11 @@ var installSkill = (skillsDirs, skillMd, verbose) => {
149954
150002
  }
149955
150003
  return installed;
149956
150004
  };
149957
- var installHookScripts = async (home, hooksJson, webBase, verbose) => {
149958
- const names = /* @__PURE__ */ new Set();
149959
- for (const entries of Object.values(hooksJson.hooks)) {
149960
- for (const entry of entries ?? []) {
149961
- for (const name of entryScriptNames(entry)) names.add(name);
149962
- }
149963
- }
149964
- if (names.size === 0) {
149965
- throw new Error("hooks.json declared no recognizable hook scripts");
149966
- }
150005
+ var installHookScripts = async (home, names, webBase, verbose) => {
149967
150006
  const hooksDir = join(home, ".zero", "hooks");
149968
150007
  mkdirSync(hooksDir, { recursive: true });
149969
150008
  return await Promise.all(
149970
- [...names].map(async (name) => {
150009
+ names.map(async (name) => {
149971
150010
  const body = await fetchText(`${webBase}/hooks/${name}`);
149972
150011
  const dest = join(hooksDir, name);
149973
150012
  writeFileSync(dest, body);
@@ -149998,53 +150037,17 @@ var ensureSandboxDomains = (settings, domains, verbose) => {
149998
150037
  }
149999
150038
  }
150000
150039
  };
150001
- var registerHooksInJsonFile = (home, hooksJson, target, verbose) => {
150040
+ var registerHooksInJsonFile = (home, target, verbose) => {
150002
150041
  const hooksDir = join(home, ".zero", "hooks");
150003
150042
  const configPath = join(home, target.relativePath);
150043
+ const scriptNames = fragmentScriptNames(target.fragment);
150044
+ const fragment = substituteHooksDir(target.fragment, hooksDir);
150004
150045
  const result = updateJsonConfigFile(
150005
150046
  configPath,
150006
150047
  (config2) => {
150007
- if (!config2.hooks || typeof config2.hooks !== "object") {
150008
- config2.hooks = {};
150009
- }
150010
- const hooks = config2.hooks;
150011
- for (const [event, entries] of Object.entries(hooksJson.hooks)) {
150012
- for (const entry of entries ?? []) {
150013
- const scripts = entryScriptNames(entry);
150014
- if (scripts.length === 0) {
150015
- if (verbose) {
150016
- stepInfo(`${event}: entry has no plugin hook script \u2014 skipped`);
150017
- }
150018
- continue;
150019
- }
150020
- const rewritten = {
150021
- ...entry,
150022
- hooks: (entry.hooks ?? []).map((hook) => ({
150023
- ...hook,
150024
- command: typeof hook.command === "string" ? hook.command.replaceAll(PLUGIN_HOOKS_ROOT, hooksDir) : hook.command
150025
- }))
150026
- };
150027
- if (!Array.isArray(hooks[event])) hooks[event] = [];
150028
- const list2 = hooks[event];
150029
- const existingIdx = list2.findIndex((existing) => {
150030
- const existingHooks = existing?.hooks;
150031
- if (!Array.isArray(existingHooks)) return false;
150032
- return existingHooks.some(
150033
- (h2) => typeof h2.command === "string" && scripts.some((s) => h2.command.includes(s))
150034
- );
150035
- });
150036
- if (existingIdx >= 0) {
150037
- list2[existingIdx] = rewritten;
150038
- if (verbose) {
150039
- stepInfo(`${event}: ${scripts.join(", ")} entry replaced`);
150040
- }
150041
- } else {
150042
- list2.push(rewritten);
150043
- if (verbose) {
150044
- stepInfo(`${event}: ${scripts.join(", ")} entry appended`);
150045
- }
150046
- }
150047
- }
150048
+ mergeHookFragment(config2, fragment, scriptNames);
150049
+ if (verbose) {
150050
+ stepInfo(`merged hook fragment (${scriptNames.join(", ")})`);
150048
150051
  }
150049
150052
  ensureSandboxDomains(config2, target.network, verbose);
150050
150053
  return true;
@@ -150060,18 +150063,21 @@ var registerHooksInJsonFile = (home, hooksJson, target, verbose) => {
150060
150063
  }
150061
150064
  return true;
150062
150065
  };
150063
- var JSON_HOOK_FORMATS = /* @__PURE__ */ new Set(["claude-settings", "codex-hooks"]);
150064
150066
  var buildHookTargets = (entries) => {
150065
150067
  const byFile = /* @__PURE__ */ new Map();
150066
150068
  for (const entry of entries) {
150067
150069
  const hooks = entry.standalone?.hooks;
150068
- if (!hooks || !JSON_HOOK_FORMATS.has(hooks.format)) continue;
150070
+ if (!hooks?.merge || Object.keys(hooks.merge).length === 0) continue;
150069
150071
  const existing = byFile.get(hooks.file);
150070
150072
  const network = unique([
150071
150073
  ...existing?.network ?? [],
150072
150074
  ...hooks.network ?? []
150073
150075
  ]);
150074
- byFile.set(hooks.file, { relativePath: hooks.file, network });
150076
+ byFile.set(hooks.file, {
150077
+ relativePath: hooks.file,
150078
+ fragment: existing?.fragment ?? hooks.merge,
150079
+ network
150080
+ });
150075
150081
  }
150076
150082
  return [...byFile.values()];
150077
150083
  };
@@ -150085,21 +150091,18 @@ var runStandaloneInit = async (appContext, options = {}) => {
150085
150091
  console.log("");
150086
150092
  const home = homedir();
150087
150093
  const webBase = appContext.env.ZERO_WEB_URL;
150088
- const [skillMd, hooksJsonRaw] = await Promise.all([
150089
- fetchText(`${webBase}/SKILL.md`),
150090
- fetchText(`${webBase}/hooks.json`)
150091
- ]);
150092
- let hooksJson;
150093
- try {
150094
- hooksJson = JSON.parse(hooksJsonRaw);
150095
- } catch {
150096
- throw new Error(`${webBase}/hooks.json is not valid JSON`);
150097
- }
150098
- if (!hooksJson?.hooks || typeof hooksJson.hooks !== "object") {
150099
- throw new Error(`${webBase}/hooks.json has no "hooks" object`);
150094
+ const hookTargets = options.hookTargets ?? [];
150095
+ const hookScriptNames = unique(
150096
+ hookTargets.flatMap((target) => fragmentScriptNames(target.fragment))
150097
+ );
150098
+ if (hookScriptNames.length === 0) {
150099
+ throw new Error(
150100
+ "the agent matrix declared no standalone hook scripts to install"
150101
+ );
150100
150102
  }
150103
+ const skillMd = await fetchText(`${webBase}/SKILL.md`);
150101
150104
  if (verbose) {
150102
- stepInfo(`fetched ${webBase}/SKILL.md and ${webBase}/hooks.json`);
150105
+ stepInfo(`fetched ${webBase}/SKILL.md`);
150103
150106
  }
150104
150107
  options.onStep?.("skill");
150105
150108
  const skillsDirs = unique([
@@ -150114,20 +150117,15 @@ var runStandaloneInit = async (appContext, options = {}) => {
150114
150117
  options.onStep?.("hooks");
150115
150118
  const hookScriptsInstalled = await installHookScripts(
150116
150119
  home,
150117
- hooksJson,
150120
+ hookScriptNames,
150118
150121
  webBase,
150119
150122
  verbose
150120
150123
  );
150121
150124
  stepSuccess("Hook scripts installed", "~/.zero/hooks");
150122
150125
  options.onStep?.("settings");
150123
150126
  const registeredFiles = /* @__PURE__ */ new Set();
150124
- for (const target of options.hookTargets ?? []) {
150125
- const registered = registerHooksInJsonFile(
150126
- home,
150127
- hooksJson,
150128
- target,
150129
- verbose
150130
- );
150127
+ for (const target of hookTargets) {
150128
+ const registered = registerHooksInJsonFile(home, target, verbose);
150131
150129
  if (registered) {
150132
150130
  registeredFiles.add(target.relativePath);
150133
150131
  stepSuccess("Hooks registered", `~/${target.relativePath}`);
@@ -150281,8 +150279,11 @@ var runInit = async (appContext, options = {}) => {
150281
150279
  if (dryRun) {
150282
150280
  stepInfo("would install/update ~/.claude/skills/zero");
150283
150281
  stepInfo("would install/update ~/.agents/skills/zero");
150282
+ const targetFiles = buildHookTargets(entries).map(
150283
+ (target) => `~/${target.relativePath}`
150284
+ );
150284
150285
  stepInfo(
150285
- "would register current standalone hooks in ~/.claude/settings.json and ~/.codex/hooks.json"
150286
+ `would register current standalone hooks in ${targetFiles.join(" and ")}`
150286
150287
  );
150287
150288
  } else {
150288
150289
  standaloneResult = await runStandaloneInit(appContext, {
@@ -150861,7 +150862,7 @@ var formatSearchResults = (results) => {
150861
150862
  const displayDescription = r2.whatItDoes ?? r2.description;
150862
150863
  const ratingBadge = formatRatingBadge(r2.rating);
150863
150864
  const statusBadge = formatStatusBadge(r2.availabilityStatus);
150864
- const baseCost = r2.cost.amount === "0" ? "Free" : r2.cost.amount === "unknown" ? "variable pricing" : `$${r2.cost.amount}/call`;
150865
+ const baseCost = r2.pricing?.summary ?? (r2.cost.amount === "0" ? "Free" : r2.cost.amount === "unknown" ? "variable pricing" : `$${r2.cost.amount}/call`);
150865
150866
  const costLabel = r2.protocol ? `${baseCost} \xB7 ${r2.protocol}` : baseCost;
150866
150867
  const tokenHint = r2.token ? ` [${r2.token}]` : "";
150867
150868
  return ` ${r2.position}.${tokenHint} ${displayName} \u2014 ${costLabel} \u2014 ${ratingBadge}${statusBadge}
@@ -152648,7 +152649,7 @@ function isErrorEvent(event) {
152648
152649
  function isEvent(candidate) {
152649
152650
  return "undefined" != typeof Event && isInstanceOf(candidate, Event);
152650
152651
  }
152651
- function isPlainObject3(candidate) {
152652
+ function isPlainObject4(candidate) {
152652
152653
  return isBuiltin(candidate, "Object");
152653
152654
  }
152654
152655
  function isInstanceOf(candidate, base2) {
@@ -155845,7 +155846,7 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
155845
155846
  };
155846
155847
  const personProperties = {};
155847
155848
  const groupProperties = {};
155848
- for (const [key, value] of Object.entries(eventProperties)) if (isPlainObject3(value) && groups && key in groups) {
155849
+ for (const [key, value] of Object.entries(eventProperties)) if (isPlainObject4(value) && groups && key in groups) {
155849
155850
  const groupProps = {};
155850
155851
  for (const [groupKey, groupValue] of Object.entries(value)) groupProps[String(groupKey)] = String(groupValue);
155851
155852
  groupProperties[String(key)] = groupProps;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxyz/cli",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zero": "dist/index.js",