openbroker 1.3.2 → 1.5.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 +23 -0
- package/dist/auto/audit.d.ts +57 -0
- package/dist/auto/audit.d.ts.map +1 -0
- package/dist/auto/audit.js +407 -0
- package/dist/auto/cli.d.ts +2 -0
- package/dist/auto/cli.d.ts.map +1 -0
- package/dist/auto/cli.js +423 -0
- package/dist/auto/events.d.ts +11 -0
- package/dist/auto/events.d.ts.map +1 -0
- package/dist/auto/events.js +36 -0
- package/dist/auto/examples/dca.d.ts +4 -0
- package/dist/auto/examples/dca.d.ts.map +1 -0
- package/dist/auto/examples/dca.js +60 -0
- package/dist/auto/examples/funding-arb.d.ts +4 -0
- package/dist/auto/examples/funding-arb.d.ts.map +1 -0
- package/dist/auto/examples/funding-arb.js +81 -0
- package/dist/auto/examples/grid.d.ts +4 -0
- package/dist/auto/examples/grid.d.ts.map +1 -0
- package/dist/auto/examples/grid.js +114 -0
- package/dist/auto/examples/mm-maker.d.ts +4 -0
- package/dist/auto/examples/mm-maker.d.ts.map +1 -0
- package/dist/auto/examples/mm-maker.js +131 -0
- package/dist/auto/examples/mm-spread.d.ts +4 -0
- package/dist/auto/examples/mm-spread.d.ts.map +1 -0
- package/dist/auto/examples/mm-spread.js +119 -0
- package/dist/auto/examples/price-alert.d.ts +4 -0
- package/dist/auto/examples/price-alert.d.ts.map +1 -0
- package/dist/auto/examples/price-alert.js +85 -0
- package/dist/auto/keep-awake.d.ts +11 -0
- package/dist/auto/keep-awake.d.ts.map +1 -0
- package/dist/auto/keep-awake.js +70 -0
- package/dist/auto/loader.d.ts +22 -0
- package/dist/auto/loader.d.ts.map +1 -0
- package/dist/auto/loader.js +127 -0
- package/dist/auto/prune.d.ts +40 -0
- package/dist/auto/prune.d.ts.map +1 -0
- package/dist/auto/prune.js +204 -0
- package/dist/auto/registry.d.ts +24 -0
- package/dist/auto/registry.d.ts.map +1 -0
- package/dist/auto/registry.js +93 -0
- package/dist/auto/report.d.ts +3 -0
- package/dist/auto/report.d.ts.map +1 -0
- package/dist/auto/report.js +385 -0
- package/dist/auto/runtime.d.ts +33 -0
- package/dist/auto/runtime.d.ts.map +1 -0
- package/dist/auto/runtime.js +844 -0
- package/dist/auto/types.d.ts +236 -0
- package/dist/auto/types.d.ts.map +1 -0
- package/dist/auto/types.js +3 -0
- package/dist/core/client.d.ts +691 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +2061 -0
- package/dist/core/config.d.ts +22 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +143 -0
- package/dist/core/types.d.ts +228 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/utils.d.ts +61 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +142 -0
- package/dist/core/ws.d.ts +121 -0
- package/dist/core/ws.d.ts.map +1 -0
- package/dist/core/ws.js +222 -0
- package/dist/info/account.d.ts +3 -0
- package/dist/info/account.d.ts.map +1 -0
- package/dist/info/account.js +198 -0
- package/dist/info/all-markets.d.ts +3 -0
- package/dist/info/all-markets.d.ts.map +1 -0
- package/dist/info/all-markets.js +272 -0
- package/dist/info/candles.d.ts +3 -0
- package/dist/info/candles.d.ts.map +1 -0
- package/dist/info/candles.js +120 -0
- package/dist/info/fees.d.ts +3 -0
- package/dist/info/fees.d.ts.map +1 -0
- package/dist/info/fees.js +87 -0
- package/dist/info/fills.d.ts +3 -0
- package/dist/info/fills.d.ts.map +1 -0
- package/dist/info/fills.js +105 -0
- package/dist/info/funding-history.d.ts +3 -0
- package/dist/info/funding-history.d.ts.map +1 -0
- package/dist/info/funding-history.js +98 -0
- package/dist/info/funding-scan.d.ts +3 -0
- package/dist/info/funding-scan.d.ts.map +1 -0
- package/dist/info/funding-scan.js +178 -0
- package/dist/info/funding.d.ts +3 -0
- package/dist/info/funding.d.ts.map +1 -0
- package/dist/info/funding.js +158 -0
- package/dist/info/markets.d.ts +3 -0
- package/dist/info/markets.d.ts.map +1 -0
- package/dist/info/markets.js +178 -0
- package/dist/info/order-status.d.ts +3 -0
- package/dist/info/order-status.d.ts.map +1 -0
- package/dist/info/order-status.js +85 -0
- package/dist/info/orders.d.ts +3 -0
- package/dist/info/orders.d.ts.map +1 -0
- package/dist/info/orders.js +162 -0
- package/dist/info/outcomes.d.ts +3 -0
- package/dist/info/outcomes.d.ts.map +1 -0
- package/dist/info/outcomes.js +175 -0
- package/dist/info/positions.d.ts +3 -0
- package/dist/info/positions.d.ts.map +1 -0
- package/dist/info/positions.js +127 -0
- package/dist/info/rate-limit.d.ts +3 -0
- package/dist/info/rate-limit.d.ts.map +1 -0
- package/dist/info/rate-limit.js +58 -0
- package/dist/info/search-markets.d.ts +3 -0
- package/dist/info/search-markets.d.ts.map +1 -0
- package/dist/info/search-markets.js +296 -0
- package/dist/info/spot.d.ts +3 -0
- package/dist/info/spot.d.ts.map +1 -0
- package/dist/info/spot.js +192 -0
- package/dist/info/trades.d.ts +3 -0
- package/dist/info/trades.d.ts.map +1 -0
- package/dist/info/trades.js +97 -0
- package/dist/lib.d.ts +14 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +17 -0
- package/dist/operations/bracket.d.ts +28 -0
- package/dist/operations/bracket.d.ts.map +1 -0
- package/dist/operations/bracket.js +266 -0
- package/dist/operations/cancel.d.ts +3 -0
- package/dist/operations/cancel.d.ts.map +1 -0
- package/dist/operations/cancel.js +107 -0
- package/dist/operations/chase.d.ts +25 -0
- package/dist/operations/chase.d.ts.map +1 -0
- package/dist/operations/chase.js +215 -0
- package/dist/operations/limit-order.d.ts +3 -0
- package/dist/operations/limit-order.d.ts.map +1 -0
- package/dist/operations/limit-order.js +144 -0
- package/dist/operations/market-order.d.ts +3 -0
- package/dist/operations/market-order.d.ts.map +1 -0
- package/dist/operations/market-order.js +153 -0
- package/dist/operations/outcome-order.d.ts +3 -0
- package/dist/operations/outcome-order.d.ts.map +1 -0
- package/dist/operations/outcome-order.js +171 -0
- package/dist/operations/scale.d.ts +3 -0
- package/dist/operations/scale.d.ts.map +1 -0
- package/dist/operations/scale.js +212 -0
- package/dist/operations/set-tpsl.d.ts +3 -0
- package/dist/operations/set-tpsl.d.ts.map +1 -0
- package/dist/operations/set-tpsl.js +277 -0
- package/dist/operations/spot-order.d.ts +3 -0
- package/dist/operations/spot-order.d.ts.map +1 -0
- package/dist/operations/spot-order.js +173 -0
- package/dist/operations/trigger-order.d.ts +3 -0
- package/dist/operations/trigger-order.d.ts.map +1 -0
- package/dist/operations/trigger-order.js +177 -0
- package/dist/operations/twap-cancel.d.ts +3 -0
- package/dist/operations/twap-cancel.d.ts.map +1 -0
- package/dist/operations/twap-cancel.js +57 -0
- package/dist/operations/twap-status.d.ts +3 -0
- package/dist/operations/twap-status.d.ts.map +1 -0
- package/dist/operations/twap-status.js +81 -0
- package/dist/operations/twap.d.ts +3 -0
- package/dist/operations/twap.d.ts.map +1 -0
- package/dist/operations/twap.js +124 -0
- package/dist/setup/approve-builder.d.ts +3 -0
- package/dist/setup/approve-builder.d.ts.map +1 -0
- package/dist/setup/approve-builder.js +155 -0
- package/dist/setup/env.d.ts +4 -0
- package/dist/setup/env.d.ts.map +1 -0
- package/dist/setup/env.js +8 -0
- package/dist/setup/onboard.d.ts +10 -0
- package/dist/setup/onboard.d.ts.map +1 -0
- package/dist/setup/onboard.js +462 -0
- package/package.json +10 -4
- package/scripts/core/client.ts +19 -1
- package/scripts/core/types.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Open Broker will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.5.0] - 2026-06-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **`getUserStateAll` stamps each position with its canonical asset index**
|
|
9
|
+
(`Position.assetId`) and normalizes HIP-3 coins to the prefixed `{dex}:{coin}`
|
|
10
|
+
form. The per-dex `clearinghouseState` may report HIP-3 coins bare; since we know
|
|
11
|
+
the dex at fetch time, we canonicalize and resolve the global index
|
|
12
|
+
(`100000 + perp_dex_index*10000 + index_in_meta`). Lets consumers key positions by
|
|
13
|
+
a unique integer instead of the coin string, which can otherwise mismatch between
|
|
14
|
+
endpoints (e.g. reconciling intended vs on-chain HIP-3 positions).
|
|
15
|
+
|
|
16
|
+
## [1.4.0] - 2026-05-31
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **Library is now runtime-importable by external consumers.** The package entry
|
|
20
|
+
previously pointed at raw TypeScript source (`main: ./scripts/lib.ts`), so any
|
|
21
|
+
in-process consumer running outside a TS loader (or under a runner that doesn't
|
|
22
|
+
transpile `node_modules`, e.g. tsx) imported an **empty module**. Added a build
|
|
23
|
+
(`npm run build` → `tsc`) that emits compiled JS + type declarations to `dist/`,
|
|
24
|
+
and pointed `main`/`types`/`exports` at `dist/lib.js` / `dist/lib.d.ts`. The CLI
|
|
25
|
+
still runs from source via tsx; only the library entry changed. `prepublishOnly`
|
|
26
|
+
now builds before publishing. No public API changes.
|
|
27
|
+
|
|
5
28
|
## [1.3.0] - 2026-05-07
|
|
6
29
|
|
|
7
30
|
### Added
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface AutomationAuditSink {
|
|
2
|
+
readonly runId: string;
|
|
3
|
+
readonly dbPath: string;
|
|
4
|
+
recordLog(level: 'info' | 'warn' | 'error' | 'debug', message: string, timestamp?: number): void;
|
|
5
|
+
recordEvent(eventType: string, source: 'poll' | 'ws' | 'manual', payload: unknown, timestamp?: number): void;
|
|
6
|
+
recordAction(args: {
|
|
7
|
+
actionId?: string;
|
|
8
|
+
phase: 'request' | 'response' | 'error';
|
|
9
|
+
method: string;
|
|
10
|
+
payload?: unknown;
|
|
11
|
+
result?: unknown;
|
|
12
|
+
error?: unknown;
|
|
13
|
+
dryRun?: boolean;
|
|
14
|
+
timestamp?: number;
|
|
15
|
+
}): void;
|
|
16
|
+
recordSnapshot(snapshot: {
|
|
17
|
+
pollCount: number;
|
|
18
|
+
equity: number;
|
|
19
|
+
marginUsed: number;
|
|
20
|
+
marginUsedPct: number;
|
|
21
|
+
positions: unknown[];
|
|
22
|
+
timestamp?: number;
|
|
23
|
+
}): void;
|
|
24
|
+
recordOrderUpdate(payload: unknown, timestamp?: number): void;
|
|
25
|
+
recordFill(payload: unknown, timestamp?: number): void;
|
|
26
|
+
recordUserEvent(payload: unknown, timestamp?: number): void;
|
|
27
|
+
recordStateChange(op: 'set' | 'delete' | 'clear', key: string | null, value?: unknown, timestamp?: number): void;
|
|
28
|
+
recordPublish(message: string, options: unknown, delivered: boolean, timestamp?: number): void;
|
|
29
|
+
recordError(stage: string, error: unknown, timestamp?: number): void;
|
|
30
|
+
recordNote(kind: string, payload?: unknown, timestamp?: number): void;
|
|
31
|
+
recordMetric(name: string, value: number, tags?: Record<string, unknown>, timestamp?: number): void;
|
|
32
|
+
stop(args: {
|
|
33
|
+
status: 'stopped' | 'error';
|
|
34
|
+
stopReason: string;
|
|
35
|
+
pollCount: number;
|
|
36
|
+
eventsEmitted: number;
|
|
37
|
+
timestamp?: number;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
export interface AuditStartOptions {
|
|
41
|
+
automationId: string;
|
|
42
|
+
scriptPath: string;
|
|
43
|
+
dryRun: boolean;
|
|
44
|
+
verbose: boolean;
|
|
45
|
+
pollIntervalMs: number;
|
|
46
|
+
useWebSocket: boolean;
|
|
47
|
+
accountAddress: string;
|
|
48
|
+
walletAddress: string;
|
|
49
|
+
isApiWallet: boolean;
|
|
50
|
+
initialState?: Record<string, unknown>;
|
|
51
|
+
persistedState?: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
export declare const AUDIT_DB_PATH: string;
|
|
54
|
+
export declare const AUDIT_SOCKET_PATH: string;
|
|
55
|
+
export declare function toSerializable<T = unknown>(value: T): T;
|
|
56
|
+
export declare function createAutomationAudit(options: AuditStartOptions): AutomationAuditSink;
|
|
57
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../scripts/auto/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { once } from 'events';
|
|
4
|
+
import net from 'net';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import readline from 'readline';
|
|
7
|
+
import { setTimeout as delay } from 'timers/promises';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { ensureConfigDir } from '../core/config.js';
|
|
10
|
+
export const AUDIT_DB_PATH = process.env.OPENBROKER_AUDIT_DB_PATH
|
|
11
|
+
|| path.join(ensureConfigDir(), 'automation-audit.sqlite');
|
|
12
|
+
export const AUDIT_SOCKET_PATH = process.env.OPENBROKER_AUDIT_SOCKET_PATH
|
|
13
|
+
|| (process.platform === 'win32'
|
|
14
|
+
? '\\\\.\\pipe\\openbroker-automation-audit-v2'
|
|
15
|
+
: path.join(ensureConfigDir(), 'automation-audit.v2.sock'));
|
|
16
|
+
function internalWarn(automationId, message) {
|
|
17
|
+
console.error(`[auto:${automationId}:audit] ${message}`);
|
|
18
|
+
}
|
|
19
|
+
export function toSerializable(value) {
|
|
20
|
+
const seen = new WeakSet();
|
|
21
|
+
const encoded = JSON.stringify(value, (_key, currentValue) => {
|
|
22
|
+
if (typeof currentValue === 'bigint') {
|
|
23
|
+
return currentValue.toString();
|
|
24
|
+
}
|
|
25
|
+
if (currentValue instanceof Error) {
|
|
26
|
+
return {
|
|
27
|
+
name: currentValue.name,
|
|
28
|
+
message: currentValue.message,
|
|
29
|
+
stack: currentValue.stack,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (currentValue instanceof Map) {
|
|
33
|
+
return Object.fromEntries(currentValue.entries());
|
|
34
|
+
}
|
|
35
|
+
if (currentValue instanceof Set) {
|
|
36
|
+
return [...currentValue.values()];
|
|
37
|
+
}
|
|
38
|
+
if (typeof currentValue === 'object' && currentValue !== null) {
|
|
39
|
+
if (seen.has(currentValue)) {
|
|
40
|
+
return '[Circular]';
|
|
41
|
+
}
|
|
42
|
+
seen.add(currentValue);
|
|
43
|
+
}
|
|
44
|
+
return currentValue;
|
|
45
|
+
});
|
|
46
|
+
if (encoded === undefined) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return JSON.parse(encoded);
|
|
50
|
+
}
|
|
51
|
+
class NoopAuditSink {
|
|
52
|
+
runId = randomUUID();
|
|
53
|
+
dbPath = AUDIT_DB_PATH;
|
|
54
|
+
recordLog() { }
|
|
55
|
+
recordEvent() { }
|
|
56
|
+
recordAction() { }
|
|
57
|
+
recordSnapshot() { }
|
|
58
|
+
recordOrderUpdate() { }
|
|
59
|
+
recordFill() { }
|
|
60
|
+
recordUserEvent() { }
|
|
61
|
+
recordStateChange() { }
|
|
62
|
+
recordPublish() { }
|
|
63
|
+
recordError() { }
|
|
64
|
+
recordNote() { }
|
|
65
|
+
recordMetric() { }
|
|
66
|
+
async stop() { }
|
|
67
|
+
}
|
|
68
|
+
class DaemonAuditSink {
|
|
69
|
+
automationId;
|
|
70
|
+
runId = randomUUID();
|
|
71
|
+
dbPath = AUDIT_DB_PATH;
|
|
72
|
+
socketPath = AUDIT_SOCKET_PATH;
|
|
73
|
+
socket = null;
|
|
74
|
+
lineReader = null;
|
|
75
|
+
connectPromise = null;
|
|
76
|
+
flushPromise = null;
|
|
77
|
+
closed = false;
|
|
78
|
+
daemonSpawnedAt = 0;
|
|
79
|
+
pendingQueue = [];
|
|
80
|
+
inFlight = new Map();
|
|
81
|
+
ackWaiters = new Map();
|
|
82
|
+
constructor(automationId, options) {
|
|
83
|
+
this.automationId = automationId;
|
|
84
|
+
this.enqueue({
|
|
85
|
+
type: 'init',
|
|
86
|
+
payload: {
|
|
87
|
+
runId: this.runId,
|
|
88
|
+
automationId: options.automationId,
|
|
89
|
+
scriptPath: options.scriptPath,
|
|
90
|
+
dryRun: options.dryRun,
|
|
91
|
+
verbose: options.verbose,
|
|
92
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
93
|
+
useWebSocket: options.useWebSocket,
|
|
94
|
+
accountAddress: options.accountAddress,
|
|
95
|
+
walletAddress: options.walletAddress,
|
|
96
|
+
isApiWallet: options.isApiWallet,
|
|
97
|
+
initialState: toSerializable(options.initialState ?? {}),
|
|
98
|
+
persistedState: toSerializable(options.persistedState ?? {}),
|
|
99
|
+
pid: process.pid,
|
|
100
|
+
startedAt: Date.now(),
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
handleSocketClose() {
|
|
105
|
+
if (this.lineReader) {
|
|
106
|
+
this.lineReader.close();
|
|
107
|
+
this.lineReader = null;
|
|
108
|
+
}
|
|
109
|
+
const inflight = [...this.inFlight.values()];
|
|
110
|
+
this.inFlight.clear();
|
|
111
|
+
if (inflight.length > 0) {
|
|
112
|
+
this.pendingQueue = inflight.concat(this.pendingQueue);
|
|
113
|
+
}
|
|
114
|
+
this.socket = null;
|
|
115
|
+
if (!this.closed) {
|
|
116
|
+
void this.ensureConnected();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
handleResponse(line) {
|
|
120
|
+
let response;
|
|
121
|
+
try {
|
|
122
|
+
response = JSON.parse(line);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
internalWarn(this.automationId, `failed to parse audit daemon response: ${error instanceof Error ? error.message : String(error)}`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.inFlight.delete(response.messageId);
|
|
129
|
+
const waiter = this.ackWaiters.get(response.messageId);
|
|
130
|
+
if (!waiter)
|
|
131
|
+
return;
|
|
132
|
+
this.ackWaiters.delete(response.messageId);
|
|
133
|
+
if (response.ok) {
|
|
134
|
+
waiter.resolve();
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
waiter.reject(new Error(response.error || 'audit daemon returned an error'));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async openConnection() {
|
|
141
|
+
const socket = net.createConnection(this.socketPath);
|
|
142
|
+
await new Promise((resolve, reject) => {
|
|
143
|
+
let settled = false;
|
|
144
|
+
const onConnect = () => {
|
|
145
|
+
if (settled)
|
|
146
|
+
return;
|
|
147
|
+
settled = true;
|
|
148
|
+
socket.off('error', onError);
|
|
149
|
+
resolve();
|
|
150
|
+
};
|
|
151
|
+
const onError = (error) => {
|
|
152
|
+
if (settled)
|
|
153
|
+
return;
|
|
154
|
+
settled = true;
|
|
155
|
+
socket.off('connect', onConnect);
|
|
156
|
+
socket.destroy();
|
|
157
|
+
reject(error);
|
|
158
|
+
};
|
|
159
|
+
socket.once('connect', onConnect);
|
|
160
|
+
socket.once('error', onError);
|
|
161
|
+
});
|
|
162
|
+
socket.setEncoding('utf8');
|
|
163
|
+
socket.on('close', () => this.handleSocketClose());
|
|
164
|
+
socket.on('error', (error) => {
|
|
165
|
+
if (!this.closed) {
|
|
166
|
+
internalWarn(this.automationId, `audit socket error: ${error.message}`);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
this.lineReader = readline.createInterface({
|
|
170
|
+
input: socket,
|
|
171
|
+
crlfDelay: Infinity,
|
|
172
|
+
});
|
|
173
|
+
this.lineReader.on('line', (line) => this.handleResponse(line));
|
|
174
|
+
this.socket = socket;
|
|
175
|
+
}
|
|
176
|
+
async spawnDaemon() {
|
|
177
|
+
const now = Date.now();
|
|
178
|
+
if (now - this.daemonSpawnedAt < 1_000)
|
|
179
|
+
return;
|
|
180
|
+
this.daemonSpawnedAt = now;
|
|
181
|
+
const daemonPath = fileURLToPath(new URL('./audit-daemon.js', import.meta.url));
|
|
182
|
+
const child = spawn(process.execPath, ['--no-warnings', '--experimental-sqlite', daemonPath, this.dbPath, this.socketPath], {
|
|
183
|
+
detached: true,
|
|
184
|
+
stdio: 'ignore',
|
|
185
|
+
env: { ...process.env },
|
|
186
|
+
});
|
|
187
|
+
child.unref();
|
|
188
|
+
}
|
|
189
|
+
async ensureConnected() {
|
|
190
|
+
if (this.closed)
|
|
191
|
+
return;
|
|
192
|
+
if (this.socket && !this.socket.destroyed)
|
|
193
|
+
return;
|
|
194
|
+
if (this.connectPromise)
|
|
195
|
+
return this.connectPromise;
|
|
196
|
+
this.connectPromise = (async () => {
|
|
197
|
+
try {
|
|
198
|
+
await this.openConnection();
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
await this.spawnDaemon();
|
|
202
|
+
let lastError = null;
|
|
203
|
+
for (let attempt = 0; attempt < 30 && !this.closed; attempt++) {
|
|
204
|
+
try {
|
|
205
|
+
await delay(100 + (attempt * 50));
|
|
206
|
+
await this.openConnection();
|
|
207
|
+
lastError = null;
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (lastError) {
|
|
215
|
+
throw lastError;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
await this.flushQueue();
|
|
219
|
+
})().catch((error) => {
|
|
220
|
+
internalWarn(this.automationId, `audit daemon unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
221
|
+
}).finally(() => {
|
|
222
|
+
this.connectPromise = null;
|
|
223
|
+
});
|
|
224
|
+
return this.connectPromise;
|
|
225
|
+
}
|
|
226
|
+
enqueue(message) {
|
|
227
|
+
const payload = message.type === 'init'
|
|
228
|
+
? message.payload
|
|
229
|
+
: { runId: this.runId, ...message.payload };
|
|
230
|
+
const wire = {
|
|
231
|
+
messageId: randomUUID(),
|
|
232
|
+
type: message.type,
|
|
233
|
+
payload,
|
|
234
|
+
};
|
|
235
|
+
this.pendingQueue.push(wire);
|
|
236
|
+
void this.ensureConnected();
|
|
237
|
+
if (this.socket && !this.socket.destroyed) {
|
|
238
|
+
void this.flushQueue();
|
|
239
|
+
}
|
|
240
|
+
return wire;
|
|
241
|
+
}
|
|
242
|
+
async flushQueue() {
|
|
243
|
+
if (this.flushPromise)
|
|
244
|
+
return this.flushPromise;
|
|
245
|
+
this.flushPromise = (async () => {
|
|
246
|
+
while (!this.closed && this.socket && !this.socket.destroyed && this.pendingQueue.length > 0) {
|
|
247
|
+
const message = this.pendingQueue.shift();
|
|
248
|
+
this.inFlight.set(message.messageId, message);
|
|
249
|
+
const line = `${JSON.stringify(message)}\n`;
|
|
250
|
+
const writable = this.socket.write(line);
|
|
251
|
+
if (!writable && this.socket) {
|
|
252
|
+
await once(this.socket, 'drain');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
})().finally(() => {
|
|
256
|
+
this.flushPromise = null;
|
|
257
|
+
});
|
|
258
|
+
return this.flushPromise;
|
|
259
|
+
}
|
|
260
|
+
send(message, waitForAck = false) {
|
|
261
|
+
if (this.closed)
|
|
262
|
+
return Promise.resolve();
|
|
263
|
+
const wire = this.enqueue(message);
|
|
264
|
+
if (!waitForAck)
|
|
265
|
+
return Promise.resolve();
|
|
266
|
+
return new Promise((resolve, reject) => {
|
|
267
|
+
this.ackWaiters.set(wire.messageId, { resolve, reject });
|
|
268
|
+
void this.flushQueue();
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
recordLog(level, message, timestamp = Date.now()) {
|
|
272
|
+
void this.send({ type: 'log', payload: { timestamp, level, message } });
|
|
273
|
+
}
|
|
274
|
+
recordEvent(eventType, source, payload, timestamp = Date.now()) {
|
|
275
|
+
void this.send({ type: 'event', payload: { timestamp, eventType, source, payload: toSerializable(payload) } });
|
|
276
|
+
}
|
|
277
|
+
recordAction(args) {
|
|
278
|
+
void this.send({
|
|
279
|
+
type: 'action',
|
|
280
|
+
payload: {
|
|
281
|
+
timestamp: args.timestamp ?? Date.now(),
|
|
282
|
+
actionId: args.actionId ?? randomUUID(),
|
|
283
|
+
phase: args.phase,
|
|
284
|
+
method: args.method,
|
|
285
|
+
payload: toSerializable(args.payload),
|
|
286
|
+
result: toSerializable(args.result),
|
|
287
|
+
error: toSerializable(args.error),
|
|
288
|
+
dryRun: args.dryRun ?? false,
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
recordSnapshot(snapshot) {
|
|
293
|
+
void this.send({
|
|
294
|
+
type: 'snapshot',
|
|
295
|
+
payload: {
|
|
296
|
+
timestamp: snapshot.timestamp ?? Date.now(),
|
|
297
|
+
pollCount: snapshot.pollCount,
|
|
298
|
+
equity: snapshot.equity,
|
|
299
|
+
marginUsed: snapshot.marginUsed,
|
|
300
|
+
marginUsedPct: snapshot.marginUsedPct,
|
|
301
|
+
positions: toSerializable(snapshot.positions),
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
recordOrderUpdate(payload, timestamp = Date.now()) {
|
|
306
|
+
void this.send({ type: 'order_update', payload: { timestamp, payload: toSerializable(payload) } });
|
|
307
|
+
}
|
|
308
|
+
recordFill(payload, timestamp = Date.now()) {
|
|
309
|
+
void this.send({ type: 'fill', payload: { timestamp, payload: toSerializable(payload) } });
|
|
310
|
+
}
|
|
311
|
+
recordUserEvent(payload, timestamp = Date.now()) {
|
|
312
|
+
void this.send({ type: 'user_event', payload: { timestamp, payload: toSerializable(payload) } });
|
|
313
|
+
}
|
|
314
|
+
recordStateChange(op, key, value, timestamp = Date.now()) {
|
|
315
|
+
void this.send({
|
|
316
|
+
type: 'state_change',
|
|
317
|
+
payload: {
|
|
318
|
+
timestamp,
|
|
319
|
+
op,
|
|
320
|
+
key,
|
|
321
|
+
value: toSerializable(value),
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
recordPublish(message, options, delivered, timestamp = Date.now()) {
|
|
326
|
+
void this.send({
|
|
327
|
+
type: 'publish',
|
|
328
|
+
payload: {
|
|
329
|
+
timestamp,
|
|
330
|
+
message,
|
|
331
|
+
options: toSerializable(options),
|
|
332
|
+
delivered,
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
recordError(stage, error, timestamp = Date.now()) {
|
|
337
|
+
void this.send({
|
|
338
|
+
type: 'error',
|
|
339
|
+
payload: {
|
|
340
|
+
timestamp,
|
|
341
|
+
stage,
|
|
342
|
+
error: toSerializable(error),
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
recordNote(kind, payload, timestamp = Date.now()) {
|
|
347
|
+
void this.send({
|
|
348
|
+
type: 'note',
|
|
349
|
+
payload: {
|
|
350
|
+
timestamp,
|
|
351
|
+
kind,
|
|
352
|
+
payload: toSerializable(payload),
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
recordMetric(name, value, tags, timestamp = Date.now()) {
|
|
357
|
+
void this.send({
|
|
358
|
+
type: 'metric',
|
|
359
|
+
payload: {
|
|
360
|
+
timestamp,
|
|
361
|
+
name,
|
|
362
|
+
value,
|
|
363
|
+
tags: toSerializable(tags ?? {}),
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
async stop(args) {
|
|
368
|
+
if (this.closed)
|
|
369
|
+
return;
|
|
370
|
+
try {
|
|
371
|
+
await this.send({
|
|
372
|
+
type: 'stop',
|
|
373
|
+
payload: {
|
|
374
|
+
timestamp: args.timestamp ?? Date.now(),
|
|
375
|
+
status: args.status,
|
|
376
|
+
stopReason: args.stopReason,
|
|
377
|
+
pollCount: args.pollCount,
|
|
378
|
+
eventsEmitted: args.eventsEmitted,
|
|
379
|
+
},
|
|
380
|
+
}, true);
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
internalWarn(this.automationId, `failed to flush stop audit message: ${error instanceof Error ? error.message : String(error)}`);
|
|
384
|
+
}
|
|
385
|
+
this.closed = true;
|
|
386
|
+
if (this.lineReader) {
|
|
387
|
+
this.lineReader.close();
|
|
388
|
+
this.lineReader = null;
|
|
389
|
+
}
|
|
390
|
+
if (this.socket && !this.socket.destroyed) {
|
|
391
|
+
this.socket.end();
|
|
392
|
+
}
|
|
393
|
+
this.socket = null;
|
|
394
|
+
this.pendingQueue = [];
|
|
395
|
+
this.inFlight.clear();
|
|
396
|
+
this.ackWaiters.clear();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
export function createAutomationAudit(options) {
|
|
400
|
+
try {
|
|
401
|
+
return new DaemonAuditSink(options.automationId, options);
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
internalWarn(options.automationId, `audit disabled: ${error instanceof Error ? error.message : String(error)}`);
|
|
405
|
+
return new NoopAuditSink();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../scripts/auto/cli.ts"],"names":[],"mappings":""}
|