@namera-ai/cli 0.0.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.mjs ADDED
@@ -0,0 +1,3270 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";
3
+ import { NodeHttpServer, NodeRuntime, NodeServices, NodeStdio } from "@effect/platform-node";
4
+ import { Config, ConfigProvider, Console, Data, Duration, Effect, FileSystem, Layer, Logger, Option, Path, Redacted, Schema, SchemaTransformation, ServiceMap, Struct } from "effect";
5
+ import { Argument, Command, Flag, GlobalFlag, Prompt } from "effect/unstable/cli";
6
+ import { arbitrum, arbitrumSepolia, arcTestnet, avalanche, avalancheFuji, base, baseSepolia, celo, celoSepolia, mainnet, monad, monadTestnet, optimism, optimismSepolia, polygon, polygonAmoy, scroll, scrollSepolia, sepolia, tempoModerato, unichain, unichainSepolia, zora, zoraSepolia } from "viem/chains";
7
+ import os from "node:os";
8
+ import { Wallet } from "@ethereumjs/wallet";
9
+ import { createPublicClient, formatEther, formatUnits, hexToBytes, http, isHex, parseEther, parseUnits, toFunctionSelector, toHex, zeroAddress } from "viem";
10
+ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
11
+ import { createServer } from "node:http";
12
+ import { McpServer, Tool, Toolkit } from "effect/unstable/ai";
13
+ import { HttpRouter } from "effect/unstable/http";
14
+ import { getBalance, readContract } from "viem/actions";
15
+ import { createSessionKey, createSessionKeyClient, deserializePermissionAccountParams, isSessionKeyInstalled } from "@namera-ai/sdk/session-key";
16
+ import { executeTransaction } from "@namera-ai/sdk/transaction";
17
+ import { CallPolicyVersion, CallType, ParamCondition, toCallPolicy, toGasPolicy, toRateLimitPolicy, toSignatureCallerPolicy, toSudoPolicy, toTimestampPolicy } from "@namera-ai/sdk/policy";
18
+ import { createAccountClient } from "@namera-ai/sdk/account";
19
+ import { NdJson } from "json-nd";
20
+ //#region src/dto/keystore.ts
21
+ const Keystore = Schema.Struct({
22
+ version: Schema.Number,
23
+ id: Schema.String,
24
+ address: Schema.String,
25
+ crypto: Schema.Any
26
+ });
27
+ Schema.Struct({
28
+ path: Schema.String,
29
+ alias: Schema.String,
30
+ data: Keystore
31
+ });
32
+ const GetKeystoreParams = Schema.Struct({ alias: Schema.String.annotate({ description: "The alias of the wallet to retrieve" }) });
33
+ const ListKeystoreParams = Schema.Void;
34
+ const CreateKeystoreParams = Schema.Struct({
35
+ alias: Schema.String.annotate({ description: "The alias of the wallet to create" }),
36
+ password: Schema.redact(Schema.String).annotate({ description: "The password to encrypt the keystore with" })
37
+ });
38
+ const DecryptKeystoreParams = Schema.Struct({
39
+ alias: Schema.String.annotate({ description: "The alias of the keystore to decrypt" }),
40
+ password: Schema.redact(Schema.String).annotate({ description: "The password to decrypt the keystore with" })
41
+ });
42
+ Schema.Struct({
43
+ address: Schema.String.annotate({ description: "The address of the keystore" }),
44
+ alias: Schema.String.annotate({ description: "The alias of the keystore" }),
45
+ privateKey: Schema.Redacted(Schema.String).annotate({ description: "The private key of the keystore" }),
46
+ publicKey: Schema.String.annotate({ description: "The public key of the keystore" })
47
+ });
48
+ const ImportKeystoreParams = Schema.Struct({
49
+ alias: Schema.String.annotate({ description: "The alias of the keystore to import" }),
50
+ privateKey: Schema.String.annotate({ description: "The private key of the keystore to import" }),
51
+ password: Schema.redact(Schema.String).annotate({ description: "The password to encrypt the keystore with" })
52
+ });
53
+ const RemoveKeystoreParams = Schema.Struct({ alias: Schema.String.annotate({ description: "The alias of the keystore to remove" }) });
54
+ Schema.Struct({
55
+ alias: Schema.String.annotate({ description: "The alias of the keystore to get the signer for" }),
56
+ password: Schema.redact(Schema.String).annotate({ description: "The password to decrypt the keystore with" })
57
+ });
58
+ //#endregion
59
+ //#region src/dto/mcp.ts
60
+ const StartMcpServerParams = Schema.Struct({
61
+ smartAccountAlias: Schema.String.annotate({ description: "The alias of the smart account to use for the MCP server" }),
62
+ transport: Schema.Literals(["http", "stdio"]).annotate({ description: "The transport to use for the MCP server" }),
63
+ port: Schema.optional(Schema.Int.check(Schema.makeFilter((v) => v > 0 && v < 65536))).annotate({ description: "The port to use for the MCP server when using http transport" }),
64
+ sessionKeys: Schema.mutableKey(Schema.Record(Schema.String, Schema.String))
65
+ });
66
+ //#endregion
67
+ //#region src/schema/chain.ts
68
+ const supportedMainnetChains = {
69
+ "eth-mainnet": {
70
+ ...mainnet,
71
+ key: "eth-mainnet"
72
+ },
73
+ "opt-mainnet": {
74
+ ...optimism,
75
+ key: "opt-mainnet"
76
+ },
77
+ "polygon-mainnet": {
78
+ ...polygon,
79
+ key: "polygon-mainnet"
80
+ },
81
+ "arb-mainnet": {
82
+ ...arbitrum,
83
+ key: "arb-mainnet"
84
+ },
85
+ "zora-mainnet": {
86
+ ...zora,
87
+ key: "zora-mainnet"
88
+ },
89
+ "base-mainnet": {
90
+ ...base,
91
+ key: "base-mainnet"
92
+ },
93
+ "avax-mainnet": {
94
+ ...avalanche,
95
+ key: "avax-mainnet"
96
+ },
97
+ "unichain-mainnet": {
98
+ ...unichain,
99
+ key: "unichain-mainnet"
100
+ },
101
+ "celo-mainnet": {
102
+ ...celo,
103
+ key: "celo-mainnet"
104
+ },
105
+ "scroll-mainnet": {
106
+ ...scroll,
107
+ key: "scroll-mainnet"
108
+ },
109
+ "monad-mainnet": {
110
+ ...monad,
111
+ key: "monad-mainnet"
112
+ }
113
+ };
114
+ const supportedTestnetChains = {
115
+ "eth-sepolia": {
116
+ ...sepolia,
117
+ key: "eth-sepolia"
118
+ },
119
+ "opt-sepolia": {
120
+ ...optimismSepolia,
121
+ key: "opt-sepolia"
122
+ },
123
+ "polygon-amoy": {
124
+ ...polygonAmoy,
125
+ key: "polygon-amoy"
126
+ },
127
+ "arb-sepolia": {
128
+ ...arbitrumSepolia,
129
+ key: "arb-sepolia"
130
+ },
131
+ "zora-sepolia": {
132
+ ...zoraSepolia,
133
+ key: "zora-sepolia"
134
+ },
135
+ "base-sepolia": {
136
+ ...baseSepolia,
137
+ key: "base-sepolia"
138
+ },
139
+ "tempo-moderato": {
140
+ ...tempoModerato,
141
+ key: "tempo-moderato"
142
+ },
143
+ "avax-fuji": {
144
+ ...avalancheFuji,
145
+ key: "avax-fuji"
146
+ },
147
+ "unichain-sepolia": {
148
+ ...unichainSepolia,
149
+ key: "unichain-sepolia"
150
+ },
151
+ "monad-testnet": {
152
+ ...monadTestnet,
153
+ key: "monad-testnet"
154
+ },
155
+ "celo-sepolia": {
156
+ ...celoSepolia,
157
+ key: "celo-sepolia"
158
+ },
159
+ "scroll-sepolia": {
160
+ ...scrollSepolia,
161
+ key: "scroll-sepolia"
162
+ },
163
+ "arc-testnet": {
164
+ ...arcTestnet,
165
+ key: "arc-testnet"
166
+ }
167
+ };
168
+ const SupportedChain = Schema.Literals([...Object.keys(supportedMainnetChains), ...Object.keys(supportedTestnetChains)]);
169
+ const supportedChains = {
170
+ ...supportedMainnetChains,
171
+ ...supportedTestnetChains
172
+ };
173
+ const getChain = (chain) => {
174
+ return supportedChains[chain];
175
+ };
176
+ const getChainFromId = (chainId) => {
177
+ return Object.values(supportedChains).find((c) => c.id === chainId);
178
+ };
179
+ const chainIdToChainName = (chainId) => {
180
+ const chain = getChainFromId(chainId);
181
+ if (!chain) throw new Error(`Chain with id ${chainId} not found`);
182
+ return chain.key;
183
+ };
184
+ //#endregion
185
+ //#region src/schema/common.ts
186
+ const BigIntFromString = Schema.String.pipe(Schema.decodeTo(Schema.BigInt, SchemaTransformation.transform({
187
+ encode: (v) => v.toString(),
188
+ decode: (v) => BigInt(v)
189
+ })));
190
+ const EthereumAddress = Schema.TemplateLiteral(["0x", Schema.String.check(Schema.isPattern(/^[0-9a-fA-F]{40}$/))]);
191
+ const Hex = Schema.TemplateLiteral(["0x", Schema.String.check(Schema.isPattern(/^[0-9a-fA-F]*$/))]);
192
+ const EntrypointVersion = Schema.Literals([
193
+ "0.7",
194
+ "0.8",
195
+ "0.9"
196
+ ]);
197
+ const KernelVersion = Schema.Literals([
198
+ "0.3.0",
199
+ "0.3.1",
200
+ "0.3.2",
201
+ "0.3.3"
202
+ ]);
203
+ const OwnerType = Schema.Literals([
204
+ "ecdsa",
205
+ "passkey",
206
+ "multisig"
207
+ ]);
208
+ //#endregion
209
+ //#region src/schema/policy.ts
210
+ const SudoPolicyParams = Schema.Struct({ type: Schema.Literal("sudo") });
211
+ const TimestampPolicyParams = Schema.Struct({
212
+ type: Schema.Literal("timestamp"),
213
+ validAfter: Schema.optional(Schema.Number).pipe(Schema.withDecodingDefault(() => 0)).annotate({ description: "The timestamp in seconds after which signer is valid. If not provided, the signer is valid immediately." }),
214
+ validUntil: Schema.optional(Schema.Number).pipe(Schema.withDecodingDefault(() => 0)).annotate({ description: "The timestamp in seconds until which signer is valid. If not provided, the signer is valid indefinitely." })
215
+ });
216
+ const SignatureCallerPolicyParams = Schema.Struct({
217
+ type: Schema.Literal("signature-caller"),
218
+ allowedCallers: Schema.mutable(Schema.Array(EthereumAddress)).annotate({ description: "List of addresses that are allowed to validate messages signed by the signer." })
219
+ });
220
+ const RateLimitPolicyParams = Schema.Struct({
221
+ type: Schema.Literal("rate-limit"),
222
+ interval: Schema.optional(Schema.Number).annotate({ description: "Length of interval in seconds" }),
223
+ count: Schema.Number.annotate({ description: "The number of calls allowed within the interval" }),
224
+ startAt: Schema.optional(Schema.Number).annotate({ description: "The timestamp in seconds at which the rate limit starts. Before this signer cannot sign any UserOperations" })
225
+ });
226
+ const GasPolicyParams = Schema.Struct({
227
+ type: Schema.Literal("gas"),
228
+ amount: Schema.optional(BigIntFromString).annotate({ description: "Amount, in wei that the signer can spend on gas, in total across all UserOps it sends." }),
229
+ enforcePaymaster: Schema.optional(Schema.Boolean).annotate({
230
+ description: "If set to true, enforce that a paymaster must be used.",
231
+ default: false
232
+ })
233
+ });
234
+ const CallPolicyVersion$1 = Schema.Literals([
235
+ "0.0.1",
236
+ "0.0.2",
237
+ "0.0.3",
238
+ "0.0.4",
239
+ "0.0.5"
240
+ ]);
241
+ const CallType$1 = Schema.Literals([
242
+ "call",
243
+ "delegatecall",
244
+ "batch-call"
245
+ ]);
246
+ const ParamCondition$1 = Schema.Literals([
247
+ "EQUAL",
248
+ "GREATER_THAN",
249
+ "LESS_THAN",
250
+ "GREATER_THAN_OR_EQUAL",
251
+ "LESS_THAN_OR_EQUAL",
252
+ "NOT_EQUAL",
253
+ "ONE_OF",
254
+ "SLICE_EQUAL"
255
+ ]);
256
+ const ConditionValue = Schema.Union([
257
+ Schema.Struct({
258
+ condition: ParamCondition$1.pick(["ONE_OF"]),
259
+ value: Schema.mutable(Schema.Array(Schema.Any)).annotate({ description: "The value of the argument to use with the operator." })
260
+ }),
261
+ Schema.Struct({
262
+ condition: ParamCondition$1.pick(["SLICE_EQUAL"]),
263
+ value: Schema.Any.annotate({ description: "The value of the argument to use with the operator." }),
264
+ start: Schema.Number,
265
+ length: Schema.Number
266
+ }),
267
+ Schema.Struct({
268
+ condition: ParamCondition$1.pick([
269
+ "EQUAL",
270
+ "GREATER_THAN",
271
+ "LESS_THAN",
272
+ "GREATER_THAN_OR_EQUAL",
273
+ "LESS_THAN_OR_EQUAL",
274
+ "NOT_EQUAL",
275
+ "SLICE_EQUAL"
276
+ ]),
277
+ value: Schema.Any.annotate({ description: "The value of the argument to use with the operator." })
278
+ }),
279
+ Schema.Null
280
+ ]);
281
+ const ParamRule = Schema.Struct({
282
+ condition: ParamCondition$1,
283
+ offset: Schema.Number,
284
+ params: Schema.Union([Hex, Schema.mutable(Schema.Array(Hex))])
285
+ });
286
+ const PermissionCore = Schema.Struct({
287
+ callType: Schema.optional(CallType$1).pipe(Schema.withDecodingDefault(() => "call")).annotate({
288
+ description: "The type of call to make",
289
+ default: "call"
290
+ }),
291
+ target: EthereumAddress.annotate({ description: "The target contract to call or address to send ETH to. If this is zeroAddress, then the target can be any contract as long as the ABI matches (or it can be any address if no ABI is specified)" }),
292
+ selector: Schema.optional(Hex).annotate({ description: "The function selector if the target is a contract" }),
293
+ valueLimit: Schema.optional(BigIntFromString).annotate({ description: "The maximum value in wei that can be sent to the target" }),
294
+ rules: Schema.optional(Schema.Array(ParamRule))
295
+ });
296
+ const PermissionManual = PermissionCore;
297
+ const PermissionWithABI = PermissionCore.mapFields(Struct.omit(["rules"])).mapFields(Struct.assign({
298
+ abi: Schema.mutable(Schema.Array(Schema.Any)).annotate({ description: "The ABI of the target contract" }),
299
+ functionName: Schema.String.annotate({ description: "The function name" }),
300
+ args: Schema.optional(Schema.Array(ConditionValue)).annotate({ description: "An array of conditions, each corresponding to an argument, in the order that the arguments are laid out. use null to skip an argument." })
301
+ }));
302
+ const Permission = Schema.Union([PermissionManual, PermissionWithABI]);
303
+ const CallPolicyParams = Schema.Struct({
304
+ type: Schema.Literal("call"),
305
+ policyVersion: CallPolicyVersion$1,
306
+ permissions: Schema.optional(Schema.Array(Permission))
307
+ });
308
+ const PolicyParams = Schema.Union([
309
+ SudoPolicyParams,
310
+ TimestampPolicyParams,
311
+ SignatureCallerPolicyParams,
312
+ RateLimitPolicyParams,
313
+ GasPolicyParams,
314
+ CallPolicyParams
315
+ ]);
316
+ //#endregion
317
+ //#region src/dto/session-key.ts
318
+ const BaseSessionKey = Schema.Struct({
319
+ smartAccountAlias: Schema.String,
320
+ serializedAccounts: Schema.Array(Schema.Struct({
321
+ chain: SupportedChain,
322
+ serializedAccount: Schema.String
323
+ }))
324
+ }).mapFields(Struct.assign(Keystore.fields));
325
+ const EcdsaSessionKey = Schema.Struct({
326
+ sessionKeyType: Schema.Literal("ecdsa"),
327
+ sessionKeyAddress: EthereumAddress,
328
+ smartAccountAlias: Schema.String
329
+ }).mapFields(Struct.assign(BaseSessionKey.fields));
330
+ const PasskeySessionKey = Schema.Struct({
331
+ sessionKeyType: Schema.Literal("passkey"),
332
+ passKeyName: Schema.String
333
+ }).mapFields(Struct.assign(BaseSessionKey.fields));
334
+ const SessionKey = Schema.Union([EcdsaSessionKey, PasskeySessionKey]);
335
+ Schema.Struct({
336
+ alias: Schema.String,
337
+ data: SessionKey,
338
+ path: Schema.String
339
+ });
340
+ const CreateSessionKeyParams = Schema.Struct({
341
+ alias: Schema.String.annotate({ description: "The alias of the session key to create" }),
342
+ chains: Schema.mutable(Schema.Array(SupportedChain)).annotate({ description: "The chains to create the session key for" }),
343
+ policyParams: Schema.mutable(Schema.Array(PolicyParams)),
344
+ smartAccountAlias: Schema.String.annotate({ description: "The alias of the smart account to create the session key for" }),
345
+ ownerKeystorePassword: Schema.redact(Schema.String).annotate({ description: "The password to decrypt the owner keystore with" }),
346
+ sessionKeyPassword: Schema.redact(Schema.String).annotate({ description: "The password to encrypt the session key with" })
347
+ });
348
+ Schema.Struct({ alias: Schema.String.annotate({ description: "The alias of the session key to retrieve" }) });
349
+ const ListSessionKeysParams = Schema.Struct({ smartAccount: Schema.optional(Schema.String).annotate({ description: "The alias of the smart account to list session keys for" }) });
350
+ const GetSessionKeyInfoParams = Schema.Struct({ alias: Schema.String.annotate({ description: "The alias of the session key to retrieve" }) });
351
+ const GetSessionKeyStatusParams = Schema.Struct({
352
+ alias: Schema.String.annotate({ description: "The alias of the session key to retrieve" }),
353
+ chain: SupportedChain.annotate({ description: "The chain to retrieve the session key status for" }),
354
+ rpcUrl: Schema.redact(Schema.String.pipe(Schema.optional)).annotate({
355
+ description: "The RPC URL to use for the chain",
356
+ default: "Public RPC URL"
357
+ })
358
+ });
359
+ const RemoveSessionKeyParams = Schema.Struct({ alias: Schema.String.annotate({ description: "The alias of the session key to remove" }) });
360
+ //#endregion
361
+ //#region src/dto/smart-account.ts
362
+ const LocalSmartAccount = Schema.Struct({
363
+ entryPointVersion: EntrypointVersion,
364
+ index: BigIntFromString,
365
+ kernelVersion: KernelVersion,
366
+ ownerAlias: Schema.String,
367
+ ownerType: OwnerType,
368
+ smartAccountAddress: EthereumAddress
369
+ });
370
+ Schema.Struct({
371
+ data: LocalSmartAccount,
372
+ path: Schema.String,
373
+ alias: Schema.String
374
+ });
375
+ const CreateSmartAccountParams = Schema.Struct({
376
+ alias: Schema.String.annotate({ description: "The alias of the smart account to create" }),
377
+ ownerAlias: Schema.String.annotate({ description: "The alias of the owner keystore" }),
378
+ ownerPassword: Schema.redact(Schema.String).annotate({ description: "The password of the owner keystore" }),
379
+ index: Schema.optional(BigIntFromString.check(Schema.makeFilter((v) => v >= 0n))).annotate({
380
+ description: "The index of the smart account",
381
+ default: 0n
382
+ })
383
+ });
384
+ Schema.Struct({ alias: Schema.String.annotate({ description: "The alias of the smart account to retrieve" }) });
385
+ const ListSmartAccountParams = Schema.Void;
386
+ const GetSmartAccountInfoParams = Schema.Struct({ alias: Schema.String.annotate({ description: "The alias of the smart account to retrieve" }) });
387
+ const RemoveSmartAccountParams = Schema.Struct({ alias: Schema.String.annotate({ description: "The alias of the smart account to remove" }) });
388
+ const GetSmartAccountStatusParams = Schema.Struct({
389
+ alias: Schema.String.annotate({ description: "The alias of the smart account" }),
390
+ chain: SupportedChain.annotate({ description: "The chain to get the status for" }),
391
+ rpcUrl: Schema.redact(Schema.String.pipe(Schema.optional)).annotate({
392
+ description: "The RPC URL to use for the chain",
393
+ default: "Public RPC URL"
394
+ })
395
+ });
396
+ const ImportSmartAccountParams = LocalSmartAccount.pipe(Schema.fieldsAssign({ alias: Schema.String.annotate({ description: "The alias of the smart account to import" }) }));
397
+ //#endregion
398
+ //#region src/flags/global.ts
399
+ const globalOutput = GlobalFlag.setting("output")({ flag: Flag.choice("output", [
400
+ "pretty",
401
+ "json",
402
+ "ndjson"
403
+ ]).pipe(Flag.withAlias("o"), Flag.withDefault("pretty"), Flag.withDescription("Output format (pretty, json, ndjson)")) });
404
+ const globalQuite = GlobalFlag.setting("quite")({ flag: Flag.boolean("quite").pipe(Flag.withAlias("q"), Flag.withDefault(false), Flag.withDescription("Do not print output")) });
405
+ const globalParams = GlobalFlag.setting("params")({ flag: Flag.string("params").pipe(Flag.optional, Flag.withDescription("JSON Parameters to pass to the command")) });
406
+ const getGlobalFlags = () => Effect.gen(function* () {
407
+ const out = yield* globalOutput;
408
+ const quite = yield* globalQuite;
409
+ return {
410
+ out,
411
+ params: yield* globalParams,
412
+ quite
413
+ };
414
+ });
415
+ const globalFlags = [
416
+ globalOutput,
417
+ globalQuite,
418
+ globalParams
419
+ ];
420
+ //#endregion
421
+ //#region src/types/index.ts
422
+ const entityName = {
423
+ keystore: "Keystore",
424
+ "session-key": "Session Key",
425
+ "smart-account": "Smart Account"
426
+ };
427
+ //#endregion
428
+ //#region src/layers/config.ts
429
+ /**
430
+ * Domain error for configuration and entity storage operations.
431
+ */
432
+ var ConfigManagerError = class extends Data.TaggedError("@namera-ai/cli/ConfigManagerError") {};
433
+ /**
434
+ * Service tag for resolving {@link ConfigManager} from the Effect context.
435
+ */
436
+ const ConfigManager = ServiceMap.Service("@namera-ai/cli/ConfigManager");
437
+ /**
438
+ * Live layer that persists CLI entities in the user's config directory.
439
+ */
440
+ const layer$7 = Layer.effect(ConfigManager, Effect.gen(function* () {
441
+ const fs = yield* FileSystem.FileSystem;
442
+ const path = yield* Path.Path;
443
+ const getConfigDirPath = () => Effect.gen(function* () {
444
+ const homeDir = yield* Effect.sync(() => os.homedir());
445
+ return path.join(homeDir, ".namera");
446
+ });
447
+ const ensureConfigDirExists = () => Effect.gen(function* () {
448
+ const baseDir = yield* getConfigDirPath();
449
+ const directoriesToCreate = [
450
+ "smart-accounts",
451
+ "session-keys",
452
+ "keystores"
453
+ ].map((dir) => path.join(baseDir, dir));
454
+ yield* Effect.forEach(directoriesToCreate, (dirPath) => fs.makeDirectory(dirPath, { recursive: true }).pipe(Effect.catchTag("PlatformError", (e) => Effect.fail(new ConfigManagerError({
455
+ code: "InitializationError",
456
+ message: e.message
457
+ })))), { concurrency: "unbounded" });
458
+ });
459
+ const getEntityPath = (entity) => Effect.gen(function* () {
460
+ const baseDir = yield* getConfigDirPath();
461
+ return path.join(baseDir, `${entity.type}s`, entity.alias);
462
+ });
463
+ const checkEntityExists = (entity) => Effect.gen(function* () {
464
+ const entityPath = yield* getEntityPath(entity);
465
+ return yield* fs.exists(entityPath).pipe(Effect.catchTag("PlatformError", (e) => Effect.fail(new ConfigManagerError({
466
+ code: e.reason._tag,
467
+ message: e.message
468
+ }))));
469
+ });
470
+ const getEntity = (entity) => Effect.gen(function* () {
471
+ const entityPath = yield* getEntityPath(entity);
472
+ if (yield* checkEntityExists(entity)) {
473
+ const content = yield* fs.readFileString(entityPath);
474
+ return {
475
+ alias: entity.alias,
476
+ content,
477
+ path: entityPath,
478
+ type: entity.type
479
+ };
480
+ }
481
+ return yield* Effect.fail(new ConfigManagerError({
482
+ code: "EntityNotFound",
483
+ message: `${entityName[entity.type]} with alias ${entity.alias} does not exist`
484
+ }));
485
+ }).pipe(Effect.catchTag("PlatformError", (e) => Effect.fail(new ConfigManagerError({
486
+ code: e.reason._tag,
487
+ message: e.message
488
+ }))));
489
+ const getEntitiesForType = (type) => Effect.gen(function* () {
490
+ const baseDir = yield* getConfigDirPath();
491
+ const entitiesDir = path.join(baseDir, `${type}s`);
492
+ const effects = (yield* fs.readDirectory(entitiesDir)).map((entityName) => Effect.gen(function* () {
493
+ const entityPath = path.join(entitiesDir, entityName);
494
+ return {
495
+ alias: entityName,
496
+ content: yield* fs.readFileString(entityPath),
497
+ path: entityPath,
498
+ type
499
+ };
500
+ }));
501
+ return yield* Effect.all(effects, { concurrency: "unbounded" });
502
+ }).pipe(Effect.catchTag("PlatformError", (e) => Effect.fail(new ConfigManagerError({
503
+ code: e.reason._tag,
504
+ message: e.message
505
+ }))));
506
+ const storeEntity = (entity) => Effect.gen(function* () {
507
+ const entityPath = yield* getEntityPath(entity);
508
+ if (yield* checkEntityExists({
509
+ alias: entity.alias,
510
+ type: entity.type
511
+ })) return yield* Effect.fail(new ConfigManagerError({
512
+ code: "EntityAlreadyExists",
513
+ message: `Entity ${entity.alias} already exists`
514
+ }));
515
+ yield* fs.writeFileString(entityPath, entity.content);
516
+ return {
517
+ alias: entity.alias,
518
+ content: entity.content,
519
+ path: entityPath,
520
+ type: entity.type
521
+ };
522
+ }).pipe(Effect.catchTag("PlatformError", (e) => Effect.fail(new ConfigManagerError({
523
+ code: e.reason._tag,
524
+ message: e.message
525
+ }))));
526
+ const removeEntity = (entity) => Effect.gen(function* () {
527
+ const entityPath = yield* getEntityPath(entity);
528
+ yield* fs.remove(entityPath).pipe(Effect.catchTag("PlatformError", (e) => Effect.fail(new ConfigManagerError({
529
+ code: e.reason._tag,
530
+ message: e.message
531
+ }))));
532
+ });
533
+ return ConfigManager.of({
534
+ checkEntityExists,
535
+ ensureConfigDirExists,
536
+ getConfigDirPath,
537
+ getEntitiesForType,
538
+ getEntity,
539
+ getEntityPath,
540
+ storeEntity,
541
+ removeEntity
542
+ });
543
+ }));
544
+ //#endregion
545
+ //#region src/layers/prompt.ts
546
+ /**
547
+ * Service tag for resolving {@link PromptManager} from the Effect context.
548
+ */
549
+ const PromptManager = ServiceMap.Service("@namera-ai/cli/PromptManager");
550
+ /**
551
+ * Live layer that validates and returns interactive prompt input.
552
+ */
553
+ const layer$6 = Layer.effect(PromptManager, Effect.gen(function* () {
554
+ const configManager = yield* ConfigManager;
555
+ const aliasPrompt = (params) => Effect.gen(function* () {
556
+ return yield* Prompt.text({
557
+ message: params.message,
558
+ validate: (v) => Effect.gen(function* () {
559
+ if (v.trim() === "") return yield* Effect.fail("Alias cannot be empty");
560
+ const exists = yield* configManager.checkEntityExists({
561
+ alias: v,
562
+ type: params.type
563
+ }).pipe(Effect.catchTag("@namera-ai/cli/ConfigManagerError", (e) => Effect.fail(e.message)));
564
+ if (params.aliasType === "new" && exists) return yield* Effect.fail(`${entityName[params.type]} with alias ${v} already exists`);
565
+ if (params.aliasType === "existing" && !exists) return yield* Effect.fail(`${entityName[params.type]} with alias ${v} does not exist`);
566
+ return v;
567
+ })
568
+ });
569
+ });
570
+ const passwordPrompt = (params) => Effect.gen(function* () {
571
+ return yield* Prompt.password({
572
+ message: params.message,
573
+ validate: (v) => Effect.gen(function* () {
574
+ if (v.trim() === "") return yield* Effect.fail("Password cannot be empty");
575
+ if (params.validate) return yield* params.validate(v);
576
+ return v;
577
+ })
578
+ });
579
+ });
580
+ const selectPrompt = (params) => Effect.gen(function* () {
581
+ return yield* Prompt.select(params);
582
+ });
583
+ const multiSelectPrompt = (params) => Effect.gen(function* () {
584
+ return yield* Prompt.multiSelect(params);
585
+ });
586
+ function hexPrompt(params) {
587
+ return Effect.gen(function* () {
588
+ const validate = (v) => Effect.gen(function* () {
589
+ if (v.trim() === "") return yield* Effect.fail("Hex cannot be empty");
590
+ if (!isHex(v)) return yield* Effect.fail("Invalid hex value");
591
+ if (hexToBytes(v).length !== params.length) return yield* Effect.fail(`Hex value must be ${params.length} bytes`);
592
+ return v;
593
+ });
594
+ if (params.redacted) return yield* Prompt.password({
595
+ message: params.message,
596
+ validate
597
+ });
598
+ return yield* Prompt.text({
599
+ message: params.message,
600
+ validate
601
+ });
602
+ });
603
+ }
604
+ return PromptManager.of({
605
+ aliasPrompt,
606
+ passwordPrompt,
607
+ selectPrompt,
608
+ hexPrompt,
609
+ multiSelectPrompt
610
+ });
611
+ }));
612
+ //#endregion
613
+ //#region src/layers/keystore.ts
614
+ /**
615
+ * Service tag for resolving {@link KeystoreManager} from the Effect context.
616
+ */
617
+ const KeystoreManager = ServiceMap.Service("@namera-ai/cli/KeystoreManager");
618
+ /**
619
+ * Domain error for keystore management operations.
620
+ */
621
+ var KeystoreManagerError = class extends Data.TaggedError("@namera-ai/cli/KeystoreManagerError") {};
622
+ /**
623
+ * Live layer wiring the keystore manager with configuration and prompts.
624
+ */
625
+ const layer$5 = Layer.effect(KeystoreManager, Effect.gen(function* () {
626
+ const configManager = yield* ConfigManager;
627
+ const promptManager = yield* PromptManager;
628
+ const getKeystore = (params) => Effect.gen(function* () {
629
+ const res = yield* configManager.getEntity({
630
+ alias: params.alias,
631
+ type: "keystore"
632
+ });
633
+ const parsedKeystore = yield* Effect.try({
634
+ catch: () => new KeystoreManagerError({
635
+ code: "KeystoreParseError",
636
+ message: "Unable to parse keystore"
637
+ }),
638
+ try: () => JSON.parse(res.content)
639
+ });
640
+ return {
641
+ alias: res.alias,
642
+ data: {
643
+ ...parsedKeystore,
644
+ address: `0x${parsedKeystore.address}`
645
+ },
646
+ path: res.path
647
+ };
648
+ });
649
+ const listKeystores = () => Effect.gen(function* () {
650
+ const effects = (yield* configManager.getEntitiesForType("keystore")).map((entity) => Effect.gen(function* () {
651
+ const parsedKeystore = yield* Effect.try({
652
+ catch: () => new KeystoreManagerError({
653
+ code: "KeystoreParseError",
654
+ message: "Unable to parse keystore"
655
+ }),
656
+ try: () => JSON.parse(entity.content)
657
+ });
658
+ return {
659
+ alias: entity.alias,
660
+ data: {
661
+ ...parsedKeystore,
662
+ address: `0x${parsedKeystore.address}`
663
+ },
664
+ path: entity.path
665
+ };
666
+ }));
667
+ return yield* Effect.all(effects, { concurrency: "unbounded" });
668
+ });
669
+ const createKeystore = (params) => Effect.gen(function* () {
670
+ const entityPath = yield* configManager.getEntityPath({
671
+ alias: params.alias,
672
+ type: "keystore"
673
+ });
674
+ if (yield* configManager.checkEntityExists({
675
+ alias: params.alias,
676
+ type: "keystore"
677
+ })) return yield* Effect.fail(new KeystoreManagerError({
678
+ code: "KeystoreAlreadyExists",
679
+ message: `Keystore with alias ${params.alias} already exists`
680
+ }));
681
+ const keystoreString = yield* Effect.tryPromise({
682
+ catch: () => new KeystoreManagerError({
683
+ code: "KeystoreCreationFailed",
684
+ message: "Failed to create keystore"
685
+ }),
686
+ try: () => Wallet.generate().toV3String(params.password)
687
+ });
688
+ yield* configManager.storeEntity({
689
+ alias: params.alias,
690
+ content: keystoreString,
691
+ path: entityPath,
692
+ type: "keystore"
693
+ });
694
+ const keystore = yield* Effect.try({
695
+ catch: () => new KeystoreManagerError({
696
+ code: "KeystoreParseError",
697
+ message: "Unable to parse keystore"
698
+ }),
699
+ try: () => JSON.parse(keystoreString)
700
+ });
701
+ return {
702
+ alias: params.alias,
703
+ data: {
704
+ ...keystore,
705
+ address: `0x${keystore.address}`
706
+ },
707
+ path: entityPath
708
+ };
709
+ });
710
+ const decryptKeystore = (params) => Effect.gen(function* () {
711
+ const keystore = yield* getKeystore({ alias: params.alias });
712
+ const wallet = yield* Effect.tryPromise({
713
+ catch: () => new KeystoreManagerError({
714
+ code: "KeystoreDecryptionFailed",
715
+ message: "Failed to decrypt keystore"
716
+ }),
717
+ try: () => Wallet.fromV3(keystore.data, params.password)
718
+ });
719
+ return {
720
+ address: wallet.getChecksumAddressString(),
721
+ alias: params.alias,
722
+ privateKey: Redacted.make(wallet.getPrivateKeyString()),
723
+ publicKey: wallet.getPublicKeyString()
724
+ };
725
+ });
726
+ const selectKeystore = (params) => Effect.gen(function* () {
727
+ const keystores = yield* listKeystores();
728
+ return yield* promptManager.selectPrompt({
729
+ message: params.message,
730
+ choices: keystores.map((k) => ({
731
+ title: k.alias,
732
+ value: k,
733
+ description: k.data.address
734
+ }))
735
+ });
736
+ });
737
+ const importKeystore = (params) => Effect.gen(function* () {
738
+ const entityPath = yield* configManager.getEntityPath({
739
+ alias: params.alias,
740
+ type: "keystore"
741
+ });
742
+ if (yield* configManager.checkEntityExists({
743
+ alias: params.alias,
744
+ type: "keystore"
745
+ })) return yield* Effect.fail(new KeystoreManagerError({
746
+ code: "KeystoreAlreadyExists",
747
+ message: `Keystore with alias ${params.alias} already exists`
748
+ }));
749
+ const keystoreString = yield* Effect.tryPromise({
750
+ catch: () => new KeystoreManagerError({
751
+ code: "KeystoreCreationFailed",
752
+ message: "Failed to create keystore"
753
+ }),
754
+ try: () => Wallet.fromPrivateKey(hexToBytes(params.privateKey)).toV3String(params.password)
755
+ });
756
+ yield* configManager.storeEntity({
757
+ alias: params.alias,
758
+ content: keystoreString,
759
+ path: entityPath,
760
+ type: "keystore"
761
+ });
762
+ const keystore = yield* Effect.try({
763
+ catch: () => new KeystoreManagerError({
764
+ code: "KeystoreParseError",
765
+ message: "Unable to parse keystore"
766
+ }),
767
+ try: () => JSON.parse(keystoreString)
768
+ });
769
+ return {
770
+ alias: params.alias,
771
+ data: keystore,
772
+ path: entityPath
773
+ };
774
+ });
775
+ const removeKeystore = (params) => Effect.gen(function* () {
776
+ if (!(yield* configManager.checkEntityExists({
777
+ alias: params.alias,
778
+ type: "keystore"
779
+ }))) return yield* Effect.fail(new KeystoreManagerError({
780
+ code: "KeystoreNotFound",
781
+ message: `Keystore with alias ${params.alias} does not exist`
782
+ }));
783
+ return yield* configManager.removeEntity({
784
+ alias: params.alias,
785
+ type: "keystore"
786
+ });
787
+ });
788
+ const getSigner = (params) => Effect.gen(function* () {
789
+ const keystore = yield* getKeystore({ alias: params.alias });
790
+ return privateKeyToAccount(toHex((yield* Effect.tryPromise({
791
+ catch: () => new KeystoreManagerError({
792
+ code: "KeystoreDecryptionFailed",
793
+ message: "Failed to decrypt keystore"
794
+ }),
795
+ try: () => Wallet.fromV3(keystore.data, params.password)
796
+ })).getPrivateKey()));
797
+ });
798
+ const getKeystorePassword = (params) => Effect.gen(function* () {
799
+ const keystore = yield* getKeystore({ alias: params.alias });
800
+ return yield* promptManager.passwordPrompt({
801
+ message: params.message,
802
+ validate: (v) => Effect.gen(function* () {
803
+ if (v.trim() === "") return yield* Effect.fail("Password cannot be empty");
804
+ yield* Effect.tryPromise({
805
+ try: () => Wallet.fromV3(keystore.data, v),
806
+ catch: () => new KeystoreManagerError({
807
+ code: "DecryptError",
808
+ message: `Invalid password for keystore ${params.alias}`
809
+ })
810
+ }).pipe(Effect.catch(() => Effect.fail("Invalid password")));
811
+ return v;
812
+ })
813
+ });
814
+ });
815
+ return KeystoreManager.of({
816
+ createKeystore,
817
+ decryptKeystore,
818
+ getKeystore,
819
+ listKeystores,
820
+ selectKeystore,
821
+ importKeystore,
822
+ removeKeystore,
823
+ getSigner,
824
+ getKeystorePassword
825
+ });
826
+ }));
827
+ //#endregion
828
+ //#region src/layers/mcp-context.ts
829
+ const McpContext = ServiceMap.Service("@namera-ai/cli/McpContext");
830
+ //#endregion
831
+ //#region src/mcp/helpers/common.ts
832
+ const EmptyArgs = Schema.Record(Schema.String, Schema.Unknown);
833
+ var InsufficientPermissions = class extends Schema.TaggedErrorClass()("InsufficientPermissions", {}) {};
834
+ //#endregion
835
+ //#region src/mcp/tools/account/address.ts
836
+ const GetAddressTool = Tool.make("get_wallet_address", {
837
+ dependencies: [McpContext],
838
+ description: "Get the address of the wallet",
839
+ failure: Schema.Never,
840
+ parameters: EmptyArgs,
841
+ success: Schema.String
842
+ });
843
+ const getAddressToolHandler = () => Effect.gen(function* () {
844
+ return (yield* McpContext).smartAccount.smartAccountAddress;
845
+ });
846
+ //#endregion
847
+ //#region src/layers/web3.ts
848
+ /**
849
+ * Service tag for resolving {@link Web3Service} from the Effect context.
850
+ */
851
+ const Web3Service = ServiceMap.Service("@namera-ai/cli/Web3Service");
852
+ /**
853
+ * Live layer that wires web3 clients and chain selection prompts.
854
+ */
855
+ const layer$4 = Layer.effect(Web3Service, Effect.gen(function* () {
856
+ const promptManager = yield* PromptManager;
857
+ const chainNameToEnvVar = (chain, suffix) => {
858
+ return `${chain.replaceAll("-", "_").toUpperCase()}_${suffix}`;
859
+ };
860
+ const getRpcUrl = (params) => Effect.gen(function* () {
861
+ let rpcUrl;
862
+ if (params.rpcUrl) rpcUrl = params.rpcUrl;
863
+ else {
864
+ const envVarName = chainNameToEnvVar(params.chain, "RPC_URL");
865
+ const envRpcUrl = yield* Config.option(Config.redacted(envVarName));
866
+ if (envRpcUrl._tag === "Some") rpcUrl = Redacted.value(envRpcUrl.value);
867
+ else rpcUrl = void 0;
868
+ }
869
+ return rpcUrl;
870
+ }).pipe(Effect.orDie);
871
+ const getBundlerUrl = (params) => Effect.gen(function* () {
872
+ let bundlerUrl;
873
+ if (params.bundlerUrl) bundlerUrl = params.bundlerUrl;
874
+ else {
875
+ const envVarName = chainNameToEnvVar(params.chain, "BUNDLER_URL");
876
+ const envRpcUrl = yield* Config.option(Config.redacted(envVarName));
877
+ if (envRpcUrl._tag === "Some") bundlerUrl = Redacted.value(envRpcUrl.value);
878
+ else bundlerUrl = `https://public.pimlico.io/v2/${getChain(params.chain).id}/rpc`;
879
+ }
880
+ return bundlerUrl;
881
+ }).pipe(Effect.orDie);
882
+ const getPublicClient = (params) => Effect.gen(function* () {
883
+ const rpcUrl = yield* getRpcUrl(params);
884
+ return createPublicClient({
885
+ chain: getChain(params.chain),
886
+ transport: http(rpcUrl)
887
+ });
888
+ });
889
+ const getBundlerTransport = (params) => Effect.gen(function* () {
890
+ return http(yield* getBundlerUrl(params));
891
+ });
892
+ const selectChain = (params) => Effect.gen(function* () {
893
+ const chains = Object.values(supportedChains);
894
+ return yield* promptManager.selectPrompt({
895
+ message: params.message,
896
+ choices: chains.map((c) => ({
897
+ title: c.name,
898
+ value: c.key
899
+ }))
900
+ });
901
+ });
902
+ const multiSelectChain = (params) => Effect.gen(function* () {
903
+ const chains = Object.values(supportedChains);
904
+ return yield* promptManager.multiSelectPrompt({
905
+ message: params.message,
906
+ choices: chains.map((c) => ({
907
+ title: c.name,
908
+ value: c.key
909
+ })),
910
+ min: 1
911
+ });
912
+ });
913
+ return Web3Service.of({
914
+ getPublicClient,
915
+ selectChain,
916
+ multiSelectChain,
917
+ getBundlerTransport
918
+ });
919
+ }));
920
+ //#endregion
921
+ //#region src/mcp/helpers/session-key.ts
922
+ const toPolicyMap = (policies) => {
923
+ const map = {};
924
+ for (const p of policies) {
925
+ if (p.type === "timestamp") {
926
+ if (map.timestamp) return null;
927
+ map.timestamp = p;
928
+ }
929
+ if (p.type === "call") {
930
+ if (map.call) return null;
931
+ map.call = p;
932
+ }
933
+ if (p.type === "signature-caller") {
934
+ if (map.signatureCaller) return null;
935
+ map.signatureCaller = p;
936
+ }
937
+ if (p.type === "gas") {
938
+ if (map.gas) return null;
939
+ map.gas = p;
940
+ }
941
+ if (p.type === "rate-limit") {
942
+ if (map.rateLimit) return null;
943
+ map.rateLimit = p;
944
+ }
945
+ if (p.type === "sudo") {
946
+ if (map.sudo) return null;
947
+ map.sudo = p;
948
+ }
949
+ }
950
+ return map;
951
+ };
952
+ function evaluateTimestamp(policy, _chainIds, _operation) {
953
+ const now = Date.now();
954
+ if (policy.validAfter !== void 0 && now < policy.validAfter * 1e3) return false;
955
+ if (policy.validUntil !== void 0 && now >= policy.validUntil * 1e3) return false;
956
+ return true;
957
+ }
958
+ function evaluateSignature(policy, chainIds, operation) {
959
+ const callers = operation.allowedCallers;
960
+ if (!callers || callers.length === 0) return false;
961
+ if (!chainIds.includes(operation.chainId)) return false;
962
+ return callers.every((caller) => policy.allowedCallers.includes(caller));
963
+ }
964
+ function evaluateCall(policy, chainIds, operation) {
965
+ const permissions = policy.permissions ?? [];
966
+ if (permissions.length === 0) return false;
967
+ const { batches } = operation;
968
+ if (batches.length === 0) return false;
969
+ return batches.every((batch) => {
970
+ const { calls, chainId } = batch;
971
+ return calls.every((call) => {
972
+ const { to, value = 0n, data } = call;
973
+ if (!chainIds.includes(chainId)) return false;
974
+ return permissions.some((p) => {
975
+ if (p.target !== to) return false;
976
+ if (value > (p.valueLimit ?? 0n)) return false;
977
+ const isContractCall = data.length >= 10;
978
+ if ("functionName" in p) {
979
+ if (!isContractCall) return false;
980
+ if (!p.selector) return false;
981
+ if (data.slice(0, 10) !== p.selector) return false;
982
+ return true;
983
+ }
984
+ return true;
985
+ });
986
+ });
987
+ });
988
+ }
989
+ const evaluateSessionKey = (sessionKey, operation) => {
990
+ if (operation.intent === "read") return true;
991
+ const accountParams = deserializePermissionAccountParams(sessionKey.data.serializedAccounts[0]?.serializedAccount ?? "");
992
+ const chainIds = sessionKey.data.serializedAccounts.map((a) => getChain(a.chain).id);
993
+ const p = toPolicyMap((accountParams.permissionParams.policies ?? []).map((x) => x.policyParams));
994
+ if (!p) return false;
995
+ const hasSudo = Boolean(p.sudo);
996
+ if (hasSudo && (p.call || p.signatureCaller)) return false;
997
+ if (p.timestamp && !evaluateTimestamp(p.timestamp, chainIds, operation)) return false;
998
+ if (!hasSudo) {
999
+ if (operation.intent === "transaction") {
1000
+ if (!p.call) return false;
1001
+ if (!evaluateCall(p.call, chainIds, operation)) return false;
1002
+ }
1003
+ if (operation.intent === "sign") {
1004
+ if (!p.signatureCaller) return false;
1005
+ if (!evaluateSignature(p.signatureCaller, chainIds, operation)) return false;
1006
+ }
1007
+ }
1008
+ return true;
1009
+ };
1010
+ const getSessionKeyClient = (params) => Effect.gen(function* () {
1011
+ const { sessionKeys, smartAccount } = yield* McpContext;
1012
+ const key = sessionKeys.filter((sk) => {
1013
+ return evaluateSessionKey(sk, params.operation);
1014
+ })[0];
1015
+ if (!key) return yield* Effect.fail(new InsufficientPermissions());
1016
+ const web3Service = yield* Web3Service;
1017
+ let chainId;
1018
+ if (params.operation.intent === "read") chainId = params.operation.chainId;
1019
+ else if (params.operation.intent === "sign") chainId = params.operation.chainId;
1020
+ else chainId = params.operation.batches[0]?.chainId ?? 1;
1021
+ const chainName = chainIdToChainName(chainId);
1022
+ const chain = getChain(chainName);
1023
+ const publicClient = yield* web3Service.getPublicClient({ chain: chainName });
1024
+ const bundlerTransport = yield* web3Service.getBundlerTransport({ chain: chainName });
1025
+ const serializedAccount = key.data.serializedAccounts.find((a) => a.chain === chainName)?.serializedAccount ?? "";
1026
+ return yield* Effect.promise(() => createSessionKeyClient({
1027
+ type: "ecdsa",
1028
+ bundlerTransport,
1029
+ chain,
1030
+ client: publicClient,
1031
+ entrypointVersion: smartAccount.entryPointVersion,
1032
+ kernelVersion: smartAccount.kernelVersion,
1033
+ serializedAccount,
1034
+ sessionKeySigner: key.signer
1035
+ }));
1036
+ });
1037
+ //#endregion
1038
+ //#region src/mcp/tools/account/get-balance.ts
1039
+ const GetBalanceToolParams = Schema.Struct({
1040
+ address: EthereumAddress.annotate({ description: "The address to get the balance for" }),
1041
+ chain: SupportedChain.annotate({ description: "The chain to get the balance for" })
1042
+ });
1043
+ const GetBalanceToolResult = Schema.Struct({
1044
+ nativeCurrency: Schema.Struct({
1045
+ name: Schema.String.annotate({ description: "The name of the currency" }),
1046
+ symbol: Schema.String.annotate({ description: "The symbol of the currency" }),
1047
+ decimals: Schema.Number.annotate({ description: "The number of decimals the currency has" })
1048
+ }).annotate({ description: "The native currency of the chain" }),
1049
+ balance: Schema.Struct({
1050
+ amount: Schema.String.annotate({ description: "Balance in atomic units" }),
1051
+ formatted: Schema.String.annotate({ description: "Balance in native currency units" })
1052
+ })
1053
+ });
1054
+ const GetBalanceTool = Tool.make("get_balance", {
1055
+ dependencies: [McpContext, Web3Service],
1056
+ description: "Get the address of the wallet",
1057
+ failure: InsufficientPermissions,
1058
+ parameters: GetBalanceToolParams,
1059
+ success: GetBalanceToolResult
1060
+ });
1061
+ const getBalanceToolHandler = (params) => Effect.gen(function* () {
1062
+ const sessionKeyClient = yield* getSessionKeyClient({ operation: {
1063
+ intent: "read",
1064
+ chainId: getChain(params.chain).id
1065
+ } });
1066
+ const res = yield* Effect.promise(() => getBalance(sessionKeyClient.client, { address: params.address }));
1067
+ return {
1068
+ nativeCurrency: sessionKeyClient.chain.nativeCurrency,
1069
+ balance: {
1070
+ amount: res.toString(),
1071
+ formatted: formatUnits(res, sessionKeyClient.chain.nativeCurrency.decimals)
1072
+ }
1073
+ };
1074
+ });
1075
+ //#endregion
1076
+ //#region src/mcp/tools/account/index.ts
1077
+ const AccountTools = Toolkit.make(GetAddressTool, GetBalanceTool);
1078
+ const AccountToolsHandlers = AccountTools.toLayer(Effect.succeed({
1079
+ get_wallet_address: getAddressToolHandler,
1080
+ get_balance: getBalanceToolHandler
1081
+ }));
1082
+ //#endregion
1083
+ //#region src/mcp/tools/read/read-contract.ts
1084
+ const ReadContractToolParams = Schema.Struct({
1085
+ chain: SupportedChain.annotate({ description: "The chain to use for the transfer" }),
1086
+ contractAddress: EthereumAddress.annotate({ description: "The ethereum address of the contract to read" }),
1087
+ abi: Schema.Array(Schema.Any).annotate({ description: "The ABI of the contract to read" }),
1088
+ functionName: Schema.String.annotate({ description: "The name of the function to call on the contract" }),
1089
+ args: Schema.Array(Schema.Any).annotate({ description: "The arguments to pass to the function" })
1090
+ });
1091
+ const ReadContractTool = Tool.make("read_contract", {
1092
+ dependencies: [McpContext, Web3Service],
1093
+ description: "Read data from a specified contract.",
1094
+ failure: InsufficientPermissions,
1095
+ parameters: ReadContractToolParams,
1096
+ success: Schema.Any.annotate({ description: "The data returned by the contract" })
1097
+ });
1098
+ const readContractToolHandler = (params) => Effect.gen(function* () {
1099
+ const client = yield* getSessionKeyClient({ operation: {
1100
+ intent: "read",
1101
+ chainId: getChain(params.chain).id
1102
+ } });
1103
+ readContract(client, {
1104
+ address: params.contractAddress,
1105
+ abi: params.abi,
1106
+ functionName: params.functionName,
1107
+ args: params.args
1108
+ });
1109
+ return yield* Effect.promise(() => readContract(client, {
1110
+ address: params.contractAddress,
1111
+ abi: params.abi,
1112
+ functionName: params.functionName,
1113
+ args: params.args
1114
+ }));
1115
+ });
1116
+ //#endregion
1117
+ //#region src/mcp/tools/read/index.ts
1118
+ const ReadTools = Toolkit.make(ReadContractTool);
1119
+ const ReadToolsHandlers = ReadTools.toLayer(Effect.succeed({ read_contract: (params) => readContractToolHandler(params) }));
1120
+ //#endregion
1121
+ //#region src/schema/tx.ts
1122
+ const Call = Schema.Struct({
1123
+ to: EthereumAddress.annotate({ description: "The target address to call" }),
1124
+ data: Hex.annotate({ description: "The data to send with the call" }),
1125
+ value: Schema.optional(BigIntFromString).annotate({ description: "The value to send with the call" })
1126
+ });
1127
+ const Batch = Schema.Struct({
1128
+ chainId: Schema.Int.annotate({ description: "The chain ID to execute the transaction on" }),
1129
+ nonceKey: Schema.optional(Schema.String).annotate({ description: "The nonce key to use, for 2D Parallel transactions." }),
1130
+ calls: Schema.mutable(Schema.Array(Call))
1131
+ });
1132
+ Schema.Struct({ batches: Schema.mutable(Schema.Array(Batch)) });
1133
+ //#endregion
1134
+ //#region src/mcp/tools/transaction/execute-transaction.ts
1135
+ const ExecuteTransactionToolParams = Batch;
1136
+ const ExecuteTransactionTool = Tool.make("execute_transaction", {
1137
+ dependencies: [McpContext, Web3Service],
1138
+ description: "Send transactions.",
1139
+ failure: InsufficientPermissions,
1140
+ parameters: ExecuteTransactionToolParams,
1141
+ success: Schema.String.annotate({ description: "The transaction hash for the executed transaction." })
1142
+ });
1143
+ const executeTransactionToolHandler = (params) => Effect.gen(function* () {
1144
+ const batches = [{
1145
+ chainId: params.chainId,
1146
+ nonceKey: params.nonceKey,
1147
+ calls: params.calls
1148
+ }];
1149
+ const client = yield* getSessionKeyClient({ operation: {
1150
+ intent: "transaction",
1151
+ batches
1152
+ } });
1153
+ return (yield* Effect.promise(() => executeTransaction({
1154
+ clients: [client],
1155
+ batches
1156
+ })))[0]?.receipt.transactionHash;
1157
+ });
1158
+ //#endregion
1159
+ //#region src/mcp/tools/transaction/native-transfer.ts
1160
+ const NativeTransferToolParams = Schema.Struct({
1161
+ chain: SupportedChain.annotate({ description: "The chain to use for the transfer" }),
1162
+ address: EthereumAddress.annotate({ description: "The ethereum address to transfer to" }),
1163
+ amount: Schema.String.annotate({ description: "The amount of native tokens to transfer" }),
1164
+ unit: Schema.Literals([
1165
+ "wei",
1166
+ "gwei",
1167
+ "ether"
1168
+ ])
1169
+ });
1170
+ const NativeTransferTool = Tool.make("native_transfer", {
1171
+ dependencies: [McpContext, Web3Service],
1172
+ description: "Transfer Native Tokens to a specified address.",
1173
+ failure: InsufficientPermissions,
1174
+ parameters: NativeTransferToolParams,
1175
+ success: Schema.String.annotate({ description: "The transaction hash of the transfer" })
1176
+ });
1177
+ const nativeTransferToolHandler = (params) => Effect.gen(function* () {
1178
+ const decimals = () => {
1179
+ if (params.unit === "wei") return 1;
1180
+ if (params.unit === "gwei") return 9;
1181
+ return 18;
1182
+ };
1183
+ const amount = parseUnits(params.amount.toString(), decimals());
1184
+ const batches = [{
1185
+ chainId: getChain(params.chain).id,
1186
+ calls: [{
1187
+ to: params.address,
1188
+ value: amount,
1189
+ data: "0x"
1190
+ }]
1191
+ }];
1192
+ const client = yield* getSessionKeyClient({ operation: {
1193
+ intent: "transaction",
1194
+ batches
1195
+ } });
1196
+ return (yield* Effect.promise(() => executeTransaction({
1197
+ clients: [client],
1198
+ batches
1199
+ })))[0]?.receipt.transactionHash;
1200
+ });
1201
+ //#endregion
1202
+ //#region src/mcp/tools/transaction/index.ts
1203
+ const TransactionTools = Toolkit.make(NativeTransferTool, ExecuteTransactionTool);
1204
+ const TransactionToolsHandlers = TransactionTools.toLayer(Effect.succeed({
1205
+ native_transfer: (params) => nativeTransferToolHandler(params),
1206
+ execute_transaction: (params) => executeTransactionToolHandler(params)
1207
+ }));
1208
+ //#endregion
1209
+ //#region src/mcp/mcp.ts
1210
+ const Account = Layer.effectDiscard(McpServer.registerToolkit(AccountTools)).pipe(Layer.provideMerge(AccountToolsHandlers));
1211
+ const Transfer = Layer.effectDiscard(McpServer.registerToolkit(TransactionTools)).pipe(Layer.provideMerge(TransactionToolsHandlers));
1212
+ const Read = Layer.effectDiscard(McpServer.registerToolkit(ReadTools)).pipe(Layer.provideMerge(ReadToolsHandlers));
1213
+ const McpLive = Layer.mergeAll(Account, Transfer, Read);
1214
+ //#endregion
1215
+ //#region src/mcp/index.ts
1216
+ const MCP_SERVER_NAME = "Namera MCP Server";
1217
+ const MCP_SERVER_VERSION = "0.0.1";
1218
+ const McpRouter = McpLive.pipe(Layer.provideMerge(McpServer.layerHttp({
1219
+ name: MCP_SERVER_NAME,
1220
+ path: "/mcp",
1221
+ version: MCP_SERVER_VERSION
1222
+ })), Layer.provideMerge(HttpRouter.cors({
1223
+ allowedHeaders: [
1224
+ "Content-Type",
1225
+ "Authorization",
1226
+ "mcp-session-id",
1227
+ "mcp-protocol-version"
1228
+ ],
1229
+ allowedMethods: [
1230
+ "GET",
1231
+ "POST",
1232
+ "PUT",
1233
+ "DELETE",
1234
+ "PATCH",
1235
+ "OPTIONS"
1236
+ ],
1237
+ allowedOrigins: ["*"],
1238
+ credentials: false,
1239
+ exposedHeaders: ["mcp-session-id", "mcp-protocol-version"]
1240
+ })));
1241
+ const McpStdio = McpLive.pipe(Layer.provideMerge(McpServer.layerStdio({
1242
+ name: MCP_SERVER_NAME,
1243
+ version: MCP_SERVER_VERSION
1244
+ })), Layer.provide(NodeStdio.layer), Layer.provide(Layer.succeed(Logger.LogToStderr)(true)));
1245
+ const startMcpHttpServer = (port) => Layer.launch(HttpRouter.serve(McpRouter).pipe(Layer.provideMerge(NodeHttpServer.layer(createServer, { port }))));
1246
+ const startMcpStdioServer = () => Layer.launch(McpStdio);
1247
+ //#endregion
1248
+ //#region src/helpers/policy.ts
1249
+ /** biome-ignore-all lint/complexity/noExcessiveCognitiveComplexity: safe */
1250
+ const toCallPolicyVersion = (version) => {
1251
+ switch (version) {
1252
+ case "0.0.1": return CallPolicyVersion.V0_0_1;
1253
+ case "0.0.2": return CallPolicyVersion.V0_0_2;
1254
+ case "0.0.3": return CallPolicyVersion.V0_0_3;
1255
+ case "0.0.4": return CallPolicyVersion.V0_0_4;
1256
+ case "0.0.5": return CallPolicyVersion.V0_0_5;
1257
+ default: throw new Error("Invalid call policy version");
1258
+ }
1259
+ };
1260
+ const conditionToParamCondition = (condition) => {
1261
+ if (condition === "EQUAL") return ParamCondition.EQUAL;
1262
+ if (condition === "GREATER_THAN") return ParamCondition.GREATER_THAN;
1263
+ if (condition === "LESS_THAN") return ParamCondition.LESS_THAN;
1264
+ if (condition === "GREATER_THAN_OR_EQUAL") return ParamCondition.GREATER_THAN_OR_EQUAL;
1265
+ if (condition === "LESS_THAN_OR_EQUAL") return ParamCondition.LESS_THAN_OR_EQUAL;
1266
+ if (condition === "NOT_EQUAL") return ParamCondition.NOT_EQUAL;
1267
+ if (condition === "ONE_OF") return ParamCondition.ONE_OF;
1268
+ if (condition === "SLICE_EQUAL") return ParamCondition.SLICE_EQUAL;
1269
+ return ParamCondition.EQUAL;
1270
+ };
1271
+ const policyParamsToPolicies = (params) => {
1272
+ const policies = [];
1273
+ for (const param of params) if (param.type === "sudo") policies.push(toSudoPolicy({}));
1274
+ else if (param.type === "timestamp") policies.push(toTimestampPolicy(param));
1275
+ else if (param.type === "gas") policies.push(toGasPolicy(param));
1276
+ else if (param.type === "rate-limit") policies.push(toRateLimitPolicy(param));
1277
+ else if (param.type === "signature-caller") policies.push(toSignatureCallerPolicy(param));
1278
+ else if (param.type === "call") policies.push(toCallPolicy({
1279
+ policyVersion: toCallPolicyVersion(param.policyVersion),
1280
+ permissions: !param.permissions ? void 0 : param.permissions.map((p) => {
1281
+ const callType = (() => {
1282
+ if (!p.callType) return CallType.CALL;
1283
+ if (p.callType === "call") return CallType.CALL;
1284
+ if (p.callType === "delegatecall") return CallType.DELEGATE_CALL;
1285
+ return CallType.BATCH_CALL;
1286
+ })();
1287
+ if ("abi" in p) return {
1288
+ callType,
1289
+ target: p.target,
1290
+ valueLimit: p.valueLimit,
1291
+ abi: p.abi,
1292
+ functionName: p.functionName,
1293
+ selector: p.selector,
1294
+ args: !p.args ? void 0 : p.args.map((a) => {
1295
+ if (a === null) return null;
1296
+ const c = a.condition;
1297
+ if (c === "ONE_OF") return {
1298
+ condition: ParamCondition.ONE_OF,
1299
+ value: a.value
1300
+ };
1301
+ if (c === "SLICE_EQUAL" && "start" in a) return {
1302
+ condition: ParamCondition.SLICE_EQUAL,
1303
+ value: a.value,
1304
+ start: a.start,
1305
+ length: a.length
1306
+ };
1307
+ return {
1308
+ condition: conditionToParamCondition(c),
1309
+ value: a.value
1310
+ };
1311
+ })
1312
+ };
1313
+ return {
1314
+ callType,
1315
+ target: p.target,
1316
+ selector: p.selector,
1317
+ valueLimit: p.valueLimit,
1318
+ rules: p.rules ? p.rules.map((r) => {
1319
+ return {
1320
+ offset: r.offset,
1321
+ params: r.params,
1322
+ condition: conditionToParamCondition(r.condition)
1323
+ };
1324
+ }) : void 0
1325
+ };
1326
+ })
1327
+ }));
1328
+ else throw new Error("Invalid policy type");
1329
+ return policies;
1330
+ };
1331
+ //#endregion
1332
+ //#region src/layers/smart-account.ts
1333
+ /**
1334
+ * Service tag for resolving {@link SmartAccountManager} from the Effect context.
1335
+ */
1336
+ const SmartAccountManager = ServiceMap.Service("@namera-ai/cli/SmartAccountManager");
1337
+ /**
1338
+ * Domain error for smart account lifecycle operations.
1339
+ */
1340
+ var SmartAccountManagerError = class extends Data.TaggedError("@namera-ai/cli/SmartAccountManagerError") {};
1341
+ /**
1342
+ * Live layer that manages smart account storage and derivation.
1343
+ */
1344
+ const layer$3 = Layer.effect(SmartAccountManager, Effect.gen(function* () {
1345
+ const configManager = yield* ConfigManager;
1346
+ const keystoreManager = yield* KeystoreManager;
1347
+ const promptManager = yield* PromptManager;
1348
+ const web3Service = yield* Web3Service;
1349
+ const getSmartAccount = (params) => Effect.gen(function* () {
1350
+ const res = yield* configManager.getEntity({
1351
+ alias: params.alias,
1352
+ type: "smart-account"
1353
+ });
1354
+ const parsedSmartAccount = Schema.decodeUnknownSync(Schema.fromJsonString(LocalSmartAccount))(res.content);
1355
+ return {
1356
+ alias: res.alias,
1357
+ data: parsedSmartAccount,
1358
+ path: res.path
1359
+ };
1360
+ });
1361
+ const listSmartAccounts = () => Effect.gen(function* () {
1362
+ const effects = (yield* configManager.getEntitiesForType("smart-account")).map((entity) => Effect.gen(function* () {
1363
+ const parsedSmartAccount = Schema.decodeUnknownSync(Schema.fromJsonString(LocalSmartAccount))(entity.content);
1364
+ return {
1365
+ alias: entity.alias,
1366
+ data: parsedSmartAccount,
1367
+ path: entity.path
1368
+ };
1369
+ }));
1370
+ return yield* Effect.all(effects, { concurrency: "unbounded" });
1371
+ });
1372
+ const createSmartAccount = (params) => Effect.gen(function* () {
1373
+ const existingAccounts = yield* listSmartAccounts();
1374
+ const entityPath = yield* configManager.getEntityPath({
1375
+ alias: params.alias,
1376
+ type: "smart-account"
1377
+ });
1378
+ const ownerKeystore = yield* keystoreManager.getKeystore({ alias: params.ownerAlias });
1379
+ const ownerSigner = yield* keystoreManager.getSigner({
1380
+ alias: params.ownerAlias,
1381
+ password: params.ownerPassword
1382
+ });
1383
+ const publicClient = createPublicClient({
1384
+ chain: mainnet,
1385
+ transport: http()
1386
+ });
1387
+ const entryPointVersion = "0.7";
1388
+ const kernelVersion = "0.3.2";
1389
+ const index = BigInt(params.index ?? 0);
1390
+ const ownerType = "ecdsa";
1391
+ const saAddress = (yield* Effect.tryPromise({
1392
+ try: () => createAccountClient({
1393
+ type: "ecdsa",
1394
+ bundlerTransport: http(),
1395
+ chain: mainnet,
1396
+ client: publicClient,
1397
+ entrypointVersion: entryPointVersion,
1398
+ kernelVersion,
1399
+ signer: ownerSigner
1400
+ }),
1401
+ catch: () => new SmartAccountManagerError({
1402
+ code: "KernelAddressGenerationError",
1403
+ message: `Unable to compute smart account address for Address: ${ownerKeystore.data.address} and Index: ${params.index}`
1404
+ })
1405
+ })).account.address;
1406
+ if (existingAccounts.find((d) => d.data.smartAccountAddress === saAddress)) return yield* Effect.fail(new SmartAccountManagerError({
1407
+ code: "SmartAccountAlreadyExists",
1408
+ message: `Smart account for owner: ${ownerKeystore.alias} and index: ${params.index} already exists`
1409
+ }));
1410
+ const saData = {
1411
+ entryPointVersion,
1412
+ kernelVersion,
1413
+ ownerAlias: ownerKeystore.alias,
1414
+ ownerType,
1415
+ smartAccountAddress: saAddress,
1416
+ index
1417
+ };
1418
+ const data = {
1419
+ data: saData,
1420
+ path: entityPath,
1421
+ alias: params.alias
1422
+ };
1423
+ const encoded = Schema.encodeSync(LocalSmartAccount)(saData);
1424
+ yield* configManager.storeEntity({
1425
+ alias: params.alias,
1426
+ content: JSON.stringify(encoded),
1427
+ path: entityPath,
1428
+ type: "smart-account"
1429
+ });
1430
+ return data;
1431
+ });
1432
+ const selectSmartAccount = (params) => Effect.gen(function* () {
1433
+ const smartAccounts = yield* listSmartAccounts();
1434
+ return yield* promptManager.selectPrompt({
1435
+ message: params.message,
1436
+ choices: smartAccounts.map((a) => ({
1437
+ title: a.alias,
1438
+ value: a,
1439
+ description: a.data.smartAccountAddress
1440
+ }))
1441
+ });
1442
+ });
1443
+ const removeSmartAccount = (params) => Effect.gen(function* () {
1444
+ const sa = yield* getSmartAccount({ alias: params.alias });
1445
+ return yield* configManager.removeEntity({
1446
+ alias: sa.alias,
1447
+ type: "smart-account"
1448
+ });
1449
+ });
1450
+ const getSmartAccountStatus = (params) => Effect.gen(function* () {
1451
+ const sa = yield* getSmartAccount({ alias: params.alias });
1452
+ const publicClient = yield* web3Service.getPublicClient({
1453
+ chain: params.chain,
1454
+ rpcUrl: params.rpcUrl
1455
+ });
1456
+ const code = yield* Effect.promise(() => publicClient.getCode({ address: sa.data.smartAccountAddress }));
1457
+ return Boolean(code);
1458
+ });
1459
+ const importSmartAccount = (params) => Effect.gen(function* () {
1460
+ const { alias, ...rest } = params;
1461
+ const entityPath = yield* configManager.getEntityPath({
1462
+ alias: params.alias,
1463
+ type: "smart-account"
1464
+ });
1465
+ const ownerKeystore = yield* keystoreManager.getKeystore({ alias: params.ownerAlias });
1466
+ if ((yield* listSmartAccounts()).find((d) => d.data.smartAccountAddress === rest.smartAccountAddress)) return yield* Effect.fail(new SmartAccountManagerError({
1467
+ code: "SmartAccountAlreadyExists",
1468
+ message: `Smart account for owner: ${ownerKeystore.alias} and address: ${rest.smartAccountAddress} already exists`
1469
+ }));
1470
+ yield* configManager.storeEntity({
1471
+ alias: params.alias,
1472
+ content: JSON.stringify(Schema.encodeSync(LocalSmartAccount)(rest)),
1473
+ path: entityPath,
1474
+ type: "smart-account"
1475
+ });
1476
+ return {
1477
+ alias: params.alias,
1478
+ data: rest,
1479
+ path: entityPath
1480
+ };
1481
+ });
1482
+ return SmartAccountManager.of({
1483
+ createSmartAccount,
1484
+ getSmartAccount,
1485
+ listSmartAccounts,
1486
+ selectSmartAccount,
1487
+ removeSmartAccount,
1488
+ getSmartAccountStatus,
1489
+ importSmartAccount
1490
+ });
1491
+ }));
1492
+ //#endregion
1493
+ //#region src/layers/session-key.ts
1494
+ const SessionKeyManager = ServiceMap.Service("@namera-ai/cli/SessionKeyManager");
1495
+ var SessionKeyManagerError = class extends Data.TaggedError("@namera-ai/cli/SessionKeyManagerError") {};
1496
+ const layer$2 = Layer.effect(SessionKeyManager, Effect.gen(function* () {
1497
+ const configManager = yield* ConfigManager;
1498
+ const promptManager = yield* PromptManager;
1499
+ const keystoreManager = yield* KeystoreManager;
1500
+ const smartAccountManager = yield* SmartAccountManager;
1501
+ const web3Service = yield* Web3Service;
1502
+ const createSessionKey$1 = (params) => Effect.gen(function* () {
1503
+ if (yield* configManager.checkEntityExists({
1504
+ alias: params.alias,
1505
+ type: "session-key"
1506
+ })) return yield* Effect.fail(new SessionKeyManagerError({
1507
+ code: "SessionKeyAlreadyExists",
1508
+ message: `Session key for alias ${params.alias} already exists`
1509
+ }));
1510
+ const entityPath = yield* configManager.getEntityPath({
1511
+ alias: params.alias,
1512
+ type: "session-key"
1513
+ });
1514
+ const sa = yield* smartAccountManager.getSmartAccount({ alias: params.smartAccountAlias });
1515
+ const ownerSigner = yield* keystoreManager.getSigner({
1516
+ alias: sa.data.ownerAlias,
1517
+ password: params.ownerKeystorePassword
1518
+ });
1519
+ const policies = policyParamsToPolicies(params.policyParams);
1520
+ const clients = yield* Effect.all(params.chains.map((chain) => web3Service.getPublicClient({ chain })), { concurrency: "unbounded" });
1521
+ const sessionPrivateKey = generatePrivateKey();
1522
+ const sessionKeyAccount = privateKeyToAccount(sessionPrivateKey);
1523
+ const res = yield* Effect.promise(() => createSessionKey({
1524
+ type: "ecdsa",
1525
+ accountType: "ecdsa",
1526
+ sessionPrivateKey,
1527
+ clients,
1528
+ entrypointVersion: sa.data.entryPointVersion,
1529
+ kernelVersion: sa.data.kernelVersion,
1530
+ index: sa.data.index,
1531
+ signer: ownerSigner,
1532
+ policies
1533
+ }));
1534
+ const encData = yield* Effect.tryPromise({
1535
+ try: () => Wallet.fromPrivateKey(hexToBytes(sessionPrivateKey)).toV3(params.sessionKeyPassword),
1536
+ catch: () => new SessionKeyManagerError({
1537
+ code: "EncryptionError",
1538
+ message: "Failed to encrypt session key"
1539
+ })
1540
+ });
1541
+ const data = {
1542
+ sessionKeyType: "ecdsa",
1543
+ serializedAccounts: res.serializedAccounts.map((a) => ({
1544
+ chain: chainIdToChainName(a.chainId),
1545
+ serializedAccount: a.serializedAccount
1546
+ })),
1547
+ sessionKeyAddress: sessionKeyAccount.address,
1548
+ smartAccountAlias: sa.alias,
1549
+ ...encData
1550
+ };
1551
+ yield* configManager.storeEntity({
1552
+ alias: params.alias,
1553
+ content: JSON.stringify(data),
1554
+ path: entityPath,
1555
+ type: "session-key"
1556
+ });
1557
+ return {
1558
+ alias: params.alias,
1559
+ data,
1560
+ path: entityPath
1561
+ };
1562
+ });
1563
+ const getSessionKey = (params) => Effect.gen(function* () {
1564
+ const res = yield* configManager.getEntity({
1565
+ alias: params.alias,
1566
+ type: "session-key"
1567
+ });
1568
+ const parsed = Schema.decodeUnknownOption(Schema.fromJsonString(SessionKey))(res.content);
1569
+ if (parsed._tag === "None") return yield* Effect.fail(new SessionKeyManagerError({
1570
+ code: "SessionKeyParseError",
1571
+ message: "Unable to parse session key"
1572
+ }));
1573
+ return {
1574
+ alias: res.alias,
1575
+ data: parsed.value,
1576
+ path: res.path
1577
+ };
1578
+ });
1579
+ const listSessionKeys = (params) => Effect.gen(function* () {
1580
+ const effects = (yield* configManager.getEntitiesForType("session-key")).map((entity) => Effect.gen(function* () {
1581
+ const parsed = Schema.decodeUnknownOption(Schema.fromJsonString(SessionKey))(entity.content);
1582
+ if (parsed._tag === "None") return yield* Effect.fail(new SessionKeyManagerError({
1583
+ code: "SessionKeyParseError",
1584
+ message: "Unable to parse session key"
1585
+ }));
1586
+ return {
1587
+ alias: entity.alias,
1588
+ data: parsed.value,
1589
+ path: entity.path
1590
+ };
1591
+ }));
1592
+ const allKeys = yield* Effect.all(effects, { concurrency: "unbounded" });
1593
+ if (params.smartAccount) return allKeys.filter((k) => k.data.smartAccountAlias === params.smartAccount);
1594
+ return allKeys;
1595
+ });
1596
+ const selectSessionKey = (params) => Effect.gen(function* () {
1597
+ const sessionKeys = yield* listSessionKeys({});
1598
+ return yield* promptManager.selectPrompt({
1599
+ message: params.message,
1600
+ choices: sessionKeys.map((sk) => ({
1601
+ title: sk.alias,
1602
+ value: sk,
1603
+ description: `${sk.data.sessionKeyType === "ecdsa" ? sk.data.sessionKeyAddress : sk.data.passKeyName} (${sk.data.smartAccountAlias})`
1604
+ }))
1605
+ });
1606
+ });
1607
+ const getSessionKeyStatus = (params) => Effect.gen(function* () {
1608
+ const sessionKey = yield* getSessionKey({ alias: params.alias });
1609
+ const sa = yield* smartAccountManager.getSmartAccount({ alias: sessionKey.data.smartAccountAlias });
1610
+ const publicClient = yield* web3Service.getPublicClient({
1611
+ chain: params.chain,
1612
+ rpcUrl: params.rpcUrl
1613
+ });
1614
+ if (sessionKey.data.sessionKeyType !== "ecdsa") return yield* Effect.fail(new SessionKeyManagerError({
1615
+ code: "SessionKeyParseError",
1616
+ message: "Only ECDSA session keys have an on-chain status"
1617
+ }));
1618
+ const sessionKeyAddress = sessionKey.data.sessionKeyAddress;
1619
+ return yield* Effect.tryPromise({
1620
+ try: () => isSessionKeyInstalled(publicClient, {
1621
+ accountAddress: sa.data.smartAccountAddress,
1622
+ sessionKeyAddress
1623
+ }),
1624
+ catch: () => false
1625
+ }).pipe(Effect.catch(() => Effect.succeed(false)));
1626
+ });
1627
+ const multiSelectSessionKeys = (params) => Effect.gen(function* () {
1628
+ const allKeys = yield* listSessionKeys({ smartAccount: params.smartAccount });
1629
+ return yield* promptManager.multiSelectPrompt({
1630
+ message: params.message,
1631
+ choices: allKeys.map((k) => ({
1632
+ title: k.alias,
1633
+ value: k,
1634
+ description: `${k.data.sessionKeyType === "ecdsa" ? k.data.sessionKeyAddress : k.data.passKeyName} (${k.data.smartAccountAlias})`
1635
+ })),
1636
+ min: 1
1637
+ });
1638
+ });
1639
+ const getSessionKeyPassword = (params) => Effect.gen(function* () {
1640
+ const key = yield* getSessionKey({ alias: params.alias });
1641
+ return yield* promptManager.passwordPrompt({
1642
+ message: params.message,
1643
+ validate: (v) => Effect.gen(function* () {
1644
+ if (v.trim() === "") return yield* Effect.fail("Password cannot be empty");
1645
+ yield* Effect.tryPromise({
1646
+ try: () => Wallet.fromV3(key.data, v),
1647
+ catch: () => new SessionKeyManagerError({
1648
+ code: "DecryptError",
1649
+ message: `Invalid password for session key ${params.alias}`
1650
+ })
1651
+ }).pipe(Effect.catch(() => Effect.fail("Invalid password")));
1652
+ return v;
1653
+ })
1654
+ });
1655
+ });
1656
+ const getSessionKeySigner = (params) => Effect.gen(function* () {
1657
+ const key = yield* getSessionKey({ alias: params.alias });
1658
+ const signer = yield* Effect.tryPromise({
1659
+ try: async () => {
1660
+ return privateKeyToAccount(toHex((await Wallet.fromV3(key.data, params.password)).getPrivateKey()));
1661
+ },
1662
+ catch: () => new SessionKeyManagerError({
1663
+ code: "DecryptError",
1664
+ message: `Invalid password for session key ${params.alias}`
1665
+ })
1666
+ });
1667
+ return {
1668
+ ...key,
1669
+ signer
1670
+ };
1671
+ });
1672
+ const removeSessionKey = (params) => Effect.gen(function* () {
1673
+ const key = yield* getSessionKey({ alias: params.alias });
1674
+ return yield* configManager.removeEntity({
1675
+ alias: key.alias,
1676
+ type: "session-key"
1677
+ });
1678
+ });
1679
+ return SessionKeyManager.of({
1680
+ createSessionKey: createSessionKey$1,
1681
+ getSessionKey,
1682
+ listSessionKeys,
1683
+ selectSessionKey,
1684
+ getSessionKeyStatus,
1685
+ multiSelectSessionKeys,
1686
+ getSessionKeyPassword,
1687
+ getSessionKeySigner,
1688
+ removeSessionKey
1689
+ });
1690
+ }));
1691
+ //#endregion
1692
+ //#region src/layers/mcp.ts
1693
+ const McpManager = ServiceMap.Service("@namera-ai/cli/McpManager");
1694
+ const layer$1 = Layer.effect(McpManager, Effect.gen(function* () {
1695
+ const smartAccountManager = yield* SmartAccountManager;
1696
+ const sessionKeyManager = yield* SessionKeyManager;
1697
+ const startMcpServer = (params) => Effect.gen(function* () {
1698
+ const sa = yield* smartAccountManager.getSmartAccount({ alias: params.smartAccountAlias });
1699
+ const sessionKeys = Object.entries(params.sessionKeys);
1700
+ const sessionKeysWithSigners = yield* Effect.all(sessionKeys.map(([alias, password]) => sessionKeyManager.getSessionKeySigner({
1701
+ alias,
1702
+ password
1703
+ })));
1704
+ const context = McpContext.of({
1705
+ smartAccount: sa.data,
1706
+ sessionKeys: sessionKeysWithSigners
1707
+ });
1708
+ if (params.transport === "stdio") yield* startMcpStdioServer().pipe(Effect.provideService(McpContext, context));
1709
+ else yield* startMcpHttpServer(params.port ?? 8080).pipe(Effect.provideService(McpContext, context));
1710
+ });
1711
+ return McpManager.of({ startMcpServer });
1712
+ }));
1713
+ //#endregion
1714
+ //#region src/helpers/pretty.ts
1715
+ /** biome-ignore-all lint/suspicious/noExplicitAny: safe */
1716
+ const keyColors = [
1717
+ "\x1B[36m",
1718
+ "\x1B[35m",
1719
+ "\x1B[34m",
1720
+ "\x1B[32m",
1721
+ "\x1B[33m"
1722
+ ];
1723
+ const reset = "\x1B[0m";
1724
+ const bold = "\x1B[1m";
1725
+ /**
1726
+ * Returns a shallow copy of an object with humanized key names.
1727
+ */
1728
+ const transformKeys = (obj) => {
1729
+ return Object.fromEntries(Object.entries(obj).map(([k, v]) => [formatKey(k), v]));
1730
+ };
1731
+ /**
1732
+ * Humanizes a key by spacing words and title-casing tokens.
1733
+ */
1734
+ const formatKey = (key) => key.replace(/[_-]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
1735
+ /**
1736
+ * Formats a primitive-like value for display.
1737
+ */
1738
+ const formatValue = (value) => {
1739
+ if (value === null) return "null";
1740
+ if (typeof value === "bigint") return `${value}n`;
1741
+ if (value instanceof Date) return value.toISOString();
1742
+ if (typeof value === "string") return value;
1743
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
1744
+ return String(value);
1745
+ };
1746
+ /**
1747
+ * Returns true when a value is a display-friendly primitive.
1748
+ */
1749
+ const isPrimitive = (v) => v === null || typeof v !== "object" || v instanceof Date;
1750
+ /**
1751
+ * Determines whether an array contains only flat objects of primitives.
1752
+ */
1753
+ const isFlatObjectArray = (arr) => arr.length > 0 && arr.every((item) => item && typeof item === "object" && !Array.isArray(item) && Object.values(item).every(isPrimitive));
1754
+ /**
1755
+ * Formats objects or arrays into a human-friendly CLI display string.
1756
+ *
1757
+ * Uses colorized keys when printing objects, and falls back to tabular
1758
+ * output for flat object arrays at the root level.
1759
+ */
1760
+ const prettyFormat = (input, depth = 0, isRoot = true) => {
1761
+ const indent = 2;
1762
+ const maxDepth = 6;
1763
+ const space = " ".repeat(indent * depth);
1764
+ if (depth > maxDepth) return `${space}…`;
1765
+ if (isRoot && Array.isArray(input) && isFlatObjectArray(input)) {
1766
+ const transformed = input.map(transformKeys);
1767
+ console.table(transformed);
1768
+ return "";
1769
+ }
1770
+ if (Array.isArray(input)) return input.map((item, i) => {
1771
+ if (isPrimitive(item)) return `${space}${formatValue(item)}`;
1772
+ return `${space}[${i}]\n${prettyFormat(item, depth + 1, false)}`;
1773
+ }).join("\n");
1774
+ return Object.entries(input).map(([key, value]) => {
1775
+ const k = `${bold}${keyColors[depth % keyColors.length]}${formatKey(key)}${reset}`;
1776
+ if (isPrimitive(value)) return `${space}${k}: ${formatValue(value)}`;
1777
+ return `${space}${k}:\n${prettyFormat(value, depth + 1, false)}`;
1778
+ }).join("\n");
1779
+ };
1780
+ //#endregion
1781
+ //#region src/layers/output.ts
1782
+ /**
1783
+ * Service tag for resolving {@link OutputFormatter} from the Effect context.
1784
+ */
1785
+ const OutputFormatter = ServiceMap.Service("@namera-ai/cli/OutputFormatter");
1786
+ /**
1787
+ * Live layer that formats output as pretty, JSON, or NDJSON.
1788
+ */
1789
+ const layer = Layer.succeed(OutputFormatter, OutputFormatter.of({ format: (data, format) => Effect.gen(function* () {
1790
+ if (format === "pretty") return prettyFormat(data);
1791
+ if (format === "json") return JSON.stringify(data, null, 2);
1792
+ if (Array.isArray(data)) return NdJson.stringify(data);
1793
+ return NdJson.stringify([data]);
1794
+ }) }));
1795
+ //#endregion
1796
+ //#region src/commands/keystore/create.ts
1797
+ const createKeystoreHandler = (flagAlias, flagPassword) => Effect.gen(function* () {
1798
+ const promptManager = yield* PromptManager;
1799
+ const keystoreManager = yield* KeystoreManager;
1800
+ const outputFormatter = yield* OutputFormatter;
1801
+ const globalFlags = yield* getGlobalFlags();
1802
+ let params;
1803
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(CreateKeystoreParams))(globalFlags.params.value);
1804
+ else {
1805
+ let alias;
1806
+ let password;
1807
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
1808
+ else alias = yield* promptManager.aliasPrompt({
1809
+ aliasType: "new",
1810
+ message: "Enter alias for keystore:",
1811
+ type: "keystore"
1812
+ });
1813
+ if (flagPassword._tag === "Some") password = flagPassword.value;
1814
+ else password = yield* promptManager.passwordPrompt({ message: "Enter password for keystore:" });
1815
+ params = {
1816
+ alias,
1817
+ password: Redacted.value(password)
1818
+ };
1819
+ }
1820
+ const res = yield* keystoreManager.createKeystore(params);
1821
+ const data = {
1822
+ address: res.data.address,
1823
+ alias: res.alias,
1824
+ path: res.path
1825
+ };
1826
+ if (globalFlags.quite) return;
1827
+ const output = yield* outputFormatter.format(data, globalFlags.out);
1828
+ yield* Console.log(output);
1829
+ });
1830
+ const alias$12 = Flag.string("alias").pipe(Flag.withDescription("The alias of the keystore to create"), Flag.withAlias("a"), Flag.optional);
1831
+ const password$2 = Flag.redacted("password").pipe(Flag.withDescription("The password to encrypt the keystore with"), Flag.withAlias("p"), Flag.optional);
1832
+ /**
1833
+ * Command that creates a new keystore and stores it locally.
1834
+ */
1835
+ const createKeystoreCommand = Command.make("create", {
1836
+ alias: alias$12,
1837
+ password: password$2
1838
+ }, ({ alias, password }) => createKeystoreHandler(alias, password)).pipe(Command.withAlias("c"), Command.withDescription("Creates a random keypair and stores it as keystore"), Command.withExamples([
1839
+ {
1840
+ command: "namera keystore create -a my-wallet",
1841
+ description: "Creates a new keystore with alias 'my-wallet'"
1842
+ },
1843
+ {
1844
+ command: "namera keystore create --alias my-wallet --password my-password",
1845
+ description: "Creates a new keystore with alias 'my-wallet' and password 'my-password'"
1846
+ },
1847
+ {
1848
+ command: `namera keystore create --params '{"alias":"my-wallet","password":"my-password"}'`,
1849
+ description: "Creates a new keystore with json params"
1850
+ },
1851
+ {
1852
+ command: "namera schema keystore.create",
1853
+ description: "Get the schema for the create command"
1854
+ }
1855
+ ]));
1856
+ //#endregion
1857
+ //#region src/commands/keystore/decrypt.ts
1858
+ const handler$14 = (flagAlias, flagPassword) => Effect.gen(function* () {
1859
+ const keystoreManager = yield* KeystoreManager;
1860
+ const promptManager = yield* PromptManager;
1861
+ const outputFormatter = yield* OutputFormatter;
1862
+ const globalFlags = yield* getGlobalFlags();
1863
+ let params;
1864
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(DecryptKeystoreParams))(globalFlags.params.value);
1865
+ else {
1866
+ let alias;
1867
+ let password;
1868
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
1869
+ else alias = (yield* keystoreManager.selectKeystore({ message: "Select keystore:" })).alias;
1870
+ if (flagPassword._tag === "Some") password = flagPassword.value;
1871
+ else password = yield* promptManager.passwordPrompt({ message: "Enter password for keystore:" });
1872
+ params = {
1873
+ alias,
1874
+ password: Redacted.value(password)
1875
+ };
1876
+ }
1877
+ const res = yield* keystoreManager.decryptKeystore(params);
1878
+ const data = {
1879
+ alias: res.alias,
1880
+ address: res.address,
1881
+ publicKey: res.publicKey,
1882
+ privateKey: Redacted.value(res.privateKey)
1883
+ };
1884
+ if (globalFlags.quite) return;
1885
+ const output = yield* outputFormatter.format(data, globalFlags.out);
1886
+ yield* Console.log(output);
1887
+ });
1888
+ const alias$11 = Flag.string("alias").pipe(Flag.withDescription("The alias of the keystore to decrypt"), Flag.withAlias("a"), Flag.optional);
1889
+ const password$1 = Flag.redacted("password").pipe(Flag.withDescription("The password to decrypt the keystore with"), Flag.withAlias("p"), Flag.optional);
1890
+ /**
1891
+ * Command that decrypts a keystore and prints key material.
1892
+ */
1893
+ const decryptKeystoreCommand = Command.make("decrypt", {
1894
+ alias: alias$11,
1895
+ password: password$1
1896
+ }, ({ alias, password }) => handler$14(alias, password)).pipe(Command.withAlias("d"), Command.withDescription("Decrypt a keystore to get the private key"), Command.withExamples([
1897
+ {
1898
+ command: "namera keystore decrypt",
1899
+ description: "Decrypt a keystore with alias and password prompts"
1900
+ },
1901
+ {
1902
+ command: "namera keystore decrypt --alias my-wallet --password my-password",
1903
+ description: "Decrypt a keystore with alias and password"
1904
+ },
1905
+ {
1906
+ command: `namera keystore decrypt --params '{"alias":"my-wallet","password":"my-password"}'`,
1907
+ description: "Decrypt a keystore with json params"
1908
+ },
1909
+ {
1910
+ command: "namera schema keystore.decrypt",
1911
+ description: "Get the schema for the decrypt command"
1912
+ }
1913
+ ]));
1914
+ //#endregion
1915
+ //#region src/commands/keystore/import.ts
1916
+ const handler$13 = (flagAlias, flagPassword, flagPrivateKey) => Effect.gen(function* () {
1917
+ const keystoreManager = yield* KeystoreManager;
1918
+ const promptManager = yield* PromptManager;
1919
+ const outputFormatter = yield* OutputFormatter;
1920
+ const globalFlags = yield* getGlobalFlags();
1921
+ let params;
1922
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(ImportKeystoreParams))(globalFlags.params.value);
1923
+ else {
1924
+ let alias;
1925
+ let password;
1926
+ let privateKey;
1927
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
1928
+ else alias = yield* promptManager.aliasPrompt({
1929
+ aliasType: "new",
1930
+ message: "Enter alias for keystore:",
1931
+ type: "keystore"
1932
+ });
1933
+ if (flagPassword._tag === "Some") password = flagPassword.value;
1934
+ else password = yield* promptManager.passwordPrompt({ message: "Enter password for keystore:" });
1935
+ if (flagPrivateKey._tag === "Some") privateKey = flagPrivateKey.value;
1936
+ else privateKey = yield* promptManager.hexPrompt({
1937
+ redacted: true,
1938
+ length: 32,
1939
+ message: "Enter private key:"
1940
+ });
1941
+ params = {
1942
+ alias,
1943
+ password: Redacted.value(password),
1944
+ privateKey: Redacted.value(privateKey)
1945
+ };
1946
+ }
1947
+ const res = yield* keystoreManager.importKeystore(params);
1948
+ const data = {
1949
+ alias: res.alias,
1950
+ address: res.data.address,
1951
+ path: res.path
1952
+ };
1953
+ if (globalFlags.quite) return;
1954
+ const output = yield* outputFormatter.format(data, globalFlags.out);
1955
+ yield* Console.log(output);
1956
+ });
1957
+ const alias$10 = Flag.string("alias").pipe(Flag.withDescription("The alias of the keystore to import"), Flag.withAlias("a"), Flag.optional);
1958
+ const privateKey = Flag.redacted("private-key").pipe(Flag.withDescription("The private key of the keystore to import"), Flag.optional);
1959
+ const password = Flag.redacted("password").pipe(Flag.withDescription("The password to encrypt the keystore with"), Flag.withAlias("p"), Flag.optional);
1960
+ /**
1961
+ * Command that imports a keystore from a private key.
1962
+ */
1963
+ const importKeystoreCommand = Command.make("import", {
1964
+ alias: alias$10,
1965
+ privateKey,
1966
+ password
1967
+ }, ({ alias, password, privateKey }) => handler$13(alias, password, privateKey)).pipe(Command.withAlias("i"), Command.withDescription("Import a keystore from a private key"), Command.withExamples([
1968
+ {
1969
+ command: "namera keystore import",
1970
+ description: "Import a keystore with interactive prompts"
1971
+ },
1972
+ {
1973
+ command: "namera keystore import --alias my-wallet",
1974
+ description: "Import a keystore with alias 'my-wallet'"
1975
+ },
1976
+ {
1977
+ command: "namera keystore import -a my-wallet -p my-password --private-key 0x1234567890abcdef",
1978
+ description: "Import a keystore with alias 'my-wallet', password 'my-password', and private key '0x1234567890abcdef'"
1979
+ },
1980
+ {
1981
+ command: "namera schema keystore.import",
1982
+ description: "Get the schema for the import command"
1983
+ }
1984
+ ]));
1985
+ //#endregion
1986
+ //#region src/commands/keystore/info.ts
1987
+ const getKeystoreInfoHandler = (flagAlias) => Effect.gen(function* () {
1988
+ const keystoreManager = yield* KeystoreManager;
1989
+ const outputFormatter = yield* OutputFormatter;
1990
+ const globalFlags = yield* getGlobalFlags();
1991
+ let params;
1992
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(GetKeystoreParams))(globalFlags.params.value);
1993
+ else {
1994
+ let alias;
1995
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
1996
+ else alias = (yield* keystoreManager.selectKeystore({ message: "Select keystore:" })).alias;
1997
+ params = { alias };
1998
+ }
1999
+ const res = yield* keystoreManager.getKeystore(params);
2000
+ const data = {
2001
+ alias: res.alias,
2002
+ address: res.data.address,
2003
+ path: res.path
2004
+ };
2005
+ if (globalFlags.quite) return;
2006
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2007
+ yield* Console.log(output);
2008
+ });
2009
+ const alias$9 = Flag.string("alias").pipe(Flag.withDescription("The alias of the keystore to retrieve"), Flag.withAlias("a"), Flag.optional);
2010
+ /**
2011
+ * Command that returns metadata for a keystore.
2012
+ */
2013
+ const getKeystoreInfoCommand = Command.make("info", { alias: alias$9 }, ({ alias }) => getKeystoreInfoHandler(alias)).pipe(Command.withDescription("Get information about a keystore"), Command.withExamples([
2014
+ {
2015
+ command: "namera keystore info",
2016
+ description: "Get information about a keystore with alias prompt"
2017
+ },
2018
+ {
2019
+ command: "namera keystore info --alias my-wallet",
2020
+ description: "Get information about a keystore with alias 'my-wallet'"
2021
+ },
2022
+ {
2023
+ command: `namera keystore info --params '{"alias":"my-wallet"}'`,
2024
+ description: "Get information about a keystore with json params"
2025
+ },
2026
+ {
2027
+ command: "namera keystore info --alias my-wallet --output json",
2028
+ description: "Get information about a keystore in json format"
2029
+ },
2030
+ {
2031
+ command: "namera schema keystore.info",
2032
+ description: "Get the schema for the info command"
2033
+ }
2034
+ ]));
2035
+ //#endregion
2036
+ //#region src/commands/keystore/list.ts
2037
+ const listKeystoreHandler = () => Effect.gen(function* () {
2038
+ const keystoreManager = yield* KeystoreManager;
2039
+ const outputFormatter = yield* OutputFormatter;
2040
+ const globalFlags = yield* getGlobalFlags();
2041
+ const data = (yield* keystoreManager.listKeystores()).map((k) => ({
2042
+ alias: k.alias,
2043
+ address: k.data.address,
2044
+ path: k.path
2045
+ }));
2046
+ if (globalFlags.quite) return;
2047
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2048
+ yield* Console.log(output);
2049
+ });
2050
+ /**
2051
+ * Command that lists all stored keystores.
2052
+ */
2053
+ const listKeystoresCommand = Command.make("list", {}, () => listKeystoreHandler()).pipe(Command.withAlias("ls"), Command.withDescription("List all keystores"), Command.withExamples([
2054
+ {
2055
+ command: "namera keystore list",
2056
+ description: "List all keystores"
2057
+ },
2058
+ {
2059
+ command: "namera keystore list --output json",
2060
+ description: "List all keystores in json format"
2061
+ },
2062
+ {
2063
+ command: "namera keystore list --output ndjson",
2064
+ description: "List all keystores in ndjson format"
2065
+ },
2066
+ {
2067
+ command: "namera schema keystore.list",
2068
+ description: "Get the schema for the list command"
2069
+ }
2070
+ ]));
2071
+ //#endregion
2072
+ //#region src/commands/keystore/remove.ts
2073
+ const handler$12 = (flagAlias) => Effect.gen(function* () {
2074
+ const keystoreManager = yield* KeystoreManager;
2075
+ const outputFormatter = yield* OutputFormatter;
2076
+ const globalFlags = yield* getGlobalFlags();
2077
+ let params;
2078
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(RemoveKeystoreParams))(globalFlags.params.value);
2079
+ else {
2080
+ let alias;
2081
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
2082
+ else alias = (yield* keystoreManager.selectKeystore({ message: "Select keystore:" })).alias;
2083
+ params = { alias };
2084
+ }
2085
+ yield* keystoreManager.removeKeystore(params);
2086
+ const data = { success: true };
2087
+ if (globalFlags.quite) return;
2088
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2089
+ yield* Console.log(output);
2090
+ });
2091
+ const alias$8 = Flag.string("alias").pipe(Flag.withDescription("The alias of the keystore to remove"), Flag.withAlias("a"), Flag.optional);
2092
+ const removeKeystoreCommand = Command.make("remove", { alias: alias$8 }, ({ alias }) => handler$12(alias)).pipe(Command.withAlias("rm"), Command.withDescription("Remove a keystore"), Command.withExamples([
2093
+ {
2094
+ command: "namera keystore remove",
2095
+ description: "Remove a keystore with alias prompt"
2096
+ },
2097
+ {
2098
+ command: "namera keystore remove --alias my-wallet",
2099
+ description: "Remove a keystore with alias 'my-wallet'"
2100
+ },
2101
+ {
2102
+ command: `namera keystore remove --params '{"alias":"my-wallet"}'`,
2103
+ description: "Remove a keystore with json params"
2104
+ },
2105
+ {
2106
+ command: "namera schema keystore.remove",
2107
+ description: "Get the schema for the remove command"
2108
+ }
2109
+ ]));
2110
+ //#endregion
2111
+ //#region src/commands/keystore/index.ts
2112
+ /**
2113
+ * Command group for keystore-related operations.
2114
+ */
2115
+ const keystoreCommands = Command.make("keystore", {}, () => Effect.void).pipe(Command.withDescription("Keystore management utilities."), Command.withAlias("k"), Command.withSubcommands([
2116
+ createKeystoreCommand,
2117
+ listKeystoresCommand,
2118
+ getKeystoreInfoCommand,
2119
+ decryptKeystoreCommand,
2120
+ importKeystoreCommand,
2121
+ removeKeystoreCommand
2122
+ ]));
2123
+ //#endregion
2124
+ //#region src/commands/mcp/start.ts
2125
+ const handler$11 = (flagSmartAccount, flagSessionKeys, flagTransport, flagPort) => Effect.gen(function* () {
2126
+ const smartAccountManager = yield* SmartAccountManager;
2127
+ const sessionKeyManager = yield* SessionKeyManager;
2128
+ const mcpManager = yield* McpManager;
2129
+ const globalFlags = yield* getGlobalFlags();
2130
+ let params;
2131
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(StartMcpServerParams))(globalFlags.params.value);
2132
+ else {
2133
+ let smartAccountAlias;
2134
+ let sessionKeys = {};
2135
+ let transport;
2136
+ let port;
2137
+ if (flagSmartAccount._tag === "Some") smartAccountAlias = flagSmartAccount.value;
2138
+ else smartAccountAlias = (yield* smartAccountManager.selectSmartAccount({ message: "Select smart account to use for the MCP server" })).alias;
2139
+ if (flagSessionKeys._tag === "Some") sessionKeys = flagSessionKeys.value;
2140
+ else {
2141
+ const keys = yield* sessionKeyManager.multiSelectSessionKeys({
2142
+ message: "Select session keys to use for the MCP server",
2143
+ smartAccount: smartAccountAlias
2144
+ });
2145
+ for (const key of keys) {
2146
+ const pass = yield* sessionKeyManager.getSessionKeyPassword({
2147
+ alias: key.alias,
2148
+ message: `Enter password for session key ${key.alias}:`
2149
+ });
2150
+ sessionKeys[key.alias] = Redacted.value(pass);
2151
+ }
2152
+ }
2153
+ if (flagTransport._tag === "Some") transport = flagTransport.value;
2154
+ else transport = "stdio";
2155
+ if (flagPort._tag === "Some") port = flagPort.value;
2156
+ else port = 8080;
2157
+ params = {
2158
+ smartAccountAlias,
2159
+ sessionKeys,
2160
+ transport,
2161
+ port
2162
+ };
2163
+ }
2164
+ yield* mcpManager.startMcpServer(params);
2165
+ });
2166
+ const smartAccount = Flag.string("smart-account").pipe(Flag.withAlias("sa"), Flag.optional, Flag.withDescription("The smart account alias to use for the MCP server"));
2167
+ const sessionKeys = Flag.keyValuePair("session-key").pipe(Flag.withAlias("sk"), Flag.optional, Flag.withDescription("The session key aliases, password pairs to use for the MCP server"));
2168
+ const transport = Flag.choice("transport", ["stdio", "http"]).pipe(Flag.withAlias("t"), Flag.optional, Flag.withDescription("The transport to use for the MCP server"));
2169
+ const port = Flag.integer("port").pipe(Flag.withAlias("p"), Flag.optional, Flag.withDescription("The port to use for the MCP server when using http transport"));
2170
+ const startMcpCommand = Command.make("start", {
2171
+ smartAccount,
2172
+ sessionKeys,
2173
+ transport,
2174
+ port
2175
+ }, ({ smartAccount, sessionKeys, transport, port }) => handler$11(smartAccount, sessionKeys, transport, port)).pipe(Command.withAlias("s"), Command.withDescription("Starts the local MCP server."), Command.withExamples([
2176
+ {
2177
+ command: "namera mcp start --smart-account my-smart --session-key my-key=my-password",
2178
+ description: "Starts the local MCP server with a single session key and the default transport"
2179
+ },
2180
+ {
2181
+ command: "namera mcp start --smart-account my-smart --session-key my-key=my-password --transport http --port 8080",
2182
+ description: "Starts the local MCP server with a single session key and http transport on port 8080"
2183
+ },
2184
+ {
2185
+ command: "namera mcp start --smart-account my-smart --session-key my-key=my-password --session-key my-other-key=my-other-password",
2186
+ description: "Starts the local MCP server with multiple session keys and the default transport"
2187
+ },
2188
+ {
2189
+ command: "namera schema mcp.start",
2190
+ description: "Get the schema for the start command"
2191
+ }
2192
+ ]));
2193
+ //#endregion
2194
+ //#region src/commands/mcp/index.ts
2195
+ const mcpCommands = Command.make("mcp", {}, () => Effect.void).pipe(Command.withDescription("Start and manage the Namera MCP server"), Command.withSubcommands([startMcpCommand]));
2196
+ //#endregion
2197
+ //#region src/helpers/paths.ts
2198
+ /** biome-ignore-all lint/suspicious/noExplicitAny: safe */
2199
+ /**
2200
+ * Returns the value at a dot-delimited path within a nested object.
2201
+ *
2202
+ * @param obj - Object to traverse.
2203
+ * @param path - Dot-delimited lookup path.
2204
+ */
2205
+ const getSchema = (obj, path) => {
2206
+ return path.split(".").reduce((acc, key) => acc[key], obj);
2207
+ };
2208
+ /**
2209
+ * Extracts all dot-delimited paths that terminate at Effect schemas.
2210
+ *
2211
+ * @param obj - Object tree to scan.
2212
+ * @param prefix - Internal prefix used to build nested paths.
2213
+ */
2214
+ const extractPaths = (obj, prefix = "") => {
2215
+ return Object.entries(obj).flatMap(([key, value]) => {
2216
+ const newPath = prefix ? `${prefix}.${key}` : key;
2217
+ if (Schema.isSchema(value)) return [newPath];
2218
+ if (typeof value === "object" && value !== null) return extractPaths(value, newPath);
2219
+ return [];
2220
+ });
2221
+ };
2222
+ //#endregion
2223
+ //#region src/commands/schema/index.ts
2224
+ /**
2225
+ * Schema registry keyed by command path.
2226
+ */
2227
+ const schemas = {
2228
+ keystore: {
2229
+ create: CreateKeystoreParams,
2230
+ decrypt: DecryptKeystoreParams,
2231
+ info: GetKeystoreParams,
2232
+ list: ListKeystoreParams,
2233
+ import: ImportKeystoreParams,
2234
+ remove: RemoveKeystoreParams
2235
+ },
2236
+ "smart-account": {
2237
+ create: CreateSmartAccountParams,
2238
+ list: ListSmartAccountParams,
2239
+ info: GetSmartAccountInfoParams,
2240
+ remove: RemoveSmartAccountParams,
2241
+ status: GetSmartAccountStatusParams,
2242
+ import: ImportSmartAccountParams
2243
+ },
2244
+ "session-key": {
2245
+ create: CreateSessionKeyParams,
2246
+ list: ListSessionKeysParams,
2247
+ info: GetSessionKeyInfoParams,
2248
+ remove: RemoveSessionKeyParams,
2249
+ status: GetSessionKeyStatusParams
2250
+ },
2251
+ mcp: { start: StartMcpServerParams }
2252
+ };
2253
+ const commands$1 = extractPaths(schemas);
2254
+ const command$1 = Argument.choice("command", commands$1).pipe(Argument.withDescription("The command to get the schema for"));
2255
+ /**
2256
+ * Command that prints JSON Schema for a given CLI command.
2257
+ */
2258
+ const schemaCommand = Command.make("schema", { command: command$1 }, ({ command }) => Effect.gen(function* () {
2259
+ const schema = getSchema(schemas, command);
2260
+ const json = Schema.toJsonSchemaDocument(schema);
2261
+ yield* Console.log(JSON.stringify(json, null, 2));
2262
+ })).pipe(Command.withDescription("Get the schema for a command"));
2263
+ //#endregion
2264
+ //#region src/commands/session-key/create/prompts/base.ts
2265
+ const policyChoices = [
2266
+ {
2267
+ description: "Grant access to all operations",
2268
+ title: "Sudo Permission",
2269
+ value: "sudo"
2270
+ },
2271
+ {
2272
+ description: "Whitelist addresses, contract and functions",
2273
+ title: "Call Permission",
2274
+ value: "call"
2275
+ },
2276
+ {
2277
+ description: "Specify the start and end time for when the key is valid",
2278
+ title: "Timestamp Permission",
2279
+ value: "timestamp"
2280
+ },
2281
+ {
2282
+ description: "Specify the allowed gas usage for the session key",
2283
+ title: "Gas Permission",
2284
+ value: "gas"
2285
+ },
2286
+ {
2287
+ description: "Specify which addresses can verify signatures from this session key",
2288
+ title: "Signature Permission",
2289
+ value: "signature-caller"
2290
+ },
2291
+ {
2292
+ description: "Specify the allowed gas usage for the session key",
2293
+ title: "Rate Limit Permission",
2294
+ value: "rate-limit"
2295
+ },
2296
+ {
2297
+ description: "Complete the session key creation",
2298
+ title: "Done",
2299
+ value: "done"
2300
+ }
2301
+ ];
2302
+ const policyChoicePrompt = (prevPolicies) => Effect.gen(function* () {
2303
+ const choices = policyChoices.filter((c) => !prevPolicies.some((p) => p.type === c.value));
2304
+ return yield* Prompt.select({
2305
+ choices,
2306
+ message: "Select Permission type you want to add for this session key"
2307
+ });
2308
+ });
2309
+ //#endregion
2310
+ //#region src/commands/session-key/create/prompts/common.ts
2311
+ const addressPrompt = (message) => Prompt.text({
2312
+ message,
2313
+ validate: (value) => Effect.gen(function* () {
2314
+ const res = Schema.decodeUnknownOption(EthereumAddress)(value);
2315
+ if (Option.isNone(res)) return yield* Effect.fail("Invalid Ethereum Address");
2316
+ return value;
2317
+ })
2318
+ });
2319
+ const etherPrompt = (message) => Prompt.text({
2320
+ message,
2321
+ validate: (val) => Effect.gen(function* () {
2322
+ const schema = Schema.NumberFromString.check(Schema.isGreaterThanOrEqualTo(0));
2323
+ const res = Schema.decodeOption(schema)(val);
2324
+ if (Option.isNone(res)) return yield* Effect.fail("Invalid Number");
2325
+ return res.value.toString();
2326
+ })
2327
+ });
2328
+ //#endregion
2329
+ //#region src/commands/session-key/create/prompts/call.ts
2330
+ const getEOAPermission = () => Effect.gen(function* () {
2331
+ return [{
2332
+ target: yield* addressPrompt("Enter target address:"),
2333
+ valueLimit: parseEther(yield* etherPrompt("Max value that can be transferred (in ETH units):"), "wei")
2334
+ }];
2335
+ });
2336
+ const getSmartContractPermission = () => Effect.gen(function* () {
2337
+ const fs = yield* FileSystem.FileSystem;
2338
+ const address = yield* addressPrompt("Enter smart contract address:");
2339
+ const maxLimit = yield* etherPrompt("Max value that can be transferred (in ETH units)");
2340
+ const abiFilePath = yield* Prompt.file({ message: "Select the ABI file for the smart contract" });
2341
+ const abiString = yield* fs.readFileString(abiFilePath).pipe(Effect.orDie);
2342
+ const functions = JSON.parse(abiString).filter((e) => e.type === "function");
2343
+ const allowedFunctions = yield* Prompt.multiSelect({
2344
+ choices: functions.map((f) => {
2345
+ let signature = `${f.name}(`;
2346
+ for (let i = 0; i < f.inputs.length; i++) {
2347
+ const sep = f.inputs.length === 0 || i === f.inputs.length - 1 ? "" : ", ";
2348
+ const input = f.inputs[i];
2349
+ signature += `${input.type}${input.name ? ` ${input.name}` : ""}${sep}`;
2350
+ }
2351
+ signature += ")";
2352
+ return {
2353
+ description: signature,
2354
+ title: f.name,
2355
+ value: f
2356
+ };
2357
+ }),
2358
+ message: "Select the functions you want to allow",
2359
+ min: 1
2360
+ });
2361
+ const weiUnits = parseEther(maxLimit);
2362
+ return allowedFunctions.map((f) => {
2363
+ return {
2364
+ abi: [f],
2365
+ functionName: f.name,
2366
+ selector: toFunctionSelector(f),
2367
+ target: address,
2368
+ valueLimit: weiUnits
2369
+ };
2370
+ });
2371
+ });
2372
+ const getPermissions = () => Effect.gen(function* () {
2373
+ const permissions = [];
2374
+ const targetAddressPrompt = Prompt.select({
2375
+ choices: [{
2376
+ title: "EOA",
2377
+ value: "eoa"
2378
+ }, {
2379
+ title: "Smart Contract",
2380
+ value: "smart-contract"
2381
+ }],
2382
+ message: "Select target address type"
2383
+ });
2384
+ while (true) {
2385
+ if (permissions.length > 0) {
2386
+ if (!(yield* Prompt.confirm({ message: "Do you want to add another call permission?" }))) break;
2387
+ }
2388
+ if ((yield* targetAddressPrompt) === "eoa") {
2389
+ const res = yield* getEOAPermission();
2390
+ permissions.push(...res);
2391
+ } else {
2392
+ const res = yield* getSmartContractPermission();
2393
+ permissions.push(...res);
2394
+ }
2395
+ }
2396
+ return permissions;
2397
+ });
2398
+ const getCallPolicyParams = Effect.gen(function* () {
2399
+ return {
2400
+ permissions: yield* getPermissions(),
2401
+ policyVersion: "0.0.4",
2402
+ type: "call"
2403
+ };
2404
+ });
2405
+ //#endregion
2406
+ //#region src/commands/session-key/create/prompts/gas.ts
2407
+ const getGasPolicyParams = Effect.gen(function* () {
2408
+ const allowed = yield* etherPrompt("Total amount of gas allowed (in ETH units)");
2409
+ const enforcePaymaster = yield* Prompt.confirm({ message: "Should UserOperations require Paymaster?" });
2410
+ return {
2411
+ amount: parseEther(allowed),
2412
+ type: "gas",
2413
+ enforcePaymaster
2414
+ };
2415
+ });
2416
+ //#endregion
2417
+ //#region src/commands/session-key/create/prompts/rate-limit.ts
2418
+ /** biome-ignore-all lint/suspicious/noExplicitAny: safe */
2419
+ const getRateLimitPolicyParams = Effect.gen(function* () {
2420
+ const interval = yield* Prompt.text({
2421
+ message: "Duration for which rate limit is enforced? (e.g. 1 hour, 1 day, 1 week)",
2422
+ validate: (v) => Effect.gen(function* () {
2423
+ if (Duration.fromInput(v)._tag === "None") return yield* Effect.fail("Invalid duration");
2424
+ return v;
2425
+ })
2426
+ });
2427
+ const intervalDuration = Duration.fromInputUnsafe(interval);
2428
+ const count = yield* Prompt.integer({
2429
+ message: "Number of requests allowed per interval.",
2430
+ validate: (v) => Effect.gen(function* () {
2431
+ if (v < 1) return yield* Effect.fail("Count must be greater than 0");
2432
+ return v;
2433
+ })
2434
+ });
2435
+ const startAt = yield* Prompt.date({
2436
+ initial: /* @__PURE__ */ new Date(),
2437
+ message: "Date at which the rate limit is enforced.",
2438
+ validate: (d) => Effect.gen(function* () {
2439
+ if (d < /* @__PURE__ */ new Date()) return yield* Effect.fail("Date must be in the future");
2440
+ return d;
2441
+ })
2442
+ });
2443
+ return {
2444
+ type: "rate-limit",
2445
+ count,
2446
+ interval: Duration.toMillis(intervalDuration),
2447
+ startAt: Math.floor(startAt.getTime() / 1e3)
2448
+ };
2449
+ });
2450
+ //#endregion
2451
+ //#region src/commands/session-key/create/prompts/signature-caller.ts
2452
+ const getSignatureCallerPolicyParams = Effect.gen(function* () {
2453
+ return {
2454
+ type: "signature-caller",
2455
+ allowedCallers: (yield* Prompt.text({
2456
+ message: "Comma-separated list of allowed callers:",
2457
+ validate: (v) => Effect.gen(function* () {
2458
+ const address = v.split(",").map((a) => a.trim());
2459
+ for (let i = 0; i < address.length; i++) {
2460
+ const a = address[i];
2461
+ if (Schema.decodeUnknownOption(EthereumAddress)(a)._tag === "None") return yield* Effect.fail(`Invalid Ethereum Address at index ${i}`);
2462
+ }
2463
+ return v;
2464
+ })
2465
+ })).split(",").map((a) => a.trim()).map((a) => Schema.decodeUnknownSync(EthereumAddress)(a))
2466
+ };
2467
+ });
2468
+ //#endregion
2469
+ //#region src/commands/session-key/create/prompts/timestamp.ts
2470
+ const getTimestampPolicyParams = Effect.gen(function* () {
2471
+ const validAfter = yield* Prompt.date({
2472
+ initial: /* @__PURE__ */ new Date(),
2473
+ message: "Time after which the key becomes valid.",
2474
+ validate: (d) => Effect.gen(function* () {
2475
+ if (d < /* @__PURE__ */ new Date()) return yield* Effect.fail("Date must be in the future");
2476
+ return d;
2477
+ })
2478
+ });
2479
+ const validUntil = yield* Prompt.date({
2480
+ initial: /* @__PURE__ */ new Date(),
2481
+ message: "Time until which the key becomes valid.",
2482
+ validate: (d) => Effect.gen(function* () {
2483
+ if (d < /* @__PURE__ */ new Date()) return yield* Effect.fail("Date must be in the future");
2484
+ if (d < validAfter) return yield* Effect.fail("Valid until must be after valid after");
2485
+ return d;
2486
+ })
2487
+ });
2488
+ return {
2489
+ type: "timestamp",
2490
+ validAfter: Math.floor(validAfter.getTime() / 1e3),
2491
+ validUntil: Math.floor(validUntil.getTime() / 1e3)
2492
+ };
2493
+ });
2494
+ //#endregion
2495
+ //#region src/commands/session-key/create/prompts/index.ts
2496
+ const getPoliciesFromUser = () => Effect.gen(function* () {
2497
+ const policyParams = [];
2498
+ while (true) {
2499
+ if (policyParams.length > 0) {
2500
+ if (!(yield* Prompt.confirm({ message: "Do you want to add another policy?" }))) break;
2501
+ }
2502
+ const policyType = yield* policyChoicePrompt(policyParams);
2503
+ if (policyType === "sudo") {
2504
+ policyParams.push({ type: "sudo" });
2505
+ break;
2506
+ }
2507
+ if (policyType === "timestamp") {
2508
+ const res = yield* getTimestampPolicyParams;
2509
+ policyParams.push(res);
2510
+ } else if (policyType === "call") {
2511
+ const res = yield* getCallPolicyParams;
2512
+ policyParams.push(res);
2513
+ } else if (policyType === "gas") {
2514
+ const res = yield* getGasPolicyParams;
2515
+ policyParams.push(res);
2516
+ } else if (policyType === "rate-limit") {
2517
+ const res = yield* getRateLimitPolicyParams;
2518
+ policyParams.push(res);
2519
+ } else if (policyType === "signature-caller") {
2520
+ const res = yield* getSignatureCallerPolicyParams;
2521
+ policyParams.push(res);
2522
+ } else break;
2523
+ }
2524
+ return policyParams;
2525
+ });
2526
+ //#endregion
2527
+ //#region src/commands/session-key/create/index.ts
2528
+ const handler$10 = (flagAlias, flagSmartAccountAlias, flagSessionKeyPassword, flagOwnerKeystorePassword) => Effect.gen(function* () {
2529
+ const promptManager = yield* PromptManager;
2530
+ const web3Service = yield* Web3Service;
2531
+ const smartAccountManager = yield* SmartAccountManager;
2532
+ const sessionKeyManager = yield* SessionKeyManager;
2533
+ const outputFormatter = yield* OutputFormatter;
2534
+ const globalFlags = yield* getGlobalFlags();
2535
+ let params;
2536
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(CreateSessionKeyParams))(globalFlags.params.value);
2537
+ else {
2538
+ let alias;
2539
+ let smartAccountAlias;
2540
+ let sessionKeyPassword;
2541
+ let ownerKeystorePassword;
2542
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
2543
+ else alias = yield* promptManager.aliasPrompt({
2544
+ aliasType: "new",
2545
+ message: "Enter alias for session key:",
2546
+ type: "session-key"
2547
+ });
2548
+ if (flagSmartAccountAlias._tag === "Some") smartAccountAlias = flagSmartAccountAlias.value;
2549
+ else smartAccountAlias = (yield* smartAccountManager.selectSmartAccount({ message: "Select smart account:" })).alias;
2550
+ const chains = yield* web3Service.multiSelectChain({ message: "Select chains:" });
2551
+ if (flagSessionKeyPassword._tag === "Some") sessionKeyPassword = flagSessionKeyPassword.value;
2552
+ else sessionKeyPassword = yield* promptManager.passwordPrompt({ message: "Enter session key password:" });
2553
+ const sa = yield* smartAccountManager.getSmartAccount({ alias: smartAccountAlias });
2554
+ if (flagOwnerKeystorePassword._tag === "Some") ownerKeystorePassword = flagOwnerKeystorePassword.value;
2555
+ else ownerKeystorePassword = yield* promptManager.passwordPrompt({ message: `Enter keystore password for owner (${sa.data.ownerAlias}):` });
2556
+ const policyParams = yield* getPoliciesFromUser();
2557
+ params = {
2558
+ alias,
2559
+ chains,
2560
+ smartAccountAlias,
2561
+ sessionKeyPassword: Redacted.value(sessionKeyPassword),
2562
+ ownerKeystorePassword: Redacted.value(ownerKeystorePassword),
2563
+ policyParams
2564
+ };
2565
+ }
2566
+ const res = yield* sessionKeyManager.createSessionKey(params);
2567
+ const data = {
2568
+ ...res.data.sessionKeyType === "ecdsa" ? { sessionKeyAddress: res.data.sessionKeyAddress } : { passKeyName: res.data.passKeyName },
2569
+ smartAccount: res.data.smartAccountAlias,
2570
+ chains: res.data.serializedAccounts.map((a) => getChain(a.chain).name)
2571
+ };
2572
+ if (globalFlags.quite) return;
2573
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2574
+ yield* Console.log(output);
2575
+ });
2576
+ const alias$7 = Flag.string("alias").pipe(Flag.withDescription("The alias of the session key to create"), Flag.withAlias("a"), Flag.optional);
2577
+ const smartAccountAlias$1 = Flag.string("smart-account").pipe(Flag.withDescription("The alias of the smart account to create session key for"), Flag.withAlias("sa"), Flag.optional);
2578
+ const sessionKeyPassword = Flag.redacted("password").pipe(Flag.withDescription("Password to encrypt session key"), Flag.withAlias("p"), Flag.optional);
2579
+ const ownerKeystorePassword = Flag.redacted("owner-password").pipe(Flag.withDescription("Password to encrypt session key"), Flag.withAlias("op"), Flag.optional);
2580
+ /**
2581
+ * Command that creates a new session key and stores it locally.
2582
+ */
2583
+ const createSessionKeyCommand = Command.make("create", {
2584
+ alias: alias$7,
2585
+ smartAccountAlias: smartAccountAlias$1,
2586
+ sessionKeyPassword,
2587
+ ownerKeystorePassword
2588
+ }, ({ alias, smartAccountAlias, sessionKeyPassword, ownerKeystorePassword }) => handler$10(alias, smartAccountAlias, sessionKeyPassword, ownerKeystorePassword)).pipe(Command.withAlias("c"), Command.withDescription("Creates a new session key"), Command.withExamples([{
2589
+ command: "namera session-key create",
2590
+ description: "Creates a new session key with interactive prompts"
2591
+ }, {
2592
+ command: "namera schema session-key.create",
2593
+ description: "Get the schema for session key create command"
2594
+ }]));
2595
+ //#endregion
2596
+ //#region src/helpers/humanize.ts
2597
+ const humanizeDuration = (input) => {
2598
+ const millis = Duration.toMillis(Duration.fromInputUnsafe(input));
2599
+ let seconds = Math.floor(millis / 1e3);
2600
+ const units = [
2601
+ {
2602
+ label: "year",
2603
+ value: 3600 * 24 * 365
2604
+ },
2605
+ {
2606
+ label: "month",
2607
+ value: 3600 * 24 * 30
2608
+ },
2609
+ {
2610
+ label: "day",
2611
+ value: 3600 * 24
2612
+ },
2613
+ {
2614
+ label: "hour",
2615
+ value: 3600
2616
+ },
2617
+ {
2618
+ label: "minute",
2619
+ value: 60
2620
+ },
2621
+ {
2622
+ label: "second",
2623
+ value: 1
2624
+ }
2625
+ ];
2626
+ const parts = [];
2627
+ for (const unit of units) if (seconds >= unit.value) {
2628
+ const count = Math.floor(seconds / unit.value);
2629
+ seconds %= unit.value;
2630
+ parts.push(`${count} ${unit.label}${count > 1 ? "s" : ""}`);
2631
+ }
2632
+ return parts.length > 0 ? parts.join(" ") : "0 seconds";
2633
+ };
2634
+ const humanizeRelativeTime = (target) => {
2635
+ const now = Date.now();
2636
+ const diff = (target instanceof Date ? target.getTime() : target * 1e3) - now;
2637
+ const abs = Math.abs(diff);
2638
+ const seconds = Math.floor(abs / 1e3);
2639
+ for (const unit of [
2640
+ {
2641
+ label: "year",
2642
+ value: 31536e3
2643
+ },
2644
+ {
2645
+ label: "month",
2646
+ value: 2592e3
2647
+ },
2648
+ {
2649
+ label: "day",
2650
+ value: 86400
2651
+ },
2652
+ {
2653
+ label: "hour",
2654
+ value: 3600
2655
+ },
2656
+ {
2657
+ label: "minute",
2658
+ value: 60
2659
+ },
2660
+ {
2661
+ label: "second",
2662
+ value: 1
2663
+ }
2664
+ ]) if (seconds >= unit.value) {
2665
+ const count = Math.floor(seconds / unit.value);
2666
+ const formatted = `${count} ${unit.label}${count > 1 ? "s" : ""}`;
2667
+ return diff > 0 ? `in ${formatted}` : `${formatted} ago`;
2668
+ }
2669
+ return "just now";
2670
+ };
2671
+ const humanizePolicyParams = (serializedAccount) => {
2672
+ const params = deserializePermissionAccountParams(serializedAccount);
2673
+ const result = {};
2674
+ result.permissionId = params.permissionParams.permissionId;
2675
+ for (const policy of params.permissionParams.policies ?? []) {
2676
+ const params = policy.policyParams;
2677
+ if (params.type === "sudo") result.sudoPolicy = { enabled: true };
2678
+ else if (params.type === "gas") result.gasPolicy = {
2679
+ enabled: true,
2680
+ allowed: `${formatEther(params.allowed ?? 0n)} ETH`,
2681
+ ...params.enforcePaymaster && { enforcePaymaster: params.enforcePaymaster },
2682
+ ...params.allowedPaymaster !== zeroAddress && { allowedPaymaster: params.allowedPaymaster }
2683
+ };
2684
+ else if (params.type === "timestamp") {
2685
+ const validAfter = params.validAfter ?? 0;
2686
+ const validUntil = params.validUntil ?? 0;
2687
+ result.timestampPolicy = {
2688
+ enabled: true,
2689
+ starts: humanizeRelativeTime(validAfter),
2690
+ ends: humanizeRelativeTime(validUntil)
2691
+ };
2692
+ } else if (params.type === "rate-limit") {
2693
+ const formattedInterval = humanizeDuration(params.interval ?? 0);
2694
+ result.rateLimitPolicy = {
2695
+ enabled: true,
2696
+ count: params.count,
2697
+ interval: formattedInterval,
2698
+ starts: humanizeRelativeTime(params.startAt ?? 0)
2699
+ };
2700
+ } else if (params.type === "signature-caller") result.signatureCallerPolicy = {
2701
+ enabled: true,
2702
+ allowedCallers: params.allowedCallers
2703
+ };
2704
+ else if (params.type === "call") {
2705
+ result.callPolicy = {
2706
+ enabled: true,
2707
+ policyVersion: params.policyVersion
2708
+ };
2709
+ if (!params.permissions || params.permissions.length === 0) continue;
2710
+ for (const permission of params.permissions) {
2711
+ result.callPolicy.permissions = result.callPolicy.permissions ?? [];
2712
+ if ("abi" in permission) {
2713
+ const formattedValue = formatEther(permission.valueLimit ?? 0n);
2714
+ const abiFn = permission.abi?.find((f) => f.type === "function" && f.name === permission.functionName);
2715
+ const cyan = "\x1B[36m";
2716
+ const yellow = "\x1B[33m";
2717
+ const magenta = "\x1B[35m";
2718
+ const reset = "\x1B[0m";
2719
+ let fnName = `${magenta}${permission.functionName ?? "unknown"}${reset}(`;
2720
+ if (abiFn) abiFn.inputs.forEach((input, i) => {
2721
+ if (i > 0) fnName += ", ";
2722
+ fnName += `${cyan}${input.type}${reset}${input.name ? ` ${yellow}${input.name}${reset}` : ""}`;
2723
+ });
2724
+ fnName += ")";
2725
+ result.callPolicy.permissions.push({
2726
+ targetAddress: permission.target,
2727
+ valueLimit: `${formattedValue} ETH`,
2728
+ functionName: fnName
2729
+ });
2730
+ } else {
2731
+ const formattedValue = formatEther(permission.valueLimit ?? 0n);
2732
+ result.callPolicy.permissions.push({
2733
+ targetAddress: permission.target,
2734
+ valueLimit: `${formattedValue} ETH`
2735
+ });
2736
+ }
2737
+ }
2738
+ }
2739
+ }
2740
+ return result;
2741
+ };
2742
+ //#endregion
2743
+ //#region src/commands/session-key/info.ts
2744
+ const handler$9 = (flagAlias) => Effect.gen(function* () {
2745
+ const sessionKeyManager = yield* SessionKeyManager;
2746
+ const outputFormatter = yield* OutputFormatter;
2747
+ const globalFlags = yield* getGlobalFlags();
2748
+ let params;
2749
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(GetSessionKeyInfoParams))(globalFlags.params.value);
2750
+ else {
2751
+ let alias;
2752
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
2753
+ else alias = (yield* sessionKeyManager.selectSessionKey({ message: "Select Session Key:" })).alias;
2754
+ params = { alias };
2755
+ }
2756
+ const res = yield* sessionKeyManager.getSessionKey(params);
2757
+ const data = {
2758
+ alias: res.alias,
2759
+ ...res.data.sessionKeyType === "ecdsa" ? { address: res.data.sessionKeyAddress } : { passKeyName: res.data.passKeyName },
2760
+ smartAccount: res.data.smartAccountAlias,
2761
+ chains: res.data.serializedAccounts.map((a) => getChain(a.chain).name).join(", "),
2762
+ policies: humanizePolicyParams(res.data.serializedAccounts[0]?.serializedAccount ?? "")
2763
+ };
2764
+ if (globalFlags.quite) return;
2765
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2766
+ yield* Console.log(output);
2767
+ });
2768
+ const alias$6 = Flag.string("alias").pipe(Flag.withDescription("The alias of the session key to get info for"), Flag.withAlias("a"), Flag.optional);
2769
+ const getSessionKeyInfoCommand = Command.make("info", { alias: alias$6 }, ({ alias }) => handler$9(alias)).pipe(Command.withDescription("Get information about a session key"), Command.withExamples([
2770
+ {
2771
+ command: "namera session-key info",
2772
+ description: "Get information about a session key with alias prompt"
2773
+ },
2774
+ {
2775
+ command: "namera session-key info --alias my-session-key",
2776
+ description: "Get information about a session key with alias 'my-session-key'"
2777
+ },
2778
+ {
2779
+ command: `namera session-key info --params '{"alias":"my-session-key"}'`,
2780
+ description: "Get information about a session key with json params"
2781
+ },
2782
+ {
2783
+ command: "namera session-key info --alias my-wallet --output json",
2784
+ description: "Get information about a session key in json format"
2785
+ },
2786
+ {
2787
+ command: "namera schema session-key.info",
2788
+ description: "Get the schema for the session key info command"
2789
+ }
2790
+ ]));
2791
+ //#endregion
2792
+ //#region src/commands/session-key/list.ts
2793
+ const handler$8 = (flagSmartAccountAlias) => Effect.gen(function* () {
2794
+ const sessionKeyManager = yield* SessionKeyManager;
2795
+ const outputFormatter = yield* OutputFormatter;
2796
+ const globalFlags = yield* getGlobalFlags();
2797
+ const data = (yield* sessionKeyManager.listSessionKeys({ smartAccount: flagSmartAccountAlias._tag === "Some" ? flagSmartAccountAlias.value : void 0 })).map((d) => ({
2798
+ alias: d.alias,
2799
+ ...d.data.sessionKeyType === "ecdsa" ? { address: d.data.sessionKeyAddress } : { passKeyName: d.data.passKeyName },
2800
+ smartAccount: d.data.smartAccountAlias,
2801
+ chains: d.data.serializedAccounts.map((a) => getChain(a.chain).name).join(", ")
2802
+ }));
2803
+ if (globalFlags.quite) return;
2804
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2805
+ yield* Console.log(output);
2806
+ });
2807
+ const smartAccountAlias = Flag.string("smart-account").pipe(Flag.withDescription("The alias of the smart account to list session keys for"), Flag.withAlias("sa"), Flag.optional);
2808
+ /**
2809
+ * Command that lists all locally stored session keys.
2810
+ */
2811
+ const listSessionKeysCommand = Command.make("list", { smartAccountAlias }, ({ smartAccountAlias }) => handler$8(smartAccountAlias)).pipe(Command.withAlias("ls"), Command.withDescription("List session keys"), Command.withExamples([
2812
+ {
2813
+ command: "namera session-key list",
2814
+ description: "List all session keys"
2815
+ },
2816
+ {
2817
+ command: "namera session-key list --smart-account my-smart",
2818
+ description: "List all session keys for smart account 'my-smart'"
2819
+ },
2820
+ {
2821
+ command: `namera session-key list --params '{"smartAccount":"my-smart"}'`,
2822
+ description: "List all session keys for smart account 'my-smart'"
2823
+ },
2824
+ {
2825
+ command: "namera session-key list --output json",
2826
+ description: "List all session keys in json format"
2827
+ },
2828
+ {
2829
+ command: "namera schema session-key.list",
2830
+ description: "Get the schema for the list session key command"
2831
+ }
2832
+ ]));
2833
+ //#endregion
2834
+ //#region src/commands/session-key/remove.ts
2835
+ const handler$7 = (flagAlias) => Effect.gen(function* () {
2836
+ const sessionKeyManager = yield* SessionKeyManager;
2837
+ const outputFormatter = yield* OutputFormatter;
2838
+ const globalFlags = yield* getGlobalFlags();
2839
+ let params;
2840
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(RemoveSessionKeyParams))(globalFlags.params.value);
2841
+ else {
2842
+ let alias;
2843
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
2844
+ else alias = (yield* sessionKeyManager.selectSessionKey({ message: "Select Session Key:" })).alias;
2845
+ params = { alias };
2846
+ }
2847
+ yield* sessionKeyManager.removeSessionKey(params);
2848
+ const data = { success: true };
2849
+ if (globalFlags.quite) return;
2850
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2851
+ yield* Console.log(output);
2852
+ });
2853
+ const alias$5 = Flag.string("alias").pipe(Flag.withDescription("The alias of the session key to remove"), Flag.withAlias("a"), Flag.optional);
2854
+ const removeSessionKeyCommand = Command.make("remove", { alias: alias$5 }, ({ alias }) => handler$7(alias)).pipe(Command.withAlias("rm"), Command.withDescription("Remove a Session Key"), Command.withExamples([
2855
+ {
2856
+ command: "namera session-key remove",
2857
+ description: "Remove a session key with alias prompt"
2858
+ },
2859
+ {
2860
+ command: "namera session-key remove --alias my-session-key",
2861
+ description: "Remove a session key with alias 'my-session-key'"
2862
+ },
2863
+ {
2864
+ command: `namera session-key remove --params '{"alias":"my-session-key"}'`,
2865
+ description: "Remove a session key with json params"
2866
+ },
2867
+ {
2868
+ command: "namera schema session-key.remove",
2869
+ description: "Get the schema for the remove session key command"
2870
+ }
2871
+ ]));
2872
+ const chainAndRpcUrl = {
2873
+ chain: Flag.choice("chain", Object.keys(supportedChains)).pipe(Flag.withDescription("Chain Name"), Flag.optional),
2874
+ rpcUrl: Flag.string("rpc-url").pipe(Flag.withDescription("RPC URL"), Flag.optional)
2875
+ };
2876
+ //#endregion
2877
+ //#region src/commands/session-key/status.ts
2878
+ const handler$6 = (flagAlias, flagChain, flagRpcUrl) => Effect.gen(function* () {
2879
+ const web3Service = yield* Web3Service;
2880
+ const sessionKeyManager = yield* SessionKeyManager;
2881
+ const outputFormatter = yield* OutputFormatter;
2882
+ const globalFlags = yield* getGlobalFlags();
2883
+ let params;
2884
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(GetSessionKeyStatusParams))(globalFlags.params.value);
2885
+ else {
2886
+ let alias;
2887
+ let chain;
2888
+ const rpcUrl = flagRpcUrl._tag === "Some" ? flagRpcUrl.value : void 0;
2889
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
2890
+ else alias = (yield* sessionKeyManager.selectSessionKey({ message: "Select Session Key:" })).alias;
2891
+ if (flagChain._tag === "Some") chain = flagChain.value;
2892
+ else chain = yield* web3Service.selectChain({ message: "Select Chain:" });
2893
+ params = {
2894
+ alias,
2895
+ chain,
2896
+ rpcUrl
2897
+ };
2898
+ }
2899
+ const data = { installed: yield* sessionKeyManager.getSessionKeyStatus(params) };
2900
+ if (globalFlags.quite) return;
2901
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2902
+ yield* Console.log(output);
2903
+ });
2904
+ const alias$4 = Flag.string("alias").pipe(Flag.withDescription("The alias of the session key"), Flag.withAlias("a"), Flag.optional);
2905
+ const getSessionKeyStatusCommand = Command.make("status", {
2906
+ alias: alias$4,
2907
+ ...chainAndRpcUrl
2908
+ }, ({ alias, chain, rpcUrl }) => handler$6(alias, chain, rpcUrl)).pipe(Command.withDescription("Get Session key status for a given chain"), Command.withExamples([
2909
+ {
2910
+ command: "namera session-key status -a my-smart --chain eth-mainnet",
2911
+ description: "Get Session key status for a given chain"
2912
+ },
2913
+ {
2914
+ command: "namera session-key status --alias my-smart --chain eth-mainnet --rpc-url https://rpc.ankr.com/eth",
2915
+ description: "Get Session key status for a given chain with custom RPC URL"
2916
+ },
2917
+ {
2918
+ command: `namera session-key status --params '{"alias":"my-session-key","chain":"eth-mainnet"}'`,
2919
+ description: "Get Session key status for a given chain with json params"
2920
+ },
2921
+ {
2922
+ command: "namera schema session-key.status",
2923
+ description: "Get the schema for the session key status command"
2924
+ }
2925
+ ]));
2926
+ //#endregion
2927
+ //#region src/commands/session-key/index.ts
2928
+ /**
2929
+ * Command group for session-key related operations.
2930
+ */
2931
+ const sessionKeyCommands = Command.make("session-key", {}, () => Effect.void).pipe(Command.withAlias("sk"), Command.withDescription("Session Key management commands."), Command.withSubcommands([
2932
+ createSessionKeyCommand,
2933
+ listSessionKeysCommand,
2934
+ getSessionKeyInfoCommand,
2935
+ getSessionKeyStatusCommand,
2936
+ removeSessionKeyCommand
2937
+ ]));
2938
+ //#endregion
2939
+ //#region src/commands/smart-account/create.ts
2940
+ const handler$5 = (flagAlias, flagOwnerAlias, flagOwnerPassword, flagIndex) => Effect.gen(function* () {
2941
+ const promptManager = yield* PromptManager;
2942
+ const keystoreManager = yield* KeystoreManager;
2943
+ const smartAccountManager = yield* SmartAccountManager;
2944
+ const outputFormatter = yield* OutputFormatter;
2945
+ const globalFlags = yield* getGlobalFlags();
2946
+ let params;
2947
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(CreateSmartAccountParams))(globalFlags.params.value);
2948
+ else {
2949
+ let alias;
2950
+ let ownerAlias;
2951
+ let ownerPassword;
2952
+ let index;
2953
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
2954
+ else alias = yield* promptManager.aliasPrompt({
2955
+ aliasType: "new",
2956
+ message: "Enter alias for smart account:",
2957
+ type: "smart-account"
2958
+ });
2959
+ if (flagOwnerAlias._tag === "Some") ownerAlias = flagOwnerAlias.value;
2960
+ else ownerAlias = (yield* keystoreManager.selectKeystore({ message: "Select owner keystore:" })).alias;
2961
+ if (flagOwnerPassword._tag === "Some") ownerPassword = flagOwnerPassword.value;
2962
+ else ownerPassword = yield* keystoreManager.getKeystorePassword({
2963
+ alias: ownerAlias,
2964
+ message: `Enter password for owner (${ownerAlias}):`
2965
+ });
2966
+ if (flagIndex._tag === "Some") index = BigInt(flagIndex.value);
2967
+ else index = 0n;
2968
+ params = {
2969
+ alias,
2970
+ ownerAlias,
2971
+ index,
2972
+ ownerPassword: Redacted.value(ownerPassword)
2973
+ };
2974
+ }
2975
+ const res = yield* smartAccountManager.createSmartAccount(params);
2976
+ const data = {
2977
+ address: res.data.smartAccountAddress,
2978
+ kernelVersion: res.data.kernelVersion,
2979
+ index: Number(res.data.index),
2980
+ owner: res.data.ownerAlias
2981
+ };
2982
+ if (globalFlags.quite) return;
2983
+ const output = yield* outputFormatter.format(data, globalFlags.out);
2984
+ yield* Console.log(output);
2985
+ });
2986
+ const alias$3 = Flag.string("alias").pipe(Flag.withDescription("The alias of the smart account to create"), Flag.withAlias("a"), Flag.optional);
2987
+ const ownerAlias = Flag.string("owner-alias").pipe(Flag.withDescription("The alias of the owner keystore"), Flag.withAlias("oa"), Flag.optional);
2988
+ const ownerPassword = Flag.redacted("owner-password").pipe(Flag.withDescription("The password of the owner keystore"), Flag.withAlias("op"), Flag.optional);
2989
+ const index = Flag.integer("index").pipe(Flag.withDescription("The index of the smart account"), Flag.withAlias("i"), Flag.optional);
2990
+ /**
2991
+ * Command that creates a new smart account and stores it locally.
2992
+ *
2993
+ * Accepts alias, owner alias, and index via flags or JSON params.
2994
+ */
2995
+ const createSmartAccountCommand = Command.make("create", {
2996
+ alias: alias$3,
2997
+ ownerAlias,
2998
+ ownerPassword,
2999
+ index
3000
+ }, ({ alias, ownerAlias, ownerPassword, index }) => handler$5(alias, ownerAlias, ownerPassword, index)).pipe(Command.withAlias("c"), Command.withDescription("Creates a new smart account"), Command.withExamples([
3001
+ {
3002
+ command: "namera smart-account create",
3003
+ description: "Creates a new smart account with interactive prompts"
3004
+ },
3005
+ {
3006
+ command: "namera smart-account create --alias my-smart --owner-alias my-owner --index 0",
3007
+ description: "Creates a new smart account with alias 'my-smart', owner alias 'my-owner', and index 0"
3008
+ },
3009
+ {
3010
+ command: `namera smart-account create --params '{"alias":"my-smart","owner-alias":"my-owner","index":0}'`,
3011
+ description: "Creates a new smart account with json params"
3012
+ },
3013
+ {
3014
+ command: "namera schema smart-account.create",
3015
+ description: "Get the schema for the create command"
3016
+ }
3017
+ ]));
3018
+ //#endregion
3019
+ //#region src/commands/smart-account/import.ts
3020
+ const handler$4 = () => Effect.gen(function* () {
3021
+ const smartAccountManager = yield* SmartAccountManager;
3022
+ const outputFormatter = yield* OutputFormatter;
3023
+ const globalFlags = yield* getGlobalFlags();
3024
+ if (globalFlags.params._tag === "None") return yield* Effect.fail(new SmartAccountManagerError({
3025
+ code: "SmartAccountImportError",
3026
+ message: "Smart Account import is only supported via JSON params"
3027
+ }));
3028
+ const params = Schema.decodeUnknownSync(Schema.fromJsonString(ImportSmartAccountParams))(globalFlags.params.value);
3029
+ const res = yield* smartAccountManager.importSmartAccount(params);
3030
+ const data = {
3031
+ alias: res.alias,
3032
+ address: res.data.smartAccountAddress,
3033
+ owner: res.data.ownerAlias,
3034
+ index: Number(res.data.index),
3035
+ kernelVersion: res.data.kernelVersion
3036
+ };
3037
+ if (globalFlags.quite) return;
3038
+ const output = yield* outputFormatter.format(data, globalFlags.out);
3039
+ yield* Console.log(output);
3040
+ });
3041
+ /**
3042
+ * Command that imports a smart account using JSON params only.
3043
+ */
3044
+ const importSmartAccountCommand = Command.make("import", {}, () => handler$4()).pipe(Command.withAlias("i"), Command.withDescription("Import a smart account"), Command.withExamples([{
3045
+ command: `namera smart-account import --params '{"alias":"my-smart","ownerAlias":"my-owner","index":0,"kernelVersion":"0.3.2","smartAccountAddress":"0x1bC710cbA70f8Ce638dC5c8F50FDb05d87a7D652","entryPointVersion":"0.7","ownerType":"ecdsa"}'`,
3046
+ description: "Import a smart account with json params"
3047
+ }, {
3048
+ command: "namera schema smart-account.import",
3049
+ description: "Get the schema for the smart account import command"
3050
+ }]));
3051
+ //#endregion
3052
+ //#region src/commands/smart-account/info.ts
3053
+ const handler$3 = (flagAlias) => Effect.gen(function* () {
3054
+ const smartAccountManager = yield* SmartAccountManager;
3055
+ const outputFormatter = yield* OutputFormatter;
3056
+ const globalFlags = yield* getGlobalFlags();
3057
+ let params;
3058
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(GetKeystoreParams))(globalFlags.params.value);
3059
+ else {
3060
+ let alias;
3061
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
3062
+ else alias = (yield* smartAccountManager.selectSmartAccount({ message: "Select Smart Account:" })).alias;
3063
+ params = { alias };
3064
+ }
3065
+ const res = yield* smartAccountManager.getSmartAccount(params);
3066
+ const data = {
3067
+ alias: res.alias,
3068
+ address: res.data.smartAccountAddress,
3069
+ owner: res.data.ownerAlias,
3070
+ index: Number(res.data.index),
3071
+ kernelVersion: res.data.kernelVersion
3072
+ };
3073
+ if (globalFlags.quite) return;
3074
+ const output = yield* outputFormatter.format(data, globalFlags.out);
3075
+ yield* Console.log(output);
3076
+ });
3077
+ const alias$2 = Flag.string("alias").pipe(Flag.withDescription("The alias of the smart account to retrieve"), Flag.withAlias("a"), Flag.optional);
3078
+ /**
3079
+ * Command that returns stored metadata for a smart account.
3080
+ *
3081
+ * Accepts alias via flag or JSON params, otherwise prompts for selection.
3082
+ */
3083
+ const getSmartAccountInfoCommand = Command.make("info", { alias: alias$2 }, ({ alias }) => handler$3(alias)).pipe(Command.withDescription("Get information about a smart account"), Command.withExamples([
3084
+ {
3085
+ command: "namera smart-account info",
3086
+ description: "Get information about a smart account with alias prompt"
3087
+ },
3088
+ {
3089
+ command: "namera smart-account info --alias my-smart",
3090
+ description: "Get information about a smart account with alias 'my-smart'"
3091
+ },
3092
+ {
3093
+ command: `namera smart-account info --params '{"alias":"my-smart"}'`,
3094
+ description: "Get information about a smart account with json params"
3095
+ },
3096
+ {
3097
+ command: "namera smart-account info --alias my-wallet --output json",
3098
+ description: "Get information about a smart account in json format"
3099
+ },
3100
+ {
3101
+ command: "namera schema smart-account.info",
3102
+ description: "Get the schema for the smart account info command"
3103
+ }
3104
+ ]));
3105
+ //#endregion
3106
+ //#region src/commands/smart-account/list.ts
3107
+ const handler$2 = () => Effect.gen(function* () {
3108
+ const smartAccountManager = yield* SmartAccountManager;
3109
+ const outputFormatter = yield* OutputFormatter;
3110
+ const globalFlags = yield* getGlobalFlags();
3111
+ const data = (yield* smartAccountManager.listSmartAccounts()).map((d) => ({
3112
+ alias: d.alias,
3113
+ address: d.data.smartAccountAddress,
3114
+ owner: d.data.ownerAlias,
3115
+ index: Number(d.data.index),
3116
+ kernelVersion: d.data.kernelVersion
3117
+ }));
3118
+ if (globalFlags.quite) return;
3119
+ const output = yield* outputFormatter.format(data, globalFlags.out);
3120
+ yield* Console.log(output);
3121
+ });
3122
+ /**
3123
+ * Command that lists all locally stored smart accounts.
3124
+ */
3125
+ const listSmartAccountsCommand = Command.make("list", {}, () => handler$2()).pipe(Command.withAlias("ls"), Command.withDescription("List all smart accounts"), Command.withExamples([
3126
+ {
3127
+ command: "namera smart-account list",
3128
+ description: "List all smart accounts"
3129
+ },
3130
+ {
3131
+ command: "namera smart-account list --output json",
3132
+ description: "List all smart accounts in json format"
3133
+ },
3134
+ {
3135
+ command: "namera smart-account list --output ndjson",
3136
+ description: "List all smart accounts in ndjson format"
3137
+ },
3138
+ {
3139
+ command: "namera schema smart-account.list",
3140
+ description: "Get the schema for the list smart account command"
3141
+ }
3142
+ ]));
3143
+ //#endregion
3144
+ //#region src/commands/smart-account/remove.ts
3145
+ const handler$1 = (flagAlias) => Effect.gen(function* () {
3146
+ const smartAccountManager = yield* SmartAccountManager;
3147
+ const outputFormatter = yield* OutputFormatter;
3148
+ const globalFlags = yield* getGlobalFlags();
3149
+ let params;
3150
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(RemoveSmartAccountParams))(globalFlags.params.value);
3151
+ else {
3152
+ let alias;
3153
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
3154
+ else alias = (yield* smartAccountManager.selectSmartAccount({ message: "Select Smart Account:" })).alias;
3155
+ params = { alias };
3156
+ }
3157
+ yield* smartAccountManager.removeSmartAccount(params);
3158
+ const data = { success: true };
3159
+ if (globalFlags.quite) return;
3160
+ const output = yield* outputFormatter.format(data, globalFlags.out);
3161
+ yield* Console.log(output);
3162
+ });
3163
+ const alias$1 = Flag.string("alias").pipe(Flag.withDescription("The alias of the smart account to remove"), Flag.withAlias("a"), Flag.optional);
3164
+ /**
3165
+ * Command that removes a locally stored smart account by alias.
3166
+ */
3167
+ const removeSmartAccountCommand = Command.make("remove", { alias: alias$1 }, ({ alias }) => handler$1(alias)).pipe(Command.withAlias("rm"), Command.withDescription("Remove a Smart Account"), Command.withExamples([
3168
+ {
3169
+ command: "namera smart-account remove",
3170
+ description: "Remove a smart-account with alias prompt"
3171
+ },
3172
+ {
3173
+ command: "namera smart-account remove --alias my-smart",
3174
+ description: "Remove a smart account with alias 'my-smart'"
3175
+ },
3176
+ {
3177
+ command: `namera smart-account remove --params '{"alias":"my-smart"}'`,
3178
+ description: "Remove a smart account with json params"
3179
+ },
3180
+ {
3181
+ command: "namera schema smart-account.remove",
3182
+ description: "Get the schema for the remove smart account command"
3183
+ }
3184
+ ]));
3185
+ //#endregion
3186
+ //#region src/commands/smart-account/status.ts
3187
+ const handler = (flagAlias, flagChain, flagRpcUrl) => Effect.gen(function* () {
3188
+ const web3Service = yield* Web3Service;
3189
+ const smartAccountManager = yield* SmartAccountManager;
3190
+ const outputFormatter = yield* OutputFormatter;
3191
+ const globalFlags = yield* getGlobalFlags();
3192
+ let params;
3193
+ if (globalFlags.params._tag === "Some") params = Schema.decodeUnknownSync(Schema.fromJsonString(GetSmartAccountStatusParams))(globalFlags.params.value);
3194
+ else {
3195
+ let alias;
3196
+ let chain;
3197
+ const rpcUrl = flagRpcUrl._tag === "Some" ? flagRpcUrl.value : void 0;
3198
+ if (flagAlias._tag === "Some") alias = flagAlias.value;
3199
+ else alias = (yield* smartAccountManager.selectSmartAccount({ message: "Select Smart Account:" })).alias;
3200
+ if (flagChain._tag === "Some") chain = flagChain.value;
3201
+ else chain = yield* web3Service.selectChain({ message: "Select Chain:" });
3202
+ params = {
3203
+ alias,
3204
+ chain,
3205
+ rpcUrl
3206
+ };
3207
+ }
3208
+ const data = { deployed: yield* smartAccountManager.getSmartAccountStatus(params) };
3209
+ if (globalFlags.quite) return;
3210
+ const output = yield* outputFormatter.format(data, globalFlags.out);
3211
+ yield* Console.log(output);
3212
+ });
3213
+ const alias = Flag.string("alias").pipe(Flag.withDescription("The alias of the smart account"), Flag.withAlias("a"), Flag.optional);
3214
+ /**
3215
+ * Command that checks deployment status of a smart account on a chain.
3216
+ */
3217
+ const getSmartAccountStatus = Command.make("status", {
3218
+ alias,
3219
+ ...chainAndRpcUrl
3220
+ }, ({ alias, chain, rpcUrl }) => handler(alias, chain, rpcUrl)).pipe(Command.withDescription("Get Smart Account Deployment Status for a given chain"), Command.withExamples([
3221
+ {
3222
+ command: "namera smart-account status -a my-smart --chain eth-mainnet",
3223
+ description: "Get Smart Account Deployment Status for a given chain"
3224
+ },
3225
+ {
3226
+ command: "namera smart-account status --alias my-smart --chain eth-mainnet --rpc-url https://rpc.ankr.com/eth",
3227
+ description: "Get Smart Account Deployment Status for a given chain with custom RPC URL"
3228
+ },
3229
+ {
3230
+ command: `namera smart-account status --params '{"alias":"my-smart","chain":"eth-mainnet"}'`,
3231
+ description: "Get Smart Account Deployment Status for a given chain with json params"
3232
+ }
3233
+ ]));
3234
+ //#endregion
3235
+ //#region src/commands/index.ts
3236
+ /**
3237
+ * Root CLI command set.
3238
+ */
3239
+ const commands = [
3240
+ keystoreCommands,
3241
+ Command.make("smart-account", {}, () => Effect.void).pipe(Command.withAlias("sa"), Command.withDescription("Smart Account management utilities."), Command.withSubcommands([
3242
+ createSmartAccountCommand,
3243
+ listSmartAccountsCommand,
3244
+ getSmartAccountInfoCommand,
3245
+ removeSmartAccountCommand,
3246
+ getSmartAccountStatus,
3247
+ importSmartAccountCommand
3248
+ ])),
3249
+ sessionKeyCommands,
3250
+ mcpCommands,
3251
+ schemaCommand
3252
+ ];
3253
+ //#endregion
3254
+ //#region src/index.ts
3255
+ const command = Command.make("namera", {}, () => Effect.void).pipe(Command.withDescription("Programmable Session keys for Smart Contracts Accounts."), Command.withGlobalFlags(globalFlags), Command.withSubcommands(commands), Command.withExamples([{
3256
+ command: "namera --help",
3257
+ description: "Print help"
3258
+ }, {
3259
+ command: "namera --version",
3260
+ description: "Print version"
3261
+ }]));
3262
+ const Layers = layer$1.pipe(Layer.provideMerge(layer$2), Layer.provideMerge(layer$3), Layer.provideMerge(layer$5), Layer.provideMerge(layer$4), Layer.provideMerge(layer$6), Layer.provideMerge(layer$7), Layer.provideMerge(layer), Layer.provideMerge(NodeServices.layer));
3263
+ Effect.gen(function* () {
3264
+ yield* (yield* ConfigManager).ensureConfigDirExists();
3265
+ yield* Command.run(command, { version: "0.0.1" });
3266
+ }).pipe(Effect.provide(Layers), Effect.provideService(ConfigProvider.ConfigProvider, ConfigProvider.fromEnv()), Effect.catch((e) => Console.error(e.message))).pipe(NodeRuntime.runMain);
3267
+ //#endregion
3268
+ export {};
3269
+
3270
+ //# sourceMappingURL=index.mjs.map