Skip to content

Commit 13431c8

Browse files
Hiksangclaude
andcommitted
fix: dynamic version from package.json + WASM singleton & crash guard
- Read version from package.json via createRequire (no more hardcoded strings) - Reuse singleton WASM client in init() to avoid double initialization - Monkey-patch process.exit during WASM init to prevent Go panic crashes - Saves ~55MB memory and ~1s startup time for Lighter exchange Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c0a6a83 commit 13431c8

4 files changed

Lines changed: 37 additions & 14 deletions

File tree

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "perp-cli",
3-
"version": "0.3.10",
3+
"version": "0.3.11",
44
"description": "Multi-DEX Perpetual Futures CLI - Pacifica, Hyperliquid, Lighter",
55
"bin": {
66
"perp": "./dist/index.js",

‎src/exchanges/lighter.ts‎

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,16 @@ export class LighterAdapter implements ExchangeAdapter {
100100
}
101101

102102
// Auto-generate API key if we have PK but no API key and account exists
103+
let signerReady = false;
103104
if (!this._apiKey && this._accountIndex >= 0) {
104105
try {
105106
const autoKeyIndex = 2; // default for auto-setup
106107
const { privateKey: apiKey } = await this.setupApiKey(autoKeyIndex);
107108
this._apiKey = apiKey;
108109
this._apiKeyIndex = autoKeyIndex;
110+
// setupApiKey already configured the static WASM client — reuse it
111+
this._signer = LighterAdapter._wasmClient!;
112+
signerReady = true;
109113
// Save to .env for future use
110114
try {
111115
const { setEnvVar } = await import("../commands/init.js");
@@ -120,18 +124,19 @@ export class LighterAdapter implements ExchangeAdapter {
120124
}
121125
}
122126

123-
// Initialize signer for trading if we have an API key
124-
if (this._apiKey) {
125-
const { WasmSignerClient } = await import("lighter-ts-sdk");
126-
this._signer = new WasmSignerClient({});
127-
await this._signer.initialize();
128-
await this._signer.createClient({
127+
// Initialize signer for trading if we have an API key (reuse singleton WASM client)
128+
if (this._apiKey && !signerReady) {
129+
const client = await LighterAdapter.getWasmClient();
130+
await client.createClient({
129131
url: this._baseUrl,
130132
privateKey: this._apiKey,
131133
chainId: this._chainId,
132134
apiKeyIndex: this._apiKeyIndex,
133135
accountIndex: this._accountIndex,
134136
});
137+
this._signer = client;
138+
}
139+
if (this._apiKey) {
135140
this._readOnly = false;
136141
}
137142

@@ -872,11 +877,21 @@ export class LighterAdapter implements ExchangeAdapter {
872877

873878
private static async getWasmClient(): Promise<WasmSignerClientType> {
874879
if (LighterAdapter._wasmClient) return LighterAdapter._wasmClient;
875-
const { WasmSignerClient } = await import("lighter-ts-sdk");
876-
const client = new WasmSignerClient({});
877-
await client.initialize();
878-
LighterAdapter._wasmClient = client;
879-
return client;
880+
// Prevent Go WASM runtime from killing the process on panic
881+
const origExit = process.exit;
882+
process.exit = ((code?: number) => {
883+
process.exit = origExit; // restore immediately
884+
throw new Error(`Lighter WASM runtime exited with code ${code}`);
885+
}) as typeof process.exit;
886+
try {
887+
const { WasmSignerClient } = await import("lighter-ts-sdk");
888+
const client = new WasmSignerClient({});
889+
await client.initialize();
890+
LighterAdapter._wasmClient = client;
891+
return client;
892+
} finally {
893+
process.exit = origExit;
894+
}
880895
}
881896

882897
/**

‎src/index.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env node
22
import { config } from "dotenv";
33
import { resolve } from "path";
4+
import { createRequire } from "node:module";
45

56
// Load ~/.perp/.env first (global), then CWD .env (overrides)
67
config({ path: resolve(process.env.HOME || "~", ".perp", ".env") });
@@ -47,6 +48,9 @@ import { registerEnvCommands } from "./commands/env.js";
4748
import { loadSettings, saveSettings } from "./settings.js";
4849
import { setSharedApiNetwork } from "./shared-api.js";
4950

51+
const _require = createRequire(import.meta.url);
52+
const _pkg = _require("../package.json") as { version: string };
53+
5054
const program = new Command();
5155

5256
// Resolve default exchange from settings (fallback: "pacifica")
@@ -56,7 +60,7 @@ const _defaultExchange = _settings.defaultExchange || "pacifica";
5660
program
5761
.name("perp")
5862
.description("Multi-DEX Perpetual Futures CLI (Pacifica, Hyperliquid, Lighter)")
59-
.version("0.3.7")
63+
.version(_pkg.version)
6064
.option("-e, --exchange <exchange>", `Exchange: pacifica, hyperliquid, lighter (default: ${_defaultExchange})`, _defaultExchange)
6165
.option("-n, --network <network>", "Network: mainnet or testnet", "mainnet")
6266
.option("-k, --private-key <key>", "Private key")

‎src/mcp-server.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import "dotenv/config";
11+
import { createRequire } from "node:module";
1112
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1213
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1314
import { z } from "zod";
@@ -75,8 +76,11 @@ function err(error: string, meta?: Record<string, unknown>) {
7576

7677
// ── MCP Server ──
7778

79+
const _require = createRequire(import.meta.url);
80+
const _pkg = _require("../package.json") as { version: string };
81+
7882
const server = new McpServer(
79-
{ name: "perp-cli", version: "0.3.7" },
83+
{ name: "perp-cli", version: _pkg.version },
8084
{ capabilities: { tools: {}, resources: {} } },
8185
);
8286

0 commit comments

Comments
 (0)