@aomi-labs/client 0.1.17 → 0.1.18

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/README.md CHANGED
@@ -307,7 +307,7 @@ All config can be passed as flags (which take priority over env vars):
307
307
 
308
308
  | Flag | Env Variable | Default | Description |
309
309
  | ----------------------- | ----------------- | ---------------------- | -------------------------------------------- |
310
- | `--backend-url` | `AOMI_BASE_URL` | `https://api.aomi.dev` | Backend URL |
310
+ | `--backend-url` | `AOMI_BACKEND_URL` | `https://api.aomi.dev` | Backend URL |
311
311
  | `--api-key` | `AOMI_API_KEY` | — | API key for non-default apps |
312
312
  | `--app` | `AOMI_APP` | `default` | App |
313
313
  | `--model` | `AOMI_MODEL` | — | Model rig to apply before chat |
package/dist/cli.js CHANGED
@@ -22,7 +22,7 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
22
22
  // package.json
23
23
  var package_default = {
24
24
  name: "@aomi-labs/client",
25
- version: "0.1.17",
25
+ version: "0.1.18",
26
26
  description: "Platform-agnostic TypeScript client for the Aomi backend API",
27
27
  type: "module",
28
28
  main: "./dist/index.cjs",
@@ -68,17 +68,17 @@ var CliExit = class extends Error {
68
68
  };
69
69
  function fatal(message) {
70
70
  const RED = "\x1B[31m";
71
- const DIM3 = "\x1B[2m";
72
- const RESET3 = "\x1B[0m";
71
+ const DIM2 = "\x1B[2m";
72
+ const RESET2 = "\x1B[0m";
73
73
  const lines = message.split("\n");
74
74
  const [headline, ...details] = lines;
75
- console.error(`${RED}\u274C ${headline}${RESET3}`);
75
+ console.error(`${RED}\u274C ${headline}${RESET2}`);
76
76
  for (const detail of details) {
77
77
  if (!detail.trim()) {
78
78
  console.error("");
79
79
  continue;
80
80
  }
81
- console.error(`${DIM3}${detail}${RESET3}`);
81
+ console.error(`${DIM2}${detail}${RESET2}`);
82
82
  }
83
83
  throw new CliExit(1);
84
84
  }
@@ -202,7 +202,7 @@ function getConfig(parsed) {
202
202
  fatal("`--aa-provider` and `--aa-mode` cannot be used with `--eoa`.");
203
203
  }
204
204
  return {
205
- baseUrl: (_d = (_c = parsed.flags["backend-url"]) != null ? _c : process.env.AOMI_BASE_URL) != null ? _d : "https://api.aomi.dev",
205
+ baseUrl: (_d = (_c = parsed.flags["backend-url"]) != null ? _c : process.env.AOMI_BACKEND_URL) != null ? _d : "https://api.aomi.dev",
206
206
  apiKey: (_e = parsed.flags["api-key"]) != null ? _e : process.env.AOMI_API_KEY,
207
207
  app: (_g = (_f = parsed.flags["app"]) != null ? _f : process.env.AOMI_APP) != null ? _g : "default",
208
208
  model: (_h = parsed.flags["model"]) != null ? _h : process.env.AOMI_MODEL,
@@ -1157,6 +1157,31 @@ var AomiClient = class {
1157
1157
  }
1158
1158
  return postState(this.baseUrl, "/api/control/model", payload, sessionId, apiKey);
1159
1159
  }
1160
+ // ===========================================================================
1161
+ // Batch Simulation
1162
+ // ===========================================================================
1163
+ /**
1164
+ * Simulate pending transactions as an atomic batch.
1165
+ * Each tx sees state changes from previous txs (e.g., approve → swap).
1166
+ */
1167
+ async simulateBatch(sessionId, txIds) {
1168
+ const url = joinApiPath(this.baseUrl, "/api/simulate");
1169
+ const headers = new Headers(
1170
+ withSessionHeader(sessionId, { "Content-Type": "application/json" })
1171
+ );
1172
+ if (this.apiKey) {
1173
+ headers.set(API_KEY_HEADER, this.apiKey);
1174
+ }
1175
+ const response = await fetch(url, {
1176
+ method: "POST",
1177
+ headers,
1178
+ body: JSON.stringify({ tx_ids: txIds })
1179
+ });
1180
+ if (!response.ok) {
1181
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1182
+ }
1183
+ return await response.json();
1184
+ }
1160
1185
  };
1161
1186
 
1162
1187
  // src/types.ts
@@ -2940,6 +2965,78 @@ async function sessionCommand(runtime) {
2940
2965
  );
2941
2966
  }
2942
2967
 
2968
+ // src/cli/commands/simulate.ts
2969
+ function requirePendingTx(state, txId) {
2970
+ var _a3;
2971
+ const pendingTx = ((_a3 = state.pendingTxs) != null ? _a3 : []).find((tx) => tx.id === txId);
2972
+ if (!pendingTx) {
2973
+ fatal(
2974
+ `No pending transaction with id "${txId}".
2975
+ Run \`aomi tx\` to see available IDs.`
2976
+ );
2977
+ }
2978
+ return pendingTx;
2979
+ }
2980
+ async function simulateCommand(runtime) {
2981
+ var _a3;
2982
+ const state = readState();
2983
+ if (!state) {
2984
+ fatal("No active session. Run `aomi chat` first.");
2985
+ }
2986
+ const txIds = runtime.parsed.positional;
2987
+ if (txIds.length === 0) {
2988
+ fatal("Usage: aomi simulate <tx-id> [<tx-id> ...]\nRun `aomi tx` to see available IDs.");
2989
+ }
2990
+ for (const txId of txIds) {
2991
+ requirePendingTx(state, txId);
2992
+ }
2993
+ console.log(
2994
+ `${DIM}Simulating ${txIds.length} transaction(s) as atomic batch...${RESET}`
2995
+ );
2996
+ const client = new AomiClient({
2997
+ baseUrl: state.baseUrl,
2998
+ apiKey: state.apiKey
2999
+ });
3000
+ const response = await client.simulateBatch(state.sessionId, txIds);
3001
+ const { result } = response;
3002
+ const modeLabel = result.stateful ? "stateful (Anvil snapshot)" : "stateless (independent eth_call)";
3003
+ console.log(`
3004
+ Batch simulation (${modeLabel}):`);
3005
+ console.log(`From: ${result.from} | Network: ${result.network}
3006
+ `);
3007
+ for (const step of result.steps) {
3008
+ const icon = step.success ? `${GREEN}\u2713${RESET}` : `\x1B[31m\u2717${RESET}`;
3009
+ const label = step.label || `Step ${step.step}`;
3010
+ const gasInfo = step.gas_used ? ` | gas: ${step.gas_used.toLocaleString()}` : "";
3011
+ console.log(` ${icon} ${step.step}. ${label}`);
3012
+ console.log(` ${DIM}to: ${step.tx.to} | value: ${step.tx.value_eth} ETH${gasInfo}${RESET}`);
3013
+ if (!step.success && step.revert_reason) {
3014
+ console.log(` \x1B[31mRevert: ${step.revert_reason}${RESET}`);
3015
+ }
3016
+ }
3017
+ if (result.total_gas) {
3018
+ console.log(`
3019
+ ${DIM}Total gas: ${result.total_gas.toLocaleString()}${RESET}`);
3020
+ }
3021
+ if (result.fee) {
3022
+ const feeEth = (Number(result.fee.amount_wei) / 1e18).toFixed(6);
3023
+ console.log(
3024
+ `Service fee: ${feeEth} ETH \u2192 ${result.fee.recipient}`
3025
+ );
3026
+ }
3027
+ console.log();
3028
+ if (result.batch_success) {
3029
+ console.log(
3030
+ `${GREEN}All steps passed.${RESET} Run \`aomi sign ${txIds.join(" ")}\` to execute.`
3031
+ );
3032
+ } else {
3033
+ const failed = result.steps.find((s) => !s.success);
3034
+ console.log(
3035
+ `\x1B[31mBatch failed at step ${(_a3 = failed == null ? void 0 : failed.step) != null ? _a3 : "?"}.${RESET} Fix the issue and re-queue, or run \`aomi sign\` on the successful prefix.`
3036
+ );
3037
+ }
3038
+ }
3039
+
2943
3040
  // src/cli/commands/wallet.ts
2944
3041
  import { createWalletClient, http } from "viem";
2945
3042
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
@@ -3491,7 +3588,7 @@ function txCommand() {
3491
3588
  }
3492
3589
  printDataFileLocation();
3493
3590
  }
3494
- function requirePendingTx(state, txId) {
3591
+ function requirePendingTx2(state, txId) {
3495
3592
  var _a3;
3496
3593
  const pendingTx = ((_a3 = state.pendingTxs) != null ? _a3 : []).find((tx) => tx.id === txId);
3497
3594
  if (!pendingTx) {
@@ -3507,7 +3604,7 @@ function requirePendingTxs(state, txIds) {
3507
3604
  if (uniqueIds.length !== txIds.length) {
3508
3605
  fatal("Duplicate transaction IDs are not allowed in a single `aomi sign` call.");
3509
3606
  }
3510
- return uniqueIds.map((txId) => requirePendingTx(state, txId));
3607
+ return uniqueIds.map((txId) => requirePendingTx2(state, txId));
3511
3608
  }
3512
3609
  function rewriteSessionState(runtime, state) {
3513
3610
  let changed = false;
@@ -3669,7 +3766,7 @@ async function executeTransactionWithFallback(params) {
3669
3766
  }
3670
3767
  }
3671
3768
  async function signCommand(runtime) {
3672
- var _a3, _b;
3769
+ var _a3, _b, _c, _d;
3673
3770
  const txIds = runtime.parsed.positional;
3674
3771
  if (txIds.length === 0) {
3675
3772
  fatal(
@@ -3739,6 +3836,34 @@ async function signCommand(runtime) {
3739
3836
  if (callList.length > 1 && rpcUrl && new Set(callList.map((call) => call.chainId)).size > 1) {
3740
3837
  fatal("A single `--rpc-url` override cannot be used for a mixed-chain multi-sign request.");
3741
3838
  }
3839
+ try {
3840
+ const simResponse = await session.client.simulateBatch(
3841
+ state.sessionId,
3842
+ pendingTxs.map((tx) => tx.id)
3843
+ );
3844
+ const { result: sim } = simResponse;
3845
+ if (!sim.batch_success) {
3846
+ const failed = sim.steps.find((s) => !s.success);
3847
+ fatal(
3848
+ `Simulation failed at step ${(_c = failed == null ? void 0 : failed.step) != null ? _c : "?"}: ${(_d = failed == null ? void 0 : failed.revert_reason) != null ? _d : "unknown"}`
3849
+ );
3850
+ }
3851
+ if (sim.fee) {
3852
+ const feeEth = (Number(sim.fee.amount_wei) / 1e18).toFixed(6);
3853
+ console.log(
3854
+ `Fee: ${feeEth} ETH \u2192 ${sim.fee.recipient.slice(0, 10)}...`
3855
+ );
3856
+ callList.push({
3857
+ to: sim.fee.recipient,
3858
+ value: sim.fee.amount_wei,
3859
+ chainId: primaryChainId
3860
+ });
3861
+ }
3862
+ } catch (e) {
3863
+ console.log(
3864
+ `${DIM}Simulation unavailable, skipping fee injection.${RESET}`
3865
+ );
3866
+ }
3742
3867
  const decision = resolveCliExecutionDecision({
3743
3868
  config: runtime.config,
3744
3869
  chain,
@@ -3875,6 +4000,8 @@ Usage:
3875
4000
  Delete a local session file (session-id or session-N)
3876
4001
  aomi log Show full conversation history with tool results
3877
4002
  aomi tx List pending and signed transactions
4003
+ aomi simulate <tx-id> [<tx-id> ...]
4004
+ Batch-simulate pending txs atomically (approve \u2192 swap)
3878
4005
  aomi sign <tx-id> [<tx-id> ...] [--eoa | --aa] [--aa-provider <name>] [--aa-mode <mode>]
3879
4006
  Sign and submit a pending transaction
3880
4007
  aomi secret list List configured secrets for the active session
@@ -3914,7 +4041,7 @@ Default signing behavior:
3914
4041
  fall back to EOA automatically if AA is unavailable
3915
4042
 
3916
4043
  Environment (overridden by flags):
3917
- AOMI_BASE_URL Backend URL
4044
+ AOMI_BACKEND_URL Backend URL
3918
4045
  AOMI_API_KEY API key
3919
4046
  AOMI_APP App
3920
4047
  AOMI_MODEL Model rig
@@ -3959,6 +4086,9 @@ async function main(runtime) {
3959
4086
  case "sign":
3960
4087
  await signCommand(runtime);
3961
4088
  break;
4089
+ case "simulate":
4090
+ await simulateCommand(runtime);
4091
+ break;
3962
4092
  case "status":
3963
4093
  await statusCommand(runtime);
3964
4094
  break;
@@ -3996,7 +4126,7 @@ function isPnpmExecWrapper() {
3996
4126
  async function runCli(argv = process.argv) {
3997
4127
  const runtime = createRuntime(argv);
3998
4128
  const RED = "\x1B[31m";
3999
- const RESET3 = "\x1B[0m";
4129
+ const RESET2 = "\x1B[0m";
4000
4130
  const strictExit = process.env.AOMI_CLI_STRICT_EXIT === "1";
4001
4131
  try {
4002
4132
  await main(runtime);
@@ -4009,7 +4139,7 @@ async function runCli(argv = process.argv) {
4009
4139
  return;
4010
4140
  }
4011
4141
  const message = err instanceof Error ? err.message : String(err);
4012
- console.error(`${RED}\u274C ${message}${RESET3}`);
4142
+ console.error(`${RED}\u274C ${message}${RESET2}`);
4013
4143
  process.exit(1);
4014
4144
  }
4015
4145
  }
package/dist/index.cjs CHANGED
@@ -615,6 +615,31 @@ var AomiClient = class {
615
615
  }
616
616
  return postState(this.baseUrl, "/api/control/model", payload, sessionId, apiKey);
617
617
  }
618
+ // ===========================================================================
619
+ // Batch Simulation
620
+ // ===========================================================================
621
+ /**
622
+ * Simulate pending transactions as an atomic batch.
623
+ * Each tx sees state changes from previous txs (e.g., approve → swap).
624
+ */
625
+ async simulateBatch(sessionId, txIds) {
626
+ const url = joinApiPath(this.baseUrl, "/api/simulate");
627
+ const headers = new Headers(
628
+ withSessionHeader(sessionId, { "Content-Type": "application/json" })
629
+ );
630
+ if (this.apiKey) {
631
+ headers.set(API_KEY_HEADER, this.apiKey);
632
+ }
633
+ const response = await fetch(url, {
634
+ method: "POST",
635
+ headers,
636
+ body: JSON.stringify({ tx_ids: txIds })
637
+ });
638
+ if (!response.ok) {
639
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
640
+ }
641
+ return await response.json();
642
+ }
618
643
  };
619
644
 
620
645
  // src/types.ts