raysurfer 0.7.2 → 0.7.3
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/client.d.ts +226 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1282 -0
- package/dist/sdk-client.d.ts +69 -0
- package/dist/sdk-client.d.ts.map +1 -0
- package/dist/types.d.ts +388 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,1282 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/errors.ts
|
|
21
|
+
class RaySurferError extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "RaySurferError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class APIError extends RaySurferError {
|
|
29
|
+
statusCode;
|
|
30
|
+
constructor(message, statusCode) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "APIError";
|
|
33
|
+
this.statusCode = statusCode;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class AuthenticationError extends RaySurferError {
|
|
38
|
+
constructor(message = "Invalid API key") {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = "AuthenticationError";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class CacheUnavailableError extends RaySurferError {
|
|
45
|
+
statusCode;
|
|
46
|
+
constructor(message = "Cache backend is unreachable", statusCode = 503) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "CacheUnavailableError";
|
|
49
|
+
this.statusCode = statusCode;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class RateLimitError extends RaySurferError {
|
|
54
|
+
retryAfter;
|
|
55
|
+
constructor(message = "Rate limit exceeded", retryAfter) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = "RateLimitError";
|
|
58
|
+
this.retryAfter = retryAfter;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class ValidationError extends RaySurferError {
|
|
63
|
+
field;
|
|
64
|
+
constructor(message = "Validation failed", field) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.name = "ValidationError";
|
|
67
|
+
this.field = field;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/client.ts
|
|
72
|
+
var VERSION = "0.7.3";
|
|
73
|
+
var DEFAULT_BASE_URL = "https://api.raysurfer.com";
|
|
74
|
+
var MAX_RETRIES = 3;
|
|
75
|
+
var RETRY_BASE_DELAY = 500;
|
|
76
|
+
var RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
|
|
77
|
+
function normalizeDependencies(rawDeps) {
|
|
78
|
+
if (!rawDeps)
|
|
79
|
+
return {};
|
|
80
|
+
if (Array.isArray(rawDeps)) {
|
|
81
|
+
const result = {};
|
|
82
|
+
for (const pkg of rawDeps) {
|
|
83
|
+
result[pkg] = "";
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
return rawDeps;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class RaySurfer {
|
|
91
|
+
apiKey;
|
|
92
|
+
baseUrl;
|
|
93
|
+
timeout;
|
|
94
|
+
organizationId;
|
|
95
|
+
workspaceId;
|
|
96
|
+
snipsDesired;
|
|
97
|
+
publicSnips;
|
|
98
|
+
registeredTools;
|
|
99
|
+
constructor(options = {}) {
|
|
100
|
+
this.apiKey = options.apiKey;
|
|
101
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
102
|
+
this.timeout = options.timeout ?? 60000;
|
|
103
|
+
this.organizationId = options.organizationId;
|
|
104
|
+
this.workspaceId = options.workspaceId;
|
|
105
|
+
this.snipsDesired = options.snipsDesired;
|
|
106
|
+
this.publicSnips = options.publicSnips;
|
|
107
|
+
this.registeredTools = new Map;
|
|
108
|
+
}
|
|
109
|
+
async request(method, path, body, headersOverride) {
|
|
110
|
+
const headers = {
|
|
111
|
+
"Content-Type": "application/json"
|
|
112
|
+
};
|
|
113
|
+
if (this.apiKey) {
|
|
114
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
115
|
+
}
|
|
116
|
+
if (this.organizationId) {
|
|
117
|
+
headers["X-Raysurfer-Org-Id"] = this.organizationId;
|
|
118
|
+
}
|
|
119
|
+
if (this.workspaceId) {
|
|
120
|
+
headers["X-Raysurfer-Workspace-Id"] = this.workspaceId;
|
|
121
|
+
}
|
|
122
|
+
if (this.snipsDesired) {
|
|
123
|
+
headers["X-Raysurfer-Snips-Desired"] = this.snipsDesired;
|
|
124
|
+
}
|
|
125
|
+
if (this.publicSnips) {
|
|
126
|
+
headers["X-Raysurfer-Public-Snips"] = "true";
|
|
127
|
+
}
|
|
128
|
+
headers["X-Raysurfer-SDK-Version"] = `typescript/${VERSION}`;
|
|
129
|
+
if (headersOverride) {
|
|
130
|
+
Object.assign(headers, headersOverride);
|
|
131
|
+
}
|
|
132
|
+
let lastError;
|
|
133
|
+
for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
|
|
134
|
+
const controller = new AbortController;
|
|
135
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
136
|
+
try {
|
|
137
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
138
|
+
method,
|
|
139
|
+
headers,
|
|
140
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
141
|
+
signal: controller.signal
|
|
142
|
+
});
|
|
143
|
+
if (response.status === 401) {
|
|
144
|
+
throw new AuthenticationError;
|
|
145
|
+
}
|
|
146
|
+
if (RETRYABLE_STATUS_CODES.has(response.status)) {
|
|
147
|
+
const text = await response.text();
|
|
148
|
+
if (response.status === 429) {
|
|
149
|
+
const retryAfterHeader = response.headers.get("Retry-After");
|
|
150
|
+
const retryAfterMs = retryAfterHeader ? parseFloat(retryAfterHeader) * 1000 : RETRY_BASE_DELAY * 2 ** attempt;
|
|
151
|
+
if (attempt < MAX_RETRIES) {
|
|
152
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfterMs));
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
throw new RateLimitError(text, retryAfterHeader ? parseFloat(retryAfterHeader) : undefined);
|
|
156
|
+
}
|
|
157
|
+
if (attempt < MAX_RETRIES) {
|
|
158
|
+
const delay = RETRY_BASE_DELAY * 2 ** attempt;
|
|
159
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
throw new CacheUnavailableError(text, response.status);
|
|
163
|
+
}
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
const text = await response.text();
|
|
166
|
+
throw new APIError(text, response.status);
|
|
167
|
+
}
|
|
168
|
+
return await response.json();
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (error instanceof TypeError && attempt < MAX_RETRIES) {
|
|
171
|
+
lastError = error;
|
|
172
|
+
const delay = RETRY_BASE_DELAY * 2 ** attempt;
|
|
173
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
throw error;
|
|
177
|
+
} finally {
|
|
178
|
+
clearTimeout(timeoutId);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
throw lastError ?? new APIError("Request failed after retries");
|
|
182
|
+
}
|
|
183
|
+
workspaceHeaders(workspaceId) {
|
|
184
|
+
if (!workspaceId)
|
|
185
|
+
return;
|
|
186
|
+
return { "X-Raysurfer-Workspace-Id": workspaceId };
|
|
187
|
+
}
|
|
188
|
+
async storeCodeBlock(params) {
|
|
189
|
+
const data = {
|
|
190
|
+
name: params.name,
|
|
191
|
+
description: params.description ?? "",
|
|
192
|
+
source: params.source,
|
|
193
|
+
entrypoint: params.entrypoint,
|
|
194
|
+
language: params.language,
|
|
195
|
+
input_schema: params.inputSchema ?? {},
|
|
196
|
+
output_schema: params.outputSchema ?? {},
|
|
197
|
+
language_version: params.languageVersion ?? null,
|
|
198
|
+
dependencies: params.dependencies ?? {},
|
|
199
|
+
tags: params.tags ?? [],
|
|
200
|
+
capabilities: params.capabilities ?? [],
|
|
201
|
+
example_queries: params.exampleQueries ?? null
|
|
202
|
+
};
|
|
203
|
+
const result = await this.request("POST", "/api/store/code-block", data);
|
|
204
|
+
return {
|
|
205
|
+
success: result.success,
|
|
206
|
+
codeBlockId: result.code_block_id,
|
|
207
|
+
embeddingId: result.embedding_id,
|
|
208
|
+
message: result.message
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async storeExecution(params) {
|
|
212
|
+
const io = {
|
|
213
|
+
input_data: params.inputData,
|
|
214
|
+
input_hash: "",
|
|
215
|
+
output_data: params.outputData,
|
|
216
|
+
output_hash: "",
|
|
217
|
+
output_type: typeof params.outputData
|
|
218
|
+
};
|
|
219
|
+
const data = {
|
|
220
|
+
code_block_id: params.codeBlockId,
|
|
221
|
+
triggering_task: params.triggeringTask,
|
|
222
|
+
io,
|
|
223
|
+
execution_state: params.executionState ?? "completed",
|
|
224
|
+
duration_ms: params.durationMs ?? 0,
|
|
225
|
+
error_message: params.errorMessage ?? null,
|
|
226
|
+
error_type: params.errorType ?? null,
|
|
227
|
+
verdict: params.verdict ?? null,
|
|
228
|
+
review: params.review ?? null
|
|
229
|
+
};
|
|
230
|
+
const result = await this.request("POST", "/api/store/execution", data);
|
|
231
|
+
return {
|
|
232
|
+
success: result.success,
|
|
233
|
+
executionId: result.execution_id,
|
|
234
|
+
patternUpdated: result.pattern_updated,
|
|
235
|
+
message: result.message
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async uploadNewCodeSnip(taskOrOptions, fileWritten, succeeded, cachedCodeBlocks, useRaysurferAiVoting = true, userVote, executionLogs, runUrl, workspaceId, dependencies, voteSource, voteCount) {
|
|
239
|
+
let opts;
|
|
240
|
+
if (typeof taskOrOptions === "object") {
|
|
241
|
+
opts = taskOrOptions;
|
|
242
|
+
} else {
|
|
243
|
+
opts = {
|
|
244
|
+
task: taskOrOptions,
|
|
245
|
+
fileWritten,
|
|
246
|
+
succeeded,
|
|
247
|
+
cachedCodeBlocks,
|
|
248
|
+
useRaysurferAiVoting,
|
|
249
|
+
userVote,
|
|
250
|
+
executionLogs,
|
|
251
|
+
runUrl,
|
|
252
|
+
workspaceId,
|
|
253
|
+
dependencies,
|
|
254
|
+
voteSource,
|
|
255
|
+
voteCount
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (opts.fileWritten && opts.filesWritten) {
|
|
259
|
+
throw new Error("Provide either fileWritten or filesWritten, not both.");
|
|
260
|
+
}
|
|
261
|
+
const effectiveVoting = opts.autoVote ?? opts.useRaysurferAiVoting ?? true;
|
|
262
|
+
const normalizedFiles = [];
|
|
263
|
+
if (opts.fileWritten) {
|
|
264
|
+
normalizedFiles.push(opts.fileWritten);
|
|
265
|
+
} else if (opts.filesWritten) {
|
|
266
|
+
normalizedFiles.push(...opts.filesWritten);
|
|
267
|
+
}
|
|
268
|
+
if (normalizedFiles.length === 0) {
|
|
269
|
+
throw new Error("Missing required file input: provide fileWritten or filesWritten.");
|
|
270
|
+
}
|
|
271
|
+
if (normalizedFiles.length > 1) {
|
|
272
|
+
const responses = [];
|
|
273
|
+
for (const compatFile of normalizedFiles) {
|
|
274
|
+
responses.push(await this.uploadNewCodeSnip({
|
|
275
|
+
...opts,
|
|
276
|
+
fileWritten: compatFile,
|
|
277
|
+
filesWritten: undefined,
|
|
278
|
+
useRaysurferAiVoting: effectiveVoting,
|
|
279
|
+
autoVote: undefined
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
success: responses.every((response) => response.success),
|
|
284
|
+
codeBlocksStored: responses.reduce((sum, response) => sum + response.codeBlocksStored, 0),
|
|
285
|
+
message: `Uploaded ${normalizedFiles.length} files via compatibility path.`
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const [resolvedFile] = normalizedFiles;
|
|
289
|
+
const data = {
|
|
290
|
+
task: opts.task,
|
|
291
|
+
file_written: resolvedFile,
|
|
292
|
+
succeeded: opts.succeeded,
|
|
293
|
+
use_raysurfer_ai_voting: effectiveVoting,
|
|
294
|
+
user_vote: opts.userVote,
|
|
295
|
+
execution_logs: opts.executionLogs,
|
|
296
|
+
cached_code_blocks: opts.cachedCodeBlocks && opts.cachedCodeBlocks.length > 0 ? opts.cachedCodeBlocks.map((cb) => ({
|
|
297
|
+
code_block_id: cb.codeBlockId,
|
|
298
|
+
filename: cb.filename,
|
|
299
|
+
description: cb.description
|
|
300
|
+
})) : undefined,
|
|
301
|
+
run_url: opts.runUrl,
|
|
302
|
+
dependencies: opts.dependencies,
|
|
303
|
+
public: opts.public || undefined,
|
|
304
|
+
vote_source: opts.voteSource,
|
|
305
|
+
vote_count: opts.voteCount
|
|
306
|
+
};
|
|
307
|
+
const result = await this.request("POST", "/api/store/execution-result", data, this.workspaceHeaders(opts.workspaceId));
|
|
308
|
+
return {
|
|
309
|
+
success: result.success,
|
|
310
|
+
codeBlocksStored: result.code_blocks_stored,
|
|
311
|
+
message: result.message,
|
|
312
|
+
snippetName: result.snippet_name ?? null
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
uploadNewCodeSnips = this.uploadNewCodeSnip.bind(this);
|
|
316
|
+
async uploadBulkCodeSnips(promptsOrOptions, filesWritten, logFiles, useRaysurferAiVoting = true, userVotes, workspaceId, voteSource, voteCount) {
|
|
317
|
+
let opts;
|
|
318
|
+
if (!Array.isArray(promptsOrOptions)) {
|
|
319
|
+
opts = promptsOrOptions;
|
|
320
|
+
} else {
|
|
321
|
+
opts = {
|
|
322
|
+
prompts: promptsOrOptions,
|
|
323
|
+
filesWritten,
|
|
324
|
+
logFiles,
|
|
325
|
+
useRaysurferAiVoting,
|
|
326
|
+
userVotes,
|
|
327
|
+
workspaceId,
|
|
328
|
+
voteSource,
|
|
329
|
+
voteCount
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const normalizedLogs = opts.logFiles?.map((log) => {
|
|
333
|
+
const content = typeof log.content === "string" ? log.content : Buffer.from(log.content).toString("base64");
|
|
334
|
+
const encoding = typeof log.content === "string" ? log.encoding ?? "utf-8" : "base64";
|
|
335
|
+
return {
|
|
336
|
+
path: log.path,
|
|
337
|
+
content,
|
|
338
|
+
encoding,
|
|
339
|
+
content_type: log.contentType
|
|
340
|
+
};
|
|
341
|
+
}) ?? [];
|
|
342
|
+
const data = {
|
|
343
|
+
prompts: opts.prompts,
|
|
344
|
+
files_written: opts.filesWritten,
|
|
345
|
+
log_files: normalizedLogs.length > 0 ? normalizedLogs : undefined,
|
|
346
|
+
use_raysurfer_ai_voting: opts.useRaysurferAiVoting ?? true,
|
|
347
|
+
user_votes: opts.userVotes,
|
|
348
|
+
vote_source: opts.voteSource,
|
|
349
|
+
vote_count: opts.voteCount
|
|
350
|
+
};
|
|
351
|
+
const result = await this.request("POST", "/api/store/bulk-execution-result", data, this.workspaceHeaders(opts.workspaceId));
|
|
352
|
+
return {
|
|
353
|
+
success: result.success,
|
|
354
|
+
codeBlocksStored: result.code_blocks_stored,
|
|
355
|
+
votesQueued: result.votes_queued,
|
|
356
|
+
message: result.message,
|
|
357
|
+
statusUrl: result.status_url ?? null
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
async search(params) {
|
|
361
|
+
const data = {
|
|
362
|
+
task: params.task,
|
|
363
|
+
top_k: params.topK ?? 5,
|
|
364
|
+
min_verdict_score: params.minVerdictScore ?? 0.3,
|
|
365
|
+
prefer_complete: params.preferComplete ?? false,
|
|
366
|
+
input_schema: params.inputSchema ?? null
|
|
367
|
+
};
|
|
368
|
+
const result = await this.request("POST", "/api/retrieve/search", data, this.workspaceHeaders(params.workspaceId));
|
|
369
|
+
return {
|
|
370
|
+
matches: result.matches.map((m) => {
|
|
371
|
+
const vectorScore = m.vector_score ?? m.score;
|
|
372
|
+
const verdictScore = m.verdict_score ?? m.score;
|
|
373
|
+
return {
|
|
374
|
+
codeBlock: this.parseCodeBlock(m.code_block),
|
|
375
|
+
score: m.score,
|
|
376
|
+
combinedScore: m.score,
|
|
377
|
+
vectorScore,
|
|
378
|
+
verdictScore,
|
|
379
|
+
thumbsUp: m.thumbs_up,
|
|
380
|
+
thumbsDown: m.thumbs_down,
|
|
381
|
+
filename: m.filename,
|
|
382
|
+
language: m.language,
|
|
383
|
+
entrypoint: m.entrypoint,
|
|
384
|
+
dependencies: normalizeDependencies(m.dependencies),
|
|
385
|
+
comments: m.comments ?? []
|
|
386
|
+
};
|
|
387
|
+
}),
|
|
388
|
+
totalFound: result.total_found,
|
|
389
|
+
cacheHit: result.cache_hit
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
async getCodeSnips(params) {
|
|
393
|
+
const response = await this.search({
|
|
394
|
+
task: params.task,
|
|
395
|
+
topK: params.topK ?? 10,
|
|
396
|
+
minVerdictScore: params.minVerdictScore ?? 0
|
|
397
|
+
});
|
|
398
|
+
return {
|
|
399
|
+
codeBlocks: response.matches.map((m) => ({
|
|
400
|
+
codeBlock: m.codeBlock,
|
|
401
|
+
score: m.score,
|
|
402
|
+
thumbsUp: m.thumbsUp,
|
|
403
|
+
thumbsDown: m.thumbsDown,
|
|
404
|
+
recentExecutions: []
|
|
405
|
+
})),
|
|
406
|
+
totalFound: response.totalFound
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
async retrieveBest(params) {
|
|
410
|
+
const response = await this.search({
|
|
411
|
+
task: params.task,
|
|
412
|
+
topK: params.topK ?? 10,
|
|
413
|
+
minVerdictScore: params.minVerdictScore ?? 0
|
|
414
|
+
});
|
|
415
|
+
let bestMatch = null;
|
|
416
|
+
let bestScore = 0;
|
|
417
|
+
const first = response.matches[0];
|
|
418
|
+
if (first) {
|
|
419
|
+
bestMatch = {
|
|
420
|
+
codeBlock: first.codeBlock,
|
|
421
|
+
score: first.score,
|
|
422
|
+
thumbsUp: first.thumbsUp,
|
|
423
|
+
thumbsDown: first.thumbsDown
|
|
424
|
+
};
|
|
425
|
+
bestScore = first.score;
|
|
426
|
+
}
|
|
427
|
+
const alternativeCandidates = response.matches.slice(1, 4).map((m) => ({
|
|
428
|
+
codeBlockId: m.codeBlock.id,
|
|
429
|
+
name: m.codeBlock.name,
|
|
430
|
+
score: m.score,
|
|
431
|
+
reason: m.thumbsUp > 0 ? `${m.thumbsUp} thumbs up, ${m.thumbsDown} thumbs down` : "No execution history"
|
|
432
|
+
}));
|
|
433
|
+
return {
|
|
434
|
+
bestMatch,
|
|
435
|
+
alternativeCandidates,
|
|
436
|
+
retrievalConfidence: bestMatch ? String(bestScore.toFixed(4)) : "0"
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
async getFewShotExamples(task, k = 3) {
|
|
440
|
+
const data = { task, k };
|
|
441
|
+
const result = await this.request("POST", "/api/retrieve/few-shot-examples", data);
|
|
442
|
+
return result.examples.map((ex) => ({
|
|
443
|
+
task: ex.task,
|
|
444
|
+
inputSample: ex.input_sample,
|
|
445
|
+
outputSample: ex.output_sample,
|
|
446
|
+
codeSnippet: ex.code_snippet
|
|
447
|
+
}));
|
|
448
|
+
}
|
|
449
|
+
async getTaskPatterns(params) {
|
|
450
|
+
const data = {
|
|
451
|
+
task: params.task ?? null,
|
|
452
|
+
code_block_id: params.codeBlockId ?? null,
|
|
453
|
+
min_thumbs_up: params.minThumbsUp ?? 0,
|
|
454
|
+
top_k: params.topK ?? 20
|
|
455
|
+
};
|
|
456
|
+
const result = await this.request("POST", "/api/retrieve/task-patterns", data);
|
|
457
|
+
return result.patterns.map((p) => ({
|
|
458
|
+
taskPattern: p.task_pattern,
|
|
459
|
+
codeBlockId: p.code_block_id,
|
|
460
|
+
codeBlockName: p.code_block_name,
|
|
461
|
+
thumbsUp: p.thumbs_up,
|
|
462
|
+
thumbsDown: p.thumbs_down,
|
|
463
|
+
lastThumbsUp: p.last_thumbs_up,
|
|
464
|
+
lastThumbsDown: p.last_thumbs_down
|
|
465
|
+
}));
|
|
466
|
+
}
|
|
467
|
+
async getCodeFiles(params) {
|
|
468
|
+
const response = await this.search({
|
|
469
|
+
task: params.task,
|
|
470
|
+
topK: params.topK ?? 5,
|
|
471
|
+
minVerdictScore: params.minVerdictScore ?? 0.3,
|
|
472
|
+
preferComplete: params.preferComplete ?? true
|
|
473
|
+
});
|
|
474
|
+
const files = response.matches.map((m) => ({
|
|
475
|
+
codeBlockId: m.codeBlock.id,
|
|
476
|
+
filename: m.filename,
|
|
477
|
+
source: m.codeBlock.source,
|
|
478
|
+
entrypoint: m.entrypoint,
|
|
479
|
+
description: m.codeBlock.description,
|
|
480
|
+
inputSchema: m.codeBlock.inputSchema,
|
|
481
|
+
outputSchema: m.codeBlock.outputSchema,
|
|
482
|
+
language: m.language,
|
|
483
|
+
dependencies: m.dependencies,
|
|
484
|
+
score: m.score,
|
|
485
|
+
thumbsUp: m.thumbsUp,
|
|
486
|
+
thumbsDown: m.thumbsDown
|
|
487
|
+
}));
|
|
488
|
+
const addToLlmPrompt = this.formatLlmPrompt(files, params.cacheDir ?? ".raysurfer_code");
|
|
489
|
+
return {
|
|
490
|
+
files,
|
|
491
|
+
task: params.task,
|
|
492
|
+
totalFound: response.totalFound,
|
|
493
|
+
addToLlmPrompt
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
formatLlmPrompt(files, cacheDir) {
|
|
497
|
+
if (files.length === 0)
|
|
498
|
+
return "";
|
|
499
|
+
const lines = [
|
|
500
|
+
`
|
|
501
|
+
|
|
502
|
+
## IMPORTANT: Pre-validated Code Files Available
|
|
503
|
+
`,
|
|
504
|
+
"The following validated code has been retrieved from the cache. " + `Use these files directly instead of regenerating code.
|
|
505
|
+
`
|
|
506
|
+
];
|
|
507
|
+
for (const f of files) {
|
|
508
|
+
if (cacheDir) {
|
|
509
|
+
const fullPath = `${cacheDir}/${f.filename}`;
|
|
510
|
+
lines.push(`
|
|
511
|
+
### \`${f.filename}\` → \`${fullPath}\``);
|
|
512
|
+
} else {
|
|
513
|
+
lines.push(`
|
|
514
|
+
### \`${f.filename}\``);
|
|
515
|
+
}
|
|
516
|
+
lines.push(`- **Description**: ${f.description}`);
|
|
517
|
+
lines.push(`- **Language**: ${f.language}`);
|
|
518
|
+
lines.push(`- **Entrypoint**: \`${f.entrypoint}\``);
|
|
519
|
+
lines.push(`- **Confidence**: ${Math.round(f.score * 100)}%`);
|
|
520
|
+
const deps = Object.entries(f.dependencies);
|
|
521
|
+
if (deps.length > 0) {
|
|
522
|
+
lines.push(`- **Dependencies**: ${deps.map(([k, v]) => `${k}@${v}`).join(", ")}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
lines.push(`
|
|
526
|
+
|
|
527
|
+
**Instructions**:`);
|
|
528
|
+
lines.push("1. Read the cached file(s) before writing new code");
|
|
529
|
+
lines.push("2. Use the cached code as your starting point");
|
|
530
|
+
lines.push("3. Only modify if the task requires specific changes");
|
|
531
|
+
lines.push(`4. Do not regenerate code that already exists
|
|
532
|
+
`);
|
|
533
|
+
return lines.join(`
|
|
534
|
+
`);
|
|
535
|
+
}
|
|
536
|
+
async autoReview(params) {
|
|
537
|
+
const response = await this.request("POST", "/api/store/auto-review", {
|
|
538
|
+
execution_id: params.executionId,
|
|
539
|
+
triggering_task: params.triggeringTask,
|
|
540
|
+
execution_state: params.executionState,
|
|
541
|
+
input_data: params.inputData,
|
|
542
|
+
output_data: params.outputData,
|
|
543
|
+
code_block_name: params.codeBlockName,
|
|
544
|
+
code_block_description: params.codeBlockDescription,
|
|
545
|
+
error_message: params.errorMessage
|
|
546
|
+
});
|
|
547
|
+
return {
|
|
548
|
+
success: response.success,
|
|
549
|
+
executionId: response.execution_id,
|
|
550
|
+
review: {
|
|
551
|
+
timestamp: response.review.timestamp,
|
|
552
|
+
verdict: response.review.verdict,
|
|
553
|
+
reasoning: response.review.reasoning,
|
|
554
|
+
whatWorked: response.review.what_worked,
|
|
555
|
+
whatDidntWork: response.review.what_didnt_work,
|
|
556
|
+
outputWasUseful: response.review.output_was_useful,
|
|
557
|
+
outputWasCorrect: response.review.output_was_correct,
|
|
558
|
+
outputWasComplete: response.review.output_was_complete,
|
|
559
|
+
errorWasAppropriate: response.review.error_was_appropriate,
|
|
560
|
+
wouldUseAgain: response.review.would_use_again,
|
|
561
|
+
suggestedImprovements: response.review.suggested_improvements,
|
|
562
|
+
requiredWorkaround: response.review.required_workaround,
|
|
563
|
+
workaroundDescription: response.review.workaround_description
|
|
564
|
+
},
|
|
565
|
+
message: response.message
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
async getExecutions(params = {}) {
|
|
569
|
+
const response = await this.request("POST", "/api/retrieve/executions", {
|
|
570
|
+
code_block_id: params.codeBlockId,
|
|
571
|
+
task: params.task,
|
|
572
|
+
verdict: params.verdict,
|
|
573
|
+
limit: params.limit ?? 20
|
|
574
|
+
});
|
|
575
|
+
return {
|
|
576
|
+
executions: response.executions.map((exec) => ({
|
|
577
|
+
id: exec.id,
|
|
578
|
+
codeBlockId: exec.code_block_id,
|
|
579
|
+
timestamp: exec.timestamp,
|
|
580
|
+
executionState: exec.execution_state,
|
|
581
|
+
durationMs: exec.duration_ms,
|
|
582
|
+
errorMessage: exec.error_message,
|
|
583
|
+
errorType: exec.error_type,
|
|
584
|
+
io: {
|
|
585
|
+
inputData: exec.io.input_data,
|
|
586
|
+
inputHash: exec.io.input_hash,
|
|
587
|
+
outputData: exec.io.output_data,
|
|
588
|
+
outputHash: exec.io.output_hash,
|
|
589
|
+
outputType: exec.io.output_type
|
|
590
|
+
},
|
|
591
|
+
triggeringTask: exec.triggering_task,
|
|
592
|
+
retrievalScore: exec.retrieval_score,
|
|
593
|
+
verdict: exec.verdict,
|
|
594
|
+
review: exec.review ? {
|
|
595
|
+
timestamp: exec.review.timestamp,
|
|
596
|
+
verdict: exec.review.verdict,
|
|
597
|
+
reasoning: exec.review.reasoning,
|
|
598
|
+
whatWorked: exec.review.what_worked,
|
|
599
|
+
whatDidntWork: exec.review.what_didnt_work,
|
|
600
|
+
outputWasUseful: exec.review.output_was_useful,
|
|
601
|
+
outputWasCorrect: exec.review.output_was_correct,
|
|
602
|
+
outputWasComplete: exec.review.output_was_complete,
|
|
603
|
+
errorWasAppropriate: exec.review.error_was_appropriate,
|
|
604
|
+
wouldUseAgain: exec.review.would_use_again,
|
|
605
|
+
suggestedImprovements: exec.review.suggested_improvements,
|
|
606
|
+
requiredWorkaround: exec.review.required_workaround,
|
|
607
|
+
workaroundDescription: exec.review.workaround_description
|
|
608
|
+
} : null
|
|
609
|
+
})),
|
|
610
|
+
totalFound: response.total_found
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
async voteCodeSnip(params) {
|
|
614
|
+
const data = {
|
|
615
|
+
task: params.task,
|
|
616
|
+
code_block_id: params.codeBlockId,
|
|
617
|
+
code_block_name: params.codeBlockName,
|
|
618
|
+
code_block_description: params.codeBlockDescription,
|
|
619
|
+
succeeded: params.succeeded
|
|
620
|
+
};
|
|
621
|
+
const result = await this.request("POST", "/api/store/cache-usage", data);
|
|
622
|
+
return {
|
|
623
|
+
success: result.success,
|
|
624
|
+
votePending: result.vote_pending,
|
|
625
|
+
message: result.message
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
async browsePublic(params = {}) {
|
|
629
|
+
const data = {
|
|
630
|
+
limit: params.limit ?? 20,
|
|
631
|
+
offset: params.offset ?? 0,
|
|
632
|
+
sort_by: params.sortBy ?? "upvoted",
|
|
633
|
+
language: params.language
|
|
634
|
+
};
|
|
635
|
+
const result = await this.request("POST", "/api/snippets/public/list", data);
|
|
636
|
+
return {
|
|
637
|
+
snippets: result.snippets.map((s) => ({
|
|
638
|
+
id: s.id,
|
|
639
|
+
name: s.name,
|
|
640
|
+
description: s.description,
|
|
641
|
+
source: s.source,
|
|
642
|
+
language: s.language,
|
|
643
|
+
entrypoint: s.entrypoint,
|
|
644
|
+
thumbsUp: s.thumbs_up,
|
|
645
|
+
thumbsDown: s.thumbs_down,
|
|
646
|
+
createdAt: s.created_at,
|
|
647
|
+
namespace: s.namespace
|
|
648
|
+
})),
|
|
649
|
+
total: result.total,
|
|
650
|
+
hasMore: result.has_more
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
async searchPublic(params) {
|
|
654
|
+
const data = {
|
|
655
|
+
query: params.query,
|
|
656
|
+
limit: params.limit ?? 20,
|
|
657
|
+
language: params.language
|
|
658
|
+
};
|
|
659
|
+
const result = await this.request("POST", "/api/snippets/public/search", data);
|
|
660
|
+
return {
|
|
661
|
+
snippets: result.snippets.map((s) => ({
|
|
662
|
+
id: s.id,
|
|
663
|
+
name: s.name,
|
|
664
|
+
description: s.description,
|
|
665
|
+
source: s.source,
|
|
666
|
+
language: s.language,
|
|
667
|
+
entrypoint: s.entrypoint,
|
|
668
|
+
thumbsUp: s.thumbs_up,
|
|
669
|
+
thumbsDown: s.thumbs_down,
|
|
670
|
+
createdAt: s.created_at,
|
|
671
|
+
namespace: s.namespace
|
|
672
|
+
})),
|
|
673
|
+
total: result.total,
|
|
674
|
+
query: result.query
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
tool(name, description, parameters, callback) {
|
|
678
|
+
this.registeredTools.set(name, {
|
|
679
|
+
definition: { name, description, parameters },
|
|
680
|
+
callback
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
async execute(task, options) {
|
|
684
|
+
const timeout = options?.timeout ?? 300000;
|
|
685
|
+
const forceRegenerate = options?.forceRegenerate ?? false;
|
|
686
|
+
const sessionId = crypto.randomUUID();
|
|
687
|
+
const wsUrl = `${this.baseUrl.replace(/^http/, "ws")}/api/execute/ws/${sessionId}`;
|
|
688
|
+
const toolCalls = [];
|
|
689
|
+
const ws = new WebSocket(wsUrl);
|
|
690
|
+
const wsReady = new Promise((resolve, reject) => {
|
|
691
|
+
ws.addEventListener("open", () => resolve());
|
|
692
|
+
ws.addEventListener("error", (ev) => reject(new Error(`WebSocket connection failed: ${String(ev)}`)));
|
|
693
|
+
});
|
|
694
|
+
ws.addEventListener("message", async (event) => {
|
|
695
|
+
const raw = typeof event.data === "string" ? event.data : String(event.data);
|
|
696
|
+
let msg;
|
|
697
|
+
try {
|
|
698
|
+
msg = JSON.parse(raw);
|
|
699
|
+
} catch {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
if (msg.type === "tool_call" && msg.request_id && msg.tool_name) {
|
|
703
|
+
const start = Date.now();
|
|
704
|
+
const registered = this.registeredTools.get(msg.tool_name);
|
|
705
|
+
const record = {
|
|
706
|
+
toolName: msg.tool_name,
|
|
707
|
+
arguments: msg.arguments ?? {},
|
|
708
|
+
result: null,
|
|
709
|
+
error: null,
|
|
710
|
+
durationMs: 0
|
|
711
|
+
};
|
|
712
|
+
try {
|
|
713
|
+
if (!registered) {
|
|
714
|
+
throw new Error(`Unknown tool: ${msg.tool_name}`);
|
|
715
|
+
}
|
|
716
|
+
const callbackResult = registered.callback(msg.arguments ?? {});
|
|
717
|
+
const result = callbackResult instanceof Promise ? await callbackResult : callbackResult;
|
|
718
|
+
record.result = result;
|
|
719
|
+
record.durationMs = Date.now() - start;
|
|
720
|
+
ws.send(JSON.stringify({
|
|
721
|
+
type: "tool_result",
|
|
722
|
+
request_id: msg.request_id,
|
|
723
|
+
result
|
|
724
|
+
}));
|
|
725
|
+
} catch (err) {
|
|
726
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
727
|
+
record.error = errorMsg;
|
|
728
|
+
record.durationMs = Date.now() - start;
|
|
729
|
+
ws.send(JSON.stringify({
|
|
730
|
+
type: "tool_result",
|
|
731
|
+
request_id: msg.request_id,
|
|
732
|
+
result: `Error: ${errorMsg}`
|
|
733
|
+
}));
|
|
734
|
+
}
|
|
735
|
+
toolCalls.push(record);
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
await wsReady;
|
|
739
|
+
const tools = Array.from(this.registeredTools.values()).map((t) => t.definition);
|
|
740
|
+
const response = await this.request("POST", "/api/execute/run", {
|
|
741
|
+
task,
|
|
742
|
+
tools,
|
|
743
|
+
session_id: sessionId,
|
|
744
|
+
timeout_seconds: timeout / 1000,
|
|
745
|
+
force_regenerate: forceRegenerate
|
|
746
|
+
});
|
|
747
|
+
ws.close();
|
|
748
|
+
const serverToolCalls = (response.tool_calls ?? []).map((tc) => ({
|
|
749
|
+
toolName: tc.tool_name,
|
|
750
|
+
arguments: tc.arguments,
|
|
751
|
+
result: tc.result,
|
|
752
|
+
error: tc.error,
|
|
753
|
+
durationMs: tc.duration_ms
|
|
754
|
+
}));
|
|
755
|
+
return {
|
|
756
|
+
executionId: response.execution_id,
|
|
757
|
+
result: response.result,
|
|
758
|
+
exitCode: response.exit_code,
|
|
759
|
+
durationMs: response.duration_ms,
|
|
760
|
+
cacheHit: response.cache_hit,
|
|
761
|
+
codeBlockId: response.code_block_id,
|
|
762
|
+
error: response.error,
|
|
763
|
+
toolCalls: serverToolCalls.length > 0 ? serverToolCalls : toolCalls
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
async submitExecutionResult(task, fileWritten, succeeded) {
|
|
767
|
+
return this.uploadNewCodeSnip(task, fileWritten, succeeded);
|
|
768
|
+
}
|
|
769
|
+
async retrieve(params) {
|
|
770
|
+
return this.getCodeSnips(params);
|
|
771
|
+
}
|
|
772
|
+
parseCodeBlock(data) {
|
|
773
|
+
return {
|
|
774
|
+
id: data.id,
|
|
775
|
+
name: data.name,
|
|
776
|
+
description: data.description,
|
|
777
|
+
source: data.source,
|
|
778
|
+
entrypoint: data.entrypoint,
|
|
779
|
+
inputSchema: data.input_schema ?? {},
|
|
780
|
+
outputSchema: data.output_schema ?? {},
|
|
781
|
+
language: data.language,
|
|
782
|
+
languageVersion: data.language_version ?? null,
|
|
783
|
+
dependencies: normalizeDependencies(data.dependencies),
|
|
784
|
+
tags: data.tags ?? [],
|
|
785
|
+
capabilities: data.capabilities ?? [],
|
|
786
|
+
exampleQueries: data.example_queries ?? null,
|
|
787
|
+
createdAt: data.created_at ?? null,
|
|
788
|
+
updatedAt: data.updated_at ?? null
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
var client_default = RaySurfer;
|
|
793
|
+
// src/sdk-client.ts
|
|
794
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
795
|
+
import { join } from "node:path";
|
|
796
|
+
var DEFAULT_RAYSURFER_URL = "https://api.raysurfer.com";
|
|
797
|
+
var CACHE_DIR = ".raysurfer_code";
|
|
798
|
+
var FILE_MODIFY_TOOLS = ["Write", "Edit", "MultiEdit", "NotebookEdit"];
|
|
799
|
+
var TRACKABLE_EXTENSIONS = new Set([
|
|
800
|
+
".py",
|
|
801
|
+
".js",
|
|
802
|
+
".ts",
|
|
803
|
+
".rb",
|
|
804
|
+
".go",
|
|
805
|
+
".rs",
|
|
806
|
+
".java",
|
|
807
|
+
".cpp",
|
|
808
|
+
".c",
|
|
809
|
+
".h",
|
|
810
|
+
".pdf",
|
|
811
|
+
".docx",
|
|
812
|
+
".xlsx",
|
|
813
|
+
".csv",
|
|
814
|
+
".json",
|
|
815
|
+
".yaml",
|
|
816
|
+
".yml",
|
|
817
|
+
".xml",
|
|
818
|
+
".html",
|
|
819
|
+
".css",
|
|
820
|
+
".md",
|
|
821
|
+
".txt",
|
|
822
|
+
".sh",
|
|
823
|
+
".sql"
|
|
824
|
+
]);
|
|
825
|
+
var BASH_OUTPUT_PATTERNS = [
|
|
826
|
+
/>>\s*([^\s;&|]+)/g,
|
|
827
|
+
/>\s*([^\s;&|]+)/g,
|
|
828
|
+
/-o\s+([^\s;&|]+)/g,
|
|
829
|
+
/--output[=\s]+([^\s;&|]+)/g,
|
|
830
|
+
/savefig\(['"]([^'"]+)['"]\)/g,
|
|
831
|
+
/to_csv\(['"]([^'"]+)['"]\)/g,
|
|
832
|
+
/to_excel\(['"]([^'"]+)['"]\)/g,
|
|
833
|
+
/write\(['"]([^'"]+)['"]\)/g
|
|
834
|
+
];
|
|
835
|
+
var createDebugLogger = (enabled) => ({
|
|
836
|
+
log: (...args) => enabled && console.log("[raysurfer]", ...args),
|
|
837
|
+
time: (label) => enabled && console.time(`[raysurfer] ${label}`),
|
|
838
|
+
timeEnd: (label) => enabled && console.timeEnd(`[raysurfer] ${label}`),
|
|
839
|
+
table: (data) => enabled && console.table(data),
|
|
840
|
+
group: (label) => enabled && console.group(`[raysurfer] ${label}`),
|
|
841
|
+
groupEnd: () => enabled && console.groupEnd()
|
|
842
|
+
});
|
|
843
|
+
function augmentSystemPrompt(systemPrompt, addition) {
|
|
844
|
+
if (!addition)
|
|
845
|
+
return systemPrompt;
|
|
846
|
+
if (typeof systemPrompt === "string")
|
|
847
|
+
return systemPrompt + addition;
|
|
848
|
+
if (systemPrompt?.type === "preset") {
|
|
849
|
+
return { ...systemPrompt, append: (systemPrompt.append ?? "") + addition };
|
|
850
|
+
}
|
|
851
|
+
return addition;
|
|
852
|
+
}
|
|
853
|
+
function splitOptions(options) {
|
|
854
|
+
const { workspaceId, publicSnips, debug, workingDirectory, ...sdkOptions } = options;
|
|
855
|
+
return {
|
|
856
|
+
sdkOptions,
|
|
857
|
+
extras: {
|
|
858
|
+
workspaceId,
|
|
859
|
+
publicSnips,
|
|
860
|
+
debug,
|
|
861
|
+
workingDirectory
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
class RaysurferQuery {
|
|
867
|
+
_inner = null;
|
|
868
|
+
_initPromise = null;
|
|
869
|
+
_raysurfer = null;
|
|
870
|
+
_cachedFiles = [];
|
|
871
|
+
_modifiedFilePaths = new Set;
|
|
872
|
+
_bashGeneratedFiles = new Set;
|
|
873
|
+
_executionLogs = [];
|
|
874
|
+
_taskSucceeded = false;
|
|
875
|
+
_generatedCodeBlocks = [];
|
|
876
|
+
_cacheUploadDone = false;
|
|
877
|
+
_messageCount = 0;
|
|
878
|
+
_startTime = 0;
|
|
879
|
+
_promptText;
|
|
880
|
+
_params;
|
|
881
|
+
_debug;
|
|
882
|
+
_cacheEnabled;
|
|
883
|
+
_workDir;
|
|
884
|
+
_apiKey;
|
|
885
|
+
_baseUrl;
|
|
886
|
+
_extras;
|
|
887
|
+
_sdkOptions;
|
|
888
|
+
constructor(params) {
|
|
889
|
+
this._params = params;
|
|
890
|
+
const options = params.options ?? {};
|
|
891
|
+
const { sdkOptions, extras } = splitOptions(options);
|
|
892
|
+
this._sdkOptions = sdkOptions;
|
|
893
|
+
this._extras = extras;
|
|
894
|
+
this._promptText = typeof params.prompt === "string" ? params.prompt : null;
|
|
895
|
+
const debugEnabled = extras.debug || process.env.RAYSURFER_DEBUG === "true";
|
|
896
|
+
this._debug = createDebugLogger(debugEnabled);
|
|
897
|
+
this._apiKey = process.env.RAYSURFER_API_KEY;
|
|
898
|
+
this._baseUrl = process.env.RAYSURFER_BASE_URL || DEFAULT_RAYSURFER_URL;
|
|
899
|
+
this._cacheEnabled = !!this._apiKey;
|
|
900
|
+
if (extras.workingDirectory && !sdkOptions.cwd) {
|
|
901
|
+
console.warn("[raysurfer] workingDirectory is deprecated, use cwd instead");
|
|
902
|
+
this._sdkOptions.cwd = extras.workingDirectory;
|
|
903
|
+
}
|
|
904
|
+
this._workDir = this._sdkOptions.cwd || process.cwd();
|
|
905
|
+
}
|
|
906
|
+
async _initialize() {
|
|
907
|
+
this._debug.group("Raysurfer Query Started");
|
|
908
|
+
this._debug.log("Prompt:", this._promptText ?? "<stream>");
|
|
909
|
+
this._debug.log("Cache enabled:", this._cacheEnabled);
|
|
910
|
+
this._debug.log("Base URL:", this._baseUrl);
|
|
911
|
+
if (!this._cacheEnabled) {
|
|
912
|
+
console.warn("[raysurfer] RAYSURFER_API_KEY not set - caching disabled");
|
|
913
|
+
}
|
|
914
|
+
let addToLlmPrompt = "";
|
|
915
|
+
if (this._cacheEnabled && this._promptText) {
|
|
916
|
+
this._raysurfer = new RaySurfer({
|
|
917
|
+
apiKey: this._apiKey,
|
|
918
|
+
baseUrl: this._baseUrl,
|
|
919
|
+
workspaceId: this._extras.workspaceId,
|
|
920
|
+
snipsDesired: this._extras.workspaceId ? "client" : undefined,
|
|
921
|
+
publicSnips: this._extras.publicSnips
|
|
922
|
+
});
|
|
923
|
+
try {
|
|
924
|
+
this._debug.time("Cache lookup");
|
|
925
|
+
const cacheDir = join(this._workDir, CACHE_DIR);
|
|
926
|
+
const response = await this._raysurfer.getCodeFiles({
|
|
927
|
+
task: this._promptText,
|
|
928
|
+
topK: 5,
|
|
929
|
+
minVerdictScore: 0.3,
|
|
930
|
+
preferComplete: true,
|
|
931
|
+
cacheDir
|
|
932
|
+
});
|
|
933
|
+
this._debug.timeEnd("Cache lookup");
|
|
934
|
+
this._cachedFiles = response.files;
|
|
935
|
+
addToLlmPrompt = response.addToLlmPrompt;
|
|
936
|
+
this._debug.log(`Found ${this._cachedFiles.length} cached files:`);
|
|
937
|
+
console.log("[raysurfer] Cache hit:", this._cachedFiles.length, "snippets retrieved");
|
|
938
|
+
if (this._cachedFiles.length > 0) {
|
|
939
|
+
this._debug.table(this._cachedFiles.map((f) => ({
|
|
940
|
+
filename: f.filename,
|
|
941
|
+
score: `${Math.round(f.score * 100)}%`,
|
|
942
|
+
thumbs: `${f.thumbsUp}/${f.thumbsDown}`,
|
|
943
|
+
sourceLength: `${f.source.length} chars`
|
|
944
|
+
})));
|
|
945
|
+
try {
|
|
946
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
947
|
+
for (const file of this._cachedFiles) {
|
|
948
|
+
const filePath = join(cacheDir, file.filename);
|
|
949
|
+
writeFileSync(filePath, file.source, "utf-8");
|
|
950
|
+
this._debug.log(` → Wrote cached file: ${filePath}`);
|
|
951
|
+
this._modifiedFilePaths.add(filePath);
|
|
952
|
+
}
|
|
953
|
+
} catch (writeErr) {
|
|
954
|
+
this._debug.log("Failed to write cached files:", writeErr instanceof Error ? writeErr : String(writeErr));
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
} catch (error) {
|
|
958
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
959
|
+
this._debug.log("Cache lookup failed:", errMsg);
|
|
960
|
+
console.warn("[raysurfer] Cache unavailable:", errMsg);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
const augmented = augmentSystemPrompt(this._sdkOptions.systemPrompt, addToLlmPrompt);
|
|
964
|
+
const augmentedOptions = {
|
|
965
|
+
...this._sdkOptions,
|
|
966
|
+
systemPrompt: augmented
|
|
967
|
+
};
|
|
968
|
+
this._debug.log("Augmented prompt addition:", addToLlmPrompt.length, "chars");
|
|
969
|
+
let sdkQueryFn;
|
|
970
|
+
try {
|
|
971
|
+
const sdk = await import("@anthropic-ai/claude-agent-sdk");
|
|
972
|
+
sdkQueryFn = sdk.query;
|
|
973
|
+
} catch {
|
|
974
|
+
throw new Error("Could not import @anthropic-ai/claude-agent-sdk. Install it with: npm install @anthropic-ai/claude-agent-sdk");
|
|
975
|
+
}
|
|
976
|
+
this._debug.time("Claude API call");
|
|
977
|
+
this._debug.log("Calling Claude Agent SDK...");
|
|
978
|
+
this._startTime = Date.now();
|
|
979
|
+
this._inner = sdkQueryFn({
|
|
980
|
+
prompt: this._params.prompt,
|
|
981
|
+
options: augmentedOptions
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
async _ensureInit() {
|
|
985
|
+
if (!this._inner) {
|
|
986
|
+
if (!this._initPromise) {
|
|
987
|
+
this._initPromise = this._initialize();
|
|
988
|
+
}
|
|
989
|
+
await this._initPromise;
|
|
990
|
+
}
|
|
991
|
+
return this._inner;
|
|
992
|
+
}
|
|
993
|
+
_extractBashOutputFiles(command) {
|
|
994
|
+
for (const pattern of BASH_OUTPUT_PATTERNS) {
|
|
995
|
+
pattern.lastIndex = 0;
|
|
996
|
+
let match = pattern.exec(command);
|
|
997
|
+
while (match !== null) {
|
|
998
|
+
const filePath = match[1];
|
|
999
|
+
if (filePath && filePath.length > 0) {
|
|
1000
|
+
const ext = filePath.substring(filePath.lastIndexOf(".")).toLowerCase();
|
|
1001
|
+
if (TRACKABLE_EXTENSIONS.has(ext)) {
|
|
1002
|
+
this._bashGeneratedFiles.add(filePath);
|
|
1003
|
+
this._debug.log(` → Bash output file detected: ${filePath}`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
match = pattern.exec(command);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
_trackMessage(message) {
|
|
1011
|
+
this._messageCount++;
|
|
1012
|
+
const msg = message;
|
|
1013
|
+
const elapsed = Date.now() - this._startTime;
|
|
1014
|
+
this._debug.log(`
|
|
1015
|
+
═══════════════════════════════════════════════════`);
|
|
1016
|
+
this._debug.log(`Message #${this._messageCount} [${elapsed}ms] type=${msg.type} subtype=${msg.subtype ?? "none"}`);
|
|
1017
|
+
this._debug.log(`═══════════════════════════════════════════════════`);
|
|
1018
|
+
this._debug.log(JSON.stringify(msg, null, 2));
|
|
1019
|
+
if (msg.type === "assistant" && msg.message?.content) {
|
|
1020
|
+
const blocks = msg.message.content;
|
|
1021
|
+
for (const block of blocks) {
|
|
1022
|
+
if (block.type === "tool_use" && block.name && FILE_MODIFY_TOOLS.includes(block.name)) {
|
|
1023
|
+
const filePath = block.input?.file_path ?? block.input?.notebook_path;
|
|
1024
|
+
if (filePath) {
|
|
1025
|
+
this._debug.log(` → ${block.name} tool detected:`, filePath);
|
|
1026
|
+
this._modifiedFilePaths.add(filePath);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (block.type === "tool_use" && block.name === "Bash") {
|
|
1030
|
+
const command = block.input?.command;
|
|
1031
|
+
if (command) {
|
|
1032
|
+
this._extractBashOutputFiles(command);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
if (block.type === "tool_result" && block.content) {
|
|
1036
|
+
this._executionLogs.push(block.content.slice(0, 5000));
|
|
1037
|
+
}
|
|
1038
|
+
if (block.type === "text" && block.text) {
|
|
1039
|
+
const codeMatches = block.text.match(/```(?:typescript|javascript|ts|js)?\n?([\s\S]*?)\n?```/g);
|
|
1040
|
+
if (codeMatches) {
|
|
1041
|
+
for (const match of codeMatches) {
|
|
1042
|
+
const code = match.replace(/```(?:typescript|javascript|ts|js)?\n?/, "").replace(/\n?```$/, "");
|
|
1043
|
+
if (code.trim().length > 50) {
|
|
1044
|
+
this._generatedCodeBlocks.push(code.trim());
|
|
1045
|
+
this._debug.log(` → Extracted code block (${code.length} chars)`);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (msg.type === "result" && msg.subtype === "success") {
|
|
1053
|
+
this._taskSucceeded = true;
|
|
1054
|
+
this._debug.timeEnd("Claude API call");
|
|
1055
|
+
this._debug.log("Task succeeded!");
|
|
1056
|
+
this._debug.log(" Duration:", msg.duration_ms, "ms");
|
|
1057
|
+
this._debug.log(" Total cost:", msg.total_cost_usd, "USD");
|
|
1058
|
+
this._debug.log(" Turns:", msg.num_turns);
|
|
1059
|
+
}
|
|
1060
|
+
if (msg.type === "result" && msg.subtype !== "success") {
|
|
1061
|
+
this._debug.timeEnd("Claude API call");
|
|
1062
|
+
this._debug.log("Task failed:", msg.subtype);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
async _uploadCache() {
|
|
1066
|
+
if (this._cacheUploadDone)
|
|
1067
|
+
return;
|
|
1068
|
+
this._cacheUploadDone = true;
|
|
1069
|
+
this._debug.log("Total messages streamed:", this._messageCount);
|
|
1070
|
+
this._debug.log("Modified files tracked:", this._modifiedFilePaths.size);
|
|
1071
|
+
this._debug.log("Code blocks extracted:", this._generatedCodeBlocks.length);
|
|
1072
|
+
const filesToCache = [];
|
|
1073
|
+
for (const filePath of this._modifiedFilePaths) {
|
|
1074
|
+
if (filePath.includes(CACHE_DIR)) {
|
|
1075
|
+
this._debug.log(" → Skipping cached file:", filePath);
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
try {
|
|
1079
|
+
if (existsSync(filePath)) {
|
|
1080
|
+
const content = readFileSync(filePath, "utf-8");
|
|
1081
|
+
if (content.includes("\x00")) {
|
|
1082
|
+
this._debug.log(" → Skipping binary file:", filePath);
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
filesToCache.push({ path: filePath, content });
|
|
1086
|
+
this._debug.log(" → Will cache file:", filePath, `(${content.length} chars)`);
|
|
1087
|
+
} else {
|
|
1088
|
+
this._debug.log(" → File not found:", filePath);
|
|
1089
|
+
}
|
|
1090
|
+
} catch (err) {
|
|
1091
|
+
this._debug.log(" → Failed to read file:", filePath, err instanceof Error ? err : String(err));
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
for (const filePath of this._bashGeneratedFiles) {
|
|
1095
|
+
if (this._modifiedFilePaths.has(filePath))
|
|
1096
|
+
continue;
|
|
1097
|
+
try {
|
|
1098
|
+
if (existsSync(filePath)) {
|
|
1099
|
+
const content = readFileSync(filePath, "utf-8");
|
|
1100
|
+
if (!content.includes("\x00")) {
|
|
1101
|
+
filesToCache.push({ path: filePath, content });
|
|
1102
|
+
this._debug.log(" → Will cache Bash-generated file:", filePath, `(${content.length} chars)`);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
} catch {
|
|
1106
|
+
this._debug.log(" → Failed to read Bash-generated file:", filePath);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (this._generatedCodeBlocks.length > 0) {
|
|
1110
|
+
const largestBlock = this._generatedCodeBlocks.reduce((a, b) => a.length > b.length ? a : b);
|
|
1111
|
+
filesToCache.push({
|
|
1112
|
+
path: "generated-code.ts",
|
|
1113
|
+
content: largestBlock
|
|
1114
|
+
});
|
|
1115
|
+
this._debug.log(" → Will cache generated code block:", `(${largestBlock.length} chars)`);
|
|
1116
|
+
}
|
|
1117
|
+
this._debug.log("Total items to cache:", filesToCache.length);
|
|
1118
|
+
if (this._cacheEnabled && this._raysurfer && this._taskSucceeded && this._promptText) {
|
|
1119
|
+
const cachedBlocksForVoting = this._cachedFiles.map((f) => ({
|
|
1120
|
+
codeBlockId: f.codeBlockId,
|
|
1121
|
+
filename: f.filename,
|
|
1122
|
+
description: f.description
|
|
1123
|
+
}));
|
|
1124
|
+
if (filesToCache.length > 0 || cachedBlocksForVoting.length > 0) {
|
|
1125
|
+
try {
|
|
1126
|
+
this._debug.time("Cache upload + voting");
|
|
1127
|
+
const joinedLogs = this._executionLogs.length > 0 ? this._executionLogs.join(`
|
|
1128
|
+
---
|
|
1129
|
+
`) : undefined;
|
|
1130
|
+
this._debug.log("Uploading", filesToCache.length, "files, voting for", cachedBlocksForVoting.length, "cached blocks,", this._executionLogs.length, "log entries...");
|
|
1131
|
+
for (let i = 0;i < filesToCache.length; i++) {
|
|
1132
|
+
const file = filesToCache[i];
|
|
1133
|
+
await this._raysurfer.uploadNewCodeSnip(this._promptText, file, true, i === 0 && cachedBlocksForVoting.length > 0 ? cachedBlocksForVoting : undefined, true, undefined, joinedLogs);
|
|
1134
|
+
}
|
|
1135
|
+
if (filesToCache.length === 0 && cachedBlocksForVoting.length > 0) {
|
|
1136
|
+
for (const cb of cachedBlocksForVoting) {
|
|
1137
|
+
await this._raysurfer.voteCodeSnip({
|
|
1138
|
+
task: this._promptText,
|
|
1139
|
+
codeBlockId: cb.codeBlockId,
|
|
1140
|
+
codeBlockName: cb.filename,
|
|
1141
|
+
codeBlockDescription: cb.description,
|
|
1142
|
+
succeeded: true
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
this._debug.timeEnd("Cache upload + voting");
|
|
1147
|
+
this._debug.log("Cache upload successful, voting queued on backend");
|
|
1148
|
+
console.log("[raysurfer] Cache upload successful:", filesToCache.length, "files stored");
|
|
1149
|
+
} catch (error) {
|
|
1150
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1151
|
+
this._debug.log("Cache upload failed:", errMsg);
|
|
1152
|
+
console.warn("[raysurfer] Cache upload failed:", errMsg);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
this._debug.groupEnd();
|
|
1157
|
+
}
|
|
1158
|
+
async next(...args) {
|
|
1159
|
+
const inner = await this._ensureInit();
|
|
1160
|
+
const result = await inner.next(...args);
|
|
1161
|
+
if (!result.done) {
|
|
1162
|
+
this._trackMessage(result.value);
|
|
1163
|
+
} else {
|
|
1164
|
+
await this._uploadCache();
|
|
1165
|
+
}
|
|
1166
|
+
return result;
|
|
1167
|
+
}
|
|
1168
|
+
async return(value) {
|
|
1169
|
+
if (this._inner) {
|
|
1170
|
+
await this._uploadCache();
|
|
1171
|
+
return this._inner.return(value);
|
|
1172
|
+
}
|
|
1173
|
+
return { done: true, value: undefined };
|
|
1174
|
+
}
|
|
1175
|
+
async throw(e) {
|
|
1176
|
+
if (this._inner)
|
|
1177
|
+
return this._inner.throw(e);
|
|
1178
|
+
throw e;
|
|
1179
|
+
}
|
|
1180
|
+
[Symbol.asyncIterator]() {
|
|
1181
|
+
return this;
|
|
1182
|
+
}
|
|
1183
|
+
async[Symbol.asyncDispose]() {
|
|
1184
|
+
this.close();
|
|
1185
|
+
}
|
|
1186
|
+
async interrupt() {
|
|
1187
|
+
return (await this._ensureInit()).interrupt();
|
|
1188
|
+
}
|
|
1189
|
+
async setPermissionMode(mode) {
|
|
1190
|
+
return (await this._ensureInit()).setPermissionMode(mode);
|
|
1191
|
+
}
|
|
1192
|
+
async setModel(model) {
|
|
1193
|
+
return (await this._ensureInit()).setModel(model);
|
|
1194
|
+
}
|
|
1195
|
+
async setMaxThinkingTokens(maxThinkingTokens) {
|
|
1196
|
+
return (await this._ensureInit()).setMaxThinkingTokens(maxThinkingTokens);
|
|
1197
|
+
}
|
|
1198
|
+
async supportedCommands() {
|
|
1199
|
+
return (await this._ensureInit()).supportedCommands();
|
|
1200
|
+
}
|
|
1201
|
+
async supportedModels() {
|
|
1202
|
+
return (await this._ensureInit()).supportedModels();
|
|
1203
|
+
}
|
|
1204
|
+
async mcpServerStatus() {
|
|
1205
|
+
return (await this._ensureInit()).mcpServerStatus();
|
|
1206
|
+
}
|
|
1207
|
+
async accountInfo() {
|
|
1208
|
+
return (await this._ensureInit()).accountInfo();
|
|
1209
|
+
}
|
|
1210
|
+
async rewindFiles(userMessageId, options) {
|
|
1211
|
+
return (await this._ensureInit()).rewindFiles(userMessageId, options);
|
|
1212
|
+
}
|
|
1213
|
+
async setMcpServers(servers) {
|
|
1214
|
+
return (await this._ensureInit()).setMcpServers(servers);
|
|
1215
|
+
}
|
|
1216
|
+
async streamInput(stream) {
|
|
1217
|
+
return (await this._ensureInit()).streamInput(stream);
|
|
1218
|
+
}
|
|
1219
|
+
close() {
|
|
1220
|
+
if (this._inner)
|
|
1221
|
+
this._inner.close();
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
function query(params) {
|
|
1225
|
+
return new RaysurferQuery(params);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
class ClaudeSDKClient {
|
|
1229
|
+
options;
|
|
1230
|
+
constructor(options = {}) {
|
|
1231
|
+
this.options = options;
|
|
1232
|
+
}
|
|
1233
|
+
query(prompt) {
|
|
1234
|
+
return query({ prompt, options: this.options });
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
var sdk_client_default = query;
|
|
1238
|
+
// src/types.ts
|
|
1239
|
+
var ExecutionState;
|
|
1240
|
+
((ExecutionState2) => {
|
|
1241
|
+
ExecutionState2["COMPLETED"] = "completed";
|
|
1242
|
+
ExecutionState2["ERRORED"] = "errored";
|
|
1243
|
+
ExecutionState2["TIMED_OUT"] = "timed_out";
|
|
1244
|
+
ExecutionState2["CANCELLED"] = "cancelled";
|
|
1245
|
+
})(ExecutionState ||= {});
|
|
1246
|
+
var AgentVerdict;
|
|
1247
|
+
((AgentVerdict2) => {
|
|
1248
|
+
AgentVerdict2["THUMBS_UP"] = "thumbs_up";
|
|
1249
|
+
AgentVerdict2["THUMBS_DOWN"] = "thumbs_down";
|
|
1250
|
+
AgentVerdict2["PENDING"] = "pending";
|
|
1251
|
+
})(AgentVerdict ||= {});
|
|
1252
|
+
|
|
1253
|
+
// src/index.ts
|
|
1254
|
+
import {
|
|
1255
|
+
AbortError,
|
|
1256
|
+
createSdkMcpServer,
|
|
1257
|
+
EXIT_REASONS,
|
|
1258
|
+
HOOK_EVENTS,
|
|
1259
|
+
tool
|
|
1260
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
1261
|
+
export {
|
|
1262
|
+
tool,
|
|
1263
|
+
sdk_client_default as queryDefault,
|
|
1264
|
+
query,
|
|
1265
|
+
createSdkMcpServer,
|
|
1266
|
+
ValidationError,
|
|
1267
|
+
VERSION,
|
|
1268
|
+
ClaudeSDKClient as RaysurferClient,
|
|
1269
|
+
RaySurferError,
|
|
1270
|
+
client_default as RaySurferDefault,
|
|
1271
|
+
RaySurfer,
|
|
1272
|
+
RateLimitError,
|
|
1273
|
+
HOOK_EVENTS,
|
|
1274
|
+
ExecutionState,
|
|
1275
|
+
EXIT_REASONS,
|
|
1276
|
+
ClaudeSDKClient,
|
|
1277
|
+
CacheUnavailableError,
|
|
1278
|
+
AuthenticationError,
|
|
1279
|
+
AgentVerdict,
|
|
1280
|
+
AbortError,
|
|
1281
|
+
APIError
|
|
1282
|
+
};
|