openbroker 1.3.1 → 1.4.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.
Files changed (171) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/SKILL.md +7 -4
  3. package/dist/auto/audit.d.ts +57 -0
  4. package/dist/auto/audit.d.ts.map +1 -0
  5. package/dist/auto/audit.js +407 -0
  6. package/dist/auto/cli.d.ts +2 -0
  7. package/dist/auto/cli.d.ts.map +1 -0
  8. package/dist/auto/cli.js +423 -0
  9. package/dist/auto/events.d.ts +11 -0
  10. package/dist/auto/events.d.ts.map +1 -0
  11. package/dist/auto/events.js +36 -0
  12. package/dist/auto/examples/dca.d.ts +4 -0
  13. package/dist/auto/examples/dca.d.ts.map +1 -0
  14. package/dist/auto/examples/dca.js +60 -0
  15. package/dist/auto/examples/funding-arb.d.ts +4 -0
  16. package/dist/auto/examples/funding-arb.d.ts.map +1 -0
  17. package/dist/auto/examples/funding-arb.js +81 -0
  18. package/dist/auto/examples/grid.d.ts +4 -0
  19. package/dist/auto/examples/grid.d.ts.map +1 -0
  20. package/dist/auto/examples/grid.js +114 -0
  21. package/dist/auto/examples/mm-maker.d.ts +4 -0
  22. package/dist/auto/examples/mm-maker.d.ts.map +1 -0
  23. package/dist/auto/examples/mm-maker.js +131 -0
  24. package/dist/auto/examples/mm-spread.d.ts +4 -0
  25. package/dist/auto/examples/mm-spread.d.ts.map +1 -0
  26. package/dist/auto/examples/mm-spread.js +119 -0
  27. package/dist/auto/examples/price-alert.d.ts +4 -0
  28. package/dist/auto/examples/price-alert.d.ts.map +1 -0
  29. package/dist/auto/examples/price-alert.js +85 -0
  30. package/dist/auto/keep-awake.d.ts +11 -0
  31. package/dist/auto/keep-awake.d.ts.map +1 -0
  32. package/dist/auto/keep-awake.js +70 -0
  33. package/dist/auto/loader.d.ts +22 -0
  34. package/dist/auto/loader.d.ts.map +1 -0
  35. package/dist/auto/loader.js +127 -0
  36. package/dist/auto/prune.d.ts +40 -0
  37. package/dist/auto/prune.d.ts.map +1 -0
  38. package/dist/auto/prune.js +204 -0
  39. package/dist/auto/registry.d.ts +24 -0
  40. package/dist/auto/registry.d.ts.map +1 -0
  41. package/dist/auto/registry.js +93 -0
  42. package/dist/auto/report.d.ts +3 -0
  43. package/dist/auto/report.d.ts.map +1 -0
  44. package/dist/auto/report.js +385 -0
  45. package/dist/auto/runtime.d.ts +33 -0
  46. package/dist/auto/runtime.d.ts.map +1 -0
  47. package/dist/auto/runtime.js +844 -0
  48. package/dist/auto/types.d.ts +236 -0
  49. package/dist/auto/types.d.ts.map +1 -0
  50. package/dist/auto/types.js +3 -0
  51. package/dist/core/client.d.ts +684 -0
  52. package/dist/core/client.d.ts.map +1 -0
  53. package/dist/core/client.js +2040 -0
  54. package/dist/core/config.d.ts +22 -0
  55. package/dist/core/config.d.ts.map +1 -0
  56. package/dist/core/config.js +143 -0
  57. package/dist/core/types.d.ts +221 -0
  58. package/dist/core/types.d.ts.map +1 -0
  59. package/dist/core/types.js +2 -0
  60. package/dist/core/utils.d.ts +61 -0
  61. package/dist/core/utils.d.ts.map +1 -0
  62. package/dist/core/utils.js +142 -0
  63. package/dist/core/ws.d.ts +121 -0
  64. package/dist/core/ws.d.ts.map +1 -0
  65. package/dist/core/ws.js +222 -0
  66. package/dist/info/account.d.ts +3 -0
  67. package/dist/info/account.d.ts.map +1 -0
  68. package/dist/info/account.js +198 -0
  69. package/dist/info/all-markets.d.ts +3 -0
  70. package/dist/info/all-markets.d.ts.map +1 -0
  71. package/dist/info/all-markets.js +272 -0
  72. package/dist/info/candles.d.ts +3 -0
  73. package/dist/info/candles.d.ts.map +1 -0
  74. package/dist/info/candles.js +120 -0
  75. package/dist/info/fees.d.ts +3 -0
  76. package/dist/info/fees.d.ts.map +1 -0
  77. package/dist/info/fees.js +87 -0
  78. package/dist/info/fills.d.ts +3 -0
  79. package/dist/info/fills.d.ts.map +1 -0
  80. package/dist/info/fills.js +105 -0
  81. package/dist/info/funding-history.d.ts +3 -0
  82. package/dist/info/funding-history.d.ts.map +1 -0
  83. package/dist/info/funding-history.js +98 -0
  84. package/dist/info/funding-scan.d.ts +3 -0
  85. package/dist/info/funding-scan.d.ts.map +1 -0
  86. package/dist/info/funding-scan.js +178 -0
  87. package/dist/info/funding.d.ts +3 -0
  88. package/dist/info/funding.d.ts.map +1 -0
  89. package/dist/info/funding.js +158 -0
  90. package/dist/info/markets.d.ts +3 -0
  91. package/dist/info/markets.d.ts.map +1 -0
  92. package/dist/info/markets.js +178 -0
  93. package/dist/info/order-status.d.ts +3 -0
  94. package/dist/info/order-status.d.ts.map +1 -0
  95. package/dist/info/order-status.js +85 -0
  96. package/dist/info/orders.d.ts +3 -0
  97. package/dist/info/orders.d.ts.map +1 -0
  98. package/dist/info/orders.js +162 -0
  99. package/dist/info/outcomes.d.ts +3 -0
  100. package/dist/info/outcomes.d.ts.map +1 -0
  101. package/dist/info/outcomes.js +175 -0
  102. package/dist/info/positions.d.ts +3 -0
  103. package/dist/info/positions.d.ts.map +1 -0
  104. package/dist/info/positions.js +127 -0
  105. package/dist/info/rate-limit.d.ts +3 -0
  106. package/dist/info/rate-limit.d.ts.map +1 -0
  107. package/dist/info/rate-limit.js +58 -0
  108. package/dist/info/search-markets.d.ts +3 -0
  109. package/dist/info/search-markets.d.ts.map +1 -0
  110. package/dist/info/search-markets.js +296 -0
  111. package/dist/info/spot.d.ts +3 -0
  112. package/dist/info/spot.d.ts.map +1 -0
  113. package/dist/info/spot.js +192 -0
  114. package/dist/info/trades.d.ts +3 -0
  115. package/dist/info/trades.d.ts.map +1 -0
  116. package/dist/info/trades.js +97 -0
  117. package/dist/lib.d.ts +14 -0
  118. package/dist/lib.d.ts.map +1 -0
  119. package/dist/lib.js +17 -0
  120. package/dist/operations/bracket.d.ts +28 -0
  121. package/dist/operations/bracket.d.ts.map +1 -0
  122. package/dist/operations/bracket.js +266 -0
  123. package/dist/operations/cancel.d.ts +3 -0
  124. package/dist/operations/cancel.d.ts.map +1 -0
  125. package/dist/operations/cancel.js +107 -0
  126. package/dist/operations/chase.d.ts +25 -0
  127. package/dist/operations/chase.d.ts.map +1 -0
  128. package/dist/operations/chase.js +215 -0
  129. package/dist/operations/limit-order.d.ts +3 -0
  130. package/dist/operations/limit-order.d.ts.map +1 -0
  131. package/dist/operations/limit-order.js +144 -0
  132. package/dist/operations/market-order.d.ts +3 -0
  133. package/dist/operations/market-order.d.ts.map +1 -0
  134. package/dist/operations/market-order.js +153 -0
  135. package/dist/operations/outcome-order.d.ts +3 -0
  136. package/dist/operations/outcome-order.d.ts.map +1 -0
  137. package/dist/operations/outcome-order.js +171 -0
  138. package/dist/operations/scale.d.ts +3 -0
  139. package/dist/operations/scale.d.ts.map +1 -0
  140. package/dist/operations/scale.js +212 -0
  141. package/dist/operations/set-tpsl.d.ts +3 -0
  142. package/dist/operations/set-tpsl.d.ts.map +1 -0
  143. package/dist/operations/set-tpsl.js +277 -0
  144. package/dist/operations/spot-order.d.ts +3 -0
  145. package/dist/operations/spot-order.d.ts.map +1 -0
  146. package/dist/operations/spot-order.js +173 -0
  147. package/dist/operations/trigger-order.d.ts +3 -0
  148. package/dist/operations/trigger-order.d.ts.map +1 -0
  149. package/dist/operations/trigger-order.js +177 -0
  150. package/dist/operations/twap-cancel.d.ts +3 -0
  151. package/dist/operations/twap-cancel.d.ts.map +1 -0
  152. package/dist/operations/twap-cancel.js +57 -0
  153. package/dist/operations/twap-status.d.ts +3 -0
  154. package/dist/operations/twap-status.d.ts.map +1 -0
  155. package/dist/operations/twap-status.js +81 -0
  156. package/dist/operations/twap.d.ts +3 -0
  157. package/dist/operations/twap.d.ts.map +1 -0
  158. package/dist/operations/twap.js +124 -0
  159. package/dist/setup/approve-builder.d.ts +3 -0
  160. package/dist/setup/approve-builder.d.ts.map +1 -0
  161. package/dist/setup/approve-builder.js +155 -0
  162. package/dist/setup/env.d.ts +4 -0
  163. package/dist/setup/env.d.ts.map +1 -0
  164. package/dist/setup/env.js +8 -0
  165. package/dist/setup/onboard.d.ts +10 -0
  166. package/dist/setup/onboard.d.ts.map +1 -0
  167. package/dist/setup/onboard.js +462 -0
  168. package/package.json +10 -4
  169. package/scripts/core/client.ts +13 -3
  170. package/scripts/info/all-markets.ts +18 -2
  171. package/scripts/info/search-markets.ts +18 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to Open Broker will be documented in this file.
4
4
 
5
+ ## [1.4.0] - 2026-05-31
6
+
7
+ ### Fixed
8
+ - **Library is now runtime-importable by external consumers.** The package entry
9
+ previously pointed at raw TypeScript source (`main: ./scripts/lib.ts`), so any
10
+ in-process consumer running outside a TS loader (or under a runner that doesn't
11
+ transpile `node_modules`, e.g. tsx) imported an **empty module**. Added a build
12
+ (`npm run build` → `tsc`) that emits compiled JS + type declarations to `dist/`,
13
+ and pointed `main`/`types`/`exports` at `dist/lib.js` / `dist/lib.d.ts`. The CLI
14
+ still runs from source via tsx; only the library entry changed. `prepublishOnly`
15
+ now builds before publishing. No public API changes.
16
+
5
17
  ## [1.3.0] - 2026-05-07
6
18
 
7
19
  ### Added
package/SKILL.md CHANGED
@@ -4,7 +4,7 @@ description: Hyperliquid trading CLI skill for agents. Use when an agent needs t
4
4
  license: MIT
5
5
  compatibility: Requires Node.js 22+, network access to api.hyperliquid.xyz
6
6
  homepage: https://www.npmjs.com/package/openbroker
7
- metadata: {"author": "monemetrics", "version": "1.3.1"}
7
+ metadata: {"author": "monemetrics", "version": "1.3.2"}
8
8
  allowed-tools: Bash(openbroker:*)
9
9
  ---
10
10
 
@@ -52,12 +52,15 @@ Common globals:
52
52
  openbroker search --query GOLD --json
53
53
  openbroker search --query BTC --type perp --json
54
54
  openbroker all-markets --type hip3 --json
55
+ openbroker all-markets --type outcome --json
55
56
  openbroker outcomes --query BTC --json
56
57
  ```
57
58
 
58
59
  - HIP-3 perps use `dex:COIN`, e.g. `xyz:CL`, not bare `CL`.
59
60
  - `assetId` is the canonical identifier for comparisons and persisted agent state; order placement still uses `--coin`.
60
- - HIP-4 outcome orders use `--outcome <id|#encoding|+encoding>` plus `--outcome-side yes|no` when the reference is a plain ID.
61
+ - For HIP-4 discovery, use `outcomes --json` for grouped market metadata and `all-markets --type outcome --json` for flattened side rows.
62
+ - HIP-4 outcome orders use `--outcome <id|#encoding|+encoding>` plus `--outcome-side yes|no` when the reference is a plain ID. Encoded sides use `encoding = 10 * outcomeId + side`, where side `0` is the first side and side `1` is the second side.
63
+ - HIP-4 order books use `#<encoding>` coins; spot balances may show `+<encoding>` token names.
61
64
  - On testnet, HIP-3 metadata may need an explicit prefixed coin such as `dex:COIN`.
62
65
 
63
66
  ## CLI command map
@@ -72,7 +75,7 @@ Most info commands accept `--json`. Use `--coin`, `--top`, and `--address` where
72
75
  | `positions` | Open perp positions and liquidation distance | `--coin`, `--address` |
73
76
  | `funding` | Funding rates | `--coin`, `--top`, `--sort annualized|hourly|oi`, `--all`, `--include-hip3` |
74
77
  | `markets` | Perp market data | `--coin`, `--top`, `--sort volume|oi|change`, `--include-hip3` |
75
- | `all-markets` | Browse every venue type | `--type perp|hip3|spot|outcome|all`, `--top` |
78
+ | `all-markets` | Browse every venue type | `--type perp|hip3|spot|outcome|all`, `--top`, `--json` |
76
79
  | `search` | Find markets across providers | `--query`, `--type` |
77
80
  | `spot` | Spot markets or balances | `--coin`, `--balances`, `--address`, `--top` |
78
81
  | `fills` | Recent fills | `--coin`, `--side buy|sell`, `--top`, `--address` |
@@ -84,7 +87,7 @@ Most info commands accept `--json`. Use `--coin`, `--top`, and `--address` where
84
87
  | `trades` | Recent tape | `--coin`, `--top` |
85
88
  | `rate-limit` | API usage | — |
86
89
  | `funding-scan` | Cross-dex scan | `--threshold`, `--main-only`, `--hip3-only`, `--pairs`, `--watch`, `--interval`, `--top` |
87
- | `outcomes` | HIP-4 discovery/balances | `--query`, `--outcome`, `--side`, `--balances`, `--top` |
90
+ | `outcomes` | HIP-4 discovery/balances | `--query`, `--outcome`, `--side`, `--balances`, `--top`, `--json` |
88
91
 
89
92
  ### Perp trading
90
93
 
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../scripts/auto/cli.ts"],"names":[],"mappings":""}