@grimoirelabs/cli 0.14.2 → 0.15.1
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/commands/advisory-handlers.d.ts.map +1 -1
- package/dist/commands/advisory-handlers.js +1 -180
- package/dist/commands/advisory-handlers.js.map +1 -1
- package/dist/commands/advisory-live-trace.d.ts.map +1 -1
- package/dist/commands/advisory-live-trace.js +1 -1
- package/dist/commands/advisory-live-trace.js.map +1 -1
- package/dist/commands/advisory-verbose-trace.d.ts +15 -0
- package/dist/commands/advisory-verbose-trace.d.ts.map +1 -0
- package/dist/commands/advisory-verbose-trace.js +185 -0
- package/dist/commands/advisory-verbose-trace.js.map +1 -0
- package/dist/commands/cast-cross-chain.d.ts +69 -0
- package/dist/commands/cast-cross-chain.d.ts.map +1 -0
- package/dist/commands/cast-cross-chain.js +456 -0
- package/dist/commands/cast-cross-chain.js.map +1 -0
- package/dist/commands/cast.d.ts +1 -1
- package/dist/commands/cast.d.ts.map +1 -1
- package/dist/commands/cast.js +84 -709
- package/dist/commands/cast.js.map +1 -1
- package/dist/commands/compile-all.d.ts +15 -2
- package/dist/commands/compile-all.d.ts.map +1 -1
- package/dist/commands/compile-all.js +16 -21
- package/dist/commands/compile-all.js.map +1 -1
- package/dist/commands/compile.d.ts +1 -1
- package/dist/commands/compile.d.ts.map +1 -1
- package/dist/commands/compile.js +22 -26
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/cross-chain-helpers.d.ts +0 -1
- package/dist/commands/cross-chain-helpers.d.ts.map +1 -1
- package/dist/commands/cross-chain-helpers.js +0 -3
- package/dist/commands/cross-chain-helpers.js.map +1 -1
- package/dist/commands/history.d.ts +1 -2
- package/dist/commands/history.d.ts.map +1 -1
- package/dist/commands/history.js +59 -69
- package/dist/commands/history.js.map +1 -1
- package/dist/commands/init.d.ts +5 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +25 -20
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/log.d.ts +1 -2
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +9 -12
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/resume.d.ts +18 -1
- package/dist/commands/resume.d.ts.map +1 -1
- package/dist/commands/resume.js +44 -66
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/setup-ui.d.ts +46 -0
- package/dist/commands/setup-ui.d.ts.map +1 -0
- package/dist/commands/setup-ui.js +221 -0
- package/dist/commands/setup-ui.js.map +1 -0
- package/dist/commands/setup.d.ts +31 -3
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +27 -270
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/simulate-cross-chain.d.ts +53 -0
- package/dist/commands/simulate-cross-chain.d.ts.map +1 -0
- package/dist/commands/simulate-cross-chain.js +256 -0
- package/dist/commands/simulate-cross-chain.js.map +1 -0
- package/dist/commands/simulate.d.ts +5 -1
- package/dist/commands/simulate.d.ts.map +1 -1
- package/dist/commands/simulate.js +16 -364
- package/dist/commands/simulate.js.map +1 -1
- package/dist/commands/state-helpers.d.ts.map +1 -1
- package/dist/commands/state-helpers.js +1 -5
- package/dist/commands/state-helpers.js.map +1 -1
- package/dist/commands/validate.d.ts +24 -2
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +40 -42
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/venue-doctor-morpho.d.ts +38 -0
- package/dist/commands/venue-doctor-morpho.d.ts.map +1 -0
- package/dist/commands/venue-doctor-morpho.js +214 -0
- package/dist/commands/venue-doctor-morpho.js.map +1 -0
- package/dist/commands/venue-doctor.d.ts +2 -26
- package/dist/commands/venue-doctor.d.ts.map +1 -1
- package/dist/commands/venue-doctor.js +17 -221
- package/dist/commands/venue-doctor.js.map +1 -1
- package/dist/commands/venue.d.ts.map +1 -1
- package/dist/commands/venue.js +11 -19
- package/dist/commands/venue.js.map +1 -1
- package/dist/commands/venues.d.ts +1 -5
- package/dist/commands/venues.d.ts.map +1 -1
- package/dist/commands/venues.js +5 -8
- package/dist/commands/venues.js.map +1 -1
- package/dist/commands/wallet-weth.d.ts +22 -0
- package/dist/commands/wallet-weth.d.ts.map +1 -0
- package/dist/commands/wallet-weth.js +150 -0
- package/dist/commands/wallet-weth.js.map +1 -0
- package/dist/commands/wallet.d.ts +65 -3
- package/dist/commands/wallet.d.ts.map +1 -1
- package/dist/commands/wallet.js +183 -377
- package/dist/commands/wallet.js.map +1 -1
- package/dist/index.js +375 -170
- package/dist/index.js.map +1 -1
- package/dist/lib/execution-helpers.d.ts +24 -0
- package/dist/lib/execution-helpers.d.ts.map +1 -0
- package/dist/lib/execution-helpers.js +133 -0
- package/dist/lib/execution-helpers.js.map +1 -0
- package/dist/lib/json.d.ts +8 -0
- package/dist/lib/json.d.ts.map +1 -0
- package/dist/lib/json.js +10 -0
- package/dist/lib/json.js.map +1 -0
- package/dist/lib/keystore.d.ts +12 -0
- package/dist/lib/keystore.d.ts.map +1 -0
- package/dist/lib/keystore.js +14 -0
- package/dist/lib/keystore.js.map +1 -0
- package/dist/lib/prompts.d.ts +16 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +61 -0
- package/dist/lib/prompts.js.map +1 -0
- package/package.json +4 -4
package/dist/commands/cast.js
CHANGED
|
@@ -3,24 +3,24 @@
|
|
|
3
3
|
* Executes a spell with optional live execution via private key
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import * as readline from "node:readline";
|
|
9
|
-
import { Writable } from "node:stream";
|
|
10
|
-
import { compileFile, createProvider, createRunRecord, createWalletFromConfig, execute, formatWei, getChainName, getNativeCurrencySymbol, isTestnet, loadPrivateKey, orchestrateCrossChain, SqliteStateStore, toCrossChainReceipt, } from "@grimoirelabs/core";
|
|
11
|
-
import { adapters, createAlchemyQueryProvider, createHyperliquidAdapter, } from "@grimoirelabs/venues";
|
|
6
|
+
import { compileFile, createProvider, createWalletFromConfig, execute, formatWei, getChainName, getNativeCurrencySymbol, isTestnet, } from "@grimoirelabs/core";
|
|
7
|
+
import { adapters, createAlchemyQueryProvider } from "@grimoirelabs/venues";
|
|
12
8
|
import chalk from "chalk";
|
|
13
9
|
import ora from "ora";
|
|
14
10
|
import { hydrateParamsFromEnsProfile, resolveEnsProfile } from "../lib/ens-profile.js";
|
|
11
|
+
import { resolveNoState, resolveRpcUrlFromOption } from "../lib/execution-helpers.js";
|
|
12
|
+
import { stringifyJson } from "../lib/json.js";
|
|
13
|
+
import { DEFAULT_KEYSTORE_PATH } from "../lib/keystore.js";
|
|
14
|
+
import { confirmPrompt } from "../lib/prompts.js";
|
|
15
15
|
import { resolveAdvisorSkillsDirs } from "./advisor-skill-helpers.js";
|
|
16
16
|
import { resolveAdvisoryHandler } from "./advisory-handlers.js";
|
|
17
17
|
import { createAdvisoryLiveTraceLogger } from "./advisory-live-trace.js";
|
|
18
|
-
import {
|
|
18
|
+
import { configureHyperliquidAdapters, executeCrossChainCast, resolveKeystorePassword, safeGetBlockNumber, } from "./cast-cross-chain.js";
|
|
19
19
|
import { buildRuntimeProvenanceManifest, enforceFreshnessPolicy, resolveDataPolicy, resolveReplayParams, } from "./data-provenance.js";
|
|
20
20
|
import { buildRunReportEnvelope, formatRunReportText } from "./run-report.js";
|
|
21
21
|
import { spellUsesQueryFunctions } from "./spell-analysis.js";
|
|
22
22
|
import { withStatePersistence } from "./state-helpers.js";
|
|
23
|
-
const
|
|
23
|
+
const DEFAULT_GAS_MULTIPLIER = 1.1;
|
|
24
24
|
export async function castCommand(spellPath, options) {
|
|
25
25
|
const spinner = ora(`Loading ${spellPath}...`).start();
|
|
26
26
|
try {
|
|
@@ -31,23 +31,23 @@ export async function castCommand(spellPath, options) {
|
|
|
31
31
|
}
|
|
32
32
|
catch {
|
|
33
33
|
spinner.fail(chalk.red("Invalid params JSON"));
|
|
34
|
-
|
|
34
|
+
throw new Error("Cast failed");
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
if (options.ensName) {
|
|
38
38
|
spinner.text = `Resolving ENS profile ${options.ensName}...`;
|
|
39
39
|
const profile = await resolveEnsProfile(options.ensName, { rpcUrl: options.ensRpcUrl });
|
|
40
40
|
params = hydrateParamsFromEnsProfile(params, profile);
|
|
41
|
-
console.
|
|
41
|
+
console.error(chalk.dim(`ENS profile ${profile.name} -> ${profile.address ?? "unresolved"} (risk=${profile.riskProfile ?? "n/a"}, slippage=${profile.maxSlippageBps ?? "n/a"})`));
|
|
42
42
|
}
|
|
43
43
|
spinner.text = "Compiling spell...";
|
|
44
44
|
const compileResult = await compileFile(spellPath);
|
|
45
45
|
if (!compileResult.success || !compileResult.ir) {
|
|
46
46
|
spinner.fail(chalk.red("Compilation failed"));
|
|
47
47
|
for (const error of compileResult.errors) {
|
|
48
|
-
console.
|
|
48
|
+
console.error(chalk.red(` [${error.code}] ${error.message}`));
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
throw new Error("Cast failed");
|
|
51
51
|
}
|
|
52
52
|
const spell = compileResult.ir;
|
|
53
53
|
spinner.succeed(chalk.green("Spell compiled successfully"));
|
|
@@ -55,7 +55,7 @@ export async function castCommand(spellPath, options) {
|
|
|
55
55
|
if (options.trigger) {
|
|
56
56
|
const anyTrigger = spell.triggers.find((t) => t.type === "any");
|
|
57
57
|
if (!anyTrigger || anyTrigger.type !== "any" || !spell.triggerStepMap) {
|
|
58
|
-
console.
|
|
58
|
+
console.error(chalk.yellow(`Warning: --trigger "${options.trigger}" ignored — spell has a single trigger (no filtering needed).`));
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
const noState = resolveNoState(options);
|
|
@@ -80,20 +80,20 @@ export async function castCommand(spellPath, options) {
|
|
|
80
80
|
});
|
|
81
81
|
params = replayResolution.params;
|
|
82
82
|
if (options.privateKey || options.mnemonic) {
|
|
83
|
-
console.
|
|
83
|
+
console.error(chalk.yellow("Warning: Avoid passing secrets via CLI arguments. Use --key-env or env vars instead."));
|
|
84
84
|
}
|
|
85
|
-
console.
|
|
86
|
-
console.
|
|
87
|
-
console.
|
|
88
|
-
console.
|
|
89
|
-
console.
|
|
90
|
-
console.
|
|
85
|
+
console.error();
|
|
86
|
+
console.error(chalk.cyan("Spell Info:"));
|
|
87
|
+
console.error(` ${chalk.dim("Name:")} ${spell.meta.name}`);
|
|
88
|
+
console.error(` ${chalk.dim("Version:")} ${spell.version}`);
|
|
89
|
+
console.error(` ${chalk.dim("Steps:")} ${spell.steps.length}`);
|
|
90
|
+
console.error(` ${chalk.dim("Mode:")} ${mode}`);
|
|
91
91
|
if (spell.params.length > 0) {
|
|
92
|
-
console.
|
|
93
|
-
console.
|
|
92
|
+
console.error();
|
|
93
|
+
console.error(chalk.cyan("Parameters:"));
|
|
94
94
|
for (const param of spell.params) {
|
|
95
95
|
const value = params[param.name] ?? param.default;
|
|
96
|
-
console.
|
|
96
|
+
console.error(` ${chalk.dim(param.name)}: ${JSON.stringify(value)}`);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
const chainId = Number.parseInt(options.chain ?? "1", 10);
|
|
@@ -114,16 +114,16 @@ export async function castCommand(spellPath, options) {
|
|
|
114
114
|
});
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
|
-
console.
|
|
118
|
-
console.
|
|
119
|
-
console.
|
|
120
|
-
console.
|
|
117
|
+
console.error();
|
|
118
|
+
console.error(chalk.cyan("Network:"));
|
|
119
|
+
console.error(` ${chalk.dim("Chain:")} ${chainName} (${chainId})`);
|
|
120
|
+
console.error(` ${chalk.dim("Testnet:")} ${isTest ? "Yes" : "No"}`);
|
|
121
121
|
if (hasKey && mode !== "simulate") {
|
|
122
|
-
await executeWithWallet(spell, params, options, noState, chainId, isTest, dataPolicy, replayResolution);
|
|
122
|
+
return await executeWithWallet(spell, params, options, noState, chainId, isTest, dataPolicy, replayResolution);
|
|
123
123
|
}
|
|
124
124
|
else {
|
|
125
125
|
const runtimeFlow = mode === "dry-run" ? "cast_dry_run" : "simulate";
|
|
126
|
-
await executeSimulation(spell, params, options, noState, chainId, runtimeFlow, dataPolicy, replayResolution);
|
|
126
|
+
return await executeSimulation(spell, params, options, noState, chainId, runtimeFlow, dataPolicy, replayResolution);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
catch (error) {
|
|
@@ -131,7 +131,7 @@ export async function castCommand(spellPath, options) {
|
|
|
131
131
|
if (options.verbose) {
|
|
132
132
|
console.error(error);
|
|
133
133
|
}
|
|
134
|
-
|
|
134
|
+
throw new Error("Cast failed");
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
async function executeWithWallet(spell, params, options, noState, chainId, isTest, dataPolicy, replayResolution) {
|
|
@@ -150,33 +150,17 @@ async function executeWithWallet(spell, params, options, noState, chainId, isTes
|
|
|
150
150
|
const keystorePath = options.keystore ?? DEFAULT_KEYSTORE_PATH;
|
|
151
151
|
if (!existsSync(keystorePath)) {
|
|
152
152
|
spinner.fail(chalk.red(`No key provided and no keystore found at ${keystorePath}`));
|
|
153
|
-
console.
|
|
154
|
-
|
|
155
|
-
return;
|
|
153
|
+
console.error(chalk.dim(" Run 'grimoire wallet generate' to create one."));
|
|
154
|
+
throw new Error("Cast failed");
|
|
156
155
|
}
|
|
157
156
|
const password = await resolveKeystorePassword(options, spinner);
|
|
158
157
|
if (!password) {
|
|
159
|
-
|
|
158
|
+
throw new Error("Cast failed");
|
|
160
159
|
}
|
|
161
160
|
const keystoreJson = readFileSync(keystorePath, "utf-8");
|
|
162
161
|
keyConfig = { type: "keystore", source: keystoreJson, password };
|
|
163
162
|
}
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const rawKey = loadPrivateKey(keyConfig);
|
|
167
|
-
configuredAdapters = adapters.map((adapter) => {
|
|
168
|
-
if (adapter.meta.name === "hyperliquid") {
|
|
169
|
-
return createHyperliquidAdapter({
|
|
170
|
-
privateKey: rawKey,
|
|
171
|
-
assetMap: { ETH: 4 },
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
return adapter;
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
// Keep default adapters if key extraction fails.
|
|
179
|
-
}
|
|
163
|
+
const configuredAdapters = configureHyperliquidAdapters(keyConfig);
|
|
180
164
|
const rpcUrl = resolveRpcUrlFromOption(chainId, options.rpcUrl);
|
|
181
165
|
if (!rpcUrl) {
|
|
182
166
|
spinner.warn(chalk.yellow("No RPC URL provided, using default public RPC"));
|
|
@@ -185,21 +169,23 @@ async function executeWithWallet(spell, params, options, noState, chainId, isTes
|
|
|
185
169
|
const wallet = createWalletFromConfig(keyConfig, chainId, provider.rpcUrl);
|
|
186
170
|
spinner.succeed(chalk.green(`Wallet loaded: ${wallet.address}`));
|
|
187
171
|
const balance = await provider.getBalance(wallet.address);
|
|
188
|
-
console.
|
|
172
|
+
console.error(` ${chalk.dim("Balance:")} ${formatWei(balance)} ${getNativeCurrencySymbol(chainId)}`);
|
|
189
173
|
if (balance === 0n) {
|
|
190
|
-
console.
|
|
174
|
+
console.error(chalk.yellow(` Warning: Wallet has no ${getNativeCurrencySymbol(chainId)} for gas`));
|
|
191
175
|
}
|
|
192
176
|
const vault = (options.vault ?? wallet.address);
|
|
193
177
|
const queryProvider = createAlchemyQueryProvider({ provider, chainId, vault, rpcUrl });
|
|
194
|
-
console.
|
|
178
|
+
console.error(` ${chalk.dim("Vault:")} ${vault}`);
|
|
195
179
|
if (!isTest) {
|
|
196
|
-
console.
|
|
197
|
-
console.
|
|
198
|
-
console.
|
|
180
|
+
console.error();
|
|
181
|
+
console.error(chalk.red("WARNING: This is MAINNET"));
|
|
182
|
+
console.error(chalk.red(" Real funds will be used!"));
|
|
199
183
|
}
|
|
200
184
|
const executionMode = options.dryRun ? "dry-run" : "execute";
|
|
201
185
|
const runtimeMode = executionMode === "dry-run" ? "cast_dry_run" : "cast_execute";
|
|
202
|
-
const gasMultiplier = options.gasMultiplier
|
|
186
|
+
const gasMultiplier = options.gasMultiplier
|
|
187
|
+
? Number.parseFloat(options.gasMultiplier)
|
|
188
|
+
: DEFAULT_GAS_MULTIPLIER;
|
|
203
189
|
const advisorSkillsDirs = resolveAdvisorSkillsDirs(options.advisorSkillsDir) ?? [];
|
|
204
190
|
const onAdvisory = await resolveAdvisoryHandler(spell.id, {
|
|
205
191
|
advisoryPi: options.advisoryPi,
|
|
@@ -209,7 +195,7 @@ async function executeWithWallet(spell, params, options, noState, chainId, isTes
|
|
|
209
195
|
advisoryThinking: options.advisoryThinking,
|
|
210
196
|
advisoryTools: options.advisoryTools,
|
|
211
197
|
advisoryTraceVerbose: options.advisoryTraceVerbose,
|
|
212
|
-
advisoryTraceLogger: options.json ? undefined : console.
|
|
198
|
+
advisoryTraceLogger: options.json ? undefined : console.error,
|
|
213
199
|
advisorSkillsDirs,
|
|
214
200
|
stateDir: options.stateDir,
|
|
215
201
|
noState,
|
|
@@ -218,7 +204,7 @@ async function executeWithWallet(spell, params, options, noState, chainId, isTes
|
|
|
218
204
|
});
|
|
219
205
|
const eventCallback = options.json
|
|
220
206
|
? undefined
|
|
221
|
-
: createAdvisoryLiveTraceLogger(console.
|
|
207
|
+
: createAdvisoryLiveTraceLogger(console.error, {
|
|
222
208
|
verbose: options.advisoryTraceVerbose,
|
|
223
209
|
});
|
|
224
210
|
const provenance = buildRuntimeProvenanceManifest({
|
|
@@ -232,22 +218,22 @@ async function executeWithWallet(spell, params, options, noState, chainId, isTes
|
|
|
232
218
|
});
|
|
233
219
|
const freshnessWarnings = enforceFreshnessPolicy(provenance);
|
|
234
220
|
for (const warning of freshnessWarnings) {
|
|
235
|
-
console.
|
|
221
|
+
console.error(chalk.yellow(`Warning: ${warning}`));
|
|
236
222
|
}
|
|
237
223
|
const confirmCallback = options.skipConfirm || isTest
|
|
238
224
|
? async () => true
|
|
239
225
|
: async (message) => {
|
|
240
|
-
console.
|
|
226
|
+
console.error(message);
|
|
241
227
|
return await confirmPrompt(chalk.yellow("Proceed? (yes/no): "));
|
|
242
228
|
};
|
|
243
|
-
console.
|
|
244
|
-
console.
|
|
229
|
+
console.error();
|
|
230
|
+
console.error(chalk.cyan(`Executing spell (${executionMode})...`));
|
|
245
231
|
const execResult = await withStatePersistence(spell.id, {
|
|
246
232
|
stateDir: options.stateDir,
|
|
247
233
|
noState,
|
|
248
234
|
buildRunProvenance: () => provenance,
|
|
249
235
|
onUnavailable: () => {
|
|
250
|
-
console.
|
|
236
|
+
console.error(chalk.yellow("State persistence unavailable in Node (missing better-sqlite3). Continuing without persisted state."));
|
|
251
237
|
},
|
|
252
238
|
}, async (persistentState) => {
|
|
253
239
|
return execute({
|
|
@@ -263,7 +249,7 @@ async function executeWithWallet(spell, params, options, noState, chainId, isTes
|
|
|
263
249
|
gasMultiplier,
|
|
264
250
|
confirmCallback,
|
|
265
251
|
progressCallback: (message) => {
|
|
266
|
-
console.
|
|
252
|
+
console.error(chalk.dim(` ${message}`));
|
|
267
253
|
},
|
|
268
254
|
skipTestnetConfirmation: options.skipConfirm ?? false,
|
|
269
255
|
adapters: configuredAdapters,
|
|
@@ -275,38 +261,39 @@ async function executeWithWallet(spell, params, options, noState, chainId, isTes
|
|
|
275
261
|
});
|
|
276
262
|
});
|
|
277
263
|
if (execResult.success) {
|
|
278
|
-
console.
|
|
264
|
+
console.error(chalk.green("Execution completed successfully"));
|
|
279
265
|
}
|
|
280
266
|
else {
|
|
281
|
-
console.
|
|
267
|
+
console.error(chalk.red(`Execution failed: ${execResult.error}`));
|
|
282
268
|
}
|
|
283
269
|
const report = buildRunReportEnvelope({
|
|
284
270
|
spellName: spell.meta.name,
|
|
285
271
|
result: execResult,
|
|
286
272
|
provenance,
|
|
287
273
|
});
|
|
288
|
-
|
|
274
|
+
const payload = execResult.commit
|
|
275
|
+
? {
|
|
276
|
+
success: execResult.success,
|
|
277
|
+
preview: execResult.receipt,
|
|
278
|
+
commit: execResult.commit,
|
|
279
|
+
error: execResult.structuredError,
|
|
280
|
+
}
|
|
281
|
+
: {
|
|
282
|
+
success: execResult.success,
|
|
283
|
+
receipt: execResult.receipt,
|
|
284
|
+
error: execResult.structuredError,
|
|
285
|
+
};
|
|
286
|
+
console.error();
|
|
289
287
|
if (options.json) {
|
|
290
|
-
|
|
291
|
-
? {
|
|
292
|
-
success: execResult.success,
|
|
293
|
-
preview: execResult.receipt,
|
|
294
|
-
commit: execResult.commit,
|
|
295
|
-
error: execResult.structuredError,
|
|
296
|
-
}
|
|
297
|
-
: {
|
|
298
|
-
success: execResult.success,
|
|
299
|
-
receipt: execResult.receipt,
|
|
300
|
-
error: execResult.structuredError,
|
|
301
|
-
};
|
|
302
|
-
console.log(stringifyJson(payload));
|
|
288
|
+
console.error(stringifyJson(payload));
|
|
303
289
|
}
|
|
304
290
|
else {
|
|
305
|
-
console.
|
|
291
|
+
console.error(formatRunReportText(report));
|
|
306
292
|
}
|
|
307
293
|
if (!execResult.success) {
|
|
308
|
-
|
|
294
|
+
throw new Error("Cast failed");
|
|
309
295
|
}
|
|
296
|
+
return payload;
|
|
310
297
|
}
|
|
311
298
|
async function executeSimulation(spell, params, options, noState, chainId, runtimeMode, dataPolicy, replayResolution) {
|
|
312
299
|
const spinner = ora("Running simulation...").start();
|
|
@@ -319,7 +306,7 @@ async function executeSimulation(spell, params, options, noState, chainId, runti
|
|
|
319
306
|
});
|
|
320
307
|
const freshnessWarnings = enforceFreshnessPolicy(provenance);
|
|
321
308
|
for (const warning of freshnessWarnings) {
|
|
322
|
-
console.
|
|
309
|
+
console.error(chalk.yellow(`Warning: ${warning}`));
|
|
323
310
|
}
|
|
324
311
|
const vault = (options.vault ?? "0x0000000000000000000000000000000000000000");
|
|
325
312
|
const simRpcUrl = resolveRpcUrlFromOption(chainId, options.rpcUrl);
|
|
@@ -337,7 +324,7 @@ async function executeSimulation(spell, params, options, noState, chainId, runti
|
|
|
337
324
|
advisoryThinking: options.advisoryThinking,
|
|
338
325
|
advisoryTools: options.advisoryTools,
|
|
339
326
|
advisoryTraceVerbose: options.advisoryTraceVerbose,
|
|
340
|
-
advisoryTraceLogger: options.json ? undefined : console.
|
|
327
|
+
advisoryTraceLogger: options.json ? undefined : console.error,
|
|
341
328
|
advisorSkillsDirs,
|
|
342
329
|
stateDir: options.stateDir,
|
|
343
330
|
noState,
|
|
@@ -346,7 +333,7 @@ async function executeSimulation(spell, params, options, noState, chainId, runti
|
|
|
346
333
|
});
|
|
347
334
|
const eventCallback = options.json
|
|
348
335
|
? undefined
|
|
349
|
-
: createAdvisoryLiveTraceLogger(console.
|
|
336
|
+
: createAdvisoryLiveTraceLogger(console.error, {
|
|
350
337
|
verbose: options.advisoryTraceVerbose,
|
|
351
338
|
});
|
|
352
339
|
// execute() with simulate:true internally uses preview()
|
|
@@ -355,7 +342,7 @@ async function executeSimulation(spell, params, options, noState, chainId, runti
|
|
|
355
342
|
noState,
|
|
356
343
|
buildRunProvenance: () => provenance,
|
|
357
344
|
onUnavailable: () => {
|
|
358
|
-
console.
|
|
345
|
+
console.error(chalk.yellow("State persistence unavailable in Node (missing better-sqlite3). Continuing without persisted state."));
|
|
359
346
|
},
|
|
360
347
|
}, async (persistentState) => {
|
|
361
348
|
return execute({
|
|
@@ -385,633 +372,21 @@ async function executeSimulation(spell, params, options, noState, chainId, runti
|
|
|
385
372
|
result,
|
|
386
373
|
provenance,
|
|
387
374
|
});
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
receipt: result.receipt,
|
|
393
|
-
error: result.structuredError,
|
|
394
|
-
};
|
|
395
|
-
console.log(stringifyJson(payload));
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
console.log(formatRunReportText(report));
|
|
399
|
-
}
|
|
400
|
-
if (!result.success) {
|
|
401
|
-
process.exit(1);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
async function executeCrossChainCast(input) {
|
|
405
|
-
const destinationSpellPath = input.options.destinationSpell;
|
|
406
|
-
if (!destinationSpellPath) {
|
|
407
|
-
throw new Error("Cross-chain mode requires --destination-spell");
|
|
408
|
-
}
|
|
409
|
-
const destinationChainId = parseRequiredNumber(input.options.destinationChain, "--destination-chain");
|
|
410
|
-
const handoffTimeoutSec = parseRequiredNumber(input.options.handoffTimeoutSec, "--handoff-timeout-sec");
|
|
411
|
-
const pollIntervalSec = input.options.pollIntervalSec
|
|
412
|
-
? parseRequiredNumber(input.options.pollIntervalSec, "--poll-interval-sec")
|
|
413
|
-
: 30;
|
|
414
|
-
const destinationCompile = await compileFile(destinationSpellPath);
|
|
415
|
-
if (!destinationCompile.success || !destinationCompile.ir) {
|
|
416
|
-
throw new Error(`Destination spell compilation failed: ${destinationCompile.errors.map((e) => `[${e.code}] ${e.message}`).join("; ")}`);
|
|
417
|
-
}
|
|
418
|
-
const destinationSpell = destinationCompile.ir;
|
|
419
|
-
const rpcMappings = parseRpcUrlMappings(input.options.rpcUrl);
|
|
420
|
-
requireExplicitRpcMappings(rpcMappings, input.sourceChainId, destinationChainId);
|
|
421
|
-
const sourceRpcUrl = resolveRpcUrlForChain(input.sourceChainId, rpcMappings);
|
|
422
|
-
const destinationRpcUrl = resolveRpcUrlForChain(destinationChainId, rpcMappings);
|
|
423
|
-
if (!sourceRpcUrl || !destinationRpcUrl) {
|
|
424
|
-
throw new Error("Could not resolve RPC URLs for both source and destination chains.");
|
|
425
|
-
}
|
|
426
|
-
const morphoMarketIds = parseMorphoMarketMappings({
|
|
427
|
-
morphoMarketId: input.options.morphoMarketId,
|
|
428
|
-
morphoMarketMap: input.options.morphoMarketMap,
|
|
429
|
-
});
|
|
430
|
-
validateMorphoMappingsForSpells(input.sourceSpell, destinationSpell, morphoMarketIds);
|
|
431
|
-
const sourceProvider = createProvider(input.sourceChainId, sourceRpcUrl);
|
|
432
|
-
const destinationProvider = createProvider(destinationChainId, destinationRpcUrl);
|
|
433
|
-
const spinner = ora("Preparing cross-chain orchestration...").start();
|
|
434
|
-
let keyConfig;
|
|
435
|
-
let configuredAdapters = adapters;
|
|
436
|
-
let sourceWallet;
|
|
437
|
-
let destinationWallet;
|
|
438
|
-
if (input.mode === "execute") {
|
|
439
|
-
keyConfig = await resolveKeyConfig(input.options, spinner);
|
|
440
|
-
try {
|
|
441
|
-
const rawKey = loadPrivateKey(keyConfig);
|
|
442
|
-
configuredAdapters = adapters.map((adapter) => {
|
|
443
|
-
if (adapter.meta.name === "hyperliquid") {
|
|
444
|
-
return createHyperliquidAdapter({
|
|
445
|
-
privateKey: rawKey,
|
|
446
|
-
assetMap: { ETH: 4 },
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
return adapter;
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
catch {
|
|
453
|
-
configuredAdapters = adapters;
|
|
454
|
-
}
|
|
455
|
-
sourceWallet = createWalletFromConfig(keyConfig, input.sourceChainId, sourceProvider.rpcUrl);
|
|
456
|
-
destinationWallet = createWalletFromConfig(keyConfig, destinationChainId, destinationProvider.rpcUrl);
|
|
457
|
-
}
|
|
458
|
-
else if (input.hasKey) {
|
|
459
|
-
keyConfig = await resolveKeyConfig(input.options, spinner);
|
|
460
|
-
try {
|
|
461
|
-
const rawKey = loadPrivateKey(keyConfig);
|
|
462
|
-
configuredAdapters = adapters.map((adapter) => {
|
|
463
|
-
if (adapter.meta.name === "hyperliquid") {
|
|
464
|
-
return createHyperliquidAdapter({
|
|
465
|
-
privateKey: rawKey,
|
|
466
|
-
assetMap: { ETH: 4 },
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
return adapter;
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
catch {
|
|
473
|
-
configuredAdapters = adapters;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
const vault = resolveVaultAddress(input.options.vault, sourceWallet?.address);
|
|
477
|
-
const runId = createLogicalRunId();
|
|
478
|
-
const runtimeFlow = input.mode === "execute"
|
|
479
|
-
? "cast_execute"
|
|
480
|
-
: input.mode === "dry-run"
|
|
481
|
-
? "cast_dry_run"
|
|
482
|
-
: "simulate";
|
|
483
|
-
const advisorSkillsDirs = resolveAdvisorSkillsDirs(input.options.advisorSkillsDir) ?? [];
|
|
484
|
-
const sourceOnAdvisory = await resolveAdvisoryHandler(input.sourceSpell.id, {
|
|
485
|
-
advisoryPi: input.options.advisoryPi,
|
|
486
|
-
advisoryReplay: input.options.advisoryReplay,
|
|
487
|
-
advisoryProvider: input.options.advisoryProvider,
|
|
488
|
-
advisoryModel: input.options.advisoryModel,
|
|
489
|
-
advisoryThinking: input.options.advisoryThinking,
|
|
490
|
-
advisoryTools: input.options.advisoryTools,
|
|
491
|
-
advisoryTraceVerbose: input.options.advisoryTraceVerbose,
|
|
492
|
-
advisoryTraceLogger: input.options.json ? undefined : console.log,
|
|
493
|
-
advisorSkillsDirs,
|
|
494
|
-
stateDir: input.options.stateDir,
|
|
495
|
-
noState: input.noState,
|
|
496
|
-
agentDir: input.options.piAgentDir,
|
|
497
|
-
cwd: process.cwd(),
|
|
498
|
-
});
|
|
499
|
-
const destinationOnAdvisory = await resolveAdvisoryHandler(destinationSpell.id, {
|
|
500
|
-
advisoryPi: input.options.advisoryPi,
|
|
501
|
-
advisoryReplay: input.options.advisoryReplay,
|
|
502
|
-
advisoryProvider: input.options.advisoryProvider,
|
|
503
|
-
advisoryModel: input.options.advisoryModel,
|
|
504
|
-
advisoryThinking: input.options.advisoryThinking,
|
|
505
|
-
advisoryTools: input.options.advisoryTools,
|
|
506
|
-
advisoryTraceVerbose: input.options.advisoryTraceVerbose,
|
|
507
|
-
advisoryTraceLogger: input.options.json ? undefined : console.log,
|
|
508
|
-
advisorSkillsDirs,
|
|
509
|
-
stateDir: input.options.stateDir,
|
|
510
|
-
noState: input.noState,
|
|
511
|
-
agentDir: input.options.piAgentDir,
|
|
512
|
-
cwd: process.cwd(),
|
|
513
|
-
});
|
|
514
|
-
const advisoryEventCallback = input.options.json
|
|
515
|
-
? undefined
|
|
516
|
-
: createAdvisoryLiveTraceLogger(console.log, {
|
|
517
|
-
verbose: input.options.advisoryTraceVerbose,
|
|
518
|
-
});
|
|
519
|
-
const dbPath = input.options.stateDir ? join(input.options.stateDir, "grimoire.db") : undefined;
|
|
520
|
-
let store;
|
|
521
|
-
if (!input.noState) {
|
|
522
|
-
try {
|
|
523
|
-
store = new SqliteStateStore({ dbPath });
|
|
524
|
-
}
|
|
525
|
-
catch (error) {
|
|
526
|
-
if (isMissingNodeSqliteBackend(error)) {
|
|
527
|
-
console.log(chalk.yellow("State persistence unavailable in Node (missing better-sqlite3). Continuing without persisted state."));
|
|
528
|
-
}
|
|
529
|
-
else {
|
|
530
|
-
throw error;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
let sourceState = store ? ((await store.load(input.sourceSpell.id)) ?? {}) : {};
|
|
535
|
-
let destinationState = store ? ((await store.load(destinationSpell.id)) ?? {}) : {};
|
|
536
|
-
const lifecycleEvents = [];
|
|
537
|
-
const manifest = {
|
|
538
|
-
schema_version: "grimoire.cross_chain.phase1.v1",
|
|
539
|
-
run_id: runId,
|
|
540
|
-
source_spell_path: input.sourceSpellPath,
|
|
541
|
-
destination_spell_path: destinationSpellPath,
|
|
542
|
-
source_spell_id: input.sourceSpell.id,
|
|
543
|
-
destination_spell_id: destinationSpell.id,
|
|
544
|
-
source_chain_id: input.sourceChainId,
|
|
545
|
-
destination_chain_id: destinationChainId,
|
|
546
|
-
mode: input.mode,
|
|
547
|
-
watch: input.options.watch === true,
|
|
548
|
-
handoff_timeout_sec: handoffTimeoutSec,
|
|
549
|
-
poll_interval_sec: pollIntervalSec,
|
|
550
|
-
rpc_by_chain: {
|
|
551
|
-
[input.sourceChainId]: sourceRpcUrl,
|
|
552
|
-
[destinationChainId]: destinationRpcUrl,
|
|
553
|
-
},
|
|
554
|
-
params: input.params,
|
|
555
|
-
vault,
|
|
556
|
-
morpho_market_ids: morphoMarketIds,
|
|
375
|
+
const payload = {
|
|
376
|
+
success: result.success,
|
|
377
|
+
receipt: result.receipt,
|
|
378
|
+
error: result.structuredError,
|
|
557
379
|
};
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const confirmCallback = input.options.skipConfirm || isTestnet(input.sourceChainId)
|
|
562
|
-
? async () => true
|
|
563
|
-
: async (message) => {
|
|
564
|
-
console.log(message);
|
|
565
|
-
return await confirmPrompt(chalk.yellow("Proceed? (yes/no): "));
|
|
566
|
-
};
|
|
567
|
-
spinner.text = "Running source/destination orchestration...";
|
|
568
|
-
const orchestration = await orchestrateCrossChain({
|
|
569
|
-
runId,
|
|
570
|
-
sourceSpellId: input.sourceSpell.id,
|
|
571
|
-
destinationSpellId: destinationSpell.id,
|
|
572
|
-
sourceChainId: input.sourceChainId,
|
|
573
|
-
destinationChainId,
|
|
574
|
-
vault: vault,
|
|
575
|
-
sourceParams: input.params,
|
|
576
|
-
destinationParams: input.params,
|
|
577
|
-
mode: input.mode,
|
|
578
|
-
watch: input.options.watch === true,
|
|
579
|
-
handoffTimeoutSec,
|
|
580
|
-
pollIntervalSec,
|
|
581
|
-
executeSource: async () => {
|
|
582
|
-
const result = await execute({
|
|
583
|
-
spell: input.sourceSpell,
|
|
584
|
-
runId,
|
|
585
|
-
vault: vault,
|
|
586
|
-
chain: input.sourceChainId,
|
|
587
|
-
params: input.params,
|
|
588
|
-
persistentState: sourceState,
|
|
589
|
-
simulate: input.mode === "simulate",
|
|
590
|
-
executionMode: input.mode === "simulate" ? undefined : input.mode,
|
|
591
|
-
wallet: input.mode === "execute" ? sourceWallet : undefined,
|
|
592
|
-
provider: sourceProvider,
|
|
593
|
-
gasMultiplier,
|
|
594
|
-
confirmCallback,
|
|
595
|
-
skipTestnetConfirmation: input.options.skipConfirm ?? false,
|
|
596
|
-
adapters: configuredAdapters,
|
|
597
|
-
advisorSkillsDirs: advisorSkillsDirs.length > 0 ? advisorSkillsDirs : undefined,
|
|
598
|
-
onAdvisory: sourceOnAdvisory,
|
|
599
|
-
eventCallback: advisoryEventCallback,
|
|
600
|
-
warningCallback: (message) => console.log(chalk.yellow(`Warning: ${message}`)),
|
|
601
|
-
crossChain: {
|
|
602
|
-
enabled: true,
|
|
603
|
-
runId,
|
|
604
|
-
trackId: "source",
|
|
605
|
-
role: "source",
|
|
606
|
-
morphoMarketIds,
|
|
607
|
-
},
|
|
608
|
-
});
|
|
609
|
-
sourceState = result.finalState;
|
|
610
|
-
return result;
|
|
611
|
-
},
|
|
612
|
-
executeDestination: async (params) => {
|
|
613
|
-
const result = await execute({
|
|
614
|
-
spell: destinationSpell,
|
|
615
|
-
runId,
|
|
616
|
-
vault: vault,
|
|
617
|
-
chain: destinationChainId,
|
|
618
|
-
params,
|
|
619
|
-
persistentState: destinationState,
|
|
620
|
-
simulate: input.mode === "simulate",
|
|
621
|
-
executionMode: input.mode === "simulate" ? undefined : input.mode,
|
|
622
|
-
wallet: input.mode === "execute" ? destinationWallet : undefined,
|
|
623
|
-
provider: destinationProvider,
|
|
624
|
-
gasMultiplier,
|
|
625
|
-
confirmCallback,
|
|
626
|
-
skipTestnetConfirmation: input.options.skipConfirm ?? false,
|
|
627
|
-
adapters: configuredAdapters,
|
|
628
|
-
advisorSkillsDirs: advisorSkillsDirs.length > 0 ? advisorSkillsDirs : undefined,
|
|
629
|
-
onAdvisory: destinationOnAdvisory,
|
|
630
|
-
eventCallback: advisoryEventCallback,
|
|
631
|
-
warningCallback: (message) => console.log(chalk.yellow(`Warning: ${message}`)),
|
|
632
|
-
crossChain: {
|
|
633
|
-
enabled: true,
|
|
634
|
-
runId,
|
|
635
|
-
trackId: "destination",
|
|
636
|
-
role: "destination",
|
|
637
|
-
morphoMarketIds,
|
|
638
|
-
},
|
|
639
|
-
});
|
|
640
|
-
destinationState = result.finalState;
|
|
641
|
-
return result;
|
|
642
|
-
},
|
|
643
|
-
resolveHandoffStatus: async (handoff) => {
|
|
644
|
-
const across = configuredAdapters.find((adapter) => adapter.meta.name === "across");
|
|
645
|
-
if (!across) {
|
|
646
|
-
return { status: "pending" };
|
|
647
|
-
}
|
|
648
|
-
const resolver = across.bridgeLifecycle?.resolveHandoffStatus ?? across.resolveHandoffStatus;
|
|
649
|
-
if (!resolver) {
|
|
650
|
-
return { status: "pending" };
|
|
651
|
-
}
|
|
652
|
-
return resolver({
|
|
653
|
-
handoffId: handoff.handoffId,
|
|
654
|
-
originChainId: handoff.originChainId,
|
|
655
|
-
destinationChainId: handoff.destinationChainId,
|
|
656
|
-
originTxHash: handoff.originTxHash,
|
|
657
|
-
reference: handoff.reference,
|
|
658
|
-
asset: handoff.asset,
|
|
659
|
-
submittedAmount: handoff.submittedAmount,
|
|
660
|
-
walletAddress: vault,
|
|
661
|
-
});
|
|
662
|
-
},
|
|
663
|
-
onLifecycleEvent: (event) => {
|
|
664
|
-
lifecycleEvents.push(event);
|
|
665
|
-
},
|
|
666
|
-
});
|
|
667
|
-
const crossChainReceipt = toCrossChainReceipt({
|
|
668
|
-
runId,
|
|
669
|
-
sourceSpellId: input.sourceSpell.id,
|
|
670
|
-
destinationSpellId: destinationSpell.id,
|
|
671
|
-
sourceChainId: input.sourceChainId,
|
|
672
|
-
destinationChainId,
|
|
673
|
-
tracks: orchestration.tracks,
|
|
674
|
-
handoffs: orchestration.handoffs,
|
|
675
|
-
});
|
|
676
|
-
const sourceResult = orchestration.sourceResult;
|
|
677
|
-
const destinationResult = orchestration.destinationResult;
|
|
678
|
-
if (sourceResult) {
|
|
679
|
-
sourceResult.crossChain = crossChainReceipt;
|
|
680
|
-
}
|
|
681
|
-
if (destinationResult) {
|
|
682
|
-
destinationResult.crossChain = crossChainReceipt;
|
|
683
|
-
}
|
|
684
|
-
if (orchestration.pending && !store) {
|
|
685
|
-
throw new Error("Cannot leave a run in waiting state without persistence. Disable --no-state.");
|
|
686
|
-
}
|
|
687
|
-
if (store) {
|
|
688
|
-
if (sourceResult) {
|
|
689
|
-
await store.save(input.sourceSpell.id, sourceResult.finalState);
|
|
690
|
-
const sourceProvenance = {
|
|
691
|
-
...buildRuntimeProvenanceManifest({
|
|
692
|
-
runtimeMode: runtimeFlow,
|
|
693
|
-
chainId: input.sourceChainId,
|
|
694
|
-
policy: input.dataPolicy,
|
|
695
|
-
replay: input.replayResolution,
|
|
696
|
-
params: input.params,
|
|
697
|
-
blockNumber: await safeGetBlockNumber(sourceProvider),
|
|
698
|
-
rpcUrl: sourceProvider.rpcUrl,
|
|
699
|
-
}),
|
|
700
|
-
cross_chain: manifest,
|
|
701
|
-
};
|
|
702
|
-
await store.addRun(input.sourceSpell.id, createRunRecord(sourceResult, sourceProvenance));
|
|
703
|
-
await store.saveLedger(input.sourceSpell.id, runId, appendLifecycleLedgerEntries(sourceResult.ledgerEvents, lifecycleEvents, runId, input.sourceSpell.id));
|
|
704
|
-
}
|
|
705
|
-
if (destinationResult) {
|
|
706
|
-
await store.save(destinationSpell.id, destinationResult.finalState);
|
|
707
|
-
const destinationProvenance = {
|
|
708
|
-
...buildRuntimeProvenanceManifest({
|
|
709
|
-
runtimeMode: runtimeFlow,
|
|
710
|
-
chainId: destinationChainId,
|
|
711
|
-
policy: input.dataPolicy,
|
|
712
|
-
replay: input.replayResolution,
|
|
713
|
-
params: input.params,
|
|
714
|
-
blockNumber: await safeGetBlockNumber(destinationProvider),
|
|
715
|
-
rpcUrl: destinationProvider.rpcUrl,
|
|
716
|
-
}),
|
|
717
|
-
cross_chain: manifest,
|
|
718
|
-
};
|
|
719
|
-
await store.addRun(destinationSpell.id, createRunRecord(destinationResult, destinationProvenance));
|
|
720
|
-
await store.saveLedger(destinationSpell.id, runId, destinationResult.ledgerEvents);
|
|
721
|
-
}
|
|
722
|
-
await persistCrossChainState(store, {
|
|
723
|
-
runId,
|
|
724
|
-
tracks: orchestration.tracks,
|
|
725
|
-
handoffs: orchestration.handoffs,
|
|
726
|
-
sourceSpellId: input.sourceSpell.id,
|
|
727
|
-
destinationSpellId: destinationSpell.id,
|
|
728
|
-
sourceResult,
|
|
729
|
-
destinationResult,
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
spinner.stop();
|
|
733
|
-
const mergedResult = destinationResult ?? sourceResult;
|
|
734
|
-
if (!mergedResult) {
|
|
735
|
-
throw new Error("Cross-chain orchestration returned no execution results.");
|
|
736
|
-
}
|
|
737
|
-
if (input.options.json) {
|
|
738
|
-
console.log(stringifyJson({
|
|
739
|
-
success: orchestration.success,
|
|
740
|
-
pending: orchestration.pending,
|
|
741
|
-
runId,
|
|
742
|
-
crossChain: crossChainReceipt,
|
|
743
|
-
source: sourceResult
|
|
744
|
-
? {
|
|
745
|
-
success: sourceResult.success,
|
|
746
|
-
error: sourceResult.structuredError,
|
|
747
|
-
receipt: sourceResult.receipt,
|
|
748
|
-
}
|
|
749
|
-
: undefined,
|
|
750
|
-
destination: destinationResult
|
|
751
|
-
? {
|
|
752
|
-
success: destinationResult.success,
|
|
753
|
-
error: destinationResult.structuredError,
|
|
754
|
-
receipt: destinationResult.receipt,
|
|
755
|
-
}
|
|
756
|
-
: undefined,
|
|
757
|
-
}));
|
|
380
|
+
console.error();
|
|
381
|
+
if (options.json) {
|
|
382
|
+
console.error(stringifyJson(payload));
|
|
758
383
|
}
|
|
759
384
|
else {
|
|
760
|
-
console.
|
|
761
|
-
console.log(chalk.cyan("🔀 Cross-Chain Run:"));
|
|
762
|
-
console.log(` ${chalk.dim("Run ID:")} ${runId}`);
|
|
763
|
-
console.log(` ${chalk.dim("Source:")} ${input.sourceSpell.meta.name} (${input.sourceChainId}) -> ${chalk.dim("Destination:")} ${destinationSpell.meta.name} (${destinationChainId})`);
|
|
764
|
-
console.log(` ${chalk.dim("Watch:")} ${input.options.watch === true ? "Yes" : "No"}`);
|
|
765
|
-
console.log(` ${chalk.dim("Status:")} ${orchestration.pending ? chalk.yellow("waiting") : orchestration.success ? chalk.green("completed") : chalk.red("failed")}`);
|
|
766
|
-
for (const track of orchestration.tracks) {
|
|
767
|
-
const trackStatus = track.status === "completed"
|
|
768
|
-
? chalk.green(track.status)
|
|
769
|
-
: track.status === "failed"
|
|
770
|
-
? chalk.red(track.status)
|
|
771
|
-
: track.status === "waiting"
|
|
772
|
-
? chalk.yellow(track.status)
|
|
773
|
-
: chalk.dim(track.status);
|
|
774
|
-
console.log(` ${chalk.dim(`track:${track.trackId}`)} ${trackStatus} chain=${track.chainId} spell=${track.spellId}`);
|
|
775
|
-
if (track.error) {
|
|
776
|
-
console.log(` ${chalk.red(track.error)}`);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
for (const handoff of orchestration.handoffs) {
|
|
780
|
-
console.log(` ${chalk.dim(`handoff:${handoff.handoffId}`)} ${handoff.status} submitted=${handoff.submittedAmount.toString()} settled=${handoff.settledAmount?.toString() ?? "n/a"}`);
|
|
781
|
-
}
|
|
782
|
-
if (orchestration.pending) {
|
|
783
|
-
console.log();
|
|
784
|
-
console.log(chalk.yellow("Run is waiting for handoff settlement. Resume with:"));
|
|
785
|
-
console.log(chalk.yellow(` grimoire resume ${runId} --watch`));
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
if (!orchestration.success && !orchestration.pending) {
|
|
789
|
-
process.exit(1);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
async function safeGetBlockNumber(provider) {
|
|
793
|
-
try {
|
|
794
|
-
return await provider.getBlockNumber();
|
|
795
|
-
}
|
|
796
|
-
catch {
|
|
797
|
-
return undefined;
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
async function resolveKeyConfig(options, spinner) {
|
|
801
|
-
const hasExplicitKey = !!(options.privateKey || options.mnemonic || options.keystore);
|
|
802
|
-
const hasEnvKey = !!(options.keyEnv && process.env[options.keyEnv]);
|
|
803
|
-
const hasDefaultKeystore = !hasExplicitKey && !hasEnvKey && existsSync(DEFAULT_KEYSTORE_PATH);
|
|
804
|
-
if (options.privateKey) {
|
|
805
|
-
return { type: "raw", source: options.privateKey };
|
|
806
|
-
}
|
|
807
|
-
if (options.keyEnv && process.env[options.keyEnv]) {
|
|
808
|
-
return { type: "env", source: options.keyEnv };
|
|
809
|
-
}
|
|
810
|
-
if (options.mnemonic) {
|
|
811
|
-
return { type: "mnemonic", source: options.mnemonic };
|
|
812
|
-
}
|
|
813
|
-
if (hasDefaultKeystore || options.keystore) {
|
|
814
|
-
const keystorePath = options.keystore ?? DEFAULT_KEYSTORE_PATH;
|
|
815
|
-
if (!existsSync(keystorePath)) {
|
|
816
|
-
spinner.fail(chalk.red(`No key provided and no keystore found at ${keystorePath}`));
|
|
817
|
-
console.log(chalk.dim(" Run 'grimoire wallet generate' to create one."));
|
|
818
|
-
process.exit(1);
|
|
819
|
-
throw new Error("unreachable");
|
|
820
|
-
}
|
|
821
|
-
const password = await resolveKeystorePassword(options, spinner);
|
|
822
|
-
if (!password) {
|
|
823
|
-
process.exit(1);
|
|
824
|
-
throw new Error("unreachable");
|
|
825
|
-
}
|
|
826
|
-
const keystoreJson = readFileSync(keystorePath, "utf-8");
|
|
827
|
-
return { type: "keystore", source: keystoreJson, password };
|
|
828
|
-
}
|
|
829
|
-
throw new Error("Execution mode requires wallet credentials, but no key source was provided");
|
|
830
|
-
}
|
|
831
|
-
function resolveVaultAddress(explicitVault, fallbackWalletAddress) {
|
|
832
|
-
if (explicitVault && explicitVault.length > 0) {
|
|
833
|
-
return explicitVault;
|
|
834
|
-
}
|
|
835
|
-
if (fallbackWalletAddress && fallbackWalletAddress.length > 0) {
|
|
836
|
-
return fallbackWalletAddress;
|
|
837
|
-
}
|
|
838
|
-
return "0x0000000000000000000000000000000000000000";
|
|
839
|
-
}
|
|
840
|
-
function parseRequiredNumber(value, flag) {
|
|
841
|
-
if (!value) {
|
|
842
|
-
throw new Error(`${flag} is required in cross-chain mode`);
|
|
843
|
-
}
|
|
844
|
-
const parsed = Number.parseInt(value, 10);
|
|
845
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
846
|
-
throw new Error(`${flag} must be a positive integer`);
|
|
847
|
-
}
|
|
848
|
-
return parsed;
|
|
849
|
-
}
|
|
850
|
-
function resolveRpcUrlFromOption(chainId, value) {
|
|
851
|
-
const parsed = parseRpcUrlMappings(value);
|
|
852
|
-
return resolveRpcUrlForChain(chainId, parsed);
|
|
853
|
-
}
|
|
854
|
-
function isMissingNodeSqliteBackend(error) {
|
|
855
|
-
if (!(error instanceof Error))
|
|
856
|
-
return false;
|
|
857
|
-
return error.message.includes("SqliteStateStore requires bun:sqlite (Bun) or better-sqlite3 (Node)");
|
|
858
|
-
}
|
|
859
|
-
function appendLifecycleLedgerEntries(entries, lifecycleEvents, runId, spellId) {
|
|
860
|
-
if (lifecycleEvents.length === 0) {
|
|
861
|
-
return entries;
|
|
862
|
-
}
|
|
863
|
-
const start = entries.length;
|
|
864
|
-
const extras = lifecycleEvents.map((event, index) => ({
|
|
865
|
-
id: `evt_cc_${String(start + index).padStart(3, "0")}`,
|
|
866
|
-
timestamp: Date.now(),
|
|
867
|
-
runId,
|
|
868
|
-
spellId,
|
|
869
|
-
event,
|
|
870
|
-
}));
|
|
871
|
-
return [...entries, ...extras];
|
|
872
|
-
}
|
|
873
|
-
async function persistCrossChainState(store, input) {
|
|
874
|
-
const nowIso = new Date().toISOString();
|
|
875
|
-
for (const track of input.tracks) {
|
|
876
|
-
const row = {
|
|
877
|
-
runId: input.runId,
|
|
878
|
-
trackId: track.trackId,
|
|
879
|
-
role: track.role,
|
|
880
|
-
spellId: track.spellId,
|
|
881
|
-
chainId: track.chainId,
|
|
882
|
-
status: track.status,
|
|
883
|
-
lastStepId: track.lastStepId,
|
|
884
|
-
error: track.error,
|
|
885
|
-
updatedAt: nowIso,
|
|
886
|
-
};
|
|
887
|
-
await store.upsertRunTrack(row);
|
|
888
|
-
}
|
|
889
|
-
for (const handoff of input.handoffs) {
|
|
890
|
-
const row = {
|
|
891
|
-
runId: input.runId,
|
|
892
|
-
handoffId: handoff.handoffId,
|
|
893
|
-
sourceTrackId: handoff.sourceTrackId,
|
|
894
|
-
destinationTrackId: handoff.destinationTrackId,
|
|
895
|
-
sourceStepId: handoff.sourceStepId,
|
|
896
|
-
originChainId: handoff.originChainId,
|
|
897
|
-
destinationChainId: handoff.destinationChainId,
|
|
898
|
-
asset: handoff.asset,
|
|
899
|
-
submittedAmount: handoff.submittedAmount.toString(),
|
|
900
|
-
settledAmount: handoff.settledAmount?.toString(),
|
|
901
|
-
status: handoff.status,
|
|
902
|
-
reference: handoff.reference,
|
|
903
|
-
originTxHash: handoff.originTxHash,
|
|
904
|
-
reason: handoff.reason,
|
|
905
|
-
createdAt: nowIso,
|
|
906
|
-
updatedAt: nowIso,
|
|
907
|
-
expiresAt: handoff.status === "expired" ? nowIso : undefined,
|
|
908
|
-
};
|
|
909
|
-
await store.upsertRunHandoff(row);
|
|
910
|
-
}
|
|
911
|
-
const sourceSteps = collectStepStatuses(input.sourceResult, "source", input.runId);
|
|
912
|
-
for (const step of sourceSteps) {
|
|
913
|
-
await store.upsertRunStepResult(step);
|
|
914
|
-
}
|
|
915
|
-
const destinationSteps = collectStepStatuses(input.destinationResult, "destination", input.runId);
|
|
916
|
-
for (const step of destinationSteps) {
|
|
917
|
-
await store.upsertRunStepResult(step);
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
function collectStepStatuses(result, trackId, runId) {
|
|
921
|
-
if (!result?.receipt) {
|
|
922
|
-
return [];
|
|
923
|
-
}
|
|
924
|
-
const nowIso = new Date().toISOString();
|
|
925
|
-
const byStep = new Map();
|
|
926
|
-
for (const planned of result.receipt.plannedActions) {
|
|
927
|
-
byStep.set(planned.stepId, {
|
|
928
|
-
runId,
|
|
929
|
-
trackId,
|
|
930
|
-
stepId: planned.stepId,
|
|
931
|
-
status: "pending",
|
|
932
|
-
idempotencyKey: `${runId}:${trackId}:${planned.stepId}`,
|
|
933
|
-
updatedAt: nowIso,
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
|
-
for (const tx of result.commit?.transactions ?? []) {
|
|
937
|
-
const existing = byStep.get(tx.stepId);
|
|
938
|
-
if (!existing)
|
|
939
|
-
continue;
|
|
940
|
-
existing.status = tx.success ? "confirmed" : "failed";
|
|
941
|
-
existing.reference = tx.hash;
|
|
942
|
-
existing.error = tx.error;
|
|
943
|
-
}
|
|
944
|
-
if (result.success && !result.commit) {
|
|
945
|
-
for (const step of byStep.values()) {
|
|
946
|
-
step.status = "confirmed";
|
|
947
|
-
}
|
|
385
|
+
console.error(formatRunReportText(report));
|
|
948
386
|
}
|
|
949
387
|
if (!result.success) {
|
|
950
|
-
|
|
951
|
-
if (step.status !== "confirmed") {
|
|
952
|
-
step.status = "failed";
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
return [...byStep.values()];
|
|
957
|
-
}
|
|
958
|
-
async function resolveKeystorePassword(options, spinner) {
|
|
959
|
-
const envName = options.passwordEnv ?? "KEYSTORE_PASSWORD";
|
|
960
|
-
const envValue = process.env[envName];
|
|
961
|
-
if (envValue) {
|
|
962
|
-
return envValue;
|
|
963
|
-
}
|
|
964
|
-
if (process.stdin.isTTY) {
|
|
965
|
-
spinner.stop();
|
|
966
|
-
const password = await promptPassword("Keystore password: ");
|
|
967
|
-
spinner.start("Setting up wallet...");
|
|
968
|
-
return password;
|
|
388
|
+
throw new Error("Cast failed");
|
|
969
389
|
}
|
|
970
|
-
|
|
971
|
-
process.exit(1);
|
|
972
|
-
return null;
|
|
973
|
-
}
|
|
974
|
-
async function promptPassword(message) {
|
|
975
|
-
return new Promise((resolve) => {
|
|
976
|
-
process.stdout.write(message);
|
|
977
|
-
const silentOutput = new Writable({
|
|
978
|
-
write(_chunk, _encoding, cb) {
|
|
979
|
-
cb();
|
|
980
|
-
},
|
|
981
|
-
});
|
|
982
|
-
const rl = readline.createInterface({
|
|
983
|
-
input: process.stdin,
|
|
984
|
-
output: silentOutput,
|
|
985
|
-
terminal: true,
|
|
986
|
-
});
|
|
987
|
-
rl.question("", (answer) => {
|
|
988
|
-
rl.close();
|
|
989
|
-
process.stdout.write("\n");
|
|
990
|
-
resolve(answer);
|
|
991
|
-
});
|
|
992
|
-
});
|
|
993
|
-
}
|
|
994
|
-
async function confirmPrompt(message) {
|
|
995
|
-
const rl = readline.createInterface({
|
|
996
|
-
input: process.stdin,
|
|
997
|
-
output: process.stdout,
|
|
998
|
-
});
|
|
999
|
-
return new Promise((resolve) => {
|
|
1000
|
-
rl.question(message, (answer) => {
|
|
1001
|
-
rl.close();
|
|
1002
|
-
const normalized = answer.toLowerCase().trim();
|
|
1003
|
-
resolve(normalized === "yes" || normalized === "y");
|
|
1004
|
-
});
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
function resolveNoState(options) {
|
|
1008
|
-
if (typeof options.noState === "boolean")
|
|
1009
|
-
return options.noState;
|
|
1010
|
-
if (options.state === false)
|
|
1011
|
-
return true;
|
|
1012
|
-
return false;
|
|
1013
|
-
}
|
|
1014
|
-
function stringifyJson(value) {
|
|
1015
|
-
return JSON.stringify(value, (_key, innerValue) => (typeof innerValue === "bigint" ? innerValue.toString() : innerValue), 2);
|
|
390
|
+
return payload;
|
|
1016
391
|
}
|
|
1017
392
|
//# sourceMappingURL=cast.js.map
|