nansen-cli 1.35.0 → 1.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/README.md +34 -1
- package/package.json +1 -1
- package/skills/nansen-trading/SKILL.md +49 -1
- package/src/api.js +3 -1
- package/src/bridge.js +1102 -0
- package/src/cli.js +130 -7
- package/src/hl-action.js +528 -0
- package/src/hl-client.js +168 -0
- package/src/hl-env.js +37 -0
- package/src/keychain.js +6 -2
- package/src/limit-order.js +18 -4
- package/src/perp.js +835 -0
- package/src/rpc-urls.js +18 -11
- package/src/schema.json +415 -6
- package/src/trading.js +162 -17
- package/src/wallet-signing.js +87 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`54386c0`](https://github.com/nansen-ai/nansen-cli/commit/54386c0f3280fcc06c8bb7a5d18956d45b2d3d63) Thanks [@kome12](https://github.com/kome12)! - **Output shape change:** every command failure now serializes through the same error envelope — `{success: false, error, code, status, details}`. Previously a `CommandError` printed its structured payload at the top level instead, so errors from `trade`, `limit-order` and the API-key flows (`NOT_A_TTY`, `API_KEY_REQUIRED`, `INVALID_API_KEY`, `VERIFICATION_FAILED`) came back in a different shape from everything else.
|
|
8
|
+
|
|
9
|
+
Nothing is lost — the previous top-level payload is preserved verbatim under `details` — but anything parsing those errors positionally needs to read `details` instead of the root object. The new `perp` and `bridge` commands raise `CommandError` throughout, so without this they would have been the third distinct error shape in the CLI.
|
|
10
|
+
|
|
11
|
+
One deliberate exception: a missing-argument usage banner (`MISSING_PARAM`, `MISSING_ARGS`) prints as plain text when stdout is an interactive terminal and no output format was requested, because those messages are multi-line help written to be read and serializing them renders every newline as a literal `\n`. Piped output, and any run with `--pretty`, `--table`, `--format csv` or `--stream`, still gets the envelope — so nothing consuming the CLI programmatically sees a different shape.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`8ab8bb4`](https://github.com/nansen-ai/nansen-cli/commit/8ab8bb41a1fc89b7dfa804f5c2d601e6ae38c89b) Thanks [@kome12](https://github.com/kome12)! - `bridge execute` now re-screens the wallet against the compliance blocklist immediately before signing, and fails closed if the check can't be completed — matching what the perp commands already did. Bridge quotes stay valid for an hour and the EVM deposit leg broadcasts straight to a public RPC, so previously nothing re-checked the wallet between the quote and the transaction that moves funds.
|
|
16
|
+
|
|
17
|
+
It also refuses to execute a quote with a wallet other than the one the quote was created for. Previously the signing wallet was resolved from `--wallet` or the current default independently of the quote, so a changed default (or an explicit `--wallet`) could sign with a different wallet than the one screened.
|
|
18
|
+
|
|
19
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`44805e2`](https://github.com/nansen-ai/nansen-cli/commit/44805e2cd0a002b38b32bd2818da89548c4974f0) Thanks [@kome12](https://github.com/kome12)! - `nansen schema` now describes the `bridge` command group — its three subcommands, their options, and the supported routes — so agents driving the CLI off the schema can discover it.
|
|
20
|
+
|
|
21
|
+
The mutating `perp` subcommands (`order`, `cancel`, `close`, `leverage`, `transfer`, `approve-builder-fee`) now declare `submitsTo: "https://api.hyperliquid.xyz/exchange"` in place of an `endpoint`, which is where they actually send a signed action, alongside an `apiEndpoints` list of the Nansen routes each one reads for compliance screening, market metadata and builder-fee status. Read-only subcommands keep their `endpoint` unchanged.
|
|
22
|
+
|
|
23
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`7185359`](https://github.com/nansen-ai/nansen-cli/commit/7185359d3ce657df849c371b34f6d045a4dac653) Thanks [@kome12](https://github.com/kome12)! - `nansen bridge` supports `base -> hyperliquid` for deposits, and `hyperliquid -> base`, `hyperliquid -> ethereum`, `hyperliquid -> arbitrum` for withdrawals. Any other combination is rejected at quote time with the supported routes listed.
|
|
24
|
+
|
|
25
|
+
The route set is asymmetric because the two directions need different things from the client: a deposit broadcasts an EVM transaction and so needs a locally signable origin chain, while a withdrawal signs a Hyperliquid action and never touches the destination chain.
|
|
26
|
+
|
|
27
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`19738dd`](https://github.com/nansen-ai/nansen-cli/commit/19738dd252dca12ce0e41d40fe4023ac96ff0213) Thanks [@kome12](https://github.com/kome12)! - EVM transactions are now signed as EIP-1559 (type 2) when the quote supplies fee caps, instead of being flattened into a legacy (type 0) transaction with a single gas price.
|
|
28
|
+
|
|
29
|
+
A legacy transaction pays exactly its `gasPrice`, so once the base fee rises above that value it is not merely slow — it can never be included at that nonce. A type-2 transaction pays base fee plus priority up to its cap, so it tolerates the fee moving between signing and inclusion. This affects `trade execute` and `bridge execute`, which share the signer.
|
|
30
|
+
|
|
31
|
+
`bridge execute` also stops discarding the fee fields the bridge quote provides. It previously overwrote them with a bare `eth_gasPrice` reading, producing a transaction priced at roughly the current base fee with almost no priority fee — which is what it takes to sit unmined on Base. Quoted fees are now kept, with the priority fee raised to a floor that Base will actually schedule and the cap lifted to cover both that and base-fee movement.
|
|
32
|
+
|
|
33
|
+
Two related robustness changes: signing now refuses outright when a quote carries no gas information at all, rather than falling back to a 1 wei gas price that produces a permanently unmineable transaction; and the receipt wait is longer, because by the time it runs the transaction is already broadcast, so giving up early reports a failure without undoing anything.
|
|
34
|
+
|
|
35
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`f649eea`](https://github.com/nansen-ai/nansen-cli/commit/f649eeaf90b5666d51bc623aafdeaf4087bfe9f8) Thanks [@kome12](https://github.com/kome12)! - Add a client-side Hyperliquid action builder (`src/hl-action.js`), the groundwork for submitting perp trades straight to Hyperliquid instead of round-tripping through the Nansen backend to build them.
|
|
36
|
+
|
|
37
|
+
- msgpack encoder that reproduces the reference `msgpack.packb` output byte-for-byte (insertion-ordered maps, smallest-width ints, utf-8 strings), so an action's `connectionId` hash matches the known-good path.
|
|
38
|
+
- `actionHash` + `l1Eip712` reproduce the phantom-agent EIP-712 payload (Exchange domain, mainnet `source: "a"`) that the existing signer already knows how to sign.
|
|
39
|
+
- Order-wire assembly for market/limit orders, cancels, closes and leverage updates, including TP/SL (`normalTpsl`) grouping and the builder-code attachment.
|
|
40
|
+
- Price/size rounding (`roundPrice`/`roundSize`) ported with Python-parity banker's rounding, so over-precise values that Hyperliquid would reject are rounded identically to the server path.
|
|
41
|
+
- `approveBuilderFee` and `usdClassTransfer` user-signed payload builders.
|
|
42
|
+
|
|
43
|
+
Pinned against the live prepare endpoints with golden-vector tests that assert both the built action and its `connectionId` match byte-for-byte.
|
|
44
|
+
|
|
45
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`3bd3909`](https://github.com/nansen-ai/nansen-cli/commit/3bd3909155d89b926ceabd506aaea44858d905f6) Thanks [@kome12](https://github.com/kome12)! - Add `src/hl-client.js`, the single direct-to-Hyperliquid submission path: `submitExchange()` POSTs a signed action straight to `api.hyperliquid.xyz/exchange` from the user's machine instead of routing it through the Nansen backend. Reads and market-data stay on the proxy.
|
|
46
|
+
|
|
47
|
+
It reproduces the backend proxy's failure handling that it replaces — throwing on a top-level `status: "err"` and on a per-action error nested in `response.data.statuses[].error` (a rejected order that Hyperliquid otherwise reports under a top-level `"ok"`) — so a rejected order can never be mistaken for a fill. The submit is deliberately not retried, since each carries a unique nonce and is not idempotent. The HL base URL is overridable via `NANSEN_HL_API_URL` (for testnet / tests).
|
|
48
|
+
|
|
49
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`b5d6946`](https://github.com/nansen-ai/nansen-cli/commit/b5d6946b0380b30d5dc1a4bcc33dcbc18ad0f8ad) Thanks [@kome12](https://github.com/kome12)! - Harden perp, swap, and bridge command safety:
|
|
50
|
+
|
|
51
|
+
- `perp order`/`close` now reject an invalid `--side` instead of silently opening the opposite direction, and `perp leverage` rejects an invalid `--margin-type` instead of silently switching to isolated.
|
|
52
|
+
- Perp numeric args (`--size`, `--price`, `--leverage`, `--oid`) are validated as positive numbers, with specific error messages instead of a generic usage banner.
|
|
53
|
+
- Perp commands now require an EVM wallet, with a clear error instead of querying for an `"undefined"` address.
|
|
54
|
+
- `trade quote` validates `--quote-index`, `--slippage`, and `--max-auto-slippage`, rejecting out-of-range values (e.g. a percent-vs-decimal slippage mix-up).
|
|
55
|
+
- `bridge quote` now validates `--slippage` client-side (whole basis points in `[0, 10000]`), rejecting non-numeric or out-of-range values with a clear message instead of forwarding them to an opaque backend 422 (matching how `perp` validates `--slippage`).
|
|
56
|
+
- `perp order`/`close` warn before signing when `--size` (or `--price` for `order`) is finer than the asset's Hyperliquid precision, which the exchange silently rounds.
|
|
57
|
+
- `perp order`/`close` now report the size and price the order actually executes at (post-rounding, slippage-adjusted for market orders) instead of echoing the raw input, so the printed values match the fill.
|
|
58
|
+
- `limit-order list` no longer aborts the whole render when one order has a non-integer amount.
|
|
59
|
+
- Quote loaders reject a cross-type quote (a bridge quote sent to `trade execute`, or a swap quote sent to `bridge execute`).
|
|
60
|
+
- `bridge execute` now refuses a quote that has already been executed, preventing an accidental double-bridge on retry.
|
|
61
|
+
- `bridge quote` accepts human amounts via `--amount-unit token|usd` (default stays base units), resolving token decimals per chain so the same `5` isn't 100x off between chains (USDC is 6 decimals on EVM, 8 on Hyperliquid). Hyperliquid USDC is floored to the bridge's 6-decimal precision to avoid a round-up-past-balance rejection.
|
|
62
|
+
- `perp meta` supports `--all` and `--filter <text>` so assets past the first 20 (e.g. HYPE) are listable.
|
|
63
|
+
- Deprecated top-level aliases now print a deprecation notice on stderr when run, not only in `--help`.
|
|
64
|
+
- `limit-order` rejects a zero-duration or past expiry instead of creating an order that expires immediately.
|
|
65
|
+
- A password with leading/trailing whitespace is no longer mangled when read back from the OS keychain.
|
|
66
|
+
- Nested backend error messages containing an apostrophe are no longer truncated.
|
|
67
|
+
|
|
68
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`b577954`](https://github.com/nansen-ai/nansen-cli/commit/b5779543c5a5bb714a3428bb80e3d8f5e3eb865e) Thanks [@kome12](https://github.com/kome12)! - `perp` and `bridge` no longer serve any API response from the `--cache` store, and `bridge execute` no longer retries its submission.
|
|
69
|
+
|
|
70
|
+
- **Compliance screening** is always a live check. Every mutating command re-screens the signing wallet immediately before signing; with `--cache` that verdict could previously come from a cache written up to five minutes earlier, which is exactly the window the check exists to close.
|
|
71
|
+
- **`bridge execute`** sets `retry: false`. It proxies to Relay's `/authorize` and Hyperliquid's `/exchange`, neither of which is idempotent, so an automatic re-send on a 500 or 502 could submit the same signed action twice.
|
|
72
|
+
- **Bridge status polling** now observes progress under `--cache`. The cache key is endpoint plus body, so every poll for a given request id hit the same key and the loop would re-read one stale verdict for the whole TTL.
|
|
73
|
+
- **Perp reads** (`positions`, `orders`, `account`, `meta`) and **bridge quotes** bypass the cache too: they either report live balances to the user or feed a signing decision — `close` sizes its order from the positions read, and asset ids come from `meta`.
|
|
74
|
+
|
|
75
|
+
`bridge execute` also refuses to sign a step whose EIP-712 type definition is missing or whose `primaryType` matches no entry in it. With an empty field list the digest is still well-formed but commits to none of the action's contents, so it would have produced a valid-looking signature over nothing.
|
|
76
|
+
|
|
77
|
+
- [#467](https://github.com/nansen-ai/nansen-cli/pull/467) [`4c1d3aa`](https://github.com/nansen-ai/nansen-cli/commit/4c1d3aa16874b2d994ba0e57f52131fdc2dd0748) Thanks [@kome12](https://github.com/kome12)! - Close three residual safety gaps on the perp/bridge signing paths:
|
|
78
|
+
|
|
79
|
+
- `perp order` now rejects a take-profit or stop-loss price that rounds to zero at the asset's precision, instead of encoding a `triggerPx` of `0` and resting a dead protective order while the parent position opens unprotected. This extends the existing zero-price/size guard (which only covered the parent leg) to the TP/SL trigger legs.
|
|
80
|
+
- The Privy bridge signing path now refuses to sign an EIP-712 action whose primary type has no field definitions, matching the guard the local signing path already had. An empty type list produces a valid-looking signature that commits to none of the action's contents.
|
|
81
|
+
- The EVM bridge deposit leg resolves its nonce from the signing wallet's own address rather than the server-returned `txData.from`. The transaction is signed with the local key regardless of `from`, so the nonce must come from that account — and this stays correct even if a quote omits `from`.
|
|
82
|
+
|
|
3
83
|
## 1.35.0
|
|
4
84
|
|
|
5
85
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -93,6 +93,38 @@ nansen trade limit-order update --order <orderId> --trigger-price 85
|
|
|
93
93
|
|
|
94
94
|
For EVM chains, there's no native limit-order surface — pair an external venue's resting order with a `common-token-transfer` smart alert on the settlement wallet as a best-effort fill signal. See the `nansen-limit-orders` skill for details.
|
|
95
95
|
|
|
96
|
+
## Perpetuals
|
|
97
|
+
|
|
98
|
+
Hyperliquid perpetual trading via `nansen perp`. Uses the same wallet and `NANSEN_WALLET_PASSWORD` as DEX trading (requires an EVM wallet). Select the asset with `--coin` (`--symbol` is accepted as an alias).
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
nansen perp meta --filter ETH # list assets + max leverage
|
|
102
|
+
nansen perp order --coin ETH --side buy --size 0.1 --price 1600 --type limit
|
|
103
|
+
nansen perp order --coin BTC --side sell --size 0.001 --price 95000 --type market \
|
|
104
|
+
--take-profit 90000 --stop-loss 98000
|
|
105
|
+
nansen perp close --coin ETH --size 0.1 --price 1600 --side sell # sell closes a long
|
|
106
|
+
nansen perp cancel --coin ETH --oid <orderId>
|
|
107
|
+
nansen perp leverage --coin ETH --leverage 5 --margin-type cross # or isolated
|
|
108
|
+
nansen perp transfer --direction spot-to-perp --amount 25 # Spot<->Perps (or perp-to-spot)
|
|
109
|
+
nansen perp positions
|
|
110
|
+
nansen perp account # value, unrealized PnL, margin, spot USDC
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`--side` is `buy`/`long` or `sell`/`short` to open (`buy`/`sell` to close); `--tif` is `Gtc`/`Ioc`/`Alo`; `--slippage` is a decimal in `[0,1]`; `--leverage` is a whole integer capped at the asset max. Perp orders are irreversible once signed. USDC sent to a wallet via Hyperliquid's **Send** lands in the **Spot** balance (shown as `Spot USDC`); move it to Perps with `perp transfer` before trading. See the `nansen-trading` skill for details.
|
|
114
|
+
|
|
115
|
+
## Bridge
|
|
116
|
+
|
|
117
|
+
Move USDC between EVM chains and Hyperliquid via `nansen bridge`. Uses the same wallet and `NANSEN_WALLET_PASSWORD` as perps and DEX trading. Quotes are written to a local file and executed by id; execution signs and broadcasts.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
nansen bridge quote --from-chain base --to-chain hyperliquid --from-token USDC --amount 1000000
|
|
121
|
+
nansen bridge execute --quote <quoteId>
|
|
122
|
+
nansen bridge execute --quote <quoteId> --nonce 20 --priority-fee 5 # replace a stuck EVM deposit
|
|
123
|
+
nansen bridge status --request-id <id>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Supported routes: `base → hyperliquid` (deposit), and `hyperliquid → base`/`ethereum`/`arbitrum` (withdraw). Deposits broadcast an EVM transaction locally, so only Base is offered on the deposit side; run `nansen bridge help` for the current list. `--amount` is a base-unit integer by default; pass `--amount-unit token` for a human amount. `--recipient` defaults to the wallet's own EVM address. `--priority-fee`/`--max-fee` (gwei) and `--nonce` apply only to EVM deposit legs and let a stuck transaction be replaced. Bridge transfers are irreversible once signed.
|
|
127
|
+
|
|
96
128
|
## Wallet
|
|
97
129
|
|
|
98
130
|
```bash
|
|
@@ -206,7 +238,8 @@ Any field may be absent or `null`, meaning unknown — never assume zero. A low-
|
|
|
206
238
|
|---------|-----|
|
|
207
239
|
| `command not found` | `npm install -g nansen-cli` |
|
|
208
240
|
| `UNAUTHORIZED` after login | `cat ~/.nansen/config.json` or set `NANSEN_API_KEY` |
|
|
209
|
-
| Empty perp results | Use `--symbol BTC`, not `--token`. Perps are Hyperliquid-only. |
|
|
241
|
+
| Empty perp _research_ results | Use `--symbol BTC`, not `--token`. Perps are Hyperliquid-only. |
|
|
242
|
+
| `perp` _trading_ prints the usage banner | Trading needs `--coin BTC` (`--symbol` also works); see the Perpetuals section. |
|
|
210
243
|
| `UNSUPPORTED_FILTER` on token holders | Remove `--smart-money` — not all tokens have that data. |
|
|
211
244
|
| Huge JSON response | Use `--fields` to select columns. |
|
|
212
245
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nansen-trading
|
|
3
|
-
description: Execute DEX swaps on Solana or Base
|
|
3
|
+
description: Execute DEX swaps on Solana or Base (including cross-chain bridges) and Hyperliquid perpetual trades. Use when buying or selling a token, getting a swap quote, executing a trade, or opening/closing/managing a perp position.
|
|
4
4
|
metadata:
|
|
5
5
|
openclaw:
|
|
6
6
|
requires:
|
|
@@ -191,6 +191,54 @@ If the user says "$20 worth of X", use `--amount-unit usd` directly — no manua
|
|
|
191
191
|
- A wallet is required even for quotes (the API builds sender-specific transactions).
|
|
192
192
|
- ERC-20 swaps may require an approval step — execute handles this automatically.
|
|
193
193
|
|
|
194
|
+
# Perp Trading
|
|
195
|
+
|
|
196
|
+
Use `nansen perp` for Hyperliquid perpetual trading. Uses the same wallet and `NANSEN_WALLET_PASSWORD` as DEX trading; requires an **EVM** wallet. **Perp orders are irreversible once signed.**
|
|
197
|
+
|
|
198
|
+
Subcommands: `order`, `cancel`, `close`, `leverage`, `positions`, `orders`, `account`, `meta`.
|
|
199
|
+
|
|
200
|
+
The asset is selected with `--coin` (e.g. `BTC`, `ETH`); `--symbol` is accepted as an alias. List tradable assets and their max leverage with `nansen perp meta` (use `--filter <text>` or `--all` to see beyond the first 20).
|
|
201
|
+
|
|
202
|
+
## Open a position
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Limit long: 0.1 ETH at $1600
|
|
206
|
+
nansen perp order --coin ETH --side buy --size 0.1 --price 1600 --type limit
|
|
207
|
+
|
|
208
|
+
# Market short with optional take-profit / stop-loss
|
|
209
|
+
nansen perp order --coin BTC --side sell --size 0.001 --price 95000 --type market \
|
|
210
|
+
--take-profit 90000 --stop-loss 98000
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- `--side`: `buy`/`long` to open a long, `sell`/`short` to open a short.
|
|
214
|
+
- `--size`: position size in base asset units (positive number).
|
|
215
|
+
- `--price`: limit price (or mark price for market orders).
|
|
216
|
+
- `--type`: `limit` (default) or `market`. `--tif`: `Gtc` (default), `Ioc`, `Alo`.
|
|
217
|
+
- `--slippage`: decimal in `[0,1]` for market orders (default `0.03` = 3%).
|
|
218
|
+
|
|
219
|
+
## Close / cancel
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Close: sell to close a long, buy to close a short (validated against your open position)
|
|
223
|
+
nansen perp close --coin ETH --size 0.1 --price 1600 --side sell
|
|
224
|
+
|
|
225
|
+
# Cancel a resting order by id
|
|
226
|
+
nansen perp cancel --coin ETH --oid 123456
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Leverage, transfers & account
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
nansen perp leverage --coin ETH --leverage 5 --margin-type cross # or isolated
|
|
233
|
+
nansen perp transfer --direction spot-to-perp --amount 25 # or perp-to-spot
|
|
234
|
+
nansen perp positions
|
|
235
|
+
nansen perp account # account value, unrealized PnL, margin used, withdrawable, spot USDC
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`--leverage` must be a whole integer and is capped at the asset's maximum (see `perp meta`).
|
|
239
|
+
|
|
240
|
+
**Spot vs Perps:** perp trading draws from the **Perps** balance, but USDC sent to a wallet via Hyperliquid's **Send** lands in **Spot** (and shows as `Spot USDC` in `perp account`). Move it across with `perp transfer --direction spot-to-perp --amount <usdc>` before trading. (Deposits via the bridge land in Perps directly.)
|
|
241
|
+
|
|
194
242
|
## Source
|
|
195
243
|
|
|
196
244
|
- npm: https://www.npmjs.com/package/nansen-cli
|
package/src/api.js
CHANGED
|
@@ -632,7 +632,9 @@ export class NansenAPI {
|
|
|
632
632
|
|| `API error: ${response.status}`;
|
|
633
633
|
// nansen-api proxy stringifies nested error dicts via Python str(), producing
|
|
634
634
|
// "{'message': 'actual error', ...}". Extract the inner message if present.
|
|
635
|
-
|
|
635
|
+
// Require the closing quote to be followed by a comma or closing brace so
|
|
636
|
+
// an apostrophe inside the message (e.g. "can't") doesn't truncate it.
|
|
637
|
+
const nestedMatch = typeof message === 'string' && message.match(/['"]message['"]\s*:\s*['"](.*?)['"]\s*[,}]/s);
|
|
636
638
|
if (nestedMatch) message = nestedMatch[1];
|
|
637
639
|
const code = statusToErrorCode(response.status, data);
|
|
638
640
|
const retryAfterMs = parseRetryAfter(response.headers.get('retry-after'));
|