pmxtjs 1.0.3 → 1.1.0-b3
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/dist/esm/generated/src/apis/DefaultApi.d.ts +45 -1
- package/dist/esm/generated/src/apis/DefaultApi.js +75 -1
- package/dist/esm/generated/src/models/WatchOrderBookRequest.d.ts +40 -0
- package/dist/esm/generated/src/models/WatchOrderBookRequest.js +47 -0
- package/dist/esm/generated/src/models/WatchOrderBookRequestArgsInner.d.ts +21 -0
- package/dist/esm/generated/src/models/WatchOrderBookRequestArgsInner.js +43 -0
- package/dist/esm/generated/src/models/WatchTradesRequest.d.ts +40 -0
- package/dist/esm/generated/src/models/WatchTradesRequest.js +47 -0
- package/dist/esm/generated/src/models/index.d.ts +3 -0
- package/dist/esm/generated/src/models/index.js +3 -0
- package/dist/esm/pmxt/client.d.ts +44 -0
- package/dist/esm/pmxt/client.js +99 -1
- package/dist/esm/pmxt/server-manager.d.ts +4 -1
- package/dist/esm/pmxt/server-manager.js +68 -3
- package/dist/generated/src/apis/DefaultApi.d.ts +45 -1
- package/dist/generated/src/apis/DefaultApi.js +75 -1
- package/dist/generated/src/models/WatchOrderBookRequest.d.ts +40 -0
- package/dist/generated/src/models/WatchOrderBookRequest.js +54 -0
- package/dist/generated/src/models/WatchOrderBookRequestArgsInner.d.ts +21 -0
- package/dist/generated/src/models/WatchOrderBookRequestArgsInner.js +49 -0
- package/dist/generated/src/models/WatchTradesRequest.d.ts +40 -0
- package/dist/generated/src/models/WatchTradesRequest.js +54 -0
- package/dist/generated/src/models/index.d.ts +3 -0
- package/dist/generated/src/models/index.js +3 -0
- package/dist/pmxt/client.d.ts +44 -0
- package/dist/pmxt/client.js +99 -1
- package/dist/pmxt/server-manager.d.ts +4 -1
- package/dist/pmxt/server-manager.js +68 -3
- package/generated/.openapi-generator/FILES +6 -0
- package/generated/docs/DefaultApi.md +142 -0
- package/generated/docs/WatchOrderBookRequest.md +36 -0
- package/generated/docs/WatchOrderBookRequestArgsInner.md +32 -0
- package/generated/docs/WatchTradesRequest.md +36 -0
- package/generated/package.json +1 -1
- package/generated/src/apis/DefaultApi.ts +116 -0
- package/generated/src/models/WatchOrderBookRequest.ts +89 -0
- package/generated/src/models/WatchOrderBookRequestArgsInner.ts +55 -0
- package/generated/src/models/WatchTradesRequest.ts +89 -0
- package/generated/src/models/index.ts +3 -0
- package/package.json +2 -2
- package/pmxt/client.ts +111 -1
- package/pmxt/server-manager.ts +71 -4
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/**
|
|
4
|
+
* PMXT Sidecar API
|
|
5
|
+
* A unified local sidecar API for prediction markets (Polymarket, Kalshi). This API acts as a JSON-RPC-style gateway. Each endpoint corresponds to a specific method on the generic exchange implementation.
|
|
6
|
+
*
|
|
7
|
+
* The version of the OpenAPI document: 0.4.4
|
|
8
|
+
*
|
|
9
|
+
*
|
|
10
|
+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
11
|
+
* https://openapi-generator.tech
|
|
12
|
+
* Do not edit the class manually.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { mapValues } from '../runtime';
|
|
16
|
+
import type { ExchangeCredentials } from './ExchangeCredentials';
|
|
17
|
+
import {
|
|
18
|
+
ExchangeCredentialsFromJSON,
|
|
19
|
+
ExchangeCredentialsFromJSONTyped,
|
|
20
|
+
ExchangeCredentialsToJSON,
|
|
21
|
+
ExchangeCredentialsToJSONTyped,
|
|
22
|
+
} from './ExchangeCredentials';
|
|
23
|
+
import type { WatchOrderBookRequestArgsInner } from './WatchOrderBookRequestArgsInner';
|
|
24
|
+
import {
|
|
25
|
+
WatchOrderBookRequestArgsInnerFromJSON,
|
|
26
|
+
WatchOrderBookRequestArgsInnerFromJSONTyped,
|
|
27
|
+
WatchOrderBookRequestArgsInnerToJSON,
|
|
28
|
+
WatchOrderBookRequestArgsInnerToJSONTyped,
|
|
29
|
+
} from './WatchOrderBookRequestArgsInner';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @export
|
|
34
|
+
* @interface WatchOrderBookRequest
|
|
35
|
+
*/
|
|
36
|
+
export interface WatchOrderBookRequest {
|
|
37
|
+
/**
|
|
38
|
+
* [outcomeId, limit?]
|
|
39
|
+
* @type {Array<WatchOrderBookRequestArgsInner>}
|
|
40
|
+
* @memberof WatchOrderBookRequest
|
|
41
|
+
*/
|
|
42
|
+
args: Array<WatchOrderBookRequestArgsInner>;
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
* @type {ExchangeCredentials}
|
|
46
|
+
* @memberof WatchOrderBookRequest
|
|
47
|
+
*/
|
|
48
|
+
credentials?: ExchangeCredentials;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a given object implements the WatchOrderBookRequest interface.
|
|
53
|
+
*/
|
|
54
|
+
export function instanceOfWatchOrderBookRequest(value: object): value is WatchOrderBookRequest {
|
|
55
|
+
if (!('args' in value) || value['args'] === undefined) return false;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function WatchOrderBookRequestFromJSON(json: any): WatchOrderBookRequest {
|
|
60
|
+
return WatchOrderBookRequestFromJSONTyped(json, false);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function WatchOrderBookRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): WatchOrderBookRequest {
|
|
64
|
+
if (json == null) {
|
|
65
|
+
return json;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
|
|
69
|
+
'args': ((json['args'] as Array<any>).map(WatchOrderBookRequestArgsInnerFromJSON)),
|
|
70
|
+
'credentials': json['credentials'] == null ? undefined : ExchangeCredentialsFromJSON(json['credentials']),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function WatchOrderBookRequestToJSON(json: any): WatchOrderBookRequest {
|
|
75
|
+
return WatchOrderBookRequestToJSONTyped(json, false);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function WatchOrderBookRequestToJSONTyped(value?: WatchOrderBookRequest | null, ignoreDiscriminator: boolean = false): any {
|
|
79
|
+
if (value == null) {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
|
|
85
|
+
'args': ((value['args'] as Array<any>).map(WatchOrderBookRequestArgsInnerToJSON)),
|
|
86
|
+
'credentials': ExchangeCredentialsToJSON(value['credentials']),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/**
|
|
4
|
+
* PMXT Sidecar API
|
|
5
|
+
* A unified local sidecar API for prediction markets (Polymarket, Kalshi). This API acts as a JSON-RPC-style gateway. Each endpoint corresponds to a specific method on the generic exchange implementation.
|
|
6
|
+
*
|
|
7
|
+
* The version of the OpenAPI document: 0.4.4
|
|
8
|
+
*
|
|
9
|
+
*
|
|
10
|
+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
11
|
+
* https://openapi-generator.tech
|
|
12
|
+
* Do not edit the class manually.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @type WatchOrderBookRequestArgsInner
|
|
17
|
+
*
|
|
18
|
+
* @export
|
|
19
|
+
*/
|
|
20
|
+
export type WatchOrderBookRequestArgsInner = number | string;
|
|
21
|
+
|
|
22
|
+
export function WatchOrderBookRequestArgsInnerFromJSON(json: any): WatchOrderBookRequestArgsInner {
|
|
23
|
+
return WatchOrderBookRequestArgsInnerFromJSONTyped(json, false);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function WatchOrderBookRequestArgsInnerFromJSONTyped(json: any, ignoreDiscriminator: boolean): WatchOrderBookRequestArgsInner {
|
|
27
|
+
if (json == null) {
|
|
28
|
+
return json;
|
|
29
|
+
}
|
|
30
|
+
if (typeof json === 'number') {
|
|
31
|
+
return json;
|
|
32
|
+
}
|
|
33
|
+
if (typeof json === 'string') {
|
|
34
|
+
return json;
|
|
35
|
+
}
|
|
36
|
+
return {} as any;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function WatchOrderBookRequestArgsInnerToJSON(json: any): any {
|
|
40
|
+
return WatchOrderBookRequestArgsInnerToJSONTyped(json, false);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function WatchOrderBookRequestArgsInnerToJSONTyped(value?: WatchOrderBookRequestArgsInner | null, ignoreDiscriminator: boolean = false): any {
|
|
44
|
+
if (value == null) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
if (typeof value === 'number') {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
if (typeof value === 'string') {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/**
|
|
4
|
+
* PMXT Sidecar API
|
|
5
|
+
* A unified local sidecar API for prediction markets (Polymarket, Kalshi). This API acts as a JSON-RPC-style gateway. Each endpoint corresponds to a specific method on the generic exchange implementation.
|
|
6
|
+
*
|
|
7
|
+
* The version of the OpenAPI document: 0.4.4
|
|
8
|
+
*
|
|
9
|
+
*
|
|
10
|
+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
11
|
+
* https://openapi-generator.tech
|
|
12
|
+
* Do not edit the class manually.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { mapValues } from '../runtime';
|
|
16
|
+
import type { ExchangeCredentials } from './ExchangeCredentials';
|
|
17
|
+
import {
|
|
18
|
+
ExchangeCredentialsFromJSON,
|
|
19
|
+
ExchangeCredentialsFromJSONTyped,
|
|
20
|
+
ExchangeCredentialsToJSON,
|
|
21
|
+
ExchangeCredentialsToJSONTyped,
|
|
22
|
+
} from './ExchangeCredentials';
|
|
23
|
+
import type { WatchOrderBookRequestArgsInner } from './WatchOrderBookRequestArgsInner';
|
|
24
|
+
import {
|
|
25
|
+
WatchOrderBookRequestArgsInnerFromJSON,
|
|
26
|
+
WatchOrderBookRequestArgsInnerFromJSONTyped,
|
|
27
|
+
WatchOrderBookRequestArgsInnerToJSON,
|
|
28
|
+
WatchOrderBookRequestArgsInnerToJSONTyped,
|
|
29
|
+
} from './WatchOrderBookRequestArgsInner';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @export
|
|
34
|
+
* @interface WatchTradesRequest
|
|
35
|
+
*/
|
|
36
|
+
export interface WatchTradesRequest {
|
|
37
|
+
/**
|
|
38
|
+
* [outcomeId, since?, limit?]
|
|
39
|
+
* @type {Array<WatchOrderBookRequestArgsInner>}
|
|
40
|
+
* @memberof WatchTradesRequest
|
|
41
|
+
*/
|
|
42
|
+
args: Array<WatchOrderBookRequestArgsInner>;
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
* @type {ExchangeCredentials}
|
|
46
|
+
* @memberof WatchTradesRequest
|
|
47
|
+
*/
|
|
48
|
+
credentials?: ExchangeCredentials;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a given object implements the WatchTradesRequest interface.
|
|
53
|
+
*/
|
|
54
|
+
export function instanceOfWatchTradesRequest(value: object): value is WatchTradesRequest {
|
|
55
|
+
if (!('args' in value) || value['args'] === undefined) return false;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function WatchTradesRequestFromJSON(json: any): WatchTradesRequest {
|
|
60
|
+
return WatchTradesRequestFromJSONTyped(json, false);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function WatchTradesRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): WatchTradesRequest {
|
|
64
|
+
if (json == null) {
|
|
65
|
+
return json;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
|
|
69
|
+
'args': ((json['args'] as Array<any>).map(WatchOrderBookRequestArgsInnerFromJSON)),
|
|
70
|
+
'credentials': json['credentials'] == null ? undefined : ExchangeCredentialsFromJSON(json['credentials']),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function WatchTradesRequestToJSON(json: any): WatchTradesRequest {
|
|
75
|
+
return WatchTradesRequestToJSONTyped(json, false);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function WatchTradesRequestToJSONTyped(value?: WatchTradesRequest | null, ignoreDiscriminator: boolean = false): any {
|
|
79
|
+
if (value == null) {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
|
|
85
|
+
'args': ((value['args'] as Array<any>).map(WatchOrderBookRequestArgsInnerToJSON)),
|
|
86
|
+
'credentials': ExchangeCredentialsToJSON(value['credentials']),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
@@ -38,3 +38,6 @@ export * from './SearchMarketsRequest';
|
|
|
38
38
|
export * from './SearchMarketsRequestArgsInner';
|
|
39
39
|
export * from './Trade';
|
|
40
40
|
export * from './UnifiedMarket';
|
|
41
|
+
export * from './WatchOrderBookRequest';
|
|
42
|
+
export * from './WatchOrderBookRequestArgsInner';
|
|
43
|
+
export * from './WatchTradesRequest';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxtjs",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0-b3",
|
|
4
4
|
"description": "Unified prediction market data API - The ccxt for prediction markets",
|
|
5
5
|
"author": "PMXT Contributors",
|
|
6
6
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"unified"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"pmxt-core": "1.0
|
|
45
|
+
"pmxt-core": "1.1.0-b3"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/jest": "^30.0.0",
|
package/pmxt/client.ts
CHANGED
|
@@ -203,8 +203,17 @@ export abstract class Exchange {
|
|
|
203
203
|
const actualPort = this.serverManager.getRunningPort();
|
|
204
204
|
const newBaseUrl = `http://localhost:${actualPort}`;
|
|
205
205
|
|
|
206
|
+
const accessToken = this.serverManager.getAccessToken();
|
|
207
|
+
const headers: any = {};
|
|
208
|
+
if (accessToken) {
|
|
209
|
+
headers['x-pmxt-access-token'] = accessToken;
|
|
210
|
+
}
|
|
211
|
+
|
|
206
212
|
// Update API client with actual base URL
|
|
207
|
-
const newConfig = new Configuration({
|
|
213
|
+
const newConfig = new Configuration({
|
|
214
|
+
basePath: newBaseUrl,
|
|
215
|
+
headers
|
|
216
|
+
});
|
|
208
217
|
this.api = new DefaultApi(newConfig);
|
|
209
218
|
} catch (error) {
|
|
210
219
|
throw new Error(
|
|
@@ -472,6 +481,107 @@ export abstract class Exchange {
|
|
|
472
481
|
}
|
|
473
482
|
}
|
|
474
483
|
|
|
484
|
+
// WebSocket Streaming Methods
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Watch real-time order book updates via WebSocket.
|
|
488
|
+
*
|
|
489
|
+
* Returns a promise that resolves with the next order book update.
|
|
490
|
+
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
491
|
+
*
|
|
492
|
+
* @param outcomeId - Outcome ID to watch
|
|
493
|
+
* @param limit - Optional depth limit for order book
|
|
494
|
+
* @returns Next order book update
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```typescript
|
|
498
|
+
* // Stream order book updates
|
|
499
|
+
* while (true) {
|
|
500
|
+
* const orderBook = await exchange.watchOrderBook(outcomeId);
|
|
501
|
+
* console.log(`Best bid: ${orderBook.bids[0].price}`);
|
|
502
|
+
* console.log(`Best ask: ${orderBook.asks[0].price}`);
|
|
503
|
+
* }
|
|
504
|
+
* ```
|
|
505
|
+
*/
|
|
506
|
+
async watchOrderBook(outcomeId: string, limit?: number): Promise<OrderBook> {
|
|
507
|
+
await this.initPromise;
|
|
508
|
+
try {
|
|
509
|
+
const args: any[] = [outcomeId];
|
|
510
|
+
if (limit !== undefined) {
|
|
511
|
+
args.push(limit);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const requestBody: any = {
|
|
515
|
+
args,
|
|
516
|
+
credentials: this.getCredentials()
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const response = await this.api.watchOrderBook({
|
|
520
|
+
exchange: this.exchangeName as any,
|
|
521
|
+
watchOrderBookRequest: requestBody,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
const data = this.handleResponse(response);
|
|
525
|
+
return convertOrderBook(data);
|
|
526
|
+
} catch (error) {
|
|
527
|
+
throw new Error(`Failed to watch order book: ${error}`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Watch real-time trade updates via WebSocket.
|
|
533
|
+
*
|
|
534
|
+
* Returns a promise that resolves with the next trade(s).
|
|
535
|
+
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
536
|
+
*
|
|
537
|
+
* @param outcomeId - Outcome ID to watch
|
|
538
|
+
* @param since - Optional timestamp to filter trades from
|
|
539
|
+
* @param limit - Optional limit for number of trades
|
|
540
|
+
* @returns Next trade update(s)
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```typescript
|
|
544
|
+
* // Stream trade updates
|
|
545
|
+
* while (true) {
|
|
546
|
+
* const trades = await exchange.watchTrades(outcomeId);
|
|
547
|
+
* for (const trade of trades) {
|
|
548
|
+
* console.log(`Trade: ${trade.price} @ ${trade.amount}`);
|
|
549
|
+
* }
|
|
550
|
+
* }
|
|
551
|
+
* ```
|
|
552
|
+
*/
|
|
553
|
+
async watchTrades(
|
|
554
|
+
outcomeId: string,
|
|
555
|
+
since?: number,
|
|
556
|
+
limit?: number
|
|
557
|
+
): Promise<Trade[]> {
|
|
558
|
+
await this.initPromise;
|
|
559
|
+
try {
|
|
560
|
+
const args: any[] = [outcomeId];
|
|
561
|
+
if (since !== undefined) {
|
|
562
|
+
args.push(since);
|
|
563
|
+
}
|
|
564
|
+
if (limit !== undefined) {
|
|
565
|
+
args.push(limit);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const requestBody: any = {
|
|
569
|
+
args,
|
|
570
|
+
credentials: this.getCredentials()
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
const response = await this.api.watchTrades({
|
|
574
|
+
exchange: this.exchangeName as any,
|
|
575
|
+
watchTradesRequest: requestBody,
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
const data = this.handleResponse(response);
|
|
579
|
+
return data.map(convertTrade);
|
|
580
|
+
} catch (error) {
|
|
581
|
+
throw new Error(`Failed to watch trades: ${error}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
475
585
|
// Trading Methods (require authentication)
|
|
476
586
|
|
|
477
587
|
/**
|
package/pmxt/server-manager.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface ServerManagerOptions {
|
|
|
18
18
|
interface ServerLockInfo {
|
|
19
19
|
port: number;
|
|
20
20
|
pid: number;
|
|
21
|
+
accessToken?: string;
|
|
22
|
+
version?: string;
|
|
21
23
|
timestamp: number;
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -66,8 +68,13 @@ export class ServerManager {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
|
-
*
|
|
71
|
+
* Get the access token from the lock file.
|
|
70
72
|
*/
|
|
73
|
+
getAccessToken(): string | undefined {
|
|
74
|
+
const info = this.getServerInfo();
|
|
75
|
+
return info?.accessToken;
|
|
76
|
+
}
|
|
77
|
+
|
|
71
78
|
/**
|
|
72
79
|
* Check if the server is running.
|
|
73
80
|
*/
|
|
@@ -108,9 +115,18 @@ export class ServerManager {
|
|
|
108
115
|
* Ensure the server is running, starting it if necessary.
|
|
109
116
|
*/
|
|
110
117
|
async ensureServerRunning(): Promise<void> {
|
|
111
|
-
// Check
|
|
118
|
+
// Check for force restart
|
|
119
|
+
if (process.env.PMXT_ALWAYS_RESTART === '1') {
|
|
120
|
+
await this.killOldServer();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check if already running and version matches
|
|
112
124
|
if (await this.isServerRunning()) {
|
|
113
|
-
|
|
125
|
+
if (await this.isVersionMismatch()) {
|
|
126
|
+
await this.killOldServer();
|
|
127
|
+
} else {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
114
130
|
}
|
|
115
131
|
|
|
116
132
|
// Locate pmxt-ensure-server
|
|
@@ -152,5 +168,56 @@ export class ServerManager {
|
|
|
152
168
|
);
|
|
153
169
|
}
|
|
154
170
|
}
|
|
155
|
-
}
|
|
156
171
|
|
|
172
|
+
private async isVersionMismatch(): Promise<boolean> {
|
|
173
|
+
const info = this.getServerInfo();
|
|
174
|
+
if (!info || !info.version) {
|
|
175
|
+
return true; // Old server without version
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
// 1. Try to find package.json relative to the installed location (Production)
|
|
180
|
+
let corePackageJsonPath: string | undefined;
|
|
181
|
+
try {
|
|
182
|
+
corePackageJsonPath = require.resolve('pmxt-core/package.json');
|
|
183
|
+
} catch {
|
|
184
|
+
// 2. Try dev path (Monorepo)
|
|
185
|
+
const devPath = join(dirname(__dirname), '../../core/package.json');
|
|
186
|
+
if (existsSync(devPath)) {
|
|
187
|
+
corePackageJsonPath = devPath;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (corePackageJsonPath && existsSync(corePackageJsonPath)) {
|
|
192
|
+
const content = readFileSync(corePackageJsonPath, 'utf-8');
|
|
193
|
+
const pkg = JSON.parse(content);
|
|
194
|
+
// Check if running version starts with package version
|
|
195
|
+
// (Server version might have extra hash in dev mode)
|
|
196
|
+
if (pkg.version && !info.version.startsWith(pkg.version)) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
// Ignore errors
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private async killOldServer(): Promise<void> {
|
|
207
|
+
const info = this.getServerInfo();
|
|
208
|
+
if (info && info.pid) {
|
|
209
|
+
try {
|
|
210
|
+
process.kill(info.pid, 'SIGTERM');
|
|
211
|
+
// Brief wait
|
|
212
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
213
|
+
} catch {
|
|
214
|
+
// Ignore
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Remove lock file (best effort)
|
|
218
|
+
try {
|
|
219
|
+
const { unlinkSync } = await import('fs');
|
|
220
|
+
unlinkSync(this.lockPath);
|
|
221
|
+
} catch { }
|
|
222
|
+
}
|
|
223
|
+
}
|