openbroker 1.0.89 → 1.1.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/package.json +6 -6
- package/scripts/auto/cli.ts +0 -2
- package/scripts/auto/runtime.ts +0 -4
- package/scripts/lib.ts +80 -0
- package/scripts/operations/bracket.ts +216 -202
- package/scripts/operations/chase.ts +184 -166
- package/SKILL.md +0 -1182
- package/openclaw.plugin.json +0 -86
- package/scripts/plugin/cli.ts +0 -127
- package/scripts/plugin/config-bridge.ts +0 -30
- package/scripts/plugin/index.ts +0 -133
- package/scripts/plugin/tools.ts +0 -1686
- package/scripts/plugin/types.ts +0 -158
- package/scripts/plugin/watcher.ts +0 -321
package/scripts/plugin/types.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
// OpenClaw Plugin API types for openbroker
|
|
2
|
-
// Re-exports core types where possible
|
|
3
|
-
|
|
4
|
-
export type {
|
|
5
|
-
OpenBrokerConfig,
|
|
6
|
-
ClearinghouseState,
|
|
7
|
-
Position,
|
|
8
|
-
MarginSummary,
|
|
9
|
-
OpenOrder,
|
|
10
|
-
FundingInfo,
|
|
11
|
-
AssetMeta,
|
|
12
|
-
AssetCtx,
|
|
13
|
-
MetaAndAssetCtxs,
|
|
14
|
-
} from '../core/types.js';
|
|
15
|
-
|
|
16
|
-
/** Logger provided by OpenClaw to plugins */
|
|
17
|
-
export interface PluginLogger {
|
|
18
|
-
info(message: string): void;
|
|
19
|
-
warn(message: string): void;
|
|
20
|
-
error(message: string): void;
|
|
21
|
-
debug(message: string): void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** A background service registered by a plugin */
|
|
25
|
-
export interface PluginService {
|
|
26
|
-
id: string;
|
|
27
|
-
start(): Promise<void>;
|
|
28
|
-
stop(): Promise<void>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** JSON Schema for tool parameters */
|
|
32
|
-
export interface ToolParameters {
|
|
33
|
-
type: 'object';
|
|
34
|
-
properties: Record<string, {
|
|
35
|
-
type: string;
|
|
36
|
-
description?: string;
|
|
37
|
-
enum?: string[];
|
|
38
|
-
default?: unknown;
|
|
39
|
-
}>;
|
|
40
|
-
required?: string[];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** Tool result content block */
|
|
44
|
-
export interface ToolResultContent {
|
|
45
|
-
type: 'text';
|
|
46
|
-
text: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** An agent tool registered by a plugin */
|
|
50
|
-
export interface PluginTool {
|
|
51
|
-
name: string;
|
|
52
|
-
description: string;
|
|
53
|
-
parameters: ToolParameters;
|
|
54
|
-
execute(toolCallId: string, params: Record<string, unknown>): Promise<{
|
|
55
|
-
content: ToolResultContent[];
|
|
56
|
-
details?: unknown;
|
|
57
|
-
}>;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Commander.js-style program for CLI registration */
|
|
61
|
-
export interface CliProgram {
|
|
62
|
-
command(name: string): CliCommand;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface CliCommand {
|
|
66
|
-
command(name: string): CliCommand;
|
|
67
|
-
description(desc: string): CliCommand;
|
|
68
|
-
argument(name: string, desc?: string): CliCommand;
|
|
69
|
-
option(flags: string, desc?: string, defaultValue?: unknown): CliCommand;
|
|
70
|
-
action(fn: (...args: unknown[]) => void | Promise<void>): CliCommand;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** The API that OpenClaw passes to plugin register() */
|
|
74
|
-
export interface OpenClawPluginApi {
|
|
75
|
-
logger: PluginLogger;
|
|
76
|
-
config: Record<string, unknown>;
|
|
77
|
-
pluginConfig?: Record<string, unknown>;
|
|
78
|
-
gatewayPort: number;
|
|
79
|
-
registerService(service: PluginService): void;
|
|
80
|
-
registerTool(tool: PluginTool): void;
|
|
81
|
-
registerCli(
|
|
82
|
-
setup: (ctx: { program: CliProgram }) => void,
|
|
83
|
-
opts: { commands: string[] },
|
|
84
|
-
): void;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Plugin config schema matching openclaw.plugin.json */
|
|
88
|
-
export interface OpenBrokerPluginConfig {
|
|
89
|
-
privateKey?: string;
|
|
90
|
-
accountAddress?: string;
|
|
91
|
-
network?: 'mainnet' | 'testnet';
|
|
92
|
-
hooksToken?: string;
|
|
93
|
-
watcher?: {
|
|
94
|
-
enabled?: boolean;
|
|
95
|
-
pollIntervalMs?: number;
|
|
96
|
-
pnlChangeThresholdPct?: number;
|
|
97
|
-
marginUsageWarningPct?: number;
|
|
98
|
-
notifyOnPositionChange?: boolean;
|
|
99
|
-
notifyOnFunding?: boolean;
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Snapshot of a single position for watcher comparison */
|
|
104
|
-
export interface PositionSnapshot {
|
|
105
|
-
coin: string;
|
|
106
|
-
szi: string;
|
|
107
|
-
entryPx: string;
|
|
108
|
-
positionValue: string;
|
|
109
|
-
unrealizedPnl: string;
|
|
110
|
-
returnOnEquity: string;
|
|
111
|
-
liquidationPx: string | null;
|
|
112
|
-
leverage: { type: string; value: number };
|
|
113
|
-
marginUsed: string;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Snapshot of full account state for watcher comparison */
|
|
117
|
-
export interface AccountSnapshot {
|
|
118
|
-
equity: string;
|
|
119
|
-
marginUsed: string;
|
|
120
|
-
marginUsedPct: number;
|
|
121
|
-
positions: Map<string, PositionSnapshot>;
|
|
122
|
-
timestamp: number;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** Position event types */
|
|
126
|
-
export type PositionEventType =
|
|
127
|
-
| 'position_opened'
|
|
128
|
-
| 'position_closed'
|
|
129
|
-
| 'position_size_changed'
|
|
130
|
-
| 'pnl_threshold'
|
|
131
|
-
| 'margin_warning';
|
|
132
|
-
|
|
133
|
-
/** Position event for hook notifications */
|
|
134
|
-
export interface PositionEvent {
|
|
135
|
-
type: PositionEventType;
|
|
136
|
-
coin?: string;
|
|
137
|
-
message: string;
|
|
138
|
-
details: Record<string, unknown>;
|
|
139
|
-
detectedAt: string;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** Watcher status for introspection */
|
|
143
|
-
export interface WatcherStatus {
|
|
144
|
-
running: boolean;
|
|
145
|
-
pollIntervalMs: number;
|
|
146
|
-
accountAddress: string | null;
|
|
147
|
-
positions: Array<{
|
|
148
|
-
coin: string;
|
|
149
|
-
size: string;
|
|
150
|
-
entryPrice: string;
|
|
151
|
-
unrealizedPnl: string;
|
|
152
|
-
liquidationPrice: string | null;
|
|
153
|
-
}>;
|
|
154
|
-
equity: string | null;
|
|
155
|
-
marginUsedPct: number | null;
|
|
156
|
-
eventsDetected: number;
|
|
157
|
-
lastPollAt: string | null;
|
|
158
|
-
}
|
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
// Background Position Watcher
|
|
2
|
-
// Polls Hyperliquid via the core client for position changes and sends hook notifications
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
PluginLogger,
|
|
6
|
-
PluginService,
|
|
7
|
-
AccountSnapshot,
|
|
8
|
-
PositionSnapshot,
|
|
9
|
-
PositionEvent,
|
|
10
|
-
WatcherStatus,
|
|
11
|
-
} from './types.js';
|
|
12
|
-
|
|
13
|
-
interface WatcherOptions {
|
|
14
|
-
logger: PluginLogger;
|
|
15
|
-
gatewayPort: number;
|
|
16
|
-
hooksToken?: string;
|
|
17
|
-
accountAddress?: string;
|
|
18
|
-
network?: string;
|
|
19
|
-
pollIntervalMs?: number;
|
|
20
|
-
pnlChangeThresholdPct?: number;
|
|
21
|
-
marginUsageWarningPct?: number;
|
|
22
|
-
notifyOnPositionChange?: boolean;
|
|
23
|
-
notifyOnFunding?: boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class PositionWatcher implements PluginService {
|
|
27
|
-
readonly id = 'ob-position-watcher';
|
|
28
|
-
|
|
29
|
-
private logger: PluginLogger;
|
|
30
|
-
private gatewayPort: number;
|
|
31
|
-
private hooksToken: string | undefined;
|
|
32
|
-
private pollIntervalMs: number;
|
|
33
|
-
private pnlChangeThresholdPct: number;
|
|
34
|
-
private marginUsageWarningPct: number;
|
|
35
|
-
private notifyOnPositionChange: boolean;
|
|
36
|
-
|
|
37
|
-
private accountAddress: string | undefined;
|
|
38
|
-
private previousSnapshot: AccountSnapshot | null = null;
|
|
39
|
-
private eventsDetected = 0;
|
|
40
|
-
private lastPollAt: Date | null = null;
|
|
41
|
-
private timer: ReturnType<typeof setInterval> | null = null;
|
|
42
|
-
private isPolling = false;
|
|
43
|
-
private seeded = false;
|
|
44
|
-
|
|
45
|
-
constructor(options: WatcherOptions) {
|
|
46
|
-
this.logger = options.logger;
|
|
47
|
-
this.gatewayPort = options.gatewayPort;
|
|
48
|
-
// Tokens/addresses come exclusively from options (resolved by plugin/index.ts).
|
|
49
|
-
// Reading process.env here would co-locate with the fetch() below and trip
|
|
50
|
-
// the OpenClaw "credential harvesting" scanner rule.
|
|
51
|
-
this.hooksToken = options.hooksToken;
|
|
52
|
-
this.accountAddress = options.accountAddress || undefined;
|
|
53
|
-
this.pollIntervalMs = options.pollIntervalMs ?? 30_000;
|
|
54
|
-
this.pnlChangeThresholdPct = options.pnlChangeThresholdPct ?? 5;
|
|
55
|
-
this.marginUsageWarningPct = options.marginUsageWarningPct ?? 80;
|
|
56
|
-
this.notifyOnPositionChange = options.notifyOnPositionChange ?? true;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async start(): Promise<void> {
|
|
60
|
-
// Resolve account address from client if not set
|
|
61
|
-
if (!this.accountAddress) {
|
|
62
|
-
try {
|
|
63
|
-
const { getClient } = await import('../core/client.js');
|
|
64
|
-
const client = getClient();
|
|
65
|
-
this.accountAddress = client.address;
|
|
66
|
-
} catch {
|
|
67
|
-
this.logger.warn('Could not resolve account address — position watcher cannot start. Run "openbroker setup" first.');
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
this.logger.info(`Starting position watcher for ${this.accountAddress} (poll every ${this.pollIntervalMs / 1000}s)`);
|
|
73
|
-
|
|
74
|
-
this.timer = setInterval(() => this.poll(), this.pollIntervalMs);
|
|
75
|
-
|
|
76
|
-
// Initial poll to seed state
|
|
77
|
-
await this.poll();
|
|
78
|
-
this.seeded = true;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async stop(): Promise<void> {
|
|
82
|
-
if (this.timer) {
|
|
83
|
-
clearInterval(this.timer);
|
|
84
|
-
this.timer = null;
|
|
85
|
-
}
|
|
86
|
-
this.logger.info('Position watcher stopped');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
getStatus(): WatcherStatus {
|
|
90
|
-
const positions: WatcherStatus['positions'] = [];
|
|
91
|
-
|
|
92
|
-
if (this.previousSnapshot) {
|
|
93
|
-
for (const p of this.previousSnapshot.positions.values()) {
|
|
94
|
-
positions.push({
|
|
95
|
-
coin: p.coin,
|
|
96
|
-
size: p.szi,
|
|
97
|
-
entryPrice: p.entryPx,
|
|
98
|
-
unrealizedPnl: p.unrealizedPnl,
|
|
99
|
-
liquidationPrice: p.liquidationPx,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
running: this.timer !== null,
|
|
106
|
-
pollIntervalMs: this.pollIntervalMs,
|
|
107
|
-
accountAddress: this.accountAddress ?? null,
|
|
108
|
-
positions,
|
|
109
|
-
equity: this.previousSnapshot?.equity ?? null,
|
|
110
|
-
marginUsedPct: this.previousSnapshot?.marginUsedPct ?? null,
|
|
111
|
-
eventsDetected: this.eventsDetected,
|
|
112
|
-
lastPollAt: this.lastPollAt?.toISOString() ?? null,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
private async poll(): Promise<void> {
|
|
117
|
-
if (this.isPolling) return;
|
|
118
|
-
this.isPolling = true;
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
const { getClient } = await import('../core/client.js');
|
|
122
|
-
const client = getClient();
|
|
123
|
-
const state = await client.getUserStateAll(this.accountAddress);
|
|
124
|
-
|
|
125
|
-
const snapshot = this.buildSnapshot(state);
|
|
126
|
-
|
|
127
|
-
const posCount = snapshot.positions.size;
|
|
128
|
-
const equity = parseFloat(snapshot.equity).toFixed(2);
|
|
129
|
-
const marginPct = snapshot.marginUsedPct.toFixed(1);
|
|
130
|
-
this.logger.debug(`Poll: ${posCount} position(s), equity $${equity}, margin ${marginPct}%`);
|
|
131
|
-
|
|
132
|
-
const events = this.seeded ? this.detectEvents(snapshot) : [];
|
|
133
|
-
|
|
134
|
-
if (events.length > 0) {
|
|
135
|
-
for (const event of events) {
|
|
136
|
-
this.eventsDetected++;
|
|
137
|
-
this.logger.info(`[${event.type}] ${event.message}`);
|
|
138
|
-
await this.sendHook(event);
|
|
139
|
-
}
|
|
140
|
-
} else if (this.seeded) {
|
|
141
|
-
this.logger.debug('No position changes detected');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.previousSnapshot = snapshot;
|
|
145
|
-
this.lastPollAt = new Date();
|
|
146
|
-
} catch (err) {
|
|
147
|
-
this.logger.error(`Watcher poll error: ${err instanceof Error ? err.message : String(err)}`);
|
|
148
|
-
} finally {
|
|
149
|
-
this.isPolling = false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private buildSnapshot(state: { marginSummary: { accountValue: string; totalMarginUsed: string }; assetPositions: Array<{ position: PositionSnapshot }> }): AccountSnapshot {
|
|
154
|
-
const positions = new Map<string, PositionSnapshot>();
|
|
155
|
-
|
|
156
|
-
for (const ap of state.assetPositions) {
|
|
157
|
-
const p = ap.position;
|
|
158
|
-
if (parseFloat(p.szi) === 0) continue;
|
|
159
|
-
|
|
160
|
-
positions.set(p.coin, {
|
|
161
|
-
coin: p.coin,
|
|
162
|
-
szi: p.szi,
|
|
163
|
-
entryPx: p.entryPx,
|
|
164
|
-
positionValue: p.positionValue,
|
|
165
|
-
unrealizedPnl: p.unrealizedPnl,
|
|
166
|
-
returnOnEquity: p.returnOnEquity,
|
|
167
|
-
liquidationPx: p.liquidationPx,
|
|
168
|
-
leverage: p.leverage,
|
|
169
|
-
marginUsed: p.marginUsed,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const equity = parseFloat(state.marginSummary.accountValue);
|
|
174
|
-
const marginUsed = parseFloat(state.marginSummary.totalMarginUsed);
|
|
175
|
-
const marginUsedPct = equity > 0 ? (marginUsed / equity) * 100 : 0;
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
equity: state.marginSummary.accountValue,
|
|
179
|
-
marginUsed: state.marginSummary.totalMarginUsed,
|
|
180
|
-
marginUsedPct,
|
|
181
|
-
positions,
|
|
182
|
-
timestamp: Date.now(),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
private detectEvents(current: AccountSnapshot): PositionEvent[] {
|
|
187
|
-
const events: PositionEvent[] = [];
|
|
188
|
-
const prev = this.previousSnapshot;
|
|
189
|
-
if (!prev) return events;
|
|
190
|
-
|
|
191
|
-
const now = new Date().toISOString();
|
|
192
|
-
|
|
193
|
-
// 1. New positions (opened)
|
|
194
|
-
if (this.notifyOnPositionChange) {
|
|
195
|
-
for (const [coin, pos] of current.positions) {
|
|
196
|
-
if (!prev.positions.has(coin)) {
|
|
197
|
-
const side = parseFloat(pos.szi) > 0 ? 'long' : 'short';
|
|
198
|
-
events.push({
|
|
199
|
-
type: 'position_opened',
|
|
200
|
-
coin,
|
|
201
|
-
message: `Position opened: ${side} ${Math.abs(parseFloat(pos.szi))} ${coin} at $${pos.entryPx}`,
|
|
202
|
-
details: { coin, side, size: pos.szi, entryPrice: pos.entryPx },
|
|
203
|
-
detectedAt: now,
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// 2. Closed positions
|
|
209
|
-
for (const [coin, pos] of prev.positions) {
|
|
210
|
-
if (!current.positions.has(coin)) {
|
|
211
|
-
events.push({
|
|
212
|
-
type: 'position_closed',
|
|
213
|
-
coin,
|
|
214
|
-
message: `Position closed: ${coin} (was ${pos.szi} at $${pos.entryPx}, final unrealized PnL: $${pos.unrealizedPnl})`,
|
|
215
|
-
details: { coin, previousSize: pos.szi, entryPrice: pos.entryPx, lastPnl: pos.unrealizedPnl },
|
|
216
|
-
detectedAt: now,
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// 3. Size changes
|
|
222
|
-
for (const [coin, pos] of current.positions) {
|
|
223
|
-
const prevPos = prev.positions.get(coin);
|
|
224
|
-
if (!prevPos) continue;
|
|
225
|
-
|
|
226
|
-
const currentSize = parseFloat(pos.szi);
|
|
227
|
-
const prevSize = parseFloat(prevPos.szi);
|
|
228
|
-
|
|
229
|
-
if (currentSize !== prevSize) {
|
|
230
|
-
const diff = currentSize - prevSize;
|
|
231
|
-
const action = Math.abs(currentSize) > Math.abs(prevSize) ? 'increased' : 'decreased';
|
|
232
|
-
events.push({
|
|
233
|
-
type: 'position_size_changed',
|
|
234
|
-
coin,
|
|
235
|
-
message: `Position ${coin} size ${action}: ${prevPos.szi} → ${pos.szi} (${diff > 0 ? '+' : ''}${diff.toFixed(6)})`,
|
|
236
|
-
details: { coin, previousSize: prevPos.szi, newSize: pos.szi, change: diff },
|
|
237
|
-
detectedAt: now,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// 4. PnL threshold
|
|
244
|
-
for (const [coin, pos] of current.positions) {
|
|
245
|
-
const prevPos = prev.positions.get(coin);
|
|
246
|
-
if (!prevPos) continue;
|
|
247
|
-
|
|
248
|
-
const currentPnl = parseFloat(pos.unrealizedPnl);
|
|
249
|
-
const prevPnl = parseFloat(prevPos.unrealizedPnl);
|
|
250
|
-
const posValue = parseFloat(pos.positionValue);
|
|
251
|
-
|
|
252
|
-
if (posValue === 0) continue;
|
|
253
|
-
|
|
254
|
-
const pnlChangePct = Math.abs(((currentPnl - prevPnl) / posValue) * 100);
|
|
255
|
-
|
|
256
|
-
if (pnlChangePct >= this.pnlChangeThresholdPct) {
|
|
257
|
-
events.push({
|
|
258
|
-
type: 'pnl_threshold',
|
|
259
|
-
coin,
|
|
260
|
-
message: `PnL alert on ${coin}: $${prevPnl.toFixed(2)} → $${currentPnl.toFixed(2)} (${pnlChangePct.toFixed(1)}% of position). Position value: $${posValue.toFixed(2)}`,
|
|
261
|
-
details: {
|
|
262
|
-
coin, previousPnl: prevPnl, currentPnl, changePct: pnlChangePct, positionValue: posValue,
|
|
263
|
-
},
|
|
264
|
-
detectedAt: now,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// 5. Margin usage warning
|
|
270
|
-
if (current.marginUsedPct >= this.marginUsageWarningPct) {
|
|
271
|
-
const prevMarginPct = prev.marginUsedPct;
|
|
272
|
-
if (prevMarginPct < this.marginUsageWarningPct || current.marginUsedPct - prevMarginPct >= 5) {
|
|
273
|
-
events.push({
|
|
274
|
-
type: 'margin_warning',
|
|
275
|
-
message: `Margin usage warning: ${current.marginUsedPct.toFixed(1)}% (equity: $${current.equity}, margin used: $${current.marginUsed})`,
|
|
276
|
-
details: {
|
|
277
|
-
marginUsedPct: current.marginUsedPct,
|
|
278
|
-
equity: current.equity,
|
|
279
|
-
marginUsed: current.marginUsed,
|
|
280
|
-
threshold: this.marginUsageWarningPct,
|
|
281
|
-
},
|
|
282
|
-
detectedAt: now,
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return events;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
private async sendHook(event: PositionEvent): Promise<void> {
|
|
291
|
-
const port = this.gatewayPort || 18789;
|
|
292
|
-
|
|
293
|
-
if (!this.hooksToken) {
|
|
294
|
-
this.logger.debug('sendHook skipped — no hooks token configured');
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
try {
|
|
299
|
-
const res = await fetch(`http://127.0.0.1:${port}/hooks/agent`, {
|
|
300
|
-
method: 'POST',
|
|
301
|
-
headers: {
|
|
302
|
-
'Content-Type': 'application/json',
|
|
303
|
-
'Authorization': `Bearer ${this.hooksToken}`,
|
|
304
|
-
},
|
|
305
|
-
body: JSON.stringify({
|
|
306
|
-
message: event.message,
|
|
307
|
-
name: `ob-watcher-${event.type}`,
|
|
308
|
-
wakeMode: 'now',
|
|
309
|
-
}),
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
if (!res.ok) {
|
|
313
|
-
this.logger.warn(`sendHook failed: HTTP ${res.status} ${res.statusText}`);
|
|
314
|
-
} else {
|
|
315
|
-
this.logger.debug(`sendHook delivered for ${event.type} (${event.message.length} chars)`);
|
|
316
|
-
}
|
|
317
|
-
} catch (err) {
|
|
318
|
-
this.logger.warn(`sendHook error: ${err instanceof Error ? err.message : String(err)}`);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|