@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.
- package/dist/index.js +104 -103
- 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.
|
|
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-
|
|
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-
|
|
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
|
-
//
|
|
149656
|
-
// `
|
|
149657
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
150008
|
-
|
|
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 ||
|
|
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, {
|
|
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
|
|
150089
|
-
|
|
150090
|
-
|
|
150091
|
-
|
|
150092
|
-
|
|
150093
|
-
|
|
150094
|
-
|
|
150095
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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;
|