aavegotchi-cli 0.1.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.
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runTxSendCommand = runTxSendCommand;
4
+ exports.runTxStatusCommand = runTxStatusCommand;
5
+ exports.runTxResumeCommand = runTxResumeCommand;
6
+ exports.runTxWatchCommand = runTxWatchCommand;
7
+ const args_1 = require("../args");
8
+ const chains_1 = require("../chains");
9
+ const config_1 = require("../config");
10
+ const errors_1 = require("../errors");
11
+ const tx_engine_1 = require("../tx-engine");
12
+ function parseNoncePolicy(value) {
13
+ if (!value) {
14
+ return "safe";
15
+ }
16
+ if (value === "safe" || value === "replace" || value === "manual") {
17
+ return value;
18
+ }
19
+ throw new errors_1.CliError("INVALID_NONCE_POLICY", `Unsupported nonce policy '${value}'.`, 2);
20
+ }
21
+ function requireHexAddress(value, name) {
22
+ if (!value || !/^0x[a-fA-F0-9]{40}$/.test(value)) {
23
+ throw new errors_1.CliError("INVALID_ARGUMENT", `${name} must be an EVM address.`, 2, {
24
+ value,
25
+ });
26
+ }
27
+ return value.toLowerCase();
28
+ }
29
+ function parseHexData(value) {
30
+ if (!value) {
31
+ return undefined;
32
+ }
33
+ if (!/^0x([a-fA-F0-9]{2})*$/.test(value)) {
34
+ throw new errors_1.CliError("INVALID_ARGUMENT", "--data must be hex bytes prefixed with 0x.", 2, {
35
+ value,
36
+ });
37
+ }
38
+ return value;
39
+ }
40
+ function parseOptionalBigInt(value, name) {
41
+ if (value === undefined) {
42
+ return undefined;
43
+ }
44
+ if (!/^\d+$/.test(value)) {
45
+ throw new errors_1.CliError("INVALID_ARGUMENT", `${name} must be a non-negative integer string.`, 2, {
46
+ value,
47
+ });
48
+ }
49
+ return BigInt(value);
50
+ }
51
+ function parseNumberFlag(value, flag, fallback) {
52
+ if (!value) {
53
+ return fallback;
54
+ }
55
+ const parsed = Number(value);
56
+ if (!Number.isInteger(parsed) || parsed < 0) {
57
+ throw new errors_1.CliError("INVALID_ARGUMENT", `${flag} must be a non-negative integer.`, 2, {
58
+ value,
59
+ });
60
+ }
61
+ return parsed;
62
+ }
63
+ async function runTxSendCommand(ctx) {
64
+ const config = (0, config_1.loadConfig)();
65
+ const requestedProfile = (0, args_1.getFlagString)(ctx.args.flags, "profile") || ctx.globals.profile;
66
+ const profile = (0, config_1.getProfileOrThrow)(config, requestedProfile);
67
+ const policy = (0, config_1.getPolicyOrThrow)(config, profile.policy);
68
+ const chain = (0, chains_1.resolveChain)(profile.chain);
69
+ const rpcUrl = (0, chains_1.resolveRpcUrl)(chain, (0, args_1.getFlagString)(ctx.args.flags, "rpc-url") || profile.rpcUrl);
70
+ const to = requireHexAddress((0, args_1.getFlagString)(ctx.args.flags, "to"), "--to");
71
+ const valueWei = parseOptionalBigInt((0, args_1.getFlagString)(ctx.args.flags, "value-wei"), "--value-wei");
72
+ const data = parseHexData((0, args_1.getFlagString)(ctx.args.flags, "data"));
73
+ const idempotencyKey = (0, args_1.getFlagString)(ctx.args.flags, "idempotency-key");
74
+ const noncePolicy = parseNoncePolicy((0, args_1.getFlagString)(ctx.args.flags, "nonce-policy"));
75
+ const nonceValue = (0, args_1.getFlagString)(ctx.args.flags, "nonce");
76
+ const nonce = nonceValue ? parseNumberFlag(nonceValue, "--nonce", 0) : undefined;
77
+ const waitForReceipt = (0, args_1.getFlagBoolean)(ctx.args.flags, "wait") || (0, args_1.getFlagBoolean)(ctx.args.flags, "confirm");
78
+ const timeoutMs = parseNumberFlag((0, args_1.getFlagString)(ctx.args.flags, "timeout-ms"), "--timeout-ms", 120000);
79
+ if (noncePolicy === "manual" && nonce === undefined) {
80
+ throw new errors_1.CliError("MISSING_NONCE", "--nonce is required when --nonce-policy=manual.", 2);
81
+ }
82
+ const intent = {
83
+ idempotencyKey,
84
+ profileName: profile.name,
85
+ chainId: profile.chainId,
86
+ rpcUrl,
87
+ signer: profile.signer,
88
+ policy,
89
+ to,
90
+ data,
91
+ valueWei,
92
+ noncePolicy,
93
+ nonce,
94
+ waitForReceipt,
95
+ timeoutMs,
96
+ command: "tx send",
97
+ };
98
+ const result = await (0, tx_engine_1.executeTxIntent)(intent, chain);
99
+ return {
100
+ profile: profile.name,
101
+ policy: policy.name,
102
+ chainId: chain.chainId,
103
+ ...result,
104
+ };
105
+ }
106
+ async function runTxStatusCommand(ctx) {
107
+ const idempotencyKey = (0, args_1.getFlagString)(ctx.args.flags, "idempotency-key");
108
+ const txHash = (0, args_1.getFlagString)(ctx.args.flags, "tx-hash");
109
+ if (idempotencyKey) {
110
+ const entry = (0, tx_engine_1.getJournalEntryByIdempotency)(idempotencyKey);
111
+ if (!entry) {
112
+ throw new errors_1.CliError("TX_NOT_FOUND", `No transaction found for '${idempotencyKey}'.`, 2);
113
+ }
114
+ return {
115
+ type: "idempotency",
116
+ entry,
117
+ };
118
+ }
119
+ if (txHash) {
120
+ const entry = (0, tx_engine_1.getJournalEntryByHash)(txHash);
121
+ if (!entry) {
122
+ throw new errors_1.CliError("TX_NOT_FOUND", `No transaction found for hash '${txHash}'.`, 2);
123
+ }
124
+ return {
125
+ type: "hash",
126
+ entry,
127
+ };
128
+ }
129
+ return {
130
+ type: "recent",
131
+ entries: (0, tx_engine_1.getRecentJournalEntries)(parseNumberFlag((0, args_1.getFlagString)(ctx.args.flags, "limit"), "--limit", 20)),
132
+ };
133
+ }
134
+ async function runTxResumeCommand(ctx) {
135
+ const idempotencyKey = (0, args_1.getFlagString)(ctx.args.flags, "idempotency-key");
136
+ if (!idempotencyKey) {
137
+ throw new errors_1.CliError("MISSING_ARGUMENT", "tx resume requires --idempotency-key <value>.", 2);
138
+ }
139
+ const config = (0, config_1.loadConfig)();
140
+ const requestedProfile = (0, args_1.getFlagString)(ctx.args.flags, "profile") || ctx.globals.profile;
141
+ const profile = (0, config_1.getProfileOrThrow)(config, requestedProfile);
142
+ const chain = (0, chains_1.resolveChain)(profile.chain);
143
+ const rpcUrl = (0, chains_1.resolveRpcUrl)(chain, (0, args_1.getFlagString)(ctx.args.flags, "rpc-url") || profile.rpcUrl);
144
+ const timeoutMs = parseNumberFlag((0, args_1.getFlagString)(ctx.args.flags, "timeout-ms"), "--timeout-ms", 120000);
145
+ const result = await (0, tx_engine_1.resumeTransaction)(idempotencyKey, chain, rpcUrl, timeoutMs);
146
+ return {
147
+ profile: profile.name,
148
+ ...result,
149
+ };
150
+ }
151
+ async function runTxWatchCommand(ctx) {
152
+ const idempotencyKey = (0, args_1.getFlagString)(ctx.args.flags, "idempotency-key");
153
+ if (!idempotencyKey) {
154
+ throw new errors_1.CliError("MISSING_ARGUMENT", "tx watch requires --idempotency-key <value>.", 2);
155
+ }
156
+ const intervalMs = parseNumberFlag((0, args_1.getFlagString)(ctx.args.flags, "interval-ms"), "--interval-ms", 3000);
157
+ const timeoutMs = parseNumberFlag((0, args_1.getFlagString)(ctx.args.flags, "timeout-ms"), "--timeout-ms", 180000);
158
+ const started = Date.now();
159
+ while (Date.now() - started < timeoutMs) {
160
+ const entry = (0, tx_engine_1.getJournalEntryByIdempotency)(idempotencyKey);
161
+ if (entry?.status === "confirmed") {
162
+ return {
163
+ idempotencyKey,
164
+ status: "confirmed",
165
+ entry,
166
+ };
167
+ }
168
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
169
+ }
170
+ throw new errors_1.CliError("TIMEOUT", `Timed out waiting for '${idempotencyKey}' to confirm.`, 2, {
171
+ timeoutMs,
172
+ });
173
+ }
package/dist/config.js ADDED
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createDefaultPolicy = createDefaultPolicy;
37
+ exports.resolveAgcliHome = resolveAgcliHome;
38
+ exports.resolveConfigPath = resolveConfigPath;
39
+ exports.resolveJournalPath = resolveJournalPath;
40
+ exports.resolveKeychainPath = resolveKeychainPath;
41
+ exports.loadConfig = loadConfig;
42
+ exports.saveConfig = saveConfig;
43
+ exports.upsertProfile = upsertProfile;
44
+ exports.upsertPolicy = upsertPolicy;
45
+ exports.setActiveProfile = setActiveProfile;
46
+ exports.getProfileOrThrow = getProfileOrThrow;
47
+ exports.getPolicyOrThrow = getPolicyOrThrow;
48
+ const fs = __importStar(require("fs"));
49
+ const os = __importStar(require("os"));
50
+ const path = __importStar(require("path"));
51
+ const errors_1 = require("./errors");
52
+ const schemas_1 = require("./schemas");
53
+ const CONFIG_FILE = "config.json";
54
+ const JOURNAL_FILE = "journal.sqlite";
55
+ const KEYCHAIN_FILE = "keychain.json";
56
+ function nowIso() {
57
+ return new Date().toISOString();
58
+ }
59
+ function createDefaultPolicy(name = "default", timestamp = nowIso()) {
60
+ return {
61
+ name,
62
+ maxValueWei: undefined,
63
+ maxGasLimit: undefined,
64
+ maxFeePerGasWei: undefined,
65
+ maxPriorityFeePerGasWei: undefined,
66
+ allowedTo: undefined,
67
+ createdAt: timestamp,
68
+ updatedAt: timestamp,
69
+ };
70
+ }
71
+ function createDefaultConfig() {
72
+ const timestamp = nowIso();
73
+ return {
74
+ schemaVersion: 2,
75
+ profiles: {},
76
+ policies: {
77
+ default: createDefaultPolicy("default", timestamp),
78
+ },
79
+ };
80
+ }
81
+ function resolveAgcliHome(customHome) {
82
+ if (customHome) {
83
+ return customHome;
84
+ }
85
+ if (process.env.AGCLI_HOME) {
86
+ return process.env.AGCLI_HOME;
87
+ }
88
+ return path.join(os.homedir(), ".aavegotchi-cli");
89
+ }
90
+ function resolveConfigPath(customHome) {
91
+ const home = resolveAgcliHome(customHome);
92
+ return path.join(home, CONFIG_FILE);
93
+ }
94
+ function resolveJournalPath(customHome) {
95
+ const home = resolveAgcliHome(customHome);
96
+ return path.join(home, JOURNAL_FILE);
97
+ }
98
+ function resolveKeychainPath(customHome) {
99
+ const home = resolveAgcliHome(customHome);
100
+ return path.join(home, KEYCHAIN_FILE);
101
+ }
102
+ function migrateLegacyConfig(raw) {
103
+ const legacy = schemas_1.legacyCliConfigSchema.parse(raw);
104
+ const base = createDefaultConfig();
105
+ const policies = { ...base.policies };
106
+ for (const profile of Object.values(legacy.profiles)) {
107
+ if (!policies[profile.policy]) {
108
+ policies[profile.policy] = createDefaultPolicy(profile.policy, profile.createdAt);
109
+ }
110
+ }
111
+ return {
112
+ schemaVersion: 2,
113
+ activeProfile: legacy.activeProfile,
114
+ profiles: legacy.profiles,
115
+ policies,
116
+ };
117
+ }
118
+ function loadConfig(customHome) {
119
+ const configPath = resolveConfigPath(customHome);
120
+ if (!fs.existsSync(configPath)) {
121
+ return createDefaultConfig();
122
+ }
123
+ const raw = fs.readFileSync(configPath, "utf8");
124
+ let parsedJson;
125
+ try {
126
+ parsedJson = JSON.parse(raw);
127
+ }
128
+ catch {
129
+ throw new errors_1.CliError("INVALID_CONFIG", `Config file is not valid JSON: ${configPath}`, 2);
130
+ }
131
+ if (typeof parsedJson === "object" &&
132
+ parsedJson !== null &&
133
+ "schemaVersion" in parsedJson &&
134
+ parsedJson.schemaVersion === 1) {
135
+ return migrateLegacyConfig(parsedJson);
136
+ }
137
+ const parsed = schemas_1.cliConfigSchema.safeParse(parsedJson);
138
+ if (!parsed.success) {
139
+ throw new errors_1.CliError("INVALID_CONFIG", `Unsupported config format in ${configPath}.`, 2, {
140
+ issues: parsed.error.issues,
141
+ });
142
+ }
143
+ return parsed.data;
144
+ }
145
+ function saveConfig(config, customHome) {
146
+ const home = resolveAgcliHome(customHome);
147
+ const configPath = resolveConfigPath(customHome);
148
+ fs.mkdirSync(home, { recursive: true });
149
+ const tmpPath = `${configPath}.tmp`;
150
+ fs.writeFileSync(tmpPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
151
+ fs.renameSync(tmpPath, configPath);
152
+ return configPath;
153
+ }
154
+ function upsertProfile(config, profile) {
155
+ return {
156
+ ...config,
157
+ profiles: {
158
+ ...config.profiles,
159
+ [profile.name]: profile,
160
+ },
161
+ };
162
+ }
163
+ function upsertPolicy(config, policy) {
164
+ return {
165
+ ...config,
166
+ policies: {
167
+ ...config.policies,
168
+ [policy.name]: policy,
169
+ },
170
+ };
171
+ }
172
+ function setActiveProfile(config, profileName) {
173
+ if (!config.profiles[profileName]) {
174
+ throw new errors_1.CliError("PROFILE_NOT_FOUND", `Profile '${profileName}' does not exist.`, 2);
175
+ }
176
+ return {
177
+ ...config,
178
+ activeProfile: profileName,
179
+ };
180
+ }
181
+ function getProfileOrThrow(config, profileName) {
182
+ const selectedName = profileName || config.activeProfile;
183
+ if (!selectedName) {
184
+ throw new errors_1.CliError("NO_ACTIVE_PROFILE", "No active profile. Set one with 'ag profile use --profile <name>'.", 2);
185
+ }
186
+ const profile = config.profiles[selectedName];
187
+ if (!profile) {
188
+ throw new errors_1.CliError("PROFILE_NOT_FOUND", `Profile '${selectedName}' does not exist.`, 2);
189
+ }
190
+ return profile;
191
+ }
192
+ function getPolicyOrThrow(config, policyName) {
193
+ const policy = config.policies[policyName];
194
+ if (!policy) {
195
+ throw new errors_1.CliError("POLICY_NOT_FOUND", `Policy '${policyName}' does not exist.`, 2);
196
+ }
197
+ return policy;
198
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliError = void 0;
4
+ exports.assertCondition = assertCondition;
5
+ exports.toCliError = toCliError;
6
+ class CliError extends Error {
7
+ code;
8
+ exitCode;
9
+ details;
10
+ constructor(code, message, exitCode = 1, details) {
11
+ super(message);
12
+ this.code = code;
13
+ this.exitCode = exitCode;
14
+ this.details = details;
15
+ }
16
+ }
17
+ exports.CliError = CliError;
18
+ function assertCondition(condition, code, message, details) {
19
+ if (!condition) {
20
+ throw new CliError(code, message, 2, details);
21
+ }
22
+ }
23
+ function toCliError(error) {
24
+ if (error instanceof CliError) {
25
+ return error;
26
+ }
27
+ if (error instanceof Error) {
28
+ return new CliError("UNEXPECTED_ERROR", error.message, 1);
29
+ }
30
+ return new CliError("UNEXPECTED_ERROR", "An unknown error occurred.", 1);
31
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deriveIdempotencyKey = deriveIdempotencyKey;
4
+ exports.resolveIdempotencyKey = resolveIdempotencyKey;
5
+ const crypto_1 = require("crypto");
6
+ function deriveIdempotencyKey(intent) {
7
+ const source = JSON.stringify({
8
+ command: intent.command,
9
+ profileName: intent.profileName,
10
+ chainId: intent.chainId,
11
+ to: intent.to,
12
+ data: intent.data || "0x",
13
+ valueWei: intent.valueWei?.toString() || "0",
14
+ noncePolicy: intent.noncePolicy,
15
+ nonce: intent.nonce,
16
+ });
17
+ return (0, crypto_1.createHash)("sha256").update(source).digest("hex");
18
+ }
19
+ function resolveIdempotencyKey(intent) {
20
+ return intent.idempotencyKey || deriveIdempotencyKey(intent);
21
+ }
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const args_1 = require("./args");
5
+ const command_runner_1 = require("./command-runner");
6
+ const errors_1 = require("./errors");
7
+ const logger_1 = require("./logger");
8
+ const output_1 = require("./output");
9
+ async function run() {
10
+ const args = (0, args_1.parseArgv)(process.argv.slice(2));
11
+ const globals = (0, args_1.normalizeGlobals)(args);
12
+ (0, logger_1.initializeLogger)(globals);
13
+ const commandPath = (0, command_runner_1.normalizeCommandPath)(args.positionals);
14
+ if (commandPath[0] === "help") {
15
+ (0, output_1.outputHelp)();
16
+ return;
17
+ }
18
+ const ctx = {
19
+ commandPath,
20
+ args,
21
+ globals,
22
+ };
23
+ const { commandName, data } = await (0, command_runner_1.executeCommand)(ctx);
24
+ (0, output_1.outputSuccess)(commandName, data, globals);
25
+ }
26
+ run().catch((error) => {
27
+ const args = (0, args_1.parseArgv)(process.argv.slice(2));
28
+ const globals = (0, args_1.normalizeGlobals)(args);
29
+ (0, logger_1.initializeLogger)(globals);
30
+ const cliError = (0, errors_1.toCliError)(error);
31
+ (0, output_1.outputError)(args.positionals.join(" ") || "unknown", cliError, globals);
32
+ process.exitCode = cliError.exitCode;
33
+ });