@rayrun/sdk 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -1
- package/index.d.ts +153 -1
- package/index.js +19 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ import { Rayrun } from '@rayrun/sdk';
|
|
|
9
9
|
|
|
10
10
|
const rayrun = new Rayrun({ apiKey: process.env.RAYRUN_API_KEY });
|
|
11
11
|
const { items: connections } = await rayrun.connections.list();
|
|
12
|
+
const { items: matchingTools } = await rayrun.tools.list({ query: 'create issue' });
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
Create keys in **Dashboard → Settings → API keys**. The plaintext is shown once.
|
|
@@ -17,6 +18,18 @@ The client covers catalog search, connection and credential setup, OAuth links,
|
|
|
17
18
|
client policies, reusable access profiles, activity, review queues, and webhooks. Safe reads retry
|
|
18
19
|
rate limits, server errors, and transient network failures; writes do not retry automatically.
|
|
19
20
|
|
|
21
|
+
Inspect the effective policy applied to one connected client without reproducing policy logic in
|
|
22
|
+
your application:
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
const { items: effectivePolicy } = await rayrun.clients.listTools(clientUid, {
|
|
26
|
+
query: 'create issue',
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Each result includes the effective decision and the workspace, access-profile, or client layer that
|
|
31
|
+
restricted it.
|
|
32
|
+
|
|
20
33
|
## Reuse one access ceiling across clients
|
|
21
34
|
|
|
22
35
|
```js
|
|
@@ -37,6 +50,42 @@ await rayrun.clients.setAccessProfile(clientUid, profile.uid, clientToolPolicyVe
|
|
|
37
50
|
The effective policy is always the intersection of the workspace, profile, and client rules. A
|
|
38
51
|
profile can narrow access but cannot grant something blocked by the workspace or client.
|
|
39
52
|
|
|
53
|
+
## Manage hosted tool hooks
|
|
54
|
+
|
|
55
|
+
Hooks are versioned TypeScript adapters hosted and sandboxed by Rayrun. Pull the generated types and
|
|
56
|
+
draft, test it without a live upstream call, then create an immutable shadow or active revision.
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
const { hook } = await rayrun.hooks.get(connectionUid, toolUid);
|
|
60
|
+
|
|
61
|
+
const tested = await rayrun.hooks.test(connectionUid, toolUid, {
|
|
62
|
+
source: hook.draftSource,
|
|
63
|
+
config: hook.draftConfig,
|
|
64
|
+
arguments: { query: 'release' },
|
|
65
|
+
mockResult: { items: [] },
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const saved = await rayrun.hooks.saveDraft(connectionUid, toolUid, {
|
|
69
|
+
source: hook.draftSource,
|
|
70
|
+
config: hook.draftConfig,
|
|
71
|
+
expectedVersion: hook.version,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await rayrun.hooks.deploy(connectionUid, toolUid, {
|
|
75
|
+
expectedVersion: saved.hook.version,
|
|
76
|
+
mode: 'shadow',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const runs = await rayrun.hooks.listRuns(connectionUid, toolUid, { limit: 25 });
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`setDeployment` moves an active or shadow pointer to an existing compatible revision, or deactivates
|
|
83
|
+
it with `revisionUid: null`. Every mutation uses `expectedVersion`; fetch and reconcile instead of
|
|
84
|
+
blindly retrying a `version_conflict`. Use a full-control API key for hook writes. Read-only keys can
|
|
85
|
+
inspect source, generated declarations, revision history, and deployed-run metadata; captured logs
|
|
86
|
+
and errors require `hooks:write`. `RayrunApiError.diagnostics` carries compiler line and column
|
|
87
|
+
details for editor and CI output.
|
|
88
|
+
|
|
40
89
|
## Stream code run-ahead
|
|
41
90
|
|
|
42
91
|
A model harness that receives `execute_code` arguments incrementally can let Rayrun start eligible
|
|
@@ -92,7 +141,8 @@ const valid = verifyWebhookSignature({
|
|
|
92
141
|
```
|
|
93
142
|
|
|
94
143
|
After verification, parse the body as `WebhookPayload`. Every event has a stable `id`, `type`, and
|
|
95
|
-
`occurredAt`; its `data` shape is narrowed by `type`.
|
|
144
|
+
`occurredAt`; its `data` shape is narrowed by `type`. Delivery is at least once, so make processing
|
|
145
|
+
idempotent and deduplicate on `id`.
|
|
96
146
|
For `activity.call`, `arguments` and `result` contain the captured tool payloads, or `null` when
|
|
97
147
|
capture was disabled for that connection, the webhook did not opt in with
|
|
98
148
|
`forwardToolPayloads: true`, or no result existed. `payloadCaptured` distinguishes a metadata-only
|
package/index.d.ts
CHANGED
|
@@ -51,6 +51,89 @@ export type Tool = {
|
|
|
51
51
|
inputSchema: Record<string, unknown>;
|
|
52
52
|
title: string | null;
|
|
53
53
|
};
|
|
54
|
+
export type ToolHookPublicContract = {
|
|
55
|
+
description?: string;
|
|
56
|
+
inputSchema?: Record<string, unknown>;
|
|
57
|
+
outputSchema?: Record<string, unknown>;
|
|
58
|
+
};
|
|
59
|
+
export type ToolHookRevision = {
|
|
60
|
+
config: Record<string, JsonValue>;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
publicContract: ToolHookPublicContract | null;
|
|
63
|
+
revisionNumber: number;
|
|
64
|
+
source: string;
|
|
65
|
+
sourceHash: string;
|
|
66
|
+
uid: string;
|
|
67
|
+
upstreamDefinitionHash: string;
|
|
68
|
+
};
|
|
69
|
+
export type ToolHook = {
|
|
70
|
+
activeRevisionStale: boolean;
|
|
71
|
+
activeRevisionUid: string | null;
|
|
72
|
+
draftConfig: Record<string, JsonValue>;
|
|
73
|
+
draftSource: string;
|
|
74
|
+
hookUid: string | null;
|
|
75
|
+
revisions: ToolHookRevision[];
|
|
76
|
+
shadowRevisionStale: boolean;
|
|
77
|
+
shadowRevisionUid: string | null;
|
|
78
|
+
target: {
|
|
79
|
+
definitionHash: string | null;
|
|
80
|
+
inputSchema: Record<string, unknown>;
|
|
81
|
+
name: string;
|
|
82
|
+
outputSchema: Record<string, unknown> | null;
|
|
83
|
+
toolUid: string;
|
|
84
|
+
};
|
|
85
|
+
types: string;
|
|
86
|
+
version: number;
|
|
87
|
+
};
|
|
88
|
+
export type ToolHookLogEntry = {
|
|
89
|
+
data?: JsonValue;
|
|
90
|
+
level: 'debug' | 'error' | 'info' | 'warn';
|
|
91
|
+
message: string;
|
|
92
|
+
};
|
|
93
|
+
export type ToolHookTestFailure = {
|
|
94
|
+
durationMs: number;
|
|
95
|
+
error: {
|
|
96
|
+
code: 'invalid_arguments' | 'invalid_outcome' | 'invalid_output' | 'runtime_error' | 'timeout';
|
|
97
|
+
message: string;
|
|
98
|
+
};
|
|
99
|
+
logs: ToolHookLogEntry[];
|
|
100
|
+
status: 'failed';
|
|
101
|
+
};
|
|
102
|
+
export type ToolHookBeforeTestResult =
|
|
103
|
+
| {
|
|
104
|
+
durationMs: number;
|
|
105
|
+
logs: ToolHookLogEntry[];
|
|
106
|
+
outcome:
|
|
107
|
+
| { action: 'continue'; arguments: JsonValue }
|
|
108
|
+
| { action: 'reject'; message: string; reason: string }
|
|
109
|
+
| { action: 'require_approval'; arguments: JsonValue; reason: string };
|
|
110
|
+
status: 'completed';
|
|
111
|
+
}
|
|
112
|
+
| ToolHookTestFailure;
|
|
113
|
+
export type ToolHookAfterTestResult =
|
|
114
|
+
| {
|
|
115
|
+
durationMs: number;
|
|
116
|
+
logs: ToolHookLogEntry[];
|
|
117
|
+
outcome:
|
|
118
|
+
| { action: 'return'; result: JsonValue }
|
|
119
|
+
| { action: 'fail'; message: string; reason: string };
|
|
120
|
+
status: 'completed';
|
|
121
|
+
}
|
|
122
|
+
| ToolHookTestFailure;
|
|
123
|
+
export type ToolHookRun = {
|
|
124
|
+
createdAt: string;
|
|
125
|
+
differsFromActive: boolean | null;
|
|
126
|
+
durationMs: number;
|
|
127
|
+
errorMessage: string | null;
|
|
128
|
+
id: string;
|
|
129
|
+
logs: ToolHookLogEntry[] | null;
|
|
130
|
+
outcome: 'approval-required' | 'continued' | 'failed' | 'rejected' | 'returned';
|
|
131
|
+
requestId: string;
|
|
132
|
+
revisionUid: string;
|
|
133
|
+
shadow: boolean;
|
|
134
|
+
stage: 'after' | 'before';
|
|
135
|
+
uid: string;
|
|
136
|
+
};
|
|
54
137
|
export type Client = {
|
|
55
138
|
accessProfile: Pick<AccessProfile, 'name' | 'uid'> | null;
|
|
56
139
|
clientName: string;
|
|
@@ -63,6 +146,27 @@ export type Client = {
|
|
|
63
146
|
uid: string;
|
|
64
147
|
userName: string;
|
|
65
148
|
};
|
|
149
|
+
export type ClientTool = {
|
|
150
|
+
approved: boolean;
|
|
151
|
+
clientDecision: ToolDecision | null;
|
|
152
|
+
effectiveDecision: ToolDecision;
|
|
153
|
+
effectiveReason:
|
|
154
|
+
| 'definition-unapproved'
|
|
155
|
+
| 'workspace-tool-rule'
|
|
156
|
+
| 'workspace-default'
|
|
157
|
+
| 'client-profile-tool-rule'
|
|
158
|
+
| 'client-profile-default'
|
|
159
|
+
| 'client-tool-rule'
|
|
160
|
+
| 'client-default'
|
|
161
|
+
| 'multiple-layers'
|
|
162
|
+
| 'workspace-and-client';
|
|
163
|
+
name: string;
|
|
164
|
+
riskLevel: Risk;
|
|
165
|
+
riskReason: string;
|
|
166
|
+
serviceName: string;
|
|
167
|
+
uid: string;
|
|
168
|
+
workspaceDecision: ToolDecision | null;
|
|
169
|
+
};
|
|
66
170
|
export type AccessProfile = {
|
|
67
171
|
assignedClientCount: number;
|
|
68
172
|
description: string;
|
|
@@ -222,6 +326,7 @@ export const CODE_RUN_AHEAD_META_KEY: 'io.rayrun/run-ahead-session';
|
|
|
222
326
|
|
|
223
327
|
export class RayrunApiError extends Error {
|
|
224
328
|
code: string;
|
|
329
|
+
diagnostics: Array<{ column?: number; line?: number; message: string }>;
|
|
225
330
|
requestId: string | null;
|
|
226
331
|
status: number;
|
|
227
332
|
}
|
|
@@ -263,15 +368,62 @@ export class Rayrun {
|
|
|
263
368
|
setPolicy(uid: string, mode: ToolAccessMode): Promise<void>;
|
|
264
369
|
};
|
|
265
370
|
tools: {
|
|
266
|
-
list(query?: PageQuery & { connectionUid?: string }): Promise<CursorPage<Tool>>;
|
|
371
|
+
list(query?: PageQuery & { connectionUid?: string; query?: string }): Promise<CursorPage<Tool>>;
|
|
267
372
|
setPolicy(
|
|
268
373
|
connectionUid: string,
|
|
269
374
|
toolUid: string,
|
|
270
375
|
body: { decision: ToolDecision | null; riskConfirmed?: boolean; riskOverride?: Risk | null },
|
|
271
376
|
): Promise<void>;
|
|
272
377
|
};
|
|
378
|
+
hooks: {
|
|
379
|
+
deploy(
|
|
380
|
+
connectionUid: string,
|
|
381
|
+
toolUid: string,
|
|
382
|
+
body: { expectedVersion: number; mode: 'active' | 'shadow' },
|
|
383
|
+
): Promise<{ hook: ToolHook }>;
|
|
384
|
+
get(connectionUid: string, toolUid: string): Promise<{ hook: ToolHook }>;
|
|
385
|
+
listRuns(
|
|
386
|
+
connectionUid: string,
|
|
387
|
+
toolUid: string,
|
|
388
|
+
query?: PageQuery,
|
|
389
|
+
): Promise<CursorPage<ToolHookRun>>;
|
|
390
|
+
saveDraft(
|
|
391
|
+
connectionUid: string,
|
|
392
|
+
toolUid: string,
|
|
393
|
+
body: {
|
|
394
|
+
config?: Record<string, JsonValue>;
|
|
395
|
+
expectedVersion: number;
|
|
396
|
+
source: string;
|
|
397
|
+
},
|
|
398
|
+
): Promise<{ hook: ToolHook }>;
|
|
399
|
+
setDeployment(
|
|
400
|
+
connectionUid: string,
|
|
401
|
+
toolUid: string,
|
|
402
|
+
body: {
|
|
403
|
+
expectedVersion: number;
|
|
404
|
+
mode: 'active' | 'shadow';
|
|
405
|
+
revisionUid: string | null;
|
|
406
|
+
},
|
|
407
|
+
): Promise<{ hook: ToolHook }>;
|
|
408
|
+
test(
|
|
409
|
+
connectionUid: string,
|
|
410
|
+
toolUid: string,
|
|
411
|
+
body: {
|
|
412
|
+
arguments: JsonValue;
|
|
413
|
+
config?: Record<string, JsonValue>;
|
|
414
|
+
mockResult?: JsonValue;
|
|
415
|
+
source: string;
|
|
416
|
+
},
|
|
417
|
+
): Promise<{
|
|
418
|
+
after?: ToolHookAfterTestResult;
|
|
419
|
+
before: ToolHookBeforeTestResult;
|
|
420
|
+
publicContract: ToolHookPublicContract | null;
|
|
421
|
+
sourceHash: string;
|
|
422
|
+
}>;
|
|
423
|
+
};
|
|
273
424
|
clients: {
|
|
274
425
|
list(query?: PageQuery): Promise<CursorPage<Client>>;
|
|
426
|
+
listTools(uid: string, query?: PageQuery & { query?: string }): Promise<CursorPage<ClientTool>>;
|
|
275
427
|
setAccessProfile(
|
|
276
428
|
uid: string,
|
|
277
429
|
profileUid: string | null,
|
package/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
2
|
|
|
3
3
|
export class RayrunApiError extends Error {
|
|
4
|
-
constructor(message, { code, requestId, status }) {
|
|
4
|
+
constructor(message, { code, diagnostics = [], requestId, status }) {
|
|
5
5
|
super(message);
|
|
6
6
|
this.name = 'RayrunApiError';
|
|
7
7
|
this.code = code;
|
|
8
|
+
this.diagnostics = diagnostics;
|
|
8
9
|
this.requestId = requestId;
|
|
9
10
|
this.status = status;
|
|
10
11
|
}
|
|
@@ -112,8 +113,24 @@ export class Rayrun {
|
|
|
112
113
|
setPolicy: (connectionUid, toolUid, body) =>
|
|
113
114
|
this.request('PATCH', `/connections/${connectionUid}/tools/${toolUid}/policy`, { body }),
|
|
114
115
|
};
|
|
116
|
+
const toolHookPath = (connectionUid, toolUid) =>
|
|
117
|
+
`/connections/${connectionUid}/tools/${toolUid}/hook`;
|
|
118
|
+
this.hooks = {
|
|
119
|
+
deploy: (connectionUid, toolUid, body) =>
|
|
120
|
+
this.request('POST', `${toolHookPath(connectionUid, toolUid)}/deploy`, { body }),
|
|
121
|
+
get: (connectionUid, toolUid) => this.request('GET', toolHookPath(connectionUid, toolUid)),
|
|
122
|
+
listRuns: (connectionUid, toolUid, query) =>
|
|
123
|
+
this.request('GET', `${toolHookPath(connectionUid, toolUid)}/runs`, { query }),
|
|
124
|
+
saveDraft: (connectionUid, toolUid, body) =>
|
|
125
|
+
this.request('PUT', toolHookPath(connectionUid, toolUid), { body }),
|
|
126
|
+
setDeployment: (connectionUid, toolUid, body) =>
|
|
127
|
+
this.request('POST', `${toolHookPath(connectionUid, toolUid)}/deployment`, { body }),
|
|
128
|
+
test: (connectionUid, toolUid, body) =>
|
|
129
|
+
this.request('POST', `${toolHookPath(connectionUid, toolUid)}/test`, { body }),
|
|
130
|
+
};
|
|
115
131
|
this.clients = {
|
|
116
132
|
list: (query) => this.request('GET', '/clients', { query }),
|
|
133
|
+
listTools: (uid, query) => this.request('GET', `/clients/${uid}/tools`, { query }),
|
|
117
134
|
setAccessProfile: (uid, profileUid, expectedVersion) =>
|
|
118
135
|
this.request('PATCH', `/clients/${uid}/access-profile`, {
|
|
119
136
|
body: { expectedVersion, profileUid },
|
|
@@ -188,6 +205,7 @@ export class Rayrun {
|
|
|
188
205
|
problem?.error?.message ?? `Rayrun API request failed with HTTP ${response.status}.`,
|
|
189
206
|
{
|
|
190
207
|
code: problem?.error?.code ?? 'request_failed',
|
|
208
|
+
diagnostics: Array.isArray(problem?.diagnostics) ? problem.diagnostics : [],
|
|
191
209
|
requestId: problem?.error?.requestId ?? response.headers.get('rayrun-request-id'),
|
|
192
210
|
status: response.status,
|
|
193
211
|
},
|