@sm-lab/recipes 0.2.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/cli.mjs ADDED
@@ -0,0 +1,1353 @@
1
+ #!/usr/bin/env node
2
+ import { a as deposit, d as addKeys, g as connect, i as getPubkey, n as topUpActiveKeys, r as getKeyBalance, t as increaseAllocatedBalance, v as resolveGate } from "./topup-B2NzJV7_.mjs";
3
+ import { C as snapshot, E as operatorInfo, S as revert, a as setGateAddrs, b as proposeManager, c as cancelPenalty, d as settlePenalty, f as slash, h as unvet, i as submitRewards, l as compensatePenalty, m as exit, o as addBond, p as withdraw, r as makeRewards, s as createBondDebt, t as clActivate, u as reportPenalty, v as confirmManager, w as warpBy, x as proposeReward, y as confirmReward } from "./cl-activate-CPqr6v_D.mjs";
4
+ import { createCuratedOperator, createOperatorGroup, resetOperatorGroup, seedCm, setBondCurveWeight } from "./cm.mjs";
5
+ import { formatEther, isAddress, isHex, parseEther } from "viem";
6
+ import "dotenv/config";
7
+ import { Argument, Command } from "commander";
8
+ import { Readable } from "node:stream";
9
+ import { readFileSync } from "node:fs";
10
+ //#endregion
11
+ //#region ../../node_modules/.pnpm/@hono+node-server@2.0.6_hono@4.12.27/node_modules/@hono/node-server/dist/index.mjs
12
+ const GlobalRequest = global.Request;
13
+ var Request$1 = class extends GlobalRequest {
14
+ constructor(input, options) {
15
+ if (typeof input === "object" && getRequestCache in input) {
16
+ const hasReplacementBody = options !== void 0 && "body" in options && options.body != null;
17
+ if (input[bodyConsumedDirectlyKey] && !hasReplacementBody) throw new TypeError("Cannot construct a Request with a Request object that has already been used.");
18
+ input = input[getRequestCache]();
19
+ }
20
+ if (typeof (options?.body)?.getReader !== "undefined") options.duplex ??= "half";
21
+ super(input, options);
22
+ }
23
+ };
24
+ const newHeadersFromIncoming = (incoming) => {
25
+ const headerRecord = [];
26
+ const rawHeaders = incoming.rawHeaders;
27
+ for (let i = 0, len = rawHeaders.length; i < len; i += 2) {
28
+ const key = rawHeaders[i];
29
+ if (key.charCodeAt(0) !== 58) headerRecord.push([key, rawHeaders[i + 1]]);
30
+ }
31
+ return new Headers(headerRecord);
32
+ };
33
+ const wrapBodyStream = Symbol("wrapBodyStream");
34
+ const newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
35
+ const init = {
36
+ method,
37
+ headers,
38
+ signal: abortController.signal
39
+ };
40
+ if (method === "TRACE") {
41
+ init.method = "GET";
42
+ const req = new Request$1(url, init);
43
+ Object.defineProperty(req, "method", { get() {
44
+ return "TRACE";
45
+ } });
46
+ return req;
47
+ }
48
+ if (!(method === "GET" || method === "HEAD")) if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) init.body = new ReadableStream({ start(controller) {
49
+ controller.enqueue(incoming.rawBody);
50
+ controller.close();
51
+ } });
52
+ else if (incoming[wrapBodyStream]) {
53
+ let reader;
54
+ init.body = new ReadableStream({ async pull(controller) {
55
+ try {
56
+ reader ||= Readable.toWeb(incoming).getReader();
57
+ const { done, value } = await reader.read();
58
+ if (done) controller.close();
59
+ else controller.enqueue(value);
60
+ } catch (error) {
61
+ controller.error(error);
62
+ }
63
+ } });
64
+ } else init.body = Readable.toWeb(incoming);
65
+ return new Request$1(url, init);
66
+ };
67
+ const getRequestCache = Symbol("getRequestCache");
68
+ const requestCache = Symbol("requestCache");
69
+ const incomingKey = Symbol("incomingKey");
70
+ const urlKey = Symbol("urlKey");
71
+ const methodKey = Symbol("methodKey");
72
+ const headersKey = Symbol("headersKey");
73
+ const abortControllerKey = Symbol("abortControllerKey");
74
+ const getAbortController = Symbol("getAbortController");
75
+ const abortRequest = Symbol("abortRequest");
76
+ const bodyBufferKey = Symbol("bodyBuffer");
77
+ const bodyReadPromiseKey = Symbol("bodyReadPromise");
78
+ const bodyConsumedDirectlyKey = Symbol("bodyConsumedDirectly");
79
+ const bodyLockReaderKey = Symbol("bodyLockReader");
80
+ const abortReasonKey = Symbol("abortReason");
81
+ const newBodyUnusableError = () => {
82
+ return /* @__PURE__ */ new TypeError("Body is unusable");
83
+ };
84
+ const rejectBodyUnusable = () => {
85
+ return Promise.reject(newBodyUnusableError());
86
+ };
87
+ const textDecoder = new TextDecoder();
88
+ const consumeBodyDirectOnce = (request) => {
89
+ if (request[bodyConsumedDirectlyKey]) return rejectBodyUnusable();
90
+ request[bodyConsumedDirectlyKey] = true;
91
+ };
92
+ const toArrayBuffer = (buf) => {
93
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
94
+ };
95
+ const contentType = (request) => {
96
+ return (request[headersKey] ||= newHeadersFromIncoming(request[incomingKey])).get("content-type") || "";
97
+ };
98
+ const methodTokenRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
99
+ const validateDirectReadMethod = (method) => {
100
+ if (!methodTokenRegExp.test(method)) return /* @__PURE__ */ new TypeError(`'${method}' is not a valid HTTP method.`);
101
+ const normalized = method.toUpperCase();
102
+ if (normalized === "CONNECT" || normalized === "TRACK" || normalized === "TRACE" && method !== "TRACE") return /* @__PURE__ */ new TypeError(`'${method}' HTTP method is unsupported.`);
103
+ };
104
+ const readBodyWithFastPath = (request, method, fromBuffer) => {
105
+ if (request[bodyConsumedDirectlyKey]) return rejectBodyUnusable();
106
+ const methodName = request.method;
107
+ if (methodName === "GET" || methodName === "HEAD") return request[getRequestCache]()[method]();
108
+ const methodValidationError = validateDirectReadMethod(methodName);
109
+ if (methodValidationError) return Promise.reject(methodValidationError);
110
+ if (request[requestCache]) {
111
+ if (methodName !== "TRACE") return request[requestCache][method]();
112
+ }
113
+ const alreadyUsedError = consumeBodyDirectOnce(request);
114
+ if (alreadyUsedError) return alreadyUsedError;
115
+ const raw = readRawBodyIfAvailable(request);
116
+ if (raw) {
117
+ const result = Promise.resolve(fromBuffer(raw, request));
118
+ request[bodyBufferKey] = void 0;
119
+ return result;
120
+ }
121
+ return readBodyDirect(request).then((buf) => {
122
+ const result = fromBuffer(buf, request);
123
+ request[bodyBufferKey] = void 0;
124
+ return result;
125
+ });
126
+ };
127
+ const readRawBodyIfAvailable = (request) => {
128
+ const incoming = request[incomingKey];
129
+ if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) return incoming.rawBody;
130
+ };
131
+ const readBodyDirect = (request) => {
132
+ if (request[bodyBufferKey]) return Promise.resolve(request[bodyBufferKey]);
133
+ if (request[bodyReadPromiseKey]) return request[bodyReadPromiseKey];
134
+ const incoming = request[incomingKey];
135
+ if (Readable.isDisturbed(incoming)) return rejectBodyUnusable();
136
+ const promise = new Promise((resolve, reject) => {
137
+ const chunks = [];
138
+ let settled = false;
139
+ const finish = (callback) => {
140
+ if (settled) return;
141
+ settled = true;
142
+ cleanup();
143
+ callback();
144
+ };
145
+ const onData = (chunk) => {
146
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
147
+ };
148
+ const onEnd = () => {
149
+ finish(() => {
150
+ const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
151
+ request[bodyBufferKey] = buffer;
152
+ resolve(buffer);
153
+ });
154
+ };
155
+ const onError = (error) => {
156
+ finish(() => {
157
+ reject(error);
158
+ });
159
+ };
160
+ const onClose = () => {
161
+ if (incoming.readableEnded) {
162
+ onEnd();
163
+ return;
164
+ }
165
+ finish(() => {
166
+ if (incoming.errored) {
167
+ reject(incoming.errored);
168
+ return;
169
+ }
170
+ const reason = request[abortReasonKey];
171
+ if (reason !== void 0) {
172
+ reject(reason instanceof Error ? reason : new Error(String(reason)));
173
+ return;
174
+ }
175
+ reject(/* @__PURE__ */ new Error("Client connection prematurely closed."));
176
+ });
177
+ };
178
+ const cleanup = () => {
179
+ incoming.off("data", onData);
180
+ incoming.off("end", onEnd);
181
+ incoming.off("error", onError);
182
+ incoming.off("close", onClose);
183
+ request[bodyReadPromiseKey] = void 0;
184
+ };
185
+ incoming.on("data", onData);
186
+ incoming.on("end", onEnd);
187
+ incoming.on("error", onError);
188
+ incoming.on("close", onClose);
189
+ queueMicrotask(() => {
190
+ if (settled) return;
191
+ if (incoming.readableEnded) onEnd();
192
+ else if (incoming.errored) onError(incoming.errored);
193
+ else if (incoming.destroyed) onClose();
194
+ });
195
+ });
196
+ request[bodyReadPromiseKey] = promise;
197
+ return promise;
198
+ };
199
+ const requestPrototype = {
200
+ get method() {
201
+ return this[methodKey];
202
+ },
203
+ get url() {
204
+ return this[urlKey];
205
+ },
206
+ get headers() {
207
+ return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
208
+ },
209
+ [abortRequest](reason) {
210
+ if (this[abortReasonKey] === void 0) this[abortReasonKey] = reason;
211
+ const abortController = this[abortControllerKey];
212
+ if (abortController && !abortController.signal.aborted) abortController.abort(reason);
213
+ },
214
+ [getAbortController]() {
215
+ this[abortControllerKey] ||= new AbortController();
216
+ if (this[abortReasonKey] !== void 0 && !this[abortControllerKey].signal.aborted) this[abortControllerKey].abort(this[abortReasonKey]);
217
+ return this[abortControllerKey];
218
+ },
219
+ [getRequestCache]() {
220
+ const abortController = this[getAbortController]();
221
+ if (this[requestCache]) return this[requestCache];
222
+ const method = this.method;
223
+ if (this[bodyConsumedDirectlyKey] && !(method === "GET" || method === "HEAD")) {
224
+ this[bodyBufferKey] = void 0;
225
+ const init = {
226
+ method: method === "TRACE" ? "GET" : method,
227
+ headers: this.headers,
228
+ signal: abortController.signal
229
+ };
230
+ if (method !== "TRACE") {
231
+ init.body = new ReadableStream({ start(c) {
232
+ c.close();
233
+ } });
234
+ init.duplex = "half";
235
+ }
236
+ const req = new Request$1(this[urlKey], init);
237
+ if (method === "TRACE") Object.defineProperty(req, "method", { get() {
238
+ return "TRACE";
239
+ } });
240
+ return this[requestCache] = req;
241
+ }
242
+ return this[requestCache] = newRequestFromIncoming(this.method, this[urlKey], this.headers, this[incomingKey], abortController);
243
+ },
244
+ get body() {
245
+ if (!this[bodyConsumedDirectlyKey]) return this[getRequestCache]().body;
246
+ const request = this[getRequestCache]();
247
+ if (!this[bodyLockReaderKey] && request.body) this[bodyLockReaderKey] = request.body.getReader();
248
+ return request.body;
249
+ },
250
+ get bodyUsed() {
251
+ if (this[bodyConsumedDirectlyKey]) return true;
252
+ if (this[requestCache]) return this[requestCache].bodyUsed;
253
+ return false;
254
+ }
255
+ };
256
+ Object.defineProperty(requestPrototype, "signal", { get() {
257
+ return this[getAbortController]().signal;
258
+ } });
259
+ [
260
+ "cache",
261
+ "credentials",
262
+ "destination",
263
+ "integrity",
264
+ "mode",
265
+ "redirect",
266
+ "referrer",
267
+ "referrerPolicy",
268
+ "keepalive"
269
+ ].forEach((k) => {
270
+ Object.defineProperty(requestPrototype, k, { get() {
271
+ return this[getRequestCache]()[k];
272
+ } });
273
+ });
274
+ ["clone", "formData"].forEach((k) => {
275
+ Object.defineProperty(requestPrototype, k, { value: function() {
276
+ if (this[bodyConsumedDirectlyKey]) {
277
+ if (k === "clone") throw newBodyUnusableError();
278
+ return rejectBodyUnusable();
279
+ }
280
+ return this[getRequestCache]()[k]();
281
+ } });
282
+ });
283
+ Object.defineProperty(requestPrototype, "text", { value: function() {
284
+ return readBodyWithFastPath(this, "text", (buf) => textDecoder.decode(buf));
285
+ } });
286
+ Object.defineProperty(requestPrototype, "arrayBuffer", { value: function() {
287
+ return readBodyWithFastPath(this, "arrayBuffer", (buf) => toArrayBuffer(buf));
288
+ } });
289
+ Object.defineProperty(requestPrototype, "blob", { value: function() {
290
+ return readBodyWithFastPath(this, "blob", (buf, request) => {
291
+ const type = contentType(request);
292
+ return new Response(buf, type ? { headers: { "content-type": type } } : void 0).blob();
293
+ });
294
+ } });
295
+ Object.defineProperty(requestPrototype, "json", { value: function() {
296
+ if (this[bodyConsumedDirectlyKey]) return rejectBodyUnusable();
297
+ return this.text().then(JSON.parse);
298
+ } });
299
+ Object.defineProperty(requestPrototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
300
+ return `Request (lightweight) ${inspectFn({
301
+ method: this.method,
302
+ url: this.url,
303
+ headers: this.headers,
304
+ nativeRequest: this[requestCache]
305
+ }, {
306
+ ...options,
307
+ depth: depth == null ? null : depth - 1
308
+ })}`;
309
+ } });
310
+ Object.setPrototypeOf(requestPrototype, Request$1.prototype);
311
+ const defaultContentType = "text/plain; charset=UTF-8";
312
+ const responseCache = Symbol("responseCache");
313
+ const getResponseCache = Symbol("getResponseCache");
314
+ const cacheKey = Symbol("cache");
315
+ const GlobalResponse = global.Response;
316
+ var Response$1 = class Response$1 {
317
+ #body;
318
+ #init;
319
+ [getResponseCache]() {
320
+ const cache = this[cacheKey];
321
+ const liveHeaders = cache && cache[2] instanceof Headers ? cache[2] : void 0;
322
+ delete this[cacheKey];
323
+ return this[responseCache] ||= new GlobalResponse(this.#body, liveHeaders ? {
324
+ status: this.#init?.status,
325
+ statusText: this.#init?.statusText,
326
+ headers: liveHeaders
327
+ } : this.#init);
328
+ }
329
+ constructor(body, init) {
330
+ let headers;
331
+ this.#body = body;
332
+ if (init instanceof Response$1) {
333
+ const cachedGlobalResponse = init[responseCache];
334
+ if (cachedGlobalResponse) {
335
+ this.#init = cachedGlobalResponse;
336
+ this[getResponseCache]();
337
+ return;
338
+ } else {
339
+ this.#init = init.#init;
340
+ headers = new Headers(init.headers);
341
+ }
342
+ } else this.#init = init;
343
+ if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
344
+ init?.status || 200,
345
+ body ?? null,
346
+ headers || init?.headers
347
+ ];
348
+ }
349
+ get headers() {
350
+ const cache = this[cacheKey];
351
+ if (cache) {
352
+ if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2] || (cache[1] === null ? void 0 : { "content-type": defaultContentType }));
353
+ return cache[2];
354
+ }
355
+ return this[getResponseCache]().headers;
356
+ }
357
+ get status() {
358
+ return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
359
+ }
360
+ get ok() {
361
+ const status = this.status;
362
+ return status >= 200 && status < 300;
363
+ }
364
+ };
365
+ [
366
+ "body",
367
+ "bodyUsed",
368
+ "redirected",
369
+ "statusText",
370
+ "trailers",
371
+ "type",
372
+ "url"
373
+ ].forEach((k) => {
374
+ Object.defineProperty(Response$1.prototype, k, { get() {
375
+ return this[getResponseCache]()[k];
376
+ } });
377
+ });
378
+ [
379
+ "arrayBuffer",
380
+ "blob",
381
+ "clone",
382
+ "formData",
383
+ "json",
384
+ "text"
385
+ ].forEach((k) => {
386
+ Object.defineProperty(Response$1.prototype, k, { value: function() {
387
+ return this[getResponseCache]()[k]();
388
+ } });
389
+ });
390
+ Object.defineProperty(Response$1.prototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
391
+ return `Response (lightweight) ${inspectFn({
392
+ status: this.status,
393
+ headers: this.headers,
394
+ ok: this.ok,
395
+ nativeResponse: this[responseCache]
396
+ }, {
397
+ ...options,
398
+ depth: depth == null ? null : depth - 1
399
+ })}`;
400
+ } });
401
+ Object.setPrototypeOf(Response$1, GlobalResponse);
402
+ Object.setPrototypeOf(Response$1.prototype, GlobalResponse.prototype);
403
+ const validRedirectUrl = /^https?:\/\/[!#-;=?-[\]_a-z~A-Z]+$/;
404
+ const parseRedirectUrl = (url) => {
405
+ if (url instanceof URL) return url.href;
406
+ if (validRedirectUrl.test(url)) return url;
407
+ return new URL(url).href;
408
+ };
409
+ const validRedirectStatuses = /* @__PURE__ */ new Set([
410
+ 301,
411
+ 302,
412
+ 303,
413
+ 307,
414
+ 308
415
+ ]);
416
+ Object.defineProperty(Response$1, "redirect", {
417
+ value: function redirect(url, status = 302) {
418
+ if (!validRedirectStatuses.has(status)) throw new RangeError("Invalid status code");
419
+ return new Response$1(null, {
420
+ status,
421
+ headers: { location: parseRedirectUrl(url) }
422
+ });
423
+ },
424
+ writable: true,
425
+ configurable: true
426
+ });
427
+ Object.defineProperty(Response$1, "json", {
428
+ value: function json(data, init) {
429
+ const body = JSON.stringify(data);
430
+ if (body === void 0) throw new TypeError("The data is not JSON serializable");
431
+ const initHeaders = init?.headers;
432
+ let headers;
433
+ if (initHeaders) {
434
+ headers = new Headers(initHeaders);
435
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
436
+ } else headers = { "content-type": "application/json" };
437
+ return new Response$1(body, {
438
+ status: init?.status ?? 200,
439
+ statusText: init?.statusText,
440
+ headers
441
+ });
442
+ },
443
+ writable: true,
444
+ configurable: true
445
+ });
446
+ globalThis.CloseEvent;
447
+ //#endregion
448
+ //#region ../../packages/core/src/admin.ts
449
+ /**
450
+ * Read the consuming package's version from its package.json at runtime.
451
+ *
452
+ * Pass `import.meta.url` from the CONSUMER module. core is bundled into each consumer, so
453
+ * after the build this code runs from the consumer's `dist/` and package.json sits one
454
+ * level up (`../package.json`). Never throws — returns 'unknown' on any failure.
455
+ */
456
+ function readPackageVersion(metaUrl) {
457
+ try {
458
+ return JSON.parse(readFileSync(new URL("../package.json", metaUrl), "utf8")).version ?? "unknown";
459
+ } catch {
460
+ return "unknown";
461
+ }
462
+ }
463
+ //#endregion
464
+ //#region ../../packages/core/src/cli.ts
465
+ /** Climb to the root program so commands at any nesting depth can read root-level options (e.g. --url). */
466
+ function findRoot(cmd) {
467
+ let c = cmd;
468
+ while (c.parent) c = c.parent;
469
+ return c;
470
+ }
471
+ //#endregion
472
+ //#region ../../packages/core/src/completion.ts
473
+ const SHELLS = [
474
+ "bash",
475
+ "zsh",
476
+ "fish"
477
+ ];
478
+ function firstLine(text) {
479
+ return (text.split("\n")[0] ?? "").trim();
480
+ }
481
+ function toNode(cmd, path) {
482
+ return {
483
+ name: cmd.name(),
484
+ aliases: [...cmd.aliases()],
485
+ description: firstLine(cmd.description()),
486
+ path,
487
+ options: cmd.options.filter((o) => !o.hidden).map((o) => ({
488
+ short: o.short,
489
+ long: o.long,
490
+ description: firstLine(o.description),
491
+ takesValue: o.required || o.optional,
492
+ choices: o.argChoices ? [...o.argChoices] : void 0
493
+ })),
494
+ args: cmd.registeredArguments.map((a) => ({
495
+ name: a.name(),
496
+ description: firstLine(a.description),
497
+ variadic: a.variadic,
498
+ choices: a.argChoices ? [...a.argChoices] : void 0
499
+ })),
500
+ children: cmd.commands.map((c) => toNode(c, [...path, c.name()]))
501
+ };
502
+ }
503
+ /** Flatten in registration order — the emitters iterate this, so output is deterministic. */
504
+ function walk(node) {
505
+ return [node, ...node.children.flatMap(walk)];
506
+ }
507
+ function namesOf(node) {
508
+ return [node.name, ...node.aliases];
509
+ }
510
+ function fishQuote(s) {
511
+ return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
512
+ }
513
+ function emitFish(bin, root) {
514
+ const lines = [
515
+ `# fish completion for ${bin}`,
516
+ `# Load with: ${bin} completion fish | source`,
517
+ `# Or install: ${bin} completion fish > ~/.config/fish/completions/${bin}.fish`
518
+ ];
519
+ for (const node of walk(root)) {
520
+ const seen = node.path.map((p) => `__fish_seen_subcommand_from ${p}`);
521
+ const childNames = node.children.flatMap(namesOf);
522
+ const hereParts = [...seen];
523
+ if (childNames.length > 0) hereParts.push(`not __fish_seen_subcommand_from ${childNames.join(" ")}`);
524
+ const here = hereParts.length > 0 ? ` -n ${fishQuote(hereParts.join("; and "))}` : "";
525
+ for (const child of node.children) for (const name of namesOf(child)) lines.push(`complete -c ${bin}${here} -f -a ${name} -d ${fishQuote(child.description)}`);
526
+ for (const opt of node.options) {
527
+ let line = `complete -c ${bin}${here}`;
528
+ if (opt.short) line += ` -s ${opt.short.replace(/^-+/, "")}`;
529
+ if (opt.long) line += ` -l ${opt.long.replace(/^-+/, "")}`;
530
+ if (opt.takesValue) line += opt.choices ? ` -x -a ${fishQuote(opt.choices.join(" "))}` : " -r";
531
+ line += ` -d ${fishQuote(opt.description)}`;
532
+ lines.push(line);
533
+ }
534
+ const atArgs = seen.length > 0 ? ` -n ${fishQuote(seen.join("; and "))}` : "";
535
+ for (const arg of node.args) if (arg.choices) {
536
+ const desc = arg.description || arg.name;
537
+ lines.push(`complete -c ${bin}${atArgs} -f -a ${fishQuote(arg.choices.join(" "))} -d ${fishQuote(desc)}`);
538
+ }
539
+ }
540
+ return `${lines.join("\n")}\n`;
541
+ }
542
+ function sanitizeIdent(bin) {
543
+ return bin.replace(/[^A-Za-z0-9_]/g, "_");
544
+ }
545
+ /**
546
+ * Case arms that descend the known subcommand tree token by token, normalising
547
+ * aliases to the canonical path so a single lookup table serves both spellings.
548
+ */
549
+ function descentCases(root, indent) {
550
+ return walk(root).filter((node) => node.path.length > 0).map((node) => {
551
+ const parent = node.path.slice(0, -1).join(" ");
552
+ return `${indent}${namesOf(node).map((name) => `"${parent ? `${parent} ${name}` : name}"`).join("|")}) path="${node.path.join(" ")}" ;;`;
553
+ });
554
+ }
555
+ function emitBash(bin, root) {
556
+ const fn = `_${sanitizeIdent(bin)}_completions`;
557
+ const optCases = walk(root).map((node) => {
558
+ const subs = node.children.flatMap(namesOf).join(" ");
559
+ const flags = node.options.flatMap((o) => [o.short, o.long]).filter((f) => f !== void 0).join(" ");
560
+ const args = node.args.flatMap((a) => a.choices ?? []).join(" ");
561
+ return ` "${node.path.join(" ")}") subs="${subs}"; flags="${flags}"; args="${args}" ;;`;
562
+ });
563
+ return [
564
+ `# bash completion for ${bin}`,
565
+ `# Load with: source <(${bin} completion bash)`,
566
+ `${fn}() {`,
567
+ " local cur path try w i subs flags args",
568
+ " cur=\"${COMP_WORDS[COMP_CWORD]}\"",
569
+ " path=\"\"",
570
+ " for ((i = 1; i < COMP_CWORD; i++)); do",
571
+ " w=\"${COMP_WORDS[i]}\"",
572
+ " [[ \"$w\" == -* ]] && continue",
573
+ " try=\"${path:+$path }$w\"",
574
+ " case \"$try\" in",
575
+ ...descentCases(root, " "),
576
+ " *) ;;",
577
+ " esac",
578
+ " done",
579
+ " subs=\"\"; flags=\"\"; args=\"\"",
580
+ " case \"$path\" in",
581
+ ...optCases,
582
+ " esac",
583
+ " if [[ \"$cur\" == -* ]]; then",
584
+ " COMPREPLY=($(compgen -W \"$flags\" -- \"$cur\"))",
585
+ " elif [[ -n \"$subs\" ]]; then",
586
+ " COMPREPLY=($(compgen -W \"$subs\" -- \"$cur\"))",
587
+ " elif [[ -n \"$args\" ]]; then",
588
+ " COMPREPLY=($(compgen -W \"$args\" -- \"$cur\"))",
589
+ " else",
590
+ " COMPREPLY=($(compgen -f -- \"$cur\"))",
591
+ " fi",
592
+ "}",
593
+ `complete -F ${fn} ${bin}`,
594
+ ""
595
+ ].join("\n");
596
+ }
597
+ function zshItem(name, description) {
598
+ return `'${`${name}:${description}`.replace(/'/g, `'\\''`)}'`;
599
+ }
600
+ function emitZsh(bin, root) {
601
+ const fn = `_${sanitizeIdent(bin)}`;
602
+ const optCases = walk(root).map((node) => {
603
+ const subs = node.children.flatMap((child) => namesOf(child).map((name) => zshItem(name, child.description))).join(" ");
604
+ const flags = node.options.flatMap((o) => [o.short, o.long].filter((f) => f !== void 0).map((f) => zshItem(f, o.description))).join(" ");
605
+ const args = node.args.flatMap((a) => (a.choices ?? []).map((c) => zshItem(c, a.description || a.name))).join(" ");
606
+ const body = [
607
+ subs ? `subs=(${subs})` : void 0,
608
+ flags ? `flags=(${flags})` : void 0,
609
+ args ? `argwords=(${args})` : void 0
610
+ ].filter((p) => p !== void 0).join("; ");
611
+ return ` "${node.path.join(" ")}") ${body ? `${body} ` : ""};;`;
612
+ });
613
+ return [
614
+ `#compdef ${bin}`,
615
+ `# zsh completion for ${bin}`,
616
+ `# Install: ${bin} completion zsh > "\${fpath[1]}/_${bin}" (then restart zsh)`,
617
+ `${fn}() {`,
618
+ " local -a subs flags argwords",
619
+ " local path=\"\" try w",
620
+ " for w in \"${(@)words[2,CURRENT-1]}\"; do",
621
+ " [[ \"$w\" == -* ]] && continue",
622
+ " try=\"${path:+$path }$w\"",
623
+ " case \"$try\" in",
624
+ ...descentCases(root, " "),
625
+ " *) ;;",
626
+ " esac",
627
+ " done",
628
+ " case \"$path\" in",
629
+ ...optCases,
630
+ " esac",
631
+ " if [[ \"$PREFIX\" == -* ]]; then",
632
+ " (( ${#flags[@]} )) && _describe -t options 'option' flags",
633
+ " elif (( ${#subs[@]} )); then",
634
+ " _describe -t commands 'command' subs",
635
+ " elif (( ${#argwords[@]} )); then",
636
+ " _describe -t values 'value' argwords",
637
+ " else",
638
+ " _files",
639
+ " fi",
640
+ "}",
641
+ `if [[ "\${funcstack[1]}" == "${fn}" ]]; then`,
642
+ ` ${fn} "$@"`,
643
+ "else",
644
+ ` compdef ${fn} ${bin}`,
645
+ "fi",
646
+ ""
647
+ ].join("\n");
648
+ }
649
+ /**
650
+ * Build a fully static, self-contained shell-completion script for `root`'s command
651
+ * tree (subcommands at any depth, aliases, flags, option/argument choices). Pure:
652
+ * output depends only on the tree, iterated in registration order.
653
+ */
654
+ function buildCompletionScript(root, shell) {
655
+ const bin = root.name();
656
+ const tree = toNode(root, []);
657
+ switch (shell) {
658
+ case "fish": return emitFish(bin, tree);
659
+ case "bash": return emitBash(bin, tree);
660
+ case "zsh": return emitZsh(bin, tree);
661
+ }
662
+ }
663
+ /**
664
+ * Build a `completion <shell>` subcommand that prints the static completion script
665
+ * for the ROOT program (resolved by walking `.parent`) to stdout.
666
+ */
667
+ function createCompletionCommand() {
668
+ return new Command("completion").description("Print a static shell-completion script for bash, zsh or fish. Load it in your shell, e.g. fish: `sm-cl completion fish | source`").addArgument(new Argument("<shell>", "target shell").choices(SHELLS)).action((shell, _opts, cmd) => {
669
+ process.stdout.write(buildCompletionScript(findRoot(cmd), shell));
670
+ });
671
+ }
672
+ //#endregion
673
+ //#region src/cli/define.ts
674
+ function toBigInt(s) {
675
+ return BigInt(s);
676
+ }
677
+ function toNumber(s) {
678
+ const n = Number(s);
679
+ if (Number.isNaN(n)) throw new Error(`not a number: ${s}`);
680
+ return n;
681
+ }
682
+ /** ETH (decimal string) → wei bigint. String-based; 1 wei → 1n. */
683
+ function toEth(s) {
684
+ return parseEther(s);
685
+ }
686
+ function toHexValue(s) {
687
+ if (!isHex(s)) throw new Error(`not a 0x-hex value: ${s}`);
688
+ return s;
689
+ }
690
+ function toAddressValue(s) {
691
+ if (!isAddress(s)) throw new Error(`not an address: ${s}`);
692
+ return s;
693
+ }
694
+ function identity(s) {
695
+ return s;
696
+ }
697
+ /** Repeatable '--pair <noId:bps>' → [bigint, bigint][]. */
698
+ function toPairs(raw) {
699
+ return raw.map((p) => {
700
+ const [a, b] = p.split(":");
701
+ if (a === void 0 || b === void 0) throw new Error(`bad pair "${p}", want noId:bps`);
702
+ return [BigInt(a), BigInt(b)];
703
+ });
704
+ }
705
+ /** Repeatable '--address <addr>' → Hex[]. */
706
+ function toAddresses(raw) {
707
+ return raw.map(toAddressValue);
708
+ }
709
+ /** kebab name of a flag's long form, e.g. '--operator-id <id>' → 'operator-id'. */
710
+ function flagName(flag) {
711
+ return (flag.split(/[ ,]+/).find((t) => t.startsWith("--")) ?? flag).replace(/^--/, "").replace(/<.*$/, "").trim();
712
+ }
713
+ /** commander's camelCased property name for a flag spec (mirrors commander's own rule). */
714
+ function flagProp(flag) {
715
+ return flagName(flag).replace(/-([a-z])/g, (_m, c) => c.toUpperCase());
716
+ }
717
+ const bigintReplacer = (_k, v) => typeof v === "bigint" ? v.toString() : v;
718
+ /** Run an async action; print thrown errors cleanly and exit non-zero. */
719
+ function run(fn) {
720
+ fn().catch((err) => {
721
+ console.error("Error:", err instanceof Error ? err.message : String(err));
722
+ process.exit(1);
723
+ });
724
+ }
725
+ const collect = (v, acc) => [...acc, v];
726
+ const COERCER_HELP = /* @__PURE__ */ new Map([
727
+ [toEth, "amount in ETH (decimal, 1-wei exact)"],
728
+ [toBigInt, "unsigned integer"],
729
+ [toNumber, "number"],
730
+ [toHexValue, "0x-prefixed hex value"],
731
+ [toAddressValue, "0x… address"],
732
+ [toAddresses, "0x… address"],
733
+ [toPairs, "noId:bps pair"]
734
+ ]);
735
+ const optionHelp = (o) => o.description ?? COERCER_HELP.get(o.coerce) ?? "";
736
+ /** Whether an option is also accepted positionally — explicit `positional`, else required && !repeatable. */
737
+ const isPositional = (o) => o.positional ?? (!!o.required && !o.repeatable);
738
+ function defineCommand(desc, connectImpl = connect) {
739
+ const cmd = new Command(desc.name).description(desc.summary);
740
+ for (const o of desc.options) if (o.repeatable) cmd.option(o.flag, optionHelp(o), collect, []);
741
+ else cmd.option(o.flag, optionHelp(o));
742
+ const positionals = desc.options.filter(isPositional);
743
+ const variadicAt = positionals.findIndex((o) => o.repeatable);
744
+ if (variadicAt >= 0 && variadicAt !== positionals.length - 1) throw new Error(`${desc.name}: a repeatable positional must be declared last (it is variadic)`);
745
+ for (const o of positionals) cmd.argument(`[${flagName(o.flag)}${o.repeatable ? "..." : ""}]`, optionHelp(o));
746
+ if (positionals.length > 0) {
747
+ const order = positionals.map((o) => flagName(o.flag) + (o.repeatable ? "..." : "")).join(", ");
748
+ cmd.addHelpText("after", `\nRequired options may be passed positionally in this order: ${order}`);
749
+ }
750
+ cmd.action((...actionArgs) => {
751
+ const command = actionArgs.at(-1);
752
+ const positionalValues = command.processedArgs;
753
+ run(async () => {
754
+ const g = command.optsWithGlobals();
755
+ const opts = {};
756
+ for (const o of desc.options) {
757
+ const posIndex = positionals.indexOf(o);
758
+ const posVal = posIndex >= 0 ? positionalValues[posIndex] : void 0;
759
+ const raw = (o.repeatable ? Array.isArray(posVal) && posVal.length > 0 : posVal != null) ? posVal : g[flagProp(o.flag)];
760
+ if (raw === void 0 || o.repeatable && raw.length === 0) {
761
+ if (o.required) throw new Error(`missing required option ${o.flag.split(" ")[0]}`);
762
+ continue;
763
+ }
764
+ opts[o.key] = o.coerce(raw);
765
+ }
766
+ const moduleName = desc.module ?? g.module;
767
+ if (!moduleName) throw new Error("set --module <csm|cm>");
768
+ const rpcUrl = g.rpcUrl ?? process.env.RPC_URL ?? "http://127.0.0.1:8545";
769
+ const clMockUrl = g.clMockUrl ?? process.env.CL_MOCK_URL;
770
+ if (desc.needsClMock && !clMockUrl) throw new Error(`${desc.name} needs --cl-mock-url or CL_MOCK_URL`);
771
+ const ctx = await connectImpl({
772
+ module: moduleName,
773
+ rpcUrl,
774
+ clMockUrl
775
+ });
776
+ const result = await desc.run(ctx, opts);
777
+ if (g.json) console.log(JSON.stringify(result === void 0 ? { ok: true } : result, bigintReplacer, 2));
778
+ else for (const line of desc.report(result, opts)) console.log(line);
779
+ });
780
+ });
781
+ return cmd;
782
+ }
783
+ //#endregion
784
+ //#region src/cli/commands/shared.ts
785
+ const operatorId$1 = {
786
+ flag: "--operator-id <id>",
787
+ key: "noId",
788
+ coerce: toBigInt,
789
+ required: true,
790
+ description: "node operator id (uint)"
791
+ };
792
+ const keyIndex = {
793
+ flag: "--key-index <i>",
794
+ key: "keyIndex",
795
+ coerce: toBigInt,
796
+ required: true,
797
+ description: "zero-based key index within the operator"
798
+ };
799
+ const seedHex = {
800
+ flag: "--seed <hex>",
801
+ key: "seed",
802
+ coerce: toHexValue,
803
+ description: "deterministic seed (0x-hex); omit for fresh randomness"
804
+ };
805
+ const cidEscape = (which) => ({
806
+ coerce: identity,
807
+ description: `skip IPFS pinning by supplying the ${which} CID — no running sm-ipfs needed`
808
+ });
809
+ const sharedCommands = [
810
+ {
811
+ name: "add-keys",
812
+ summary: "add N fresh validator keys to an operator (pays bond, as manager)",
813
+ options: [
814
+ operatorId$1,
815
+ {
816
+ flag: "--count <n>",
817
+ key: "count",
818
+ coerce: toNumber,
819
+ required: true
820
+ },
821
+ seedHex
822
+ ],
823
+ run: (ctx, o) => addKeys(ctx, o),
824
+ report: (r, o) => [`operator ${o.noId}: +${o.count} keys`, `pubkeys: ${r.publicKeys.join(", ")}`]
825
+ },
826
+ {
827
+ name: "operator-info",
828
+ summary: "read a node operator's on-chain record (addresses + key counts); one field per line, --json for the raw object",
829
+ options: [operatorId$1],
830
+ run: (ctx, o) => operatorInfo(ctx, o),
831
+ report: (r, o) => [`operator ${o.noId}:`, ...Object.entries(r).map(([k, v]) => ` ${k}: ${String(v)}`)]
832
+ },
833
+ {
834
+ name: "deposit",
835
+ summary: "deposit N depositable keys (as the StakingRouter)",
836
+ options: [{
837
+ flag: "--count <n>",
838
+ key: "count",
839
+ coerce: toBigInt,
840
+ required: true
841
+ }],
842
+ run: (ctx, o) => deposit(ctx, o),
843
+ report: (r) => [`deposited: ${r.deposited}`]
844
+ },
845
+ {
846
+ name: "unvet",
847
+ summary: "set an operator vetted-keys count down (as the StakingRouter)",
848
+ options: [operatorId$1, {
849
+ flag: "--vetted-keys <n>",
850
+ key: "vettedKeys",
851
+ coerce: toBigInt,
852
+ required: true
853
+ }],
854
+ run: (ctx, o) => unvet(ctx, o),
855
+ report: (_r, o) => [`operator ${o.noId}: vetted=${o.vettedKeys}`]
856
+ },
857
+ {
858
+ name: "exit",
859
+ summary: "report exited keys for an operator (as the StakingRouter)",
860
+ options: [operatorId$1, {
861
+ flag: "--exited-keys <n>",
862
+ key: "exitedKeys",
863
+ coerce: toBigInt,
864
+ required: true
865
+ }],
866
+ run: (ctx, o) => exit(ctx, o),
867
+ report: (_r, o) => [`operator ${o.noId}: exited=${o.exitedKeys}`]
868
+ },
869
+ {
870
+ name: "increase-allocated-balance",
871
+ summary: "top up one deposited key's allocated balance (ETH)",
872
+ options: [
873
+ operatorId$1,
874
+ keyIndex,
875
+ {
876
+ flag: "--amount <eth>",
877
+ key: "amountWei",
878
+ coerce: toEth,
879
+ required: true
880
+ }
881
+ ],
882
+ run: (ctx, o) => increaseAllocatedBalance(ctx, o),
883
+ report: (r) => [`+${formatEther(r.amountWei)} ETH allocated`]
884
+ },
885
+ {
886
+ name: "top-up-active-keys",
887
+ summary: "allocate deposit balance to every active key of an operator, in key-index order (TopUpQueueOps FIFO), as the StakingRouter; capped at 2016 ETH per key",
888
+ options: [operatorId$1],
889
+ run: (ctx, o) => topUpActiveKeys(ctx, o),
890
+ report: (r) => [`topped up ${r.toppedUp} key(s)`]
891
+ },
892
+ {
893
+ name: "slash",
894
+ summary: "slash a validator key (Verifier-gated)",
895
+ options: [operatorId$1, keyIndex],
896
+ run: (ctx, o) => slash(ctx, o),
897
+ report: (_r, o) => [`slashed operator ${o.noId} key ${o.keyIndex}`]
898
+ },
899
+ {
900
+ name: "withdraw",
901
+ summary: "report a withdrawn validator (Verifier-gated); balances in ETH",
902
+ options: [
903
+ operatorId$1,
904
+ keyIndex,
905
+ {
906
+ flag: "--exit-balance <eth>",
907
+ key: "exitBalance",
908
+ coerce: toEth,
909
+ required: true
910
+ },
911
+ {
912
+ flag: "--slashing-penalty <eth>",
913
+ key: "slashingPenalty",
914
+ coerce: toEth
915
+ }
916
+ ],
917
+ run: (ctx, o) => withdraw(ctx, o),
918
+ report: (_r, o) => [`withdrew operator ${o.noId} key ${o.keyIndex}`]
919
+ },
920
+ {
921
+ name: "report-penalty",
922
+ summary: "report a general delayed penalty (ETH amount)",
923
+ options: [
924
+ operatorId$1,
925
+ {
926
+ flag: "--amount <eth>",
927
+ key: "amount",
928
+ coerce: toEth,
929
+ required: true
930
+ },
931
+ {
932
+ flag: "--penalty-type <hex>",
933
+ key: "penaltyType",
934
+ coerce: toHexValue
935
+ },
936
+ {
937
+ flag: "--details <text>",
938
+ key: "details",
939
+ coerce: identity
940
+ }
941
+ ],
942
+ run: (ctx, o) => reportPenalty(ctx, o),
943
+ report: (_r, o) => [`reported penalty ${formatEther(o.amount)} ETH on operator ${o.noId}`]
944
+ },
945
+ {
946
+ name: "cancel-penalty",
947
+ summary: "cancel a reported general delayed penalty (ETH amount)",
948
+ options: [operatorId$1, {
949
+ flag: "--amount <eth>",
950
+ key: "amount",
951
+ coerce: toEth,
952
+ required: true
953
+ }],
954
+ run: (ctx, o) => cancelPenalty(ctx, o),
955
+ report: (_r, o) => [`cancelled penalty on operator ${o.noId}`]
956
+ },
957
+ {
958
+ name: "settle-penalty",
959
+ summary: "settle an operator's general delayed penalty (optional ETH cap)",
960
+ options: [operatorId$1, {
961
+ flag: "--max-amount <eth>",
962
+ key: "maxAmount",
963
+ coerce: toEth
964
+ }],
965
+ run: (ctx, o) => settlePenalty(ctx, o),
966
+ report: (_r, o) => [`settled penalty on operator ${o.noId}`]
967
+ },
968
+ {
969
+ name: "compensate-penalty",
970
+ summary: "compensate (pay off) an operator's penalty (as manager)",
971
+ options: [operatorId$1],
972
+ run: (ctx, o) => compensatePenalty(ctx, o),
973
+ report: (_r, o) => [`compensated penalty on operator ${o.noId}`]
974
+ },
975
+ {
976
+ name: "add-bond",
977
+ summary: "add bond to an operator (ETH)",
978
+ options: [operatorId$1, {
979
+ flag: "--amount <eth>",
980
+ key: "amount",
981
+ coerce: toEth,
982
+ required: true
983
+ }],
984
+ run: (ctx, o) => addBond(ctx, o),
985
+ report: (_r, o) => [`added ${formatEther(o.amount)} ETH bond to operator ${o.noId}`]
986
+ },
987
+ {
988
+ name: "create-bond-debt",
989
+ summary: "create a bond debt by penalizing an operator (ETH)",
990
+ options: [operatorId$1, {
991
+ flag: "--amount <eth>",
992
+ key: "amount",
993
+ coerce: toEth,
994
+ required: true
995
+ }],
996
+ run: (ctx, o) => createBondDebt(ctx, o),
997
+ report: (r, o) => [`operator ${o.noId}: debt created (penaltyCovered=${r.penaltyCovered})`]
998
+ },
999
+ {
1000
+ name: "propose-manager",
1001
+ summary: "propose a new manager address (as current manager)",
1002
+ options: [operatorId$1, {
1003
+ flag: "--proposed <address>",
1004
+ key: "proposed",
1005
+ coerce: toAddressValue,
1006
+ required: true
1007
+ }],
1008
+ run: (ctx, o) => proposeManager(ctx, o),
1009
+ report: (_r, o) => [`operator ${o.noId}: proposed manager ${o.proposed}`]
1010
+ },
1011
+ {
1012
+ name: "confirm-manager",
1013
+ summary: "confirm the proposed manager address (as proposed manager)",
1014
+ options: [operatorId$1],
1015
+ run: (ctx, o) => confirmManager(ctx, o),
1016
+ report: (_r, o) => [`operator ${o.noId}: manager confirmed`]
1017
+ },
1018
+ {
1019
+ name: "propose-reward",
1020
+ summary: "propose a new reward address (as current manager)",
1021
+ options: [operatorId$1, {
1022
+ flag: "--proposed <address>",
1023
+ key: "proposed",
1024
+ coerce: toAddressValue,
1025
+ required: true
1026
+ }],
1027
+ run: (ctx, o) => proposeReward(ctx, o),
1028
+ report: (_r, o) => [`operator ${o.noId}: proposed reward ${o.proposed}`]
1029
+ },
1030
+ {
1031
+ name: "confirm-reward",
1032
+ summary: "confirm the proposed reward address (as proposed reward addr)",
1033
+ options: [operatorId$1],
1034
+ run: (ctx, o) => confirmReward(ctx, o),
1035
+ report: (_r, o) => [`operator ${o.noId}: reward confirmed`]
1036
+ },
1037
+ {
1038
+ name: "make-rewards",
1039
+ summary: "build the cumulative rewards tree + pin to IPFS (no submit)",
1040
+ options: [
1041
+ seedHex,
1042
+ {
1043
+ flag: "--tree-cid <cid>",
1044
+ key: "treeCid",
1045
+ ...cidEscape("tree")
1046
+ },
1047
+ {
1048
+ flag: "--log-cid <cid>",
1049
+ key: "logCid",
1050
+ ...cidEscape("report-log")
1051
+ }
1052
+ ],
1053
+ run: (ctx, o) => makeRewards(ctx, o),
1054
+ report: (r) => [
1055
+ `tree root: ${r.treeRoot}`,
1056
+ `tree CID: ${r.treeCid || "(none)"}`,
1057
+ `log CID: ${r.logCid || "(none)"}`,
1058
+ `distributed: ${formatEther(r.distributed)} ETH`
1059
+ ]
1060
+ },
1061
+ {
1062
+ name: "submit-rewards",
1063
+ summary: "build AND submit a rewards report (warps to the next frame)",
1064
+ options: [
1065
+ seedHex,
1066
+ {
1067
+ flag: "--tree-cid <cid>",
1068
+ key: "treeCid",
1069
+ ...cidEscape("tree")
1070
+ },
1071
+ {
1072
+ flag: "--log-cid <cid>",
1073
+ key: "logCid",
1074
+ ...cidEscape("report-log")
1075
+ }
1076
+ ],
1077
+ run: async (ctx, o) => submitRewards(ctx, await makeRewards(ctx, o)),
1078
+ report: (r) => r.submitted ? [`submitted at refSlot ${r.refSlot}`, `reportHash: ${r.reportHash}`] : ["skipped: empty report (zero root)"]
1079
+ },
1080
+ {
1081
+ name: "cl-activate",
1082
+ summary: "mark a key active_ongoing on a running cl-mock (requires --cl-mock-url or CL_MOCK_URL)",
1083
+ needsClMock: true,
1084
+ options: [operatorId$1, keyIndex],
1085
+ run: (ctx, o) => clActivate(ctx, o),
1086
+ report: (r) => [`${r.pubkey}: ${r.status} @ ${r.effectiveBalanceGwei} gwei`]
1087
+ },
1088
+ {
1089
+ name: "get-pubkey",
1090
+ summary: "read a key's pubkey",
1091
+ options: [operatorId$1, keyIndex],
1092
+ run: (ctx, o) => getPubkey(ctx, o),
1093
+ report: (r) => [r]
1094
+ },
1095
+ {
1096
+ name: "get-key-balance",
1097
+ summary: "read a key's allocated balance",
1098
+ options: [operatorId$1, keyIndex],
1099
+ run: (ctx, o) => getKeyBalance(ctx, o),
1100
+ report: (r) => [`${formatEther(r)} ETH (${r} wei)`]
1101
+ },
1102
+ {
1103
+ name: "warp",
1104
+ summary: "advance the fork clock by N seconds",
1105
+ options: [{
1106
+ flag: "--by <seconds>",
1107
+ key: "by",
1108
+ coerce: toBigInt,
1109
+ required: true
1110
+ }],
1111
+ run: (ctx, o) => warpBy(ctx, o.by),
1112
+ report: (_r, o) => [`warped by ${o.by} seconds`]
1113
+ },
1114
+ {
1115
+ name: "snapshot",
1116
+ summary: "take an EVM snapshot, print its id",
1117
+ options: [],
1118
+ run: (ctx) => snapshot(ctx),
1119
+ report: (r) => [`snapshot id: ${r}`]
1120
+ },
1121
+ {
1122
+ name: "revert",
1123
+ summary: "revert the fork to a snapshot id",
1124
+ options: [{
1125
+ flag: "--id <hex>",
1126
+ key: "id",
1127
+ coerce: toHexValue,
1128
+ required: true
1129
+ }],
1130
+ run: (ctx, o) => revert(ctx, o.id),
1131
+ report: (_r, o) => [`reverted to ${o.id}`]
1132
+ }
1133
+ ];
1134
+ //#endregion
1135
+ //#region src/cli/commands/cm.ts
1136
+ const operatorId = {
1137
+ flag: "--operator-id <id>",
1138
+ key: "noId",
1139
+ coerce: toBigInt,
1140
+ required: true,
1141
+ description: "node operator id (uint)"
1142
+ };
1143
+ const cmSelectorHelp = "gate selector: po|pto|pgo|do|eeo|iodc|iodcp, gate index 0-6, or 0x… address";
1144
+ const cmCommands = [
1145
+ {
1146
+ name: "seed",
1147
+ summary: "seed a realistic cm fork (3 operators, a group, keyed/deposited/topped-up)",
1148
+ module: "cm",
1149
+ options: [{
1150
+ flag: "--selector <name>",
1151
+ key: "selector",
1152
+ coerce: identity,
1153
+ description: `${cmSelectorHelp} (default: po)`
1154
+ }, {
1155
+ flag: "--seed <hex>",
1156
+ key: "seed",
1157
+ coerce: toHexValue,
1158
+ description: "deterministic seed (0x-hex); omit for fresh randomness"
1159
+ }],
1160
+ run: (ctx, o) => seedCm(ctx, o),
1161
+ report: (r) => [`seeded operators: ${r.noIds.join(", ")}`, `addresses: ${r.operators.join(", ")}`]
1162
+ },
1163
+ {
1164
+ name: "create-curated-operator",
1165
+ summary: "create a curated node operator through a cm gate as --operator (the new operator's address); prints the new operator id",
1166
+ module: "cm",
1167
+ options: [{
1168
+ flag: "--selector <name>",
1169
+ key: "selector",
1170
+ coerce: identity,
1171
+ required: true,
1172
+ description: cmSelectorHelp
1173
+ }, {
1174
+ flag: "--operator <address>",
1175
+ key: "operator",
1176
+ coerce: toAddressValue,
1177
+ required: true
1178
+ }],
1179
+ run: (ctx, o) => createCuratedOperator(ctx, o),
1180
+ report: (r) => [`created operator ${r.noId}`]
1181
+ },
1182
+ {
1183
+ name: "create-operator-group",
1184
+ summary: "create a MetaRegistry operator group (--pair noId:bps, must sum to 10000)",
1185
+ module: "cm",
1186
+ options: [{
1187
+ flag: "--pair <noId:bps>",
1188
+ key: "pairs",
1189
+ coerce: toPairs,
1190
+ repeatable: true,
1191
+ required: true
1192
+ }],
1193
+ run: (ctx, o) => createOperatorGroup(ctx, o),
1194
+ report: (r) => [
1195
+ `group created: ${r.subNodeOperators.length} member(s)`,
1196
+ `members: ${r.subNodeOperators.map((s) => `${s.nodeOperatorId}@${s.share}bps`).join(", ")}`,
1197
+ ...r.resetGroupIds.length ? [`reset prior groups: ${r.resetGroupIds.join(", ")}`] : []
1198
+ ]
1199
+ },
1200
+ {
1201
+ name: "reset-operator-group",
1202
+ summary: "reset an operator's group membership",
1203
+ module: "cm",
1204
+ options: [operatorId],
1205
+ run: (ctx, o) => resetOperatorGroup(ctx, o),
1206
+ report: (_r, o) => [`reset group for operator ${o.noId}`]
1207
+ },
1208
+ {
1209
+ name: "set-bond-curve-weight",
1210
+ summary: "set the MetaRegistry bond-curve weight for a curve id (impersonates the role holder read from the contract)",
1211
+ module: "cm",
1212
+ options: [{
1213
+ flag: "--curve-id <n>",
1214
+ key: "curveId",
1215
+ coerce: toBigInt,
1216
+ required: true
1217
+ }, {
1218
+ flag: "--weight <n>",
1219
+ key: "weight",
1220
+ coerce: toBigInt,
1221
+ required: true
1222
+ }],
1223
+ run: (ctx, o) => setBondCurveWeight(ctx, o),
1224
+ report: (r) => [`curve ${r.curveId} weight=${r.weight}`]
1225
+ },
1226
+ {
1227
+ name: "set-gate",
1228
+ summary: "build + install a gate address tree (pins to IPFS unless --cid)",
1229
+ module: "cm",
1230
+ options: [
1231
+ {
1232
+ flag: "--selector <name>",
1233
+ key: "selector",
1234
+ coerce: identity,
1235
+ positional: true,
1236
+ description: `${cmSelectorHelp} (default: po)`
1237
+ },
1238
+ {
1239
+ flag: "--address <addr>",
1240
+ key: "addresses",
1241
+ coerce: toAddresses,
1242
+ repeatable: true,
1243
+ required: true,
1244
+ positional: true
1245
+ },
1246
+ {
1247
+ flag: "--cid <cid>",
1248
+ key: "cid",
1249
+ coerce: identity,
1250
+ description: "skip IPFS pinning by supplying the CID — no running sm-ipfs needed"
1251
+ }
1252
+ ],
1253
+ run: (ctx, o) => setGateAddrs(ctx, o),
1254
+ report: (r) => [`tree root: ${r.treeRoot}`, `tree CID: ${r.treeCid}`]
1255
+ },
1256
+ {
1257
+ name: "resolve-gate",
1258
+ summary: "resolve a cm gate contract address by selector (read-only); prints the address",
1259
+ module: "cm",
1260
+ options: [{
1261
+ flag: "--selector <name>",
1262
+ key: "selector",
1263
+ coerce: identity,
1264
+ required: true,
1265
+ description: cmSelectorHelp
1266
+ }],
1267
+ run: (ctx, o) => resolveGate(ctx, o.selector),
1268
+ report: (r, o) => [`${o.selector} → ${r}`]
1269
+ }
1270
+ ];
1271
+ //#endregion
1272
+ //#region src/cli/commands/csm.ts
1273
+ const csmSelectorHelp = "gate selector: ics (VettedGate) | idvtc (v3-only) | 0x… gate address";
1274
+ const csmCommands = [{
1275
+ name: "set-gate",
1276
+ summary: "build + install a gate address tree (pins to IPFS unless --cid)",
1277
+ module: "csm",
1278
+ options: [
1279
+ {
1280
+ flag: "--selector <name>",
1281
+ key: "selector",
1282
+ coerce: identity,
1283
+ positional: true,
1284
+ description: `${csmSelectorHelp} (default: ics)`
1285
+ },
1286
+ {
1287
+ flag: "--address <addr>",
1288
+ key: "addresses",
1289
+ coerce: toAddresses,
1290
+ repeatable: true,
1291
+ required: true,
1292
+ positional: true
1293
+ },
1294
+ {
1295
+ flag: "--cid <cid>",
1296
+ key: "cid",
1297
+ coerce: identity,
1298
+ description: "skip IPFS pinning by supplying the CID — no running sm-ipfs needed"
1299
+ }
1300
+ ],
1301
+ run: (ctx, o) => setGateAddrs(ctx, o),
1302
+ report: (r) => [`tree root: ${r.treeRoot}`, `tree CID: ${r.treeCid}`]
1303
+ }, {
1304
+ name: "resolve-gate",
1305
+ summary: "resolve a csm gate contract address by selector (read-only); prints the address",
1306
+ module: "csm",
1307
+ options: [{
1308
+ flag: "--selector <name>",
1309
+ key: "selector",
1310
+ coerce: identity,
1311
+ required: true,
1312
+ description: csmSelectorHelp
1313
+ }],
1314
+ run: (ctx, o) => resolveGate(ctx, o.selector),
1315
+ report: (r, o) => [`${o.selector} → ${r}`]
1316
+ }];
1317
+ //#endregion
1318
+ //#region src/cli/program.ts
1319
+ /** Pre-bind shared descriptors to a module so they run group-form without --module. */
1320
+ const withModule = (descs, module) => descs.map((d) => ({
1321
+ ...d,
1322
+ module
1323
+ }));
1324
+ const showGlobals = (c) => {
1325
+ c.configureHelp({ showGlobalOptions: true });
1326
+ for (const child of c.commands) showGlobals(child);
1327
+ };
1328
+ function buildProgram(connectImpl = connect) {
1329
+ const program = new Command().name("sm-recipes").description("Prepare Lido SM on-chain state on an anvil fork (run-and-exit recipes)").version(readPackageVersion(import.meta.url)).option("--rpc-url <url>", "anvil fork RPC URL (default: $RPC_URL or http://127.0.0.1:8545)").option("--module <csm|cm>", "target module for shared commands").option("--cl-mock-url <url>", "cl-mock URL for cl-activate (default: $CL_MOCK_URL)").option("--json", "emit the raw result as JSON").addHelpText("after", `
1330
+ Examples:
1331
+ sm-recipes operator-info 0 --module csm --json
1332
+ sm-recipes csm operator-info 0 --json
1333
+ sm-recipes add-keys 0 --module cm --json
1334
+ sm-recipes cm seed --json
1335
+ sm-recipes completion fish | source`).helpCommand(true);
1336
+ for (const desc of sharedCommands) program.addCommand(defineCommand(desc, connectImpl));
1337
+ program.addCommand(createCompletionCommand());
1338
+ const cm = new Command("cm").description("cm recipes + shared recipes (module forced to cm)");
1339
+ for (const desc of [...cmCommands, ...withModule(sharedCommands, "cm")]) cm.addCommand(defineCommand(desc, connectImpl));
1340
+ program.addCommand(cm);
1341
+ const csm = new Command("csm").description("csm recipes + shared recipes (module forced to csm)");
1342
+ for (const desc of [...csmCommands, ...withModule(sharedCommands, "csm")]) csm.addCommand(defineCommand(desc, connectImpl));
1343
+ program.addCommand(csm);
1344
+ showGlobals(program);
1345
+ return program;
1346
+ }
1347
+ //#endregion
1348
+ //#region src/cli/index.ts
1349
+ buildProgram().parse();
1350
+ //#endregion
1351
+ export {};
1352
+
1353
+ //# sourceMappingURL=cli.mjs.map