pmxtjs 1.0.0 → 1.0.3

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.
@@ -32,7 +32,9 @@ export declare abstract class Exchange {
32
32
  protected privateKey?: string;
33
33
  protected api: DefaultApi;
34
34
  protected serverManager: ServerManager;
35
+ protected initPromise: Promise<void>;
35
36
  constructor(exchangeName: string, options?: ExchangeOptions);
37
+ private initializeServer;
36
38
  protected handleResponse(response: any): any;
37
39
  protected getCredentials(): ExchangeCredentials | undefined;
38
40
  /**
@@ -113,25 +113,39 @@ export class Exchange {
113
113
  privateKey;
114
114
  api;
115
115
  serverManager;
116
+ initPromise;
116
117
  constructor(exchangeName, options = {}) {
117
118
  this.exchangeName = exchangeName.toLowerCase();
118
119
  this.apiKey = options.apiKey;
119
120
  this.privateKey = options.privateKey;
120
- const baseUrl = options.baseUrl || "http://localhost:3847";
121
+ let baseUrl = options.baseUrl || "http://localhost:3847";
121
122
  const autoStartServer = options.autoStartServer !== false;
122
123
  // Initialize server manager
123
124
  this.serverManager = new ServerManager({ baseUrl });
124
- // Ensure server is running (unless disabled)
125
+ // Configure the API client with the initial base URL (will be updated if port changes)
126
+ const config = new Configuration({ basePath: baseUrl });
127
+ this.api = new DefaultApi(config);
128
+ // Initialize the server connection asynchronously
129
+ this.initPromise = this.initializeServer(autoStartServer);
130
+ }
131
+ async initializeServer(autoStartServer) {
125
132
  if (autoStartServer) {
126
- this.serverManager.ensureServerRunning().catch((error) => {
133
+ try {
134
+ await this.serverManager.ensureServerRunning();
135
+ // Get the actual port the server is running on
136
+ // (may differ from default if default port was busy)
137
+ const actualPort = this.serverManager.getRunningPort();
138
+ const newBaseUrl = `http://localhost:${actualPort}`;
139
+ // Update API client with actual base URL
140
+ const newConfig = new Configuration({ basePath: newBaseUrl });
141
+ this.api = new DefaultApi(newConfig);
142
+ }
143
+ catch (error) {
127
144
  throw new Error(`Failed to start PMXT server: ${error}\n\n` +
128
145
  `Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
129
146
  `Or start the server manually: pmxt-server`);
130
- });
147
+ }
131
148
  }
132
- // Configure the API client
133
- const config = new Configuration({ basePath: baseUrl });
134
- this.api = new DefaultApi(config);
135
149
  }
136
150
  handleResponse(response) {
137
151
  if (!response.success) {
@@ -162,6 +176,7 @@ export class Exchange {
162
176
  * ```
163
177
  */
164
178
  async fetchMarkets(params) {
179
+ await this.initPromise;
165
180
  try {
166
181
  const args = [];
167
182
  if (params) {
@@ -195,6 +210,7 @@ export class Exchange {
195
210
  * ```
196
211
  */
197
212
  async searchMarkets(query, params) {
213
+ await this.initPromise;
198
214
  try {
199
215
  const args = [query];
200
216
  if (params) {
@@ -231,6 +247,7 @@ export class Exchange {
231
247
  * ```
232
248
  */
233
249
  async getMarketsBySlug(slug) {
250
+ await this.initPromise;
234
251
  try {
235
252
  const requestBody = {
236
253
  args: [slug],
@@ -269,6 +286,7 @@ export class Exchange {
269
286
  * ```
270
287
  */
271
288
  async fetchOHLCV(outcomeId, params) {
289
+ await this.initPromise;
272
290
  try {
273
291
  const paramsDict = { resolution: params.resolution };
274
292
  if (params.start) {
@@ -309,6 +327,7 @@ export class Exchange {
309
327
  * ```
310
328
  */
311
329
  async fetchOrderBook(outcomeId) {
330
+ await this.initPromise;
312
331
  try {
313
332
  const requestBody = {
314
333
  args: [outcomeId],
@@ -335,6 +354,7 @@ export class Exchange {
335
354
  * @returns List of trades
336
355
  */
337
356
  async fetchTrades(outcomeId, params) {
357
+ await this.initPromise;
338
358
  try {
339
359
  const paramsDict = { resolution: params.resolution };
340
360
  if (params.limit) {
@@ -375,6 +395,7 @@ export class Exchange {
375
395
  * ```
376
396
  */
377
397
  async createOrder(params) {
398
+ await this.initPromise;
378
399
  try {
379
400
  const paramsDict = {
380
401
  marketId: params.marketId,
@@ -408,6 +429,7 @@ export class Exchange {
408
429
  * @returns Cancelled order
409
430
  */
410
431
  async cancelOrder(orderId) {
432
+ await this.initPromise;
411
433
  try {
412
434
  const requestBody = {
413
435
  args: [orderId],
@@ -431,6 +453,7 @@ export class Exchange {
431
453
  * @returns Order details
432
454
  */
433
455
  async fetchOrder(orderId) {
456
+ await this.initPromise;
434
457
  try {
435
458
  const requestBody = {
436
459
  args: [orderId],
@@ -454,6 +477,7 @@ export class Exchange {
454
477
  * @returns List of open orders
455
478
  */
456
479
  async fetchOpenOrders(marketId) {
480
+ await this.initPromise;
457
481
  try {
458
482
  const args = [];
459
483
  if (marketId) {
@@ -481,6 +505,7 @@ export class Exchange {
481
505
  * @returns List of positions
482
506
  */
483
507
  async fetchPositions() {
508
+ await this.initPromise;
484
509
  try {
485
510
  const requestBody = {
486
511
  args: [],
@@ -503,6 +528,7 @@ export class Exchange {
503
528
  * @returns List of balances (by currency)
504
529
  */
505
530
  async fetchBalance() {
531
+ await this.initPromise;
506
532
  try {
507
533
  const requestBody = {
508
534
  args: [],
@@ -13,7 +13,23 @@ export declare class ServerManager {
13
13
  private maxRetries;
14
14
  private retryDelayMs;
15
15
  private api;
16
+ private lockPath;
17
+ private static readonly DEFAULT_PORT;
16
18
  constructor(options?: ServerManagerOptions);
19
+ /**
20
+ * Read server information from lock file.
21
+ */
22
+ private getServerInfo;
23
+ /**
24
+ * Get the actual port the server is running on.
25
+ *
26
+ * This reads the lock file to determine the actual port,
27
+ * which may differ from the default if the default port was busy.
28
+ */
29
+ getRunningPort(): number;
30
+ /**
31
+ * Check if the server is running.
32
+ */
17
33
  /**
18
34
  * Check if the server is running.
19
35
  */
@@ -4,25 +4,67 @@
4
4
  * Handles automatic server startup and health checks.
5
5
  */
6
6
  import { DefaultApi, Configuration } from "../generated/src/index.js";
7
+ import { readFileSync, existsSync } from "fs";
8
+ import { homedir } from "os";
9
+ import { join, dirname } from "path";
7
10
  export class ServerManager {
8
11
  baseUrl;
9
12
  maxRetries;
10
13
  retryDelayMs;
11
14
  api;
15
+ lockPath;
16
+ static DEFAULT_PORT = 3847;
12
17
  constructor(options = {}) {
13
- this.baseUrl = options.baseUrl || "http://localhost:3847";
18
+ this.baseUrl = options.baseUrl || `http://localhost:${ServerManager.DEFAULT_PORT}`;
14
19
  this.maxRetries = options.maxRetries || 30;
15
20
  this.retryDelayMs = options.retryDelayMs || 1000;
21
+ this.lockPath = join(homedir(), '.pmxt', 'server.lock');
16
22
  const config = new Configuration({ basePath: this.baseUrl });
17
23
  this.api = new DefaultApi(config);
18
24
  }
25
+ /**
26
+ * Read server information from lock file.
27
+ */
28
+ getServerInfo() {
29
+ try {
30
+ if (!existsSync(this.lockPath)) {
31
+ return null;
32
+ }
33
+ const content = readFileSync(this.lockPath, 'utf-8');
34
+ return JSON.parse(content);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ /**
41
+ * Get the actual port the server is running on.
42
+ *
43
+ * This reads the lock file to determine the actual port,
44
+ * which may differ from the default if the default port was busy.
45
+ */
46
+ getRunningPort() {
47
+ const info = this.getServerInfo();
48
+ return info?.port || ServerManager.DEFAULT_PORT;
49
+ }
50
+ /**
51
+ * Check if the server is running.
52
+ */
19
53
  /**
20
54
  * Check if the server is running.
21
55
  */
22
56
  async isServerRunning() {
57
+ // Read lock file to get current port
58
+ const port = this.getRunningPort();
23
59
  try {
24
- const response = await this.api.healthCheck();
25
- return response.status === "ok";
60
+ // Use native fetch to check health on the actual running port
61
+ // This avoids issues where this.api is configured with the wrong port
62
+ const response = await fetch(`http://localhost:${port}/health`);
63
+ if (response.ok) {
64
+ const data = await response.json();
65
+ return data.status === "ok";
66
+ }
67
+ return false;
26
68
  }
27
69
  catch (error) {
28
70
  return false;
@@ -48,10 +90,27 @@ export class ServerManager {
48
90
  if (await this.isServerRunning()) {
49
91
  return;
50
92
  }
93
+ // Locate pmxt-ensure-server
94
+ let launcherPath = 'pmxt-ensure-server'; // Default to PATH
95
+ try {
96
+ // Try to resolve from pmxt-core dependency
97
+ // For CommonJS build (which is primary), we can use require directly
98
+ // For ESM build, this will be transpiled appropriately
99
+ const corePackageJson = require.resolve('pmxt-core/package.json');
100
+ const coreDir = dirname(corePackageJson);
101
+ const binPath = join(coreDir, 'bin', 'pmxt-ensure-server');
102
+ if (existsSync(binPath)) {
103
+ launcherPath = binPath;
104
+ }
105
+ }
106
+ catch (error) {
107
+ // If resolution fails, fall back to PATH
108
+ // This could happen in dev environments where pmxt-core is globally installed
109
+ }
51
110
  // Try to start the server using pmxt-ensure-server
52
111
  const { spawn } = await import("child_process");
53
112
  try {
54
- const proc = spawn("pmxt-ensure-server", [], {
113
+ const proc = spawn(launcherPath, [], {
55
114
  detached: true,
56
115
  stdio: "ignore",
57
116
  });
@@ -32,7 +32,9 @@ export declare abstract class Exchange {
32
32
  protected privateKey?: string;
33
33
  protected api: DefaultApi;
34
34
  protected serverManager: ServerManager;
35
+ protected initPromise: Promise<void>;
35
36
  constructor(exchangeName: string, options?: ExchangeOptions);
37
+ private initializeServer;
36
38
  protected handleResponse(response: any): any;
37
39
  protected getCredentials(): ExchangeCredentials | undefined;
38
40
  /**
@@ -116,25 +116,39 @@ class Exchange {
116
116
  privateKey;
117
117
  api;
118
118
  serverManager;
119
+ initPromise;
119
120
  constructor(exchangeName, options = {}) {
120
121
  this.exchangeName = exchangeName.toLowerCase();
121
122
  this.apiKey = options.apiKey;
122
123
  this.privateKey = options.privateKey;
123
- const baseUrl = options.baseUrl || "http://localhost:3847";
124
+ let baseUrl = options.baseUrl || "http://localhost:3847";
124
125
  const autoStartServer = options.autoStartServer !== false;
125
126
  // Initialize server manager
126
127
  this.serverManager = new server_manager_js_1.ServerManager({ baseUrl });
127
- // Ensure server is running (unless disabled)
128
+ // Configure the API client with the initial base URL (will be updated if port changes)
129
+ const config = new index_js_1.Configuration({ basePath: baseUrl });
130
+ this.api = new index_js_1.DefaultApi(config);
131
+ // Initialize the server connection asynchronously
132
+ this.initPromise = this.initializeServer(autoStartServer);
133
+ }
134
+ async initializeServer(autoStartServer) {
128
135
  if (autoStartServer) {
129
- this.serverManager.ensureServerRunning().catch((error) => {
136
+ try {
137
+ await this.serverManager.ensureServerRunning();
138
+ // Get the actual port the server is running on
139
+ // (may differ from default if default port was busy)
140
+ const actualPort = this.serverManager.getRunningPort();
141
+ const newBaseUrl = `http://localhost:${actualPort}`;
142
+ // Update API client with actual base URL
143
+ const newConfig = new index_js_1.Configuration({ basePath: newBaseUrl });
144
+ this.api = new index_js_1.DefaultApi(newConfig);
145
+ }
146
+ catch (error) {
130
147
  throw new Error(`Failed to start PMXT server: ${error}\n\n` +
131
148
  `Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
132
149
  `Or start the server manually: pmxt-server`);
133
- });
150
+ }
134
151
  }
135
- // Configure the API client
136
- const config = new index_js_1.Configuration({ basePath: baseUrl });
137
- this.api = new index_js_1.DefaultApi(config);
138
152
  }
139
153
  handleResponse(response) {
140
154
  if (!response.success) {
@@ -165,6 +179,7 @@ class Exchange {
165
179
  * ```
166
180
  */
167
181
  async fetchMarkets(params) {
182
+ await this.initPromise;
168
183
  try {
169
184
  const args = [];
170
185
  if (params) {
@@ -198,6 +213,7 @@ class Exchange {
198
213
  * ```
199
214
  */
200
215
  async searchMarkets(query, params) {
216
+ await this.initPromise;
201
217
  try {
202
218
  const args = [query];
203
219
  if (params) {
@@ -234,6 +250,7 @@ class Exchange {
234
250
  * ```
235
251
  */
236
252
  async getMarketsBySlug(slug) {
253
+ await this.initPromise;
237
254
  try {
238
255
  const requestBody = {
239
256
  args: [slug],
@@ -272,6 +289,7 @@ class Exchange {
272
289
  * ```
273
290
  */
274
291
  async fetchOHLCV(outcomeId, params) {
292
+ await this.initPromise;
275
293
  try {
276
294
  const paramsDict = { resolution: params.resolution };
277
295
  if (params.start) {
@@ -312,6 +330,7 @@ class Exchange {
312
330
  * ```
313
331
  */
314
332
  async fetchOrderBook(outcomeId) {
333
+ await this.initPromise;
315
334
  try {
316
335
  const requestBody = {
317
336
  args: [outcomeId],
@@ -338,6 +357,7 @@ class Exchange {
338
357
  * @returns List of trades
339
358
  */
340
359
  async fetchTrades(outcomeId, params) {
360
+ await this.initPromise;
341
361
  try {
342
362
  const paramsDict = { resolution: params.resolution };
343
363
  if (params.limit) {
@@ -378,6 +398,7 @@ class Exchange {
378
398
  * ```
379
399
  */
380
400
  async createOrder(params) {
401
+ await this.initPromise;
381
402
  try {
382
403
  const paramsDict = {
383
404
  marketId: params.marketId,
@@ -411,6 +432,7 @@ class Exchange {
411
432
  * @returns Cancelled order
412
433
  */
413
434
  async cancelOrder(orderId) {
435
+ await this.initPromise;
414
436
  try {
415
437
  const requestBody = {
416
438
  args: [orderId],
@@ -434,6 +456,7 @@ class Exchange {
434
456
  * @returns Order details
435
457
  */
436
458
  async fetchOrder(orderId) {
459
+ await this.initPromise;
437
460
  try {
438
461
  const requestBody = {
439
462
  args: [orderId],
@@ -457,6 +480,7 @@ class Exchange {
457
480
  * @returns List of open orders
458
481
  */
459
482
  async fetchOpenOrders(marketId) {
483
+ await this.initPromise;
460
484
  try {
461
485
  const args = [];
462
486
  if (marketId) {
@@ -484,6 +508,7 @@ class Exchange {
484
508
  * @returns List of positions
485
509
  */
486
510
  async fetchPositions() {
511
+ await this.initPromise;
487
512
  try {
488
513
  const requestBody = {
489
514
  args: [],
@@ -506,6 +531,7 @@ class Exchange {
506
531
  * @returns List of balances (by currency)
507
532
  */
508
533
  async fetchBalance() {
534
+ await this.initPromise;
509
535
  try {
510
536
  const requestBody = {
511
537
  args: [],
@@ -13,7 +13,23 @@ export declare class ServerManager {
13
13
  private maxRetries;
14
14
  private retryDelayMs;
15
15
  private api;
16
+ private lockPath;
17
+ private static readonly DEFAULT_PORT;
16
18
  constructor(options?: ServerManagerOptions);
19
+ /**
20
+ * Read server information from lock file.
21
+ */
22
+ private getServerInfo;
23
+ /**
24
+ * Get the actual port the server is running on.
25
+ *
26
+ * This reads the lock file to determine the actual port,
27
+ * which may differ from the default if the default port was busy.
28
+ */
29
+ getRunningPort(): number;
30
+ /**
31
+ * Check if the server is running.
32
+ */
17
33
  /**
18
34
  * Check if the server is running.
19
35
  */
@@ -40,25 +40,67 @@ var __importStar = (this && this.__importStar) || (function () {
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
41
  exports.ServerManager = void 0;
42
42
  const index_js_1 = require("../generated/src/index.js");
43
+ const fs_1 = require("fs");
44
+ const os_1 = require("os");
45
+ const path_1 = require("path");
43
46
  class ServerManager {
44
47
  baseUrl;
45
48
  maxRetries;
46
49
  retryDelayMs;
47
50
  api;
51
+ lockPath;
52
+ static DEFAULT_PORT = 3847;
48
53
  constructor(options = {}) {
49
- this.baseUrl = options.baseUrl || "http://localhost:3847";
54
+ this.baseUrl = options.baseUrl || `http://localhost:${ServerManager.DEFAULT_PORT}`;
50
55
  this.maxRetries = options.maxRetries || 30;
51
56
  this.retryDelayMs = options.retryDelayMs || 1000;
57
+ this.lockPath = (0, path_1.join)((0, os_1.homedir)(), '.pmxt', 'server.lock');
52
58
  const config = new index_js_1.Configuration({ basePath: this.baseUrl });
53
59
  this.api = new index_js_1.DefaultApi(config);
54
60
  }
61
+ /**
62
+ * Read server information from lock file.
63
+ */
64
+ getServerInfo() {
65
+ try {
66
+ if (!(0, fs_1.existsSync)(this.lockPath)) {
67
+ return null;
68
+ }
69
+ const content = (0, fs_1.readFileSync)(this.lockPath, 'utf-8');
70
+ return JSON.parse(content);
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ /**
77
+ * Get the actual port the server is running on.
78
+ *
79
+ * This reads the lock file to determine the actual port,
80
+ * which may differ from the default if the default port was busy.
81
+ */
82
+ getRunningPort() {
83
+ const info = this.getServerInfo();
84
+ return info?.port || ServerManager.DEFAULT_PORT;
85
+ }
86
+ /**
87
+ * Check if the server is running.
88
+ */
55
89
  /**
56
90
  * Check if the server is running.
57
91
  */
58
92
  async isServerRunning() {
93
+ // Read lock file to get current port
94
+ const port = this.getRunningPort();
59
95
  try {
60
- const response = await this.api.healthCheck();
61
- return response.status === "ok";
96
+ // Use native fetch to check health on the actual running port
97
+ // This avoids issues where this.api is configured with the wrong port
98
+ const response = await fetch(`http://localhost:${port}/health`);
99
+ if (response.ok) {
100
+ const data = await response.json();
101
+ return data.status === "ok";
102
+ }
103
+ return false;
62
104
  }
63
105
  catch (error) {
64
106
  return false;
@@ -84,10 +126,27 @@ class ServerManager {
84
126
  if (await this.isServerRunning()) {
85
127
  return;
86
128
  }
129
+ // Locate pmxt-ensure-server
130
+ let launcherPath = 'pmxt-ensure-server'; // Default to PATH
131
+ try {
132
+ // Try to resolve from pmxt-core dependency
133
+ // For CommonJS build (which is primary), we can use require directly
134
+ // For ESM build, this will be transpiled appropriately
135
+ const corePackageJson = require.resolve('pmxt-core/package.json');
136
+ const coreDir = (0, path_1.dirname)(corePackageJson);
137
+ const binPath = (0, path_1.join)(coreDir, 'bin', 'pmxt-ensure-server');
138
+ if ((0, fs_1.existsSync)(binPath)) {
139
+ launcherPath = binPath;
140
+ }
141
+ }
142
+ catch (error) {
143
+ // If resolution fails, fall back to PATH
144
+ // This could happen in dev environments where pmxt-core is globally installed
145
+ }
87
146
  // Try to start the server using pmxt-ensure-server
88
147
  const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
89
148
  try {
90
- const proc = spawn("pmxt-ensure-server", [], {
149
+ const proc = spawn(launcherPath, [], {
91
150
  detached: true,
92
151
  stdio: "ignore",
93
152
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "OpenAPI client for pmxtjs",
5
5
  "author": "OpenAPI-Generator",
6
6
  "repository": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "Unified prediction market data API - The ccxt for prediction markets",
5
5
  "author": "PMXT Contributors",
6
6
  "repository": {
@@ -41,6 +41,9 @@
41
41
  "api",
42
42
  "unified"
43
43
  ],
44
+ "dependencies": {
45
+ "pmxt-core": "1.0.3"
46
+ },
44
47
  "devDependencies": {
45
48
  "@types/jest": "^30.0.0",
46
49
  "@types/node": "^20.0.0",
package/pmxt/client.ts CHANGED
@@ -172,32 +172,48 @@ export abstract class Exchange {
172
172
  protected privateKey?: string;
173
173
  protected api: DefaultApi;
174
174
  protected serverManager: ServerManager;
175
+ protected initPromise: Promise<void>;
175
176
 
176
177
  constructor(exchangeName: string, options: ExchangeOptions = {}) {
177
178
  this.exchangeName = exchangeName.toLowerCase();
178
179
  this.apiKey = options.apiKey;
179
180
  this.privateKey = options.privateKey;
180
181
 
181
- const baseUrl = options.baseUrl || "http://localhost:3847";
182
+ let baseUrl = options.baseUrl || "http://localhost:3847";
182
183
  const autoStartServer = options.autoStartServer !== false;
183
184
 
184
185
  // Initialize server manager
185
186
  this.serverManager = new ServerManager({ baseUrl });
186
187
 
187
- // Ensure server is running (unless disabled)
188
+ // Configure the API client with the initial base URL (will be updated if port changes)
189
+ const config = new Configuration({ basePath: baseUrl });
190
+ this.api = new DefaultApi(config);
191
+
192
+ // Initialize the server connection asynchronously
193
+ this.initPromise = this.initializeServer(autoStartServer);
194
+ }
195
+
196
+ private async initializeServer(autoStartServer: boolean): Promise<void> {
188
197
  if (autoStartServer) {
189
- this.serverManager.ensureServerRunning().catch((error) => {
198
+ try {
199
+ await this.serverManager.ensureServerRunning();
200
+
201
+ // Get the actual port the server is running on
202
+ // (may differ from default if default port was busy)
203
+ const actualPort = this.serverManager.getRunningPort();
204
+ const newBaseUrl = `http://localhost:${actualPort}`;
205
+
206
+ // Update API client with actual base URL
207
+ const newConfig = new Configuration({ basePath: newBaseUrl });
208
+ this.api = new DefaultApi(newConfig);
209
+ } catch (error) {
190
210
  throw new Error(
191
211
  `Failed to start PMXT server: ${error}\n\n` +
192
212
  `Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
193
213
  `Or start the server manually: pmxt-server`
194
214
  );
195
- });
215
+ }
196
216
  }
197
-
198
- // Configure the API client
199
- const config = new Configuration({ basePath: baseUrl });
200
- this.api = new DefaultApi(config);
201
217
  }
202
218
 
203
219
  protected handleResponse(response: any): any {
@@ -232,6 +248,7 @@ export abstract class Exchange {
232
248
  * ```
233
249
  */
234
250
  async fetchMarkets(params?: MarketFilterParams): Promise<UnifiedMarket[]> {
251
+ await this.initPromise;
235
252
  try {
236
253
  const args: any[] = [];
237
254
  if (params) {
@@ -271,6 +288,7 @@ export abstract class Exchange {
271
288
  query: string,
272
289
  params?: MarketFilterParams
273
290
  ): Promise<UnifiedMarket[]> {
291
+ await this.initPromise;
274
292
  try {
275
293
  const args: any[] = [query];
276
294
  if (params) {
@@ -310,6 +328,7 @@ export abstract class Exchange {
310
328
  * ```
311
329
  */
312
330
  async getMarketsBySlug(slug: string): Promise<UnifiedMarket[]> {
331
+ await this.initPromise;
313
332
  try {
314
333
  const requestBody: GetMarketsBySlugRequest = {
315
334
  args: [slug],
@@ -353,6 +372,7 @@ export abstract class Exchange {
353
372
  outcomeId: string,
354
373
  params: HistoryFilterParams
355
374
  ): Promise<PriceCandle[]> {
375
+ await this.initPromise;
356
376
  try {
357
377
  const paramsDict: any = { resolution: params.resolution };
358
378
  if (params.start) {
@@ -396,6 +416,7 @@ export abstract class Exchange {
396
416
  * ```
397
417
  */
398
418
  async fetchOrderBook(outcomeId: string): Promise<OrderBook> {
419
+ await this.initPromise;
399
420
  try {
400
421
  const requestBody: FetchOrderBookRequest = {
401
422
  args: [outcomeId],
@@ -427,6 +448,7 @@ export abstract class Exchange {
427
448
  outcomeId: string,
428
449
  params: HistoryFilterParams
429
450
  ): Promise<Trade[]> {
451
+ await this.initPromise;
430
452
  try {
431
453
  const paramsDict: any = { resolution: params.resolution };
432
454
  if (params.limit) {
@@ -471,6 +493,7 @@ export abstract class Exchange {
471
493
  * ```
472
494
  */
473
495
  async createOrder(params: CreateOrderParams): Promise<Order> {
496
+ await this.initPromise;
474
497
  try {
475
498
  const paramsDict: any = {
476
499
  marketId: params.marketId,
@@ -507,6 +530,7 @@ export abstract class Exchange {
507
530
  * @returns Cancelled order
508
531
  */
509
532
  async cancelOrder(orderId: string): Promise<Order> {
533
+ await this.initPromise;
510
534
  try {
511
535
  const requestBody: CancelOrderRequest = {
512
536
  args: [orderId],
@@ -532,6 +556,7 @@ export abstract class Exchange {
532
556
  * @returns Order details
533
557
  */
534
558
  async fetchOrder(orderId: string): Promise<Order> {
559
+ await this.initPromise;
535
560
  try {
536
561
  const requestBody: CancelOrderRequest = {
537
562
  args: [orderId],
@@ -557,6 +582,7 @@ export abstract class Exchange {
557
582
  * @returns List of open orders
558
583
  */
559
584
  async fetchOpenOrders(marketId?: string): Promise<Order[]> {
585
+ await this.initPromise;
560
586
  try {
561
587
  const args: any[] = [];
562
588
  if (marketId) {
@@ -588,6 +614,7 @@ export abstract class Exchange {
588
614
  * @returns List of positions
589
615
  */
590
616
  async fetchPositions(): Promise<Position[]> {
617
+ await this.initPromise;
591
618
  try {
592
619
  const requestBody: FetchPositionsRequest = {
593
620
  args: [],
@@ -612,6 +639,7 @@ export abstract class Exchange {
612
639
  * @returns List of balances (by currency)
613
640
  */
614
641
  async fetchBalance(): Promise<Balance[]> {
642
+ await this.initPromise;
615
643
  try {
616
644
  const requestBody: FetchPositionsRequest = {
617
645
  args: [],
@@ -5,6 +5,9 @@
5
5
  */
6
6
 
7
7
  import { DefaultApi, Configuration } from "../generated/src/index.js";
8
+ import { readFileSync, existsSync } from "fs";
9
+ import { homedir } from "os";
10
+ import { join, dirname } from "path";
8
11
 
9
12
  export interface ServerManagerOptions {
10
13
  baseUrl?: string;
@@ -12,28 +15,75 @@ export interface ServerManagerOptions {
12
15
  retryDelayMs?: number;
13
16
  }
14
17
 
18
+ interface ServerLockInfo {
19
+ port: number;
20
+ pid: number;
21
+ timestamp: number;
22
+ }
23
+
15
24
  export class ServerManager {
16
25
  private baseUrl: string;
17
26
  private maxRetries: number;
18
27
  private retryDelayMs: number;
19
28
  private api: DefaultApi;
29
+ private lockPath: string;
30
+ private static readonly DEFAULT_PORT = 3847;
20
31
 
21
32
  constructor(options: ServerManagerOptions = {}) {
22
- this.baseUrl = options.baseUrl || "http://localhost:3847";
33
+ this.baseUrl = options.baseUrl || `http://localhost:${ServerManager.DEFAULT_PORT}`;
23
34
  this.maxRetries = options.maxRetries || 30;
24
35
  this.retryDelayMs = options.retryDelayMs || 1000;
36
+ this.lockPath = join(homedir(), '.pmxt', 'server.lock');
25
37
 
26
38
  const config = new Configuration({ basePath: this.baseUrl });
27
39
  this.api = new DefaultApi(config);
28
40
  }
29
41
 
42
+ /**
43
+ * Read server information from lock file.
44
+ */
45
+ private getServerInfo(): ServerLockInfo | null {
46
+ try {
47
+ if (!existsSync(this.lockPath)) {
48
+ return null;
49
+ }
50
+ const content = readFileSync(this.lockPath, 'utf-8');
51
+ return JSON.parse(content) as ServerLockInfo;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Get the actual port the server is running on.
59
+ *
60
+ * This reads the lock file to determine the actual port,
61
+ * which may differ from the default if the default port was busy.
62
+ */
63
+ getRunningPort(): number {
64
+ const info = this.getServerInfo();
65
+ return info?.port || ServerManager.DEFAULT_PORT;
66
+ }
67
+
68
+ /**
69
+ * Check if the server is running.
70
+ */
30
71
  /**
31
72
  * Check if the server is running.
32
73
  */
33
74
  async isServerRunning(): Promise<boolean> {
75
+ // Read lock file to get current port
76
+ const port = this.getRunningPort();
77
+
34
78
  try {
35
- const response = await this.api.healthCheck();
36
- return response.status === "ok";
79
+ // Use native fetch to check health on the actual running port
80
+ // This avoids issues where this.api is configured with the wrong port
81
+ const response = await fetch(`http://localhost:${port}/health`);
82
+ if (response.ok) {
83
+ const data = await response.json();
84
+ return (data as any).status === "ok";
85
+ }
86
+ return false;
37
87
  } catch (error) {
38
88
  return false;
39
89
  }
@@ -63,11 +113,30 @@ export class ServerManager {
63
113
  return;
64
114
  }
65
115
 
116
+ // Locate pmxt-ensure-server
117
+ let launcherPath = 'pmxt-ensure-server'; // Default to PATH
118
+
119
+ try {
120
+ // Try to resolve from pmxt-core dependency
121
+ // For CommonJS build (which is primary), we can use require directly
122
+ // For ESM build, this will be transpiled appropriately
123
+ const corePackageJson = require.resolve('pmxt-core/package.json');
124
+ const coreDir = dirname(corePackageJson);
125
+ const binPath = join(coreDir, 'bin', 'pmxt-ensure-server');
126
+
127
+ if (existsSync(binPath)) {
128
+ launcherPath = binPath;
129
+ }
130
+ } catch (error) {
131
+ // If resolution fails, fall back to PATH
132
+ // This could happen in dev environments where pmxt-core is globally installed
133
+ }
134
+
66
135
  // Try to start the server using pmxt-ensure-server
67
136
  const { spawn } = await import("child_process");
68
137
 
69
138
  try {
70
- const proc = spawn("pmxt-ensure-server", [], {
139
+ const proc = spawn(launcherPath, [], {
71
140
  detached: true,
72
141
  stdio: "ignore",
73
142
  });
@@ -84,3 +153,4 @@ export class ServerManager {
84
153
  }
85
154
  }
86
155
  }
156
+