@grimoirelabs/cli 0.14.1 → 0.15.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.
Files changed (116) hide show
  1. package/dist/commands/advisory-handlers.d.ts.map +1 -1
  2. package/dist/commands/advisory-handlers.js +1 -180
  3. package/dist/commands/advisory-handlers.js.map +1 -1
  4. package/dist/commands/advisory-live-trace.d.ts.map +1 -1
  5. package/dist/commands/advisory-live-trace.js +1 -1
  6. package/dist/commands/advisory-live-trace.js.map +1 -1
  7. package/dist/commands/advisory-verbose-trace.d.ts +15 -0
  8. package/dist/commands/advisory-verbose-trace.d.ts.map +1 -0
  9. package/dist/commands/advisory-verbose-trace.js +185 -0
  10. package/dist/commands/advisory-verbose-trace.js.map +1 -0
  11. package/dist/commands/cast-cross-chain.d.ts +69 -0
  12. package/dist/commands/cast-cross-chain.d.ts.map +1 -0
  13. package/dist/commands/cast-cross-chain.js +456 -0
  14. package/dist/commands/cast-cross-chain.js.map +1 -0
  15. package/dist/commands/cast.d.ts +1 -1
  16. package/dist/commands/cast.d.ts.map +1 -1
  17. package/dist/commands/cast.js +84 -709
  18. package/dist/commands/cast.js.map +1 -1
  19. package/dist/commands/compile-all.d.ts +15 -2
  20. package/dist/commands/compile-all.d.ts.map +1 -1
  21. package/dist/commands/compile-all.js +16 -21
  22. package/dist/commands/compile-all.js.map +1 -1
  23. package/dist/commands/compile.d.ts +1 -1
  24. package/dist/commands/compile.d.ts.map +1 -1
  25. package/dist/commands/compile.js +22 -26
  26. package/dist/commands/compile.js.map +1 -1
  27. package/dist/commands/cross-chain-helpers.d.ts +0 -1
  28. package/dist/commands/cross-chain-helpers.d.ts.map +1 -1
  29. package/dist/commands/cross-chain-helpers.js +0 -3
  30. package/dist/commands/cross-chain-helpers.js.map +1 -1
  31. package/dist/commands/history.d.ts +1 -2
  32. package/dist/commands/history.d.ts.map +1 -1
  33. package/dist/commands/history.js +59 -69
  34. package/dist/commands/history.js.map +1 -1
  35. package/dist/commands/init.d.ts +5 -1
  36. package/dist/commands/init.d.ts.map +1 -1
  37. package/dist/commands/init.js +25 -20
  38. package/dist/commands/init.js.map +1 -1
  39. package/dist/commands/log.d.ts +1 -2
  40. package/dist/commands/log.d.ts.map +1 -1
  41. package/dist/commands/log.js +9 -12
  42. package/dist/commands/log.js.map +1 -1
  43. package/dist/commands/resume.d.ts +18 -1
  44. package/dist/commands/resume.d.ts.map +1 -1
  45. package/dist/commands/resume.js +44 -66
  46. package/dist/commands/resume.js.map +1 -1
  47. package/dist/commands/setup-ui.d.ts +46 -0
  48. package/dist/commands/setup-ui.d.ts.map +1 -0
  49. package/dist/commands/setup-ui.js +221 -0
  50. package/dist/commands/setup-ui.js.map +1 -0
  51. package/dist/commands/setup.d.ts +31 -3
  52. package/dist/commands/setup.d.ts.map +1 -1
  53. package/dist/commands/setup.js +27 -270
  54. package/dist/commands/setup.js.map +1 -1
  55. package/dist/commands/simulate-cross-chain.d.ts +53 -0
  56. package/dist/commands/simulate-cross-chain.d.ts.map +1 -0
  57. package/dist/commands/simulate-cross-chain.js +256 -0
  58. package/dist/commands/simulate-cross-chain.js.map +1 -0
  59. package/dist/commands/simulate.d.ts +5 -1
  60. package/dist/commands/simulate.d.ts.map +1 -1
  61. package/dist/commands/simulate.js +16 -364
  62. package/dist/commands/simulate.js.map +1 -1
  63. package/dist/commands/state-helpers.d.ts.map +1 -1
  64. package/dist/commands/state-helpers.js +1 -5
  65. package/dist/commands/state-helpers.js.map +1 -1
  66. package/dist/commands/validate.d.ts +24 -2
  67. package/dist/commands/validate.d.ts.map +1 -1
  68. package/dist/commands/validate.js +40 -42
  69. package/dist/commands/validate.js.map +1 -1
  70. package/dist/commands/venue-doctor-morpho.d.ts +38 -0
  71. package/dist/commands/venue-doctor-morpho.d.ts.map +1 -0
  72. package/dist/commands/venue-doctor-morpho.js +214 -0
  73. package/dist/commands/venue-doctor-morpho.js.map +1 -0
  74. package/dist/commands/venue-doctor.d.ts +2 -26
  75. package/dist/commands/venue-doctor.d.ts.map +1 -1
  76. package/dist/commands/venue-doctor.js +27 -229
  77. package/dist/commands/venue-doctor.js.map +1 -1
  78. package/dist/commands/venue.d.ts +1 -1
  79. package/dist/commands/venue.d.ts.map +1 -1
  80. package/dist/commands/venue.js +32 -122
  81. package/dist/commands/venue.js.map +1 -1
  82. package/dist/commands/venues.d.ts +1 -5
  83. package/dist/commands/venues.d.ts.map +1 -1
  84. package/dist/commands/venues.js +11 -10
  85. package/dist/commands/venues.js.map +1 -1
  86. package/dist/commands/wallet-weth.d.ts +22 -0
  87. package/dist/commands/wallet-weth.d.ts.map +1 -0
  88. package/dist/commands/wallet-weth.js +150 -0
  89. package/dist/commands/wallet-weth.js.map +1 -0
  90. package/dist/commands/wallet.d.ts +65 -3
  91. package/dist/commands/wallet.d.ts.map +1 -1
  92. package/dist/commands/wallet.js +183 -377
  93. package/dist/commands/wallet.js.map +1 -1
  94. package/dist/index.js +375 -170
  95. package/dist/index.js.map +1 -1
  96. package/dist/lib/execution-helpers.d.ts +24 -0
  97. package/dist/lib/execution-helpers.d.ts.map +1 -0
  98. package/dist/lib/execution-helpers.js +133 -0
  99. package/dist/lib/execution-helpers.js.map +1 -0
  100. package/dist/lib/json.d.ts +8 -0
  101. package/dist/lib/json.d.ts.map +1 -0
  102. package/dist/lib/json.js +10 -0
  103. package/dist/lib/json.js.map +1 -0
  104. package/dist/lib/keystore.d.ts +12 -0
  105. package/dist/lib/keystore.d.ts.map +1 -0
  106. package/dist/lib/keystore.js +14 -0
  107. package/dist/lib/keystore.js.map +1 -0
  108. package/dist/lib/prompts.d.ts +16 -0
  109. package/dist/lib/prompts.d.ts.map +1 -0
  110. package/dist/lib/prompts.js +61 -0
  111. package/dist/lib/prompts.js.map +1 -0
  112. package/dist/lib/venue-discovery.d.ts +44 -0
  113. package/dist/lib/venue-discovery.d.ts.map +1 -0
  114. package/dist/lib/venue-discovery.js +192 -0
  115. package/dist/lib/venue-discovery.js.map +1 -0
  116. package/package.json +4 -4
@@ -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 { homedir } from "node:os";
7
- import { join } from "node:path";
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 { createLogicalRunId, parseMorphoMarketMappings, parseRpcUrlMappings, requireExplicitRpcMappings, resolveRpcUrlForChain, validateMorphoMappingsForSpells, } from "./cross-chain-helpers.js";
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 DEFAULT_KEYSTORE_PATH = join(homedir(), ".grimoire", "keystore.json");
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
- process.exit(1);
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.log(chalk.dim(`ENS profile ${profile.name} -> ${profile.address ?? "unresolved"} (risk=${profile.riskProfile ?? "n/a"}, slippage=${profile.maxSlippageBps ?? "n/a"})`));
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.log(chalk.red(` [${error.code}] ${error.message}`));
48
+ console.error(chalk.red(` [${error.code}] ${error.message}`));
49
49
  }
50
- process.exit(1);
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.log(chalk.yellow(`Warning: --trigger "${options.trigger}" ignored — spell has a single trigger (no filtering needed).`));
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.log(chalk.yellow("⚠️ Avoid passing secrets via CLI arguments. Use --key-env or env vars instead."));
83
+ console.error(chalk.yellow("Warning: Avoid passing secrets via CLI arguments. Use --key-env or env vars instead."));
84
84
  }
85
- console.log();
86
- console.log(chalk.cyan("📜 Spell Info:"));
87
- console.log(` ${chalk.dim("Name:")} ${spell.meta.name}`);
88
- console.log(` ${chalk.dim("Version:")} ${spell.version}`);
89
- console.log(` ${chalk.dim("Steps:")} ${spell.steps.length}`);
90
- console.log(` ${chalk.dim("Mode:")} ${mode}`);
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.log();
93
- console.log(chalk.cyan("📊 Parameters:"));
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.log(` ${chalk.dim(param.name)}: ${JSON.stringify(value)}`);
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.log();
118
- console.log(chalk.cyan("🔗 Network:"));
119
- console.log(` ${chalk.dim("Chain:")} ${chainName} (${chainId})`);
120
- console.log(` ${chalk.dim("Testnet:")} ${isTest ? "Yes" : "No"}`);
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
- process.exit(1);
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.log(chalk.dim(" Run 'grimoire wallet generate' to create one."));
154
- process.exit(1);
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
- return;
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
- let configuredAdapters = adapters;
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.log(` ${chalk.dim("Balance:")} ${formatWei(balance)} ${getNativeCurrencySymbol(chainId)}`);
172
+ console.error(` ${chalk.dim("Balance:")} ${formatWei(balance)} ${getNativeCurrencySymbol(chainId)}`);
189
173
  if (balance === 0n) {
190
- console.log(chalk.yellow(` ⚠️ Wallet has no ${getNativeCurrencySymbol(chainId)} for gas`));
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.log(` ${chalk.dim("Vault:")} ${vault}`);
178
+ console.error(` ${chalk.dim("Vault:")} ${vault}`);
195
179
  if (!isTest) {
196
- console.log();
197
- console.log(chalk.red("⚠️ WARNING: This is MAINNET"));
198
- console.log(chalk.red(" Real funds will be used!"));
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 ? Number.parseFloat(options.gasMultiplier) : 1.1;
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.log,
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.log, {
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.log(chalk.yellow(`Warning: ${warning}`));
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.log(message);
226
+ console.error(message);
241
227
  return await confirmPrompt(chalk.yellow("Proceed? (yes/no): "));
242
228
  };
243
- console.log();
244
- console.log(chalk.cyan(`🚀 Executing spell (${executionMode})...`));
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.log(chalk.yellow("State persistence unavailable in Node (missing better-sqlite3). Continuing without persisted state."));
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.log(chalk.dim(` ${message}`));
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.log(chalk.green("Execution completed successfully"));
264
+ console.error(chalk.green("Execution completed successfully"));
279
265
  }
280
266
  else {
281
- console.log(chalk.red(`Execution failed: ${execResult.error}`));
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
- console.log();
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
- const payload = execResult.commit
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.log(formatRunReportText(report));
291
+ console.error(formatRunReportText(report));
306
292
  }
307
293
  if (!execResult.success) {
308
- process.exit(1);
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.log(chalk.yellow(`Warning: ${warning}`));
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.log,
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.log, {
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.log(chalk.yellow("State persistence unavailable in Node (missing better-sqlite3). Continuing without persisted state."));
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
- console.log();
389
- if (options.json) {
390
- const payload = {
391
- success: result.success,
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
- const gasMultiplier = input.options.gasMultiplier
559
- ? Number.parseFloat(input.options.gasMultiplier)
560
- : 1.1;
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.log();
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
- for (const step of byStep.values()) {
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
- spinner.fail(chalk.red(`No password available. Set ${envName} or run interactively.`));
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