@prbe.ai/electron-sdk 0.1.4 → 0.1.5
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/index.d.mts +332 -0
- package/dist/index.d.ts +331 -15
- package/dist/index.js +2471 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2402 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types-DHT-JxMT.d.mts +401 -0
- package/dist/types-DHT-JxMT.d.ts +401 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.d.ts +2 -14
- package/dist/types.js +160 -30
- package/dist/types.js.map +1 -1
- package/dist/types.mjs +125 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +22 -8
- package/dist/agent.d.ts +0 -106
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -878
- package/dist/agent.js.map +0 -1
- package/dist/assets/index.d.ts +0 -6
- package/dist/assets/index.d.ts.map +0 -1
- package/dist/assets/index.js +0 -13
- package/dist/assets/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/interactions.d.ts +0 -63
- package/dist/interactions.d.ts.map +0 -1
- package/dist/interactions.js +0 -27
- package/dist/interactions.js.map +0 -1
- package/dist/models.d.ts +0 -218
- package/dist/models.d.ts.map +0 -1
- package/dist/models.js +0 -121
- package/dist/models.js.map +0 -1
- package/dist/serialization.d.ts +0 -51
- package/dist/serialization.d.ts.map +0 -1
- package/dist/serialization.js +0 -72
- package/dist/serialization.js.map +0 -1
- package/dist/state.d.ts +0 -70
- package/dist/state.d.ts.map +0 -1
- package/dist/state.js +0 -303
- package/dist/state.js.map +0 -1
- package/dist/tools/bash.d.ts +0 -30
- package/dist/tools/bash.d.ts.map +0 -1
- package/dist/tools/bash.js +0 -248
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/filesystem.d.ts +0 -63
- package/dist/tools/filesystem.d.ts.map +0 -1
- package/dist/tools/filesystem.js +0 -573
- package/dist/tools/filesystem.js.map +0 -1
- package/dist/tools/index.d.ts +0 -46
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -171
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/interactive.d.ts +0 -15
- package/dist/tools/interactive.d.ts.map +0 -1
- package/dist/tools/interactive.js +0 -58
- package/dist/tools/interactive.js.map +0 -1
- package/dist/tools/logs.d.ts +0 -72
- package/dist/tools/logs.d.ts.map +0 -1
- package/dist/tools/logs.js +0 -366
- package/dist/tools/logs.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
package/dist/agent.js
DELETED
|
@@ -1,878 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* agent.ts — PRBEAgent class
|
|
4
|
-
*
|
|
5
|
-
* Main entry point for the PRBE debug agent SDK.
|
|
6
|
-
* Handles WebSocket connection to middleware, investigation lifecycle,
|
|
7
|
-
* background polling for context requests, file uploads, and persistence.
|
|
8
|
-
*
|
|
9
|
-
* Mirrors PRBEAgent.swift behavior.
|
|
10
|
-
*/
|
|
11
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
-
if (k2 === undefined) k2 = k;
|
|
13
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
-
}
|
|
17
|
-
Object.defineProperty(o, k2, desc);
|
|
18
|
-
}) : (function(o, m, k, k2) {
|
|
19
|
-
if (k2 === undefined) k2 = k;
|
|
20
|
-
o[k2] = m[k];
|
|
21
|
-
}));
|
|
22
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
-
}) : function(o, v) {
|
|
25
|
-
o["default"] = v;
|
|
26
|
-
});
|
|
27
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
-
var ownKeys = function(o) {
|
|
29
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
-
var ar = [];
|
|
31
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
-
return ar;
|
|
33
|
-
};
|
|
34
|
-
return ownKeys(o);
|
|
35
|
-
};
|
|
36
|
-
return function (mod) {
|
|
37
|
-
if (mod && mod.__esModule) return mod;
|
|
38
|
-
var result = {};
|
|
39
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
-
__setModuleDefault(result, mod);
|
|
41
|
-
return result;
|
|
42
|
-
};
|
|
43
|
-
})();
|
|
44
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.PRBEAgent = void 0;
|
|
46
|
-
const fs = __importStar(require("fs"));
|
|
47
|
-
const path = __importStar(require("path"));
|
|
48
|
-
const os = __importStar(require("os"));
|
|
49
|
-
const crypto_1 = require("crypto");
|
|
50
|
-
const models_1 = require("./models");
|
|
51
|
-
const models_2 = require("./models");
|
|
52
|
-
const state_1 = require("./state");
|
|
53
|
-
const index_1 = require("./tools/index");
|
|
54
|
-
const filesystem_1 = require("./tools/filesystem");
|
|
55
|
-
const logs_1 = require("./tools/logs");
|
|
56
|
-
const interactive_1 = require("./tools/interactive");
|
|
57
|
-
const bash_1 = require("./tools/bash");
|
|
58
|
-
const interactions_1 = require("./interactions");
|
|
59
|
-
function getPersistencePath() {
|
|
60
|
-
// Use platform-appropriate app data directory
|
|
61
|
-
const appData = process.env["APPDATA"] ||
|
|
62
|
-
(process.platform === "darwin"
|
|
63
|
-
? path.join(os.homedir(), "Library", "Application Support")
|
|
64
|
-
: path.join(os.homedir(), ".local", "share"));
|
|
65
|
-
const dir = path.join(appData, "prbe-agent");
|
|
66
|
-
if (!fs.existsSync(dir)) {
|
|
67
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
68
|
-
}
|
|
69
|
-
return path.join(dir, "agent-state.json");
|
|
70
|
-
}
|
|
71
|
-
function loadPersistedData() {
|
|
72
|
-
try {
|
|
73
|
-
const filePath = getPersistencePath();
|
|
74
|
-
if (fs.existsSync(filePath)) {
|
|
75
|
-
const raw = fs.readFileSync(filePath, "utf-8");
|
|
76
|
-
return JSON.parse(raw);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// Ignore read errors, return defaults
|
|
81
|
-
}
|
|
82
|
-
return {};
|
|
83
|
-
}
|
|
84
|
-
function savePersistedData(data) {
|
|
85
|
-
try {
|
|
86
|
-
const filePath = getPersistencePath();
|
|
87
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
console.error("[PRBEAgent] Failed to save persisted data");
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
// PRBEAgent
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
class PRBEAgent {
|
|
97
|
-
state;
|
|
98
|
-
logCapture;
|
|
99
|
-
config;
|
|
100
|
-
appDataPath;
|
|
101
|
-
interactionHandler;
|
|
102
|
-
registry = new index_1.PRBEToolRegistry();
|
|
103
|
-
grantedPaths = new Set();
|
|
104
|
-
userCancelled = false;
|
|
105
|
-
activeWS = null;
|
|
106
|
-
pollingTimer = null;
|
|
107
|
-
persistedData;
|
|
108
|
-
fetchAbortController = null;
|
|
109
|
-
currentInvestigationSource = interactions_1.InvestigationSource.USER;
|
|
110
|
-
currentCRId = null;
|
|
111
|
-
/** Files flagged during the current tool call — uploaded immediately after the tool returns. */
|
|
112
|
-
pendingFlaggedFiles = [];
|
|
113
|
-
// ---------- Persistence ----------
|
|
114
|
-
get agentID() {
|
|
115
|
-
if (this.persistedData.agentId) {
|
|
116
|
-
return this.persistedData.agentId;
|
|
117
|
-
}
|
|
118
|
-
const newID = (0, crypto_1.randomUUID)();
|
|
119
|
-
this.persistedData.agentId = newID;
|
|
120
|
-
savePersistedData(this.persistedData);
|
|
121
|
-
return newID;
|
|
122
|
-
}
|
|
123
|
-
get trackedTicketIDs() {
|
|
124
|
-
return this.persistedData.ticketIds ?? [];
|
|
125
|
-
}
|
|
126
|
-
set trackedTicketIDs(ids) {
|
|
127
|
-
this.persistedData.ticketIds = ids;
|
|
128
|
-
savePersistedData(this.persistedData);
|
|
129
|
-
this.state.updateTrackedTicketIDs(ids);
|
|
130
|
-
this.syncPolling(ids.length > 0);
|
|
131
|
-
}
|
|
132
|
-
get respondedCRIDs() {
|
|
133
|
-
return new Set(this.persistedData.respondedCRIds ?? []);
|
|
134
|
-
}
|
|
135
|
-
set respondedCRIDs(ids) {
|
|
136
|
-
this.persistedData.respondedCRIds = Array.from(ids);
|
|
137
|
-
savePersistedData(this.persistedData);
|
|
138
|
-
}
|
|
139
|
-
syncPolling(hasTickets) {
|
|
140
|
-
if (this.config.backgroundPolling && hasTickets) {
|
|
141
|
-
if (this.pollingTimer === null) {
|
|
142
|
-
this.startPolling();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
else if (!hasTickets) {
|
|
146
|
-
this.stopPolling();
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
addTrackedTicket(id) {
|
|
150
|
-
const ids = this.trackedTicketIDs;
|
|
151
|
-
if (!ids.includes(id)) {
|
|
152
|
-
this.trackedTicketIDs = [...ids, id];
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// ---------- Constructor ----------
|
|
156
|
-
constructor(config) {
|
|
157
|
-
this.config = {
|
|
158
|
-
apiKey: config.apiKey,
|
|
159
|
-
autoApprovedDirs: config.autoApprovedDirs,
|
|
160
|
-
pollingInterval: config.pollingInterval ?? 600_000,
|
|
161
|
-
maxLogEntries: config.maxLogEntries ?? 10_000,
|
|
162
|
-
captureConsole: config.captureConsole ?? true,
|
|
163
|
-
backgroundPolling: config.backgroundPolling ?? true,
|
|
164
|
-
};
|
|
165
|
-
this.interactionHandler = config.interactionHandler;
|
|
166
|
-
this.appDataPath = config.appDataPath;
|
|
167
|
-
// Auto-add appDataPath to autoApprovedDirs if not already present
|
|
168
|
-
if (this.appDataPath && !this.config.autoApprovedDirs.includes(this.appDataPath)) {
|
|
169
|
-
this.config.autoApprovedDirs.push(this.appDataPath);
|
|
170
|
-
}
|
|
171
|
-
this.state = new state_1.PRBEAgentState();
|
|
172
|
-
this.logCapture = new logs_1.PRBELogCapture(this.config.maxLogEntries);
|
|
173
|
-
this.persistedData = loadPersistedData();
|
|
174
|
-
const roots = this.config.autoApprovedDirs;
|
|
175
|
-
const requester = this.interactionHandler ? this : undefined;
|
|
176
|
-
const grantedPaths = this.grantedPaths;
|
|
177
|
-
// Register built-in filesystem tools
|
|
178
|
-
this.registry.register(new filesystem_1.ListDirectoryTool(roots, requester, grantedPaths));
|
|
179
|
-
this.registry.register(new filesystem_1.ReadFileTool(roots, requester, grantedPaths));
|
|
180
|
-
this.registry.register(new filesystem_1.SearchContentTool(roots, requester, grantedPaths));
|
|
181
|
-
this.registry.register(new filesystem_1.FindFilesTool(roots, requester, grantedPaths));
|
|
182
|
-
this.registry.register(new filesystem_1.FlagFileTool(roots, (file) => {
|
|
183
|
-
this.pendingFlaggedFiles.push(file);
|
|
184
|
-
}, requester, grantedPaths));
|
|
185
|
-
// Register built-in log tools
|
|
186
|
-
this.registry.register(new logs_1.ReadAppLogsTool(this.logCapture));
|
|
187
|
-
this.registry.register(new logs_1.SearchAppLogsTool(this.logCapture));
|
|
188
|
-
this.registry.register(new logs_1.ClearAppLogsTool(this.logCapture));
|
|
189
|
-
this.registry.register(new logs_1.FlagAppLogsTool(this.logCapture, (file) => {
|
|
190
|
-
this.pendingFlaggedFiles.push(file);
|
|
191
|
-
}));
|
|
192
|
-
// Register interactive tools (only when handler is available)
|
|
193
|
-
if (requester) {
|
|
194
|
-
this.registry.register(new interactive_1.AskUserTool(requester));
|
|
195
|
-
this.registry.register(new bash_1.BashExecuteTool(requester, roots, grantedPaths));
|
|
196
|
-
}
|
|
197
|
-
// Start console capture if configured
|
|
198
|
-
if (this.config.captureConsole) {
|
|
199
|
-
this.logCapture.startCapturing();
|
|
200
|
-
}
|
|
201
|
-
// Hook electron-log if provided
|
|
202
|
-
if (config.electronLog) {
|
|
203
|
-
this.hookElectronLog(config.electronLog);
|
|
204
|
-
}
|
|
205
|
-
// Listen for renderer log forwarding via IPC if provided
|
|
206
|
-
if (config.ipcMain) {
|
|
207
|
-
this.hookRendererLogs(config.ipcMain, config.rendererLogChannel ?? "prbe-renderer-log");
|
|
208
|
-
}
|
|
209
|
-
// Bootstrap: trigger syncPolling + state update for existing tickets
|
|
210
|
-
const existingTickets = this.trackedTicketIDs;
|
|
211
|
-
if (existingTickets.length > 0) {
|
|
212
|
-
this.trackedTicketIDs = existingTickets;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// ---------- Log integration ----------
|
|
216
|
-
hookElectronLog(electronLog) {
|
|
217
|
-
try {
|
|
218
|
-
electronLog.hooks.push((message, _transportFn, transportName) => {
|
|
219
|
-
if (transportName !== "file")
|
|
220
|
-
return message;
|
|
221
|
-
try {
|
|
222
|
-
const text = message.data
|
|
223
|
-
.map((d) => (typeof d === "string" ? d : JSON.stringify(d)))
|
|
224
|
-
.join(" ");
|
|
225
|
-
const level = PRBEAgent.mapElectronLogLevel(message.level);
|
|
226
|
-
this.logCapture.log(text, level, `electron-log.${message.level}`);
|
|
227
|
-
}
|
|
228
|
-
catch {
|
|
229
|
-
// ignore serialization errors
|
|
230
|
-
}
|
|
231
|
-
return message;
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
catch {
|
|
235
|
-
// electron-log not available or incompatible
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
hookRendererLogs(ipcMain, channel) {
|
|
239
|
-
ipcMain.on(channel, (_event, entry) => {
|
|
240
|
-
this.logCapture.log(entry.message, entry.level, `renderer.${entry.category}`);
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
static mapElectronLogLevel(level) {
|
|
244
|
-
switch (level) {
|
|
245
|
-
case "error": return "ERROR";
|
|
246
|
-
case "warn": return "WARNING";
|
|
247
|
-
case "debug":
|
|
248
|
-
case "verbose":
|
|
249
|
-
case "silly": return "DEBUG";
|
|
250
|
-
default: return "INFO";
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
// ---------- PRBEInteractionRequester implementation ----------
|
|
254
|
-
get investigationSource() {
|
|
255
|
-
return this.currentInvestigationSource;
|
|
256
|
-
}
|
|
257
|
-
async requestUserInteraction(payload) {
|
|
258
|
-
if (!this.interactionHandler) {
|
|
259
|
-
throw new models_2.PRBEAgentError(models_2.PRBEAgentErrorType.SERVER_ERROR, "No interaction handler configured");
|
|
260
|
-
}
|
|
261
|
-
// Update state to show pending interaction
|
|
262
|
-
if (this.currentInvestigationSource === interactions_1.InvestigationSource.CONTEXT_REQUEST && this.currentCRId) {
|
|
263
|
-
this.state.setCRPendingInteraction(this.currentCRId, payload);
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
this.state.setPendingInteraction(payload);
|
|
267
|
-
}
|
|
268
|
-
try {
|
|
269
|
-
const response = await this.interactionHandler.handleInteraction(payload);
|
|
270
|
-
if (this.currentInvestigationSource === interactions_1.InvestigationSource.CONTEXT_REQUEST && this.currentCRId) {
|
|
271
|
-
this.state.resolveCRInteraction(this.currentCRId, response);
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
this.state.resolveInteraction(response);
|
|
275
|
-
}
|
|
276
|
-
return response;
|
|
277
|
-
}
|
|
278
|
-
catch (err) {
|
|
279
|
-
if (this.currentInvestigationSource === interactions_1.InvestigationSource.CONTEXT_REQUEST && this.currentCRId) {
|
|
280
|
-
this.state.clearCRPendingInteraction(this.currentCRId);
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
this.state.clearPendingInteraction();
|
|
284
|
-
}
|
|
285
|
-
throw err;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Add an additional root directory to the runtime allowed roots.
|
|
290
|
-
*/
|
|
291
|
-
addAutoApprovedDir(rootPath) {
|
|
292
|
-
if (!this.config.autoApprovedDirs.includes(rootPath)) {
|
|
293
|
-
this.config.autoApprovedDirs.push(rootPath);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
// ---------- Public API ----------
|
|
297
|
-
/**
|
|
298
|
-
* Register a custom tool that the middleware can invoke during investigations.
|
|
299
|
-
*/
|
|
300
|
-
registerTool(name, description, parameters, handler) {
|
|
301
|
-
this.registry.register(new index_1.PRBEClosureTool(name, description, parameters, handler));
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* User-initiated investigation. Updates `state` events/report directly.
|
|
305
|
-
*/
|
|
306
|
-
async investigate(query, contextRequestID) {
|
|
307
|
-
this.userCancelled = false;
|
|
308
|
-
this.currentInvestigationSource = interactions_1.InvestigationSource.USER;
|
|
309
|
-
this.currentCRId = null;
|
|
310
|
-
this.state.beginInvestigation(query);
|
|
311
|
-
const emitter = (status) => {
|
|
312
|
-
// Update state based on status
|
|
313
|
-
switch (status.type) {
|
|
314
|
-
case models_2.PRBEAgentStatusType.STARTED:
|
|
315
|
-
this.state.appendEvent("Starting investigation...");
|
|
316
|
-
break;
|
|
317
|
-
case models_2.PRBEAgentStatusType.THINKING:
|
|
318
|
-
break;
|
|
319
|
-
case models_2.PRBEAgentStatusType.TOOL_CALL:
|
|
320
|
-
this.state.appendEvent(status.label);
|
|
321
|
-
break;
|
|
322
|
-
case models_2.PRBEAgentStatusType.OBSERVATION:
|
|
323
|
-
this.state.attachObservation(status.text);
|
|
324
|
-
break;
|
|
325
|
-
case models_2.PRBEAgentStatusType.THOUGHT:
|
|
326
|
-
this.state.appendEvent("Thinking", status.text);
|
|
327
|
-
break;
|
|
328
|
-
case models_2.PRBEAgentStatusType.COMPLETED:
|
|
329
|
-
this.state.completeInvestigation(status.report, status.userSummary);
|
|
330
|
-
break;
|
|
331
|
-
case models_2.PRBEAgentStatusType.ERROR:
|
|
332
|
-
this.state.failInvestigation(status.message);
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
const result = await this.connectToProxy(query, contextRequestID, emitter, () => this.userCancelled);
|
|
337
|
-
this.currentInvestigationSource = interactions_1.InvestigationSource.USER;
|
|
338
|
-
this.currentCRId = null;
|
|
339
|
-
if (result?.ticketId) {
|
|
340
|
-
this.addTrackedTicket(result.ticketId);
|
|
341
|
-
}
|
|
342
|
-
else if (!result) {
|
|
343
|
-
// No result — either cancelled or errored
|
|
344
|
-
if (this.state.isInvestigating) {
|
|
345
|
-
const message = this.userCancelled
|
|
346
|
-
? "Investigation cancelled"
|
|
347
|
-
: "Investigation ended unexpectedly";
|
|
348
|
-
this.state.failInvestigation(message);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Cancel user-initiated investigation only (does not cancel background CRs).
|
|
354
|
-
*/
|
|
355
|
-
cancelInvestigation() {
|
|
356
|
-
this.userCancelled = true;
|
|
357
|
-
if (this.activeWS) {
|
|
358
|
-
this.activeWS.close(1000, "User cancelled");
|
|
359
|
-
this.activeWS = null;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Cancel everything — user investigation and polling.
|
|
364
|
-
*/
|
|
365
|
-
cancel() {
|
|
366
|
-
this.userCancelled = true;
|
|
367
|
-
if (this.activeWS) {
|
|
368
|
-
this.activeWS.close(1000, "User cancelled");
|
|
369
|
-
this.activeWS = null;
|
|
370
|
-
}
|
|
371
|
-
this.abortInFlightRequests();
|
|
372
|
-
this.stopPolling();
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Poll the backend for context requests on tracked tickets.
|
|
376
|
-
*/
|
|
377
|
-
async poll() {
|
|
378
|
-
const ticketIDs = this.trackedTicketIDs;
|
|
379
|
-
if (ticketIDs.length === 0)
|
|
380
|
-
return null;
|
|
381
|
-
const request = {
|
|
382
|
-
agent_id: this.agentID,
|
|
383
|
-
ticket_ids: ticketIDs,
|
|
384
|
-
};
|
|
385
|
-
try {
|
|
386
|
-
const response = await this.post("/api/agent/poll", request);
|
|
387
|
-
// Discard ticket IDs the backend didn't return (deleted/unknown)
|
|
388
|
-
const returnedIDs = new Set(response.tickets.map((t) => t.ticket_id));
|
|
389
|
-
const orphaned = ticketIDs.filter((id) => !returnedIDs.has(id));
|
|
390
|
-
if (orphaned.length > 0) {
|
|
391
|
-
this.trackedTicketIDs = this.trackedTicketIDs.filter((id) => !orphaned.includes(id));
|
|
392
|
-
}
|
|
393
|
-
// Collect all CR IDs the backend knows about for pruning
|
|
394
|
-
const knownCRIDs = new Set();
|
|
395
|
-
for (const ticket of response.tickets) {
|
|
396
|
-
if (ticket.status === "resolved") {
|
|
397
|
-
this.trackedTicketIDs = this.trackedTicketIDs.filter((id) => id !== ticket.ticket_id);
|
|
398
|
-
}
|
|
399
|
-
for (const cr of ticket.context_requests) {
|
|
400
|
-
knownCRIDs.add(cr.id);
|
|
401
|
-
// Skip inactive or already-responded CRs
|
|
402
|
-
if (!cr.is_active || this.respondedCRIDs.has(cr.id))
|
|
403
|
-
continue;
|
|
404
|
-
await this.investigateForCR(cr, ticket.ticket_id);
|
|
405
|
-
const ids = this.respondedCRIDs;
|
|
406
|
-
ids.add(cr.id);
|
|
407
|
-
this.respondedCRIDs = ids;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
// Prune responded CR IDs for CRs the backend no longer returns
|
|
411
|
-
const currentRespondedIDs = this.respondedCRIDs;
|
|
412
|
-
const stale = new Set([...currentRespondedIDs].filter((id) => !knownCRIDs.has(id)));
|
|
413
|
-
if (stale.size > 0) {
|
|
414
|
-
const pruned = new Set([...currentRespondedIDs].filter((id) => !stale.has(id)));
|
|
415
|
-
this.respondedCRIDs = pruned;
|
|
416
|
-
}
|
|
417
|
-
return response;
|
|
418
|
-
}
|
|
419
|
-
catch {
|
|
420
|
-
return null;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Fetch ticket display info for all tracked tickets.
|
|
425
|
-
*/
|
|
426
|
-
async fetchTicketInfo() {
|
|
427
|
-
const ticketIDs = this.trackedTicketIDs;
|
|
428
|
-
if (ticketIDs.length === 0)
|
|
429
|
-
return [];
|
|
430
|
-
const request = { ticket_ids: ticketIDs };
|
|
431
|
-
try {
|
|
432
|
-
const response = await this.post("/api/agent/tickets", request);
|
|
433
|
-
this.state.updateTicketInfo(response.tickets);
|
|
434
|
-
return response.tickets;
|
|
435
|
-
}
|
|
436
|
-
catch {
|
|
437
|
-
return [];
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
stopPolling() {
|
|
441
|
-
if (this.pollingTimer !== null) {
|
|
442
|
-
clearInterval(this.pollingTimer);
|
|
443
|
-
this.pollingTimer = null;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
resumePolling() {
|
|
447
|
-
if (this.trackedTicketIDs.length === 0)
|
|
448
|
-
return;
|
|
449
|
-
this.startPolling();
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Destroy the agent — stops polling, stops console capture, closes WS.
|
|
453
|
-
*/
|
|
454
|
-
destroy() {
|
|
455
|
-
this.cancel();
|
|
456
|
-
this.logCapture.stopCapturing();
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Reset all persisted data (agent ID, tracked tickets, responded CRs).
|
|
460
|
-
* Also clears in-memory state.
|
|
461
|
-
*/
|
|
462
|
-
resetPersistedData() {
|
|
463
|
-
this.stopPolling();
|
|
464
|
-
this.persistedData = {};
|
|
465
|
-
savePersistedData(this.persistedData);
|
|
466
|
-
this.state.resetInvestigation();
|
|
467
|
-
this.state.completedInvestigations = [];
|
|
468
|
-
this.state.activeCRs.clear();
|
|
469
|
-
this.state.completedCRs = [];
|
|
470
|
-
this.state.trackedTicketIDs = [];
|
|
471
|
-
this.state.ticketInfo = [];
|
|
472
|
-
this.state.emit(state_1.PRBEStateEvent.STATUS);
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* Delete the persisted data file. Can be called without an agent instance.
|
|
476
|
-
*/
|
|
477
|
-
static clearPersistedData() {
|
|
478
|
-
try {
|
|
479
|
-
const filePath = getPersistencePath();
|
|
480
|
-
if (fs.existsSync(filePath)) {
|
|
481
|
-
fs.unlinkSync(filePath);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
catch {
|
|
485
|
-
// Ignore
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
// ---------- CR Investigation ----------
|
|
489
|
-
async investigateForCR(cr, ticketId) {
|
|
490
|
-
const crID = cr.id;
|
|
491
|
-
this.currentInvestigationSource = interactions_1.InvestigationSource.CONTEXT_REQUEST;
|
|
492
|
-
this.currentCRId = crID;
|
|
493
|
-
this.state.beginCR(crID, cr.query, cr.slug ?? undefined);
|
|
494
|
-
const emitter = (status) => {
|
|
495
|
-
switch (status.type) {
|
|
496
|
-
case models_2.PRBEAgentStatusType.STARTED:
|
|
497
|
-
this.state.appendCREvent(crID, "Starting investigation...");
|
|
498
|
-
break;
|
|
499
|
-
case models_2.PRBEAgentStatusType.THINKING:
|
|
500
|
-
break;
|
|
501
|
-
case models_2.PRBEAgentStatusType.TOOL_CALL:
|
|
502
|
-
this.state.appendCREvent(crID, status.label);
|
|
503
|
-
break;
|
|
504
|
-
case models_2.PRBEAgentStatusType.OBSERVATION:
|
|
505
|
-
this.state.attachCRObservation(crID, status.text);
|
|
506
|
-
break;
|
|
507
|
-
case models_2.PRBEAgentStatusType.THOUGHT:
|
|
508
|
-
this.state.appendCREvent(crID, "Thinking", status.text);
|
|
509
|
-
break;
|
|
510
|
-
case models_2.PRBEAgentStatusType.COMPLETED:
|
|
511
|
-
this.state.completeCR(crID, status.report, status.userSummary);
|
|
512
|
-
break;
|
|
513
|
-
case models_2.PRBEAgentStatusType.ERROR:
|
|
514
|
-
this.state.failCR(crID, status.message);
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
};
|
|
518
|
-
const result = await this.connectToProxy(cr.query, crID, emitter, () => false, // CRs are not user-cancellable
|
|
519
|
-
ticketId);
|
|
520
|
-
this.currentInvestigationSource = interactions_1.InvestigationSource.USER;
|
|
521
|
-
this.currentCRId = null;
|
|
522
|
-
if (result?.ticketId) {
|
|
523
|
-
this.addTrackedTicket(result.ticketId);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
// ---------- WebSocket Investigation ----------
|
|
527
|
-
connectToProxy(query, contextRequestID, emit, isCancelled, ticketId) {
|
|
528
|
-
return new Promise((resolve) => {
|
|
529
|
-
const wsUrl = `${models_1.MIDDLEWARE_URL}/api/agent/client/ws`;
|
|
530
|
-
let ws;
|
|
531
|
-
try {
|
|
532
|
-
ws = new WebSocket(wsUrl, {
|
|
533
|
-
headers: {
|
|
534
|
-
"X-API-Key": this.config.apiKey,
|
|
535
|
-
},
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
catch (err) {
|
|
539
|
-
emit({
|
|
540
|
-
type: models_2.PRBEAgentStatusType.ERROR,
|
|
541
|
-
message: `Failed to create WebSocket: ${err}`,
|
|
542
|
-
});
|
|
543
|
-
resolve(null);
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
this.activeWS = ws;
|
|
547
|
-
let uploadBaseUrl;
|
|
548
|
-
let resolved = false;
|
|
549
|
-
const finish = (result) => {
|
|
550
|
-
if (resolved)
|
|
551
|
-
return;
|
|
552
|
-
resolved = true;
|
|
553
|
-
this.activeWS = null;
|
|
554
|
-
resolve(result);
|
|
555
|
-
};
|
|
556
|
-
ws.onopen = () => {
|
|
557
|
-
// Build tool declarations for the start message
|
|
558
|
-
const toolDeclarations = this.registry
|
|
559
|
-
.allDeclarations()
|
|
560
|
-
.map((decl) => ({
|
|
561
|
-
name: decl.name,
|
|
562
|
-
description: decl.description,
|
|
563
|
-
parameters: decl.parameters.map((param) => ({
|
|
564
|
-
name: param.name,
|
|
565
|
-
type: param.type,
|
|
566
|
-
description: param.description,
|
|
567
|
-
required: param.required,
|
|
568
|
-
})),
|
|
569
|
-
...(decl.interactive ? { interactive: true } : {}),
|
|
570
|
-
}));
|
|
571
|
-
const startMetadata = {
|
|
572
|
-
agent_id: this.agentID,
|
|
573
|
-
custom_tools: toolDeclarations,
|
|
574
|
-
};
|
|
575
|
-
if (contextRequestID) {
|
|
576
|
-
startMetadata["context_request_id"] = contextRequestID;
|
|
577
|
-
}
|
|
578
|
-
if (ticketId) {
|
|
579
|
-
startMetadata["ticket_id"] = ticketId;
|
|
580
|
-
}
|
|
581
|
-
if (this.appDataPath) {
|
|
582
|
-
startMetadata["app_data_path"] = this.appDataPath;
|
|
583
|
-
}
|
|
584
|
-
const startMsg = {
|
|
585
|
-
type: models_1.WSMessageType.START,
|
|
586
|
-
content: query,
|
|
587
|
-
metadata: startMetadata,
|
|
588
|
-
};
|
|
589
|
-
try {
|
|
590
|
-
ws.send(JSON.stringify(startMsg));
|
|
591
|
-
}
|
|
592
|
-
catch (err) {
|
|
593
|
-
emit({
|
|
594
|
-
type: models_2.PRBEAgentStatusType.ERROR,
|
|
595
|
-
message: `Failed to send start message: ${err}`,
|
|
596
|
-
});
|
|
597
|
-
finish(null);
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
emit({ type: models_2.PRBEAgentStatusType.STARTED });
|
|
601
|
-
this.pendingFlaggedFiles = [];
|
|
602
|
-
};
|
|
603
|
-
ws.onmessage = async (event) => {
|
|
604
|
-
if (isCancelled()) {
|
|
605
|
-
this.sendCancel(ws);
|
|
606
|
-
ws.close(1000, "Cancelled");
|
|
607
|
-
finish(null);
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
let raw;
|
|
611
|
-
if (typeof event.data === "string") {
|
|
612
|
-
raw = event.data;
|
|
613
|
-
}
|
|
614
|
-
else if (event.data instanceof Buffer) {
|
|
615
|
-
raw = event.data.toString("utf-8");
|
|
616
|
-
}
|
|
617
|
-
else {
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
let msg;
|
|
621
|
-
try {
|
|
622
|
-
msg = JSON.parse(raw);
|
|
623
|
-
}
|
|
624
|
-
catch {
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
switch (msg.type) {
|
|
628
|
-
case models_1.WSMessageType.THOUGHT:
|
|
629
|
-
emit({
|
|
630
|
-
type: models_2.PRBEAgentStatusType.THOUGHT,
|
|
631
|
-
text: msg.content ?? "",
|
|
632
|
-
});
|
|
633
|
-
break;
|
|
634
|
-
case models_1.WSMessageType.TOOL_CALL: {
|
|
635
|
-
const toolName = msg.name ?? "";
|
|
636
|
-
const callId = msg.id ?? "";
|
|
637
|
-
const args = this.extractArgs(msg.metadata);
|
|
638
|
-
emit({
|
|
639
|
-
type: models_2.PRBEAgentStatusType.TOOL_CALL,
|
|
640
|
-
name: toolName,
|
|
641
|
-
label: msg.content ?? `Running ${toolName}`,
|
|
642
|
-
});
|
|
643
|
-
// Clear before execution so we only capture files flagged by this tool call
|
|
644
|
-
this.pendingFlaggedFiles = [];
|
|
645
|
-
const toolResult = (0, models_1.redactPII)(await this.registry.execute(toolName, args));
|
|
646
|
-
emit({
|
|
647
|
-
type: models_2.PRBEAgentStatusType.OBSERVATION,
|
|
648
|
-
text: toolResult.substring(0, 200),
|
|
649
|
-
});
|
|
650
|
-
// Build refs for any files flagged during this tool call
|
|
651
|
-
let resultMetadata;
|
|
652
|
-
if (this.pendingFlaggedFiles.length > 0 && uploadBaseUrl) {
|
|
653
|
-
const uploadPrefix = this.extractUploadPrefix(uploadBaseUrl);
|
|
654
|
-
const uploadedRefs = [];
|
|
655
|
-
for (const file of this.pendingFlaggedFiles) {
|
|
656
|
-
const filename = path.basename(file.originalPath);
|
|
657
|
-
const safeName = encodeURIComponent(filename);
|
|
658
|
-
const storagePath = `${uploadPrefix}/${safeName}`;
|
|
659
|
-
uploadedRefs.push({
|
|
660
|
-
original_path: file.originalPath,
|
|
661
|
-
reason: file.reason ?? "",
|
|
662
|
-
storage_path: storagePath,
|
|
663
|
-
file_size_bytes: file.data.length,
|
|
664
|
-
});
|
|
665
|
-
// Background upload — don't block the tool_result
|
|
666
|
-
const uploadUrl = `${uploadBaseUrl}/${safeName}`;
|
|
667
|
-
const fileData = file.data;
|
|
668
|
-
const contentType = file.isText
|
|
669
|
-
? "text/plain"
|
|
670
|
-
: "application/octet-stream";
|
|
671
|
-
void PRBEAgent.backgroundUpload(fileData, uploadUrl, this.config.apiKey, contentType, this.getFetchSignal());
|
|
672
|
-
}
|
|
673
|
-
resultMetadata = { flagged_files: uploadedRefs };
|
|
674
|
-
this.pendingFlaggedFiles = [];
|
|
675
|
-
}
|
|
676
|
-
const resultMsg = {
|
|
677
|
-
type: models_1.WSMessageType.TOOL_RESULT,
|
|
678
|
-
id: callId,
|
|
679
|
-
name: toolName,
|
|
680
|
-
content: toolResult,
|
|
681
|
-
metadata: resultMetadata,
|
|
682
|
-
};
|
|
683
|
-
try {
|
|
684
|
-
ws.send(JSON.stringify(resultMsg));
|
|
685
|
-
}
|
|
686
|
-
catch {
|
|
687
|
-
// Send failed — will be caught by onerror/onclose
|
|
688
|
-
}
|
|
689
|
-
break;
|
|
690
|
-
}
|
|
691
|
-
case models_1.WSMessageType.SERVER_TOOL_CALL:
|
|
692
|
-
emit({
|
|
693
|
-
type: models_2.PRBEAgentStatusType.TOOL_CALL,
|
|
694
|
-
name: msg.name ?? "",
|
|
695
|
-
label: msg.content ?? "",
|
|
696
|
-
});
|
|
697
|
-
break;
|
|
698
|
-
case models_1.WSMessageType.SERVER_OBSERVATION:
|
|
699
|
-
emit({
|
|
700
|
-
type: models_2.PRBEAgentStatusType.OBSERVATION,
|
|
701
|
-
text: (msg.content ?? "").substring(0, 200),
|
|
702
|
-
});
|
|
703
|
-
break;
|
|
704
|
-
case models_1.WSMessageType.COMPLETE: {
|
|
705
|
-
const report = msg.content ?? "";
|
|
706
|
-
const userSummary = msg.metadata?.["user_summary"] ?? "";
|
|
707
|
-
const ticketId = msg.metadata?.["ticket_id"];
|
|
708
|
-
emit({
|
|
709
|
-
type: models_2.PRBEAgentStatusType.COMPLETED,
|
|
710
|
-
report,
|
|
711
|
-
userSummary,
|
|
712
|
-
});
|
|
713
|
-
ws.close(1000, "Complete");
|
|
714
|
-
finish({ report, userSummary, ticketId });
|
|
715
|
-
break;
|
|
716
|
-
}
|
|
717
|
-
case models_1.WSMessageType.ERROR:
|
|
718
|
-
emit({
|
|
719
|
-
type: models_2.PRBEAgentStatusType.ERROR,
|
|
720
|
-
message: msg.content || "Unknown error",
|
|
721
|
-
});
|
|
722
|
-
ws.close(1000, "Error received");
|
|
723
|
-
finish(null);
|
|
724
|
-
break;
|
|
725
|
-
case models_1.WSMessageType.SESSION_CONFIG:
|
|
726
|
-
uploadBaseUrl = msg.metadata?.["upload_url"];
|
|
727
|
-
break;
|
|
728
|
-
case models_1.WSMessageType.PING: {
|
|
729
|
-
const pongMsg = { type: models_1.WSMessageType.PONG };
|
|
730
|
-
try {
|
|
731
|
-
ws.send(JSON.stringify(pongMsg));
|
|
732
|
-
}
|
|
733
|
-
catch {
|
|
734
|
-
// Ignore pong send failures
|
|
735
|
-
}
|
|
736
|
-
break;
|
|
737
|
-
}
|
|
738
|
-
// SDK-originated types and upload_url — ignore
|
|
739
|
-
case models_1.WSMessageType.START:
|
|
740
|
-
case models_1.WSMessageType.TOOL_RESULT:
|
|
741
|
-
case models_1.WSMessageType.UPLOAD_REQUEST:
|
|
742
|
-
case models_1.WSMessageType.CANCEL:
|
|
743
|
-
case models_1.WSMessageType.PONG:
|
|
744
|
-
case models_1.WSMessageType.UPLOAD_URL:
|
|
745
|
-
break;
|
|
746
|
-
}
|
|
747
|
-
};
|
|
748
|
-
ws.onerror = (event) => {
|
|
749
|
-
if (isCancelled()) {
|
|
750
|
-
finish(null);
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
const errorEvent = event;
|
|
754
|
-
const message = errorEvent.message || "WebSocket connection error";
|
|
755
|
-
emit({ type: models_2.PRBEAgentStatusType.ERROR, message });
|
|
756
|
-
finish(null);
|
|
757
|
-
};
|
|
758
|
-
ws.onclose = (_event) => {
|
|
759
|
-
// If we haven't resolved yet, treat as unexpected close
|
|
760
|
-
if (!resolved) {
|
|
761
|
-
if (isCancelled()) {
|
|
762
|
-
finish(null);
|
|
763
|
-
}
|
|
764
|
-
else {
|
|
765
|
-
emit({
|
|
766
|
-
type: models_2.PRBEAgentStatusType.ERROR,
|
|
767
|
-
message: "WebSocket connection closed unexpectedly",
|
|
768
|
-
});
|
|
769
|
-
finish(null);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
};
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
sendCancel(ws) {
|
|
776
|
-
try {
|
|
777
|
-
const cancelMsg = { type: models_1.WSMessageType.CANCEL };
|
|
778
|
-
ws.send(JSON.stringify(cancelMsg));
|
|
779
|
-
}
|
|
780
|
-
catch {
|
|
781
|
-
// Ignore
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
extractArgs(metadata) {
|
|
785
|
-
if (!metadata)
|
|
786
|
-
return {};
|
|
787
|
-
const argsVal = metadata["args"];
|
|
788
|
-
if (argsVal && typeof argsVal === "object" && !Array.isArray(argsVal)) {
|
|
789
|
-
return argsVal;
|
|
790
|
-
}
|
|
791
|
-
return {};
|
|
792
|
-
}
|
|
793
|
-
extractUploadPrefix(baseUrl) {
|
|
794
|
-
try {
|
|
795
|
-
const url = new URL(baseUrl);
|
|
796
|
-
const urlPath = url.pathname; // "/api/agent/upload/{tenant}/{uuid}/files"
|
|
797
|
-
const marker = "/api/agent/upload/";
|
|
798
|
-
const idx = urlPath.indexOf(marker);
|
|
799
|
-
if (idx !== -1) {
|
|
800
|
-
return urlPath.substring(idx + marker.length);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
catch {
|
|
804
|
-
// Ignore URL parse errors
|
|
805
|
-
}
|
|
806
|
-
return "";
|
|
807
|
-
}
|
|
808
|
-
// ---------- File Upload ----------
|
|
809
|
-
static async backgroundUpload(data, uploadUrl, apiKey, contentType, signal) {
|
|
810
|
-
try {
|
|
811
|
-
const response = await fetch(uploadUrl, {
|
|
812
|
-
method: "PUT",
|
|
813
|
-
headers: {
|
|
814
|
-
"Content-Type": contentType,
|
|
815
|
-
"X-API-Key": apiKey,
|
|
816
|
-
},
|
|
817
|
-
body: data,
|
|
818
|
-
signal,
|
|
819
|
-
});
|
|
820
|
-
if (!response.ok) {
|
|
821
|
-
console.error(`[PRBEAgent] file upload failed: HTTP ${response.status} for ${uploadUrl}`);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
catch (err) {
|
|
825
|
-
console.error(`[PRBEAgent] file upload error: ${err} for ${uploadUrl}`);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
// ---------- Polling ----------
|
|
829
|
-
startPolling() {
|
|
830
|
-
this.stopPolling();
|
|
831
|
-
this.pollingTimer = setInterval(() => {
|
|
832
|
-
void this.poll().then(() => {
|
|
833
|
-
if (this.trackedTicketIDs.length === 0) {
|
|
834
|
-
this.stopPolling();
|
|
835
|
-
}
|
|
836
|
-
});
|
|
837
|
-
}, this.config.pollingInterval);
|
|
838
|
-
}
|
|
839
|
-
// ---------- Networking ----------
|
|
840
|
-
/**
|
|
841
|
-
* Abort all in-flight fetch requests to prevent orphaned DNS lookups
|
|
842
|
-
* that can crash the c-ares resolver during process shutdown.
|
|
843
|
-
*/
|
|
844
|
-
abortInFlightRequests() {
|
|
845
|
-
if (this.fetchAbortController) {
|
|
846
|
-
this.fetchAbortController.abort();
|
|
847
|
-
this.fetchAbortController = null;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
/**
|
|
851
|
-
* Get an AbortSignal for fetch requests. Creates a new controller if needed.
|
|
852
|
-
*/
|
|
853
|
-
getFetchSignal() {
|
|
854
|
-
if (!this.fetchAbortController) {
|
|
855
|
-
this.fetchAbortController = new AbortController();
|
|
856
|
-
}
|
|
857
|
-
return this.fetchAbortController.signal;
|
|
858
|
-
}
|
|
859
|
-
async post(urlPath, body) {
|
|
860
|
-
const url = `${models_1.API_URL}${urlPath}`;
|
|
861
|
-
const response = await fetch(url, {
|
|
862
|
-
method: "POST",
|
|
863
|
-
headers: {
|
|
864
|
-
"Content-Type": "application/json",
|
|
865
|
-
"X-API-Key": this.config.apiKey,
|
|
866
|
-
},
|
|
867
|
-
body: JSON.stringify(body),
|
|
868
|
-
signal: this.getFetchSignal(),
|
|
869
|
-
});
|
|
870
|
-
if (!response.ok) {
|
|
871
|
-
const message = await response.text().catch(() => "Unknown error");
|
|
872
|
-
throw new models_2.PRBEAgentError(models_2.PRBEAgentErrorType.SERVER_ERROR, `Server error ${response.status}: ${message}`, response.status);
|
|
873
|
-
}
|
|
874
|
-
return (await response.json());
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
exports.PRBEAgent = PRBEAgent;
|
|
878
|
-
//# sourceMappingURL=agent.js.map
|