@hypelens/hypelens-agent-rail 0.1.15 → 0.1.16

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
@@ -2,13 +2,22 @@
2
2
  **Place, cancel, close Hyperliquid perps.** Agent wallet once · auto-round · **1bp on fills — no sub.**
3
3
  ## Install → approve → place
4
4
  ```bash
5
+ # MCP stdio (Claude / Cursor / any MCP host)
5
6
  npx -y @hypelens/hypelens-agent-rail
7
+
8
+ # Claude Code one-liner
9
+ claude mcp add hypelens -- npx -y @hypelens/hypelens-agent-rail
10
+
11
+ # OpenClaw / ClawHub skill (skill slug, not npm alone)
12
+ clawhub install hypelens-agent-rail
13
+ # skills.sh (needs public GH; polyparlay anon currently 404)
14
+ npx skills add polyparlay/hypelens -s hypelens-agent-rail -y
6
15
  ```
7
16
  1. `hl_quickstart` (optional) → `hl_new_agent_wallet` → `HYPELENS_AGENT_PK`
8
17
  2. `hl_approve_payloads(agentAddress)` — **MASTER** signs approveAgent + ApproveBuilderFee **0.01%** once, then POST both
9
18
  3. `hl_place_order` / `hl_cancel_order` / `hl_close_position` / `hl_positions` / `hl_balances`
10
19
 
11
- **First place (testnet default):** after approve, `hl_place_order({coin:"BTC", isBuy:true, sizeUsd:12, leverage:2})` — **sizeUsd alone** auto-fetches mark and rounds size; `entryPx` optional. No feed required.
20
+ **First place (testnet default):** after approve, `hl_place_order({coin:"BTC", isBuy:true, sizeUsd:12, leverage:2})` — **sizeUsd alone** auto-fetches mark and rounds size; `entryPx` optional. No feed required. On reject after sizeUsd, response includes `error` + `next` (margin / approve / min notional).
12
21
 
13
22
  Default **testnet**. `HYPELENS_NET=mainnet` for live. Fee: **1bp on fills** — no sub. Missing `HYPELENS_AGENT_PK` errors point at the same path.
14
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypelens/hypelens-agent-rail",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Place, cancel, and close Hyperliquid perps from OpenClaw/MCP. Agent wallet once, auto-round, 1bp on fills (no sub). First place: hl_place_order BTC sizeUsd 12. npx @hypelens/hypelens-agent-rail",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: hypelens-agent-rail
2
+ name: Hyperliquid Place
3
3
  description: >-
4
4
  hyperliquid place cancel close — Hyperliquid perps rail for OpenClaw/MCP.
5
5
  Agent wallet once, auto-round, 1bp builder on fills (no sub). Keywords:
@@ -31,7 +31,7 @@ metadata:
31
31
  npx -y @hypelens/hypelens-agent-rail
32
32
  ```
33
33
 
34
- **ClawHub / OpenClaw:** `clawhub install hypelens-agent-rail` · **skills.sh:** `npx skills add polyparlay/hypelens`
34
+ **ClawHub / OpenClaw:** `clawhub install hypelens-agent-rail` · **skills.sh:** `npx skills add polyparlay/hypelens -s hypelens-agent-rail -y` · **MCP:** `npx -y @hypelens/hypelens-agent-rail`
35
35
 
36
36
  ## Quickstart (place-ready)
37
37
 
package/src/exchange.js CHANGED
@@ -132,6 +132,57 @@ async function resolveSizeAndPx({ coin, size, entryPx, sizeUsd }) {
132
132
  };
133
133
  }
134
134
 
135
+ /** Extract HL place/cancel wire error + actionable next after sizeUsd resolve. */
136
+ function interpretExchangeResult(body) {
137
+ if (!body || typeof body !== 'object') {
138
+ return { ok: false, error: 'empty exchange response', next: 'retry hl_place_order({coin:"BTC", isBuy:true, sizeUsd:12, leverage:2}) on testnet' };
139
+ }
140
+ if (body.status === 'err' || body.status === 'error') {
141
+ const raw = typeof body.response === 'string' ? body.response : JSON.stringify(body.response || body);
142
+ return { ok: false, error: raw, next: hintFromHlError(raw) };
143
+ }
144
+ const statuses = body?.response?.data?.statuses;
145
+ if (Array.isArray(statuses)) {
146
+ for (const s of statuses) {
147
+ if (s && typeof s.error === 'string' && s.error) {
148
+ return { ok: false, error: s.error, next: hintFromHlError(s.error), statuses };
149
+ }
150
+ }
151
+ // resting / filled / etc
152
+ const resting = statuses.find((s) => s && (s.resting || s.filled));
153
+ if (resting) return { ok: true, error: null, next: null, statuses };
154
+ if (statuses.length) return { ok: true, error: null, next: null, statuses };
155
+ }
156
+ if (body.status === 'ok') return { ok: true, error: null, next: null };
157
+ return { ok: false, error: JSON.stringify(body), next: hintFromHlError(JSON.stringify(body)) };
158
+ }
159
+
160
+ function hintFromHlError(msg) {
161
+ const m = String(msg || '').toLowerCase();
162
+ if (m.includes('insufficient') || m.includes('margin') || m.includes('not enough')) {
163
+ return 'Fund agent on this net (default testnet) or lower sizeUsd; check hl_balances.equity then retry sizeUsd place';
164
+ }
165
+ if (m.includes('does not exist') || m.includes('user or api wallet') || m.includes('unknown user')) {
166
+ return 'MASTER must POST approveAgent + ApproveBuilderFee 1bp (hl_approve_payloads) before first place; then sizeUsd:12';
167
+ }
168
+ if (m.includes('builder') && (m.includes('fee') || m.includes('approval') || m.includes('approve'))) {
169
+ return 'MASTER must sign ApproveBuilderFee 0.01% (1bp) via hl_approve_payloads and POST it; then retry sizeUsd place';
170
+ }
171
+ if (m.includes('minimum') || m.includes('min ') || m.includes('$10') || m.includes('too small')) {
172
+ return 'Increase sizeUsd (BTC first place: sizeUsd:12) — HL min notional; auto-round may shrink tiny sizes to zero';
173
+ }
174
+ if (m.includes('oracle') || m.includes('price') && m.includes('far')) {
175
+ return 'Omit entryPx and pass sizeUsd only (uses mark), or set entryPx near mark; retry hl_place_order sizeUsd';
176
+ }
177
+ if (m.includes('leverage') || m.includes('max lev')) {
178
+ return 'Lower leverage (first place: leverage:2) or set coin max leverage on HL, then retry sizeUsd place';
179
+ }
180
+ if (m.includes('permit') || m.includes('agent') && m.includes('not')) {
181
+ return 'Run hl_approve_payloads(agentAddress) — MASTER signs approveAgent once, POST, then sizeUsd place';
182
+ }
183
+ return 'Check hl_exchange_status + hl_balances; ensure MASTER approved agent+1bp builder; retry hl_place_order({coin:"BTC", isBuy:true, sizeUsd:12, leverage:2}) on testnet';
184
+ }
185
+
135
186
  async function postL1(action) {
136
187
  const { actions, signer } = loadShipped();
137
188
  assertPlacementAllowed(actions);
@@ -142,7 +193,9 @@ async function postL1(action) {
142
193
  body: JSON.stringify({ action: signed.action, signature: signed.signature, nonce: signed.nonce })
143
194
  });
144
195
  const body = await res.json().catch(() => ({}));
145
- return { ok: res.ok && body.status === 'ok', net: net(), response: body };
196
+ const interpreted = interpretExchangeResult(body);
197
+ const ok = res.ok && interpreted.ok;
198
+ return { ok, net: net(), response: body, error: interpreted.error, next: interpreted.next, statuses: interpreted.statuses || null };
146
199
  }
147
200
 
148
201
  async function clearinghouse(user) {
@@ -240,7 +293,7 @@ export async function placeOrder({ coin, isBuy, size = null, entryPx = null, siz
240
293
  tpPx
241
294
  });
242
295
  const posted = await postL1(action);
243
- return {
296
+ const out = {
244
297
  placed: posted.ok,
245
298
  net: posted.net,
246
299
  response: posted.response,
@@ -254,6 +307,14 @@ export async function placeOrder({ coin, isBuy, size = null, entryPx = null, siz
254
307
  sizingWarn,
255
308
  feeModel: '1bp on fills only — no subscription'
256
309
  };
310
+ if (!posted.ok) {
311
+ out.error = posted.error || 'place rejected by exchange';
312
+ out.next = posted.next || hintFromHlError(posted.error || '');
313
+ if (posted.statuses) out.statuses = posted.statuses;
314
+ } else {
315
+ out.next = 'placed — cancel via hl_cancel_order; close via hl_close_position; fees 1bp on fills only';
316
+ }
317
+ return out;
257
318
  }
258
319
 
259
320
  /** Cancel by oid and/or cloid. */
@@ -266,7 +327,9 @@ export async function cancelOrder({ coin, oid = null, cloid = null }) {
266
327
  else if (oid != null) action = actions.buildCancelAction([{ assetIndex, oid: Number(oid) }]);
267
328
  else throw new Error('oid or cloid required — pass oid or cloid to hl_cancel_order');
268
329
  const posted = await postL1(action);
269
- return { cancelled: posted.ok, net: posted.net, response: posted.response, coin: resolveCoin(coin).toUpperCase(), oid, cloid };
330
+ const out = { cancelled: posted.ok, net: posted.net, response: posted.response, coin: resolveCoin(coin).toUpperCase(), oid, cloid };
331
+ if (!posted.ok) { out.error = posted.error || 'cancel rejected'; out.next = posted.next || hintFromHlError(posted.error || ''); }
332
+ return out;
270
333
  }
271
334
 
272
335
  /** IOC reduce-only close for a coin (uses position size if size omitted). */
package/src/mcp.js CHANGED
@@ -13,7 +13,7 @@ const wrap = (fn) => async (args) => {
13
13
  };
14
14
 
15
15
  export async function main() {
16
- const server = new McpServer({ name: 'hypelens-agent-rail', version: '0.1.15' });
16
+ const server = new McpServer({ name: 'hypelens-agent-rail', version: '0.1.16' });
17
17
 
18
18
  server.tool('hl_quickstart',
19
19
  'CALL FIRST: place-ready bootstrap — wallet + MASTER ApproveBuilderFee 1bp once, then hl_place_order sizeUsd:12. Default testnet. 1bp fills, no sub.',