@rayrun/sdk 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,13 +9,46 @@ 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.
15
16
 
16
17
  The client covers catalog search, connection and credential setup, OAuth links, indexing, tool and
17
- client policies, activity, review queues, and webhooks. Safe reads retry rate limits, server errors,
18
- and transient network failures; writes do not retry automatically.
18
+ client policies, reusable access profiles, activity, review queues, and webhooks. Safe reads retry
19
+ rate limits, server errors, and transient network failures; writes do not retry automatically.
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
+
33
+ ## Reuse one access ceiling across clients
34
+
35
+ ```js
36
+ const { profile } = await rayrun.accessProfiles.create({
37
+ name: 'Support agents',
38
+ description: 'Read by default; sensitive tools stay blocked.',
39
+ });
40
+
41
+ await rayrun.accessProfiles.setPolicy(profile.uid, 'read-only', profile.toolPolicyVersion);
42
+ await rayrun.accessProfiles.update(profile.uid, {
43
+ description: 'Read by default; sensitive tools stay blocked.',
44
+ expectedVersion: profile.toolPolicyVersion + 1,
45
+ name: 'Support agents',
46
+ });
47
+ await rayrun.clients.setAccessProfile(clientUid, profile.uid, clientToolPolicyVersion);
48
+ ```
49
+
50
+ The effective policy is always the intersection of the workspace, profile, and client rules. A
51
+ profile can narrow access but cannot grant something blocked by the workspace or client.
19
52
 
20
53
  ## Stream code run-ahead
21
54
 
@@ -72,10 +105,13 @@ const valid = verifyWebhookSignature({
72
105
  ```
73
106
 
74
107
  After verification, parse the body as `WebhookPayload`. Every event has a stable `id`, `type`, and
75
- `occurredAt`; its `data` shape is narrowed by `type`. Use `id` to deduplicate retries.
108
+ `occurredAt`; its `data` shape is narrowed by `type`. Delivery is at least once, so make processing
109
+ idempotent and deduplicate on `id`.
76
110
  For `activity.call`, `arguments` and `result` contain the captured tool payloads, or `null` when
77
111
  capture was disabled for that connection, the webhook did not opt in with
78
- `forwardToolPayloads: true`, or no result existed.
112
+ `forwardToolPayloads: true`, or no result existed. `payloadCaptured` distinguishes a metadata-only
113
+ webhook from a call where capture was disabled. Rayrun observes MCP tool arguments and results; it
114
+ does not receive the host's full Claude or GPT prompt, assistant response, or surrounding chat.
79
115
 
80
116
  ```ts
81
117
  import type { WebhookPayload } from '@rayrun/sdk';
package/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export type JsonValue =
10
10
  export type PageQuery = { cursor?: string; limit?: number };
11
11
  export type ToolDecision = 'allow' | 'ask' | 'block';
12
12
  export type ToolAccessMode = 'read-only' | 'ask-before-changes' | 'allow-all' | 'custom';
13
+ export type AccessProfileMode = 'read-only' | 'ask-before-changes' | 'allow-all' | 'block-all';
13
14
  export type Risk = 'read' | 'change' | 'destructive' | 'unknown';
14
15
  export type Connection = {
15
16
  authorizedAt: string | null;
@@ -24,6 +25,7 @@ export type Connection = {
24
25
  slug: string;
25
26
  status: 'connected' | 'disabled' | 'error' | 'indexing' | 'needs-authorization' | 'new';
26
27
  toolAccessMode: ToolAccessMode;
28
+ toolPolicyVersion: number;
27
29
  };
28
30
  export type CatalogEntry = {
29
31
  authType: Connection['authType'];
@@ -50,15 +52,54 @@ export type Tool = {
50
52
  title: string | null;
51
53
  };
52
54
  export type Client = {
55
+ accessProfile: Pick<AccessProfile, 'name' | 'uid'> | null;
53
56
  clientName: string;
54
57
  grantedAt: string;
55
58
  lastUsedAt: string | null;
56
59
  redirectHost: string;
57
60
  revokedAt: string | null;
58
61
  toolAccessMode: ToolAccessMode;
62
+ toolPolicyVersion: number;
59
63
  uid: string;
60
64
  userName: string;
61
65
  };
66
+ export type ClientTool = {
67
+ approved: boolean;
68
+ clientDecision: ToolDecision | null;
69
+ effectiveDecision: ToolDecision;
70
+ effectiveReason:
71
+ | 'definition-unapproved'
72
+ | 'workspace-tool-rule'
73
+ | 'workspace-default'
74
+ | 'client-profile-tool-rule'
75
+ | 'client-profile-default'
76
+ | 'client-tool-rule'
77
+ | 'client-default'
78
+ | 'multiple-layers'
79
+ | 'workspace-and-client';
80
+ name: string;
81
+ riskLevel: Risk;
82
+ riskReason: string;
83
+ serviceName: string;
84
+ uid: string;
85
+ workspaceDecision: ToolDecision | null;
86
+ };
87
+ export type AccessProfile = {
88
+ assignedClientCount: number;
89
+ description: string;
90
+ name: string;
91
+ toolAccessMode: AccessProfileMode;
92
+ toolPolicyVersion: number;
93
+ uid: string;
94
+ };
95
+ export type AccessProfileTool = {
96
+ available: boolean;
97
+ decision: ToolDecision | null;
98
+ name: string;
99
+ riskLevel: Risk;
100
+ serviceName: string;
101
+ uid: string;
102
+ };
62
103
  export type Activity = {
63
104
  calledAt: string;
64
105
  clientName: string | null;
@@ -111,7 +152,17 @@ export type WebhookEvent =
111
152
  | 'review.changed'
112
153
  | 'tool.definition-changed'
113
154
  | 'tool.policy-changed';
114
- export type WebhookEnvelope<T extends Exclude<WebhookEvent, '*'>, D> = {
155
+ export type WebhookDeliveryEvent = Exclude<WebhookEvent, '*'> | 'webhook.test';
156
+ export type ToolPolicyEventType =
157
+ | 'access-profile-archived'
158
+ | 'access-profile-assigned'
159
+ | 'access-profile-changed'
160
+ | 'access-profile-created'
161
+ | 'client-mode-changed'
162
+ | 'client-tool-rule-changed'
163
+ | 'workspace-mode-changed'
164
+ | 'workspace-tool-rule-changed';
165
+ export type WebhookEnvelope<T extends WebhookDeliveryEvent, D> = {
115
166
  data: D;
116
167
  id: string;
117
168
  occurredAt: string;
@@ -124,12 +175,23 @@ export type WebhookPayload =
124
175
  activityId: string;
125
176
  arguments: JsonValue;
126
177
  calledAt: string;
178
+ clientUid: string | null;
127
179
  connectionUid: string | null;
128
180
  errorCode: string | null;
181
+ payloadCaptured: boolean;
129
182
  policyDecision: ToolDecision | null;
130
183
  result: JsonValue;
184
+ sessionUid: string;
131
185
  succeeded: boolean | null;
132
186
  toolName: string;
187
+ userUid: string | null;
188
+ }
189
+ >
190
+ | WebhookEnvelope<
191
+ 'webhook.test',
192
+ {
193
+ endpointUid: string;
194
+ message: string;
133
195
  }
134
196
  >
135
197
  | WebhookEnvelope<
@@ -169,7 +231,10 @@ export type WebhookPayload =
169
231
  {
170
232
  connectionUid: string | null;
171
233
  decision: ToolDecision | null;
172
- eventType: string;
234
+ eventType: ToolPolicyEventType;
235
+ metadata: Record<string, JsonValue>;
236
+ oauthClientConsentUid: string | null;
237
+ oauthClientName: string | null;
173
238
  toolUid: string | null;
174
239
  }
175
240
  >;
@@ -208,7 +273,7 @@ export class Rayrun {
208
273
  | { kind: 'openapi'; specificationUrl: string; baseUrl?: string; name?: string },
209
274
  ): Promise<{ connection: { uid: string } }>;
210
275
  createOAuthLink(uid: string): Promise<{ authorizationUrl: string }>;
211
- delete(uid: string): Promise<void>;
276
+ delete(uid: string, expectedVersion: number): Promise<void>;
212
277
  index(uid: string): Promise<{ enqueued: boolean }>;
213
278
  list(query?: PageQuery): Promise<CursorPage<Connection>>;
214
279
  setCredential(uid: string, body: Credential): Promise<{ enqueued: boolean }>;
@@ -219,7 +284,7 @@ export class Rayrun {
219
284
  setPolicy(uid: string, mode: ToolAccessMode): Promise<void>;
220
285
  };
221
286
  tools: {
222
- list(query?: PageQuery & { connectionUid?: string }): Promise<CursorPage<Tool>>;
287
+ list(query?: PageQuery & { connectionUid?: string; query?: string }): Promise<CursorPage<Tool>>;
223
288
  setPolicy(
224
289
  connectionUid: string,
225
290
  toolUid: string,
@@ -228,6 +293,12 @@ export class Rayrun {
228
293
  };
229
294
  clients: {
230
295
  list(query?: PageQuery): Promise<CursorPage<Client>>;
296
+ listTools(uid: string, query?: PageQuery & { query?: string }): Promise<CursorPage<ClientTool>>;
297
+ setAccessProfile(
298
+ uid: string,
299
+ profileUid: string | null,
300
+ expectedVersion: number,
301
+ ): Promise<void>;
231
302
  setPolicy(uid: string, mode: Exclude<ToolAccessMode, 'custom'>): Promise<void>;
232
303
  setToolPolicy(
233
304
  uid: string,
@@ -235,6 +306,28 @@ export class Rayrun {
235
306
  body: { decision: ToolDecision | null; riskConfirmed?: boolean },
236
307
  ): Promise<void>;
237
308
  };
309
+ accessProfiles: {
310
+ archive(uid: string, expectedVersion: number): Promise<void>;
311
+ create(body: { description: string; name: string }): Promise<{ profile: AccessProfile }>;
312
+ /** @deprecated Use archive. */
313
+ delete(uid: string, expectedVersion: number): Promise<void>;
314
+ list(query?: PageQuery): Promise<CursorPage<AccessProfile>>;
315
+ listTools(uid: string, query?: PageQuery): Promise<CursorPage<AccessProfileTool>>;
316
+ setPolicy(uid: string, mode: AccessProfileMode, expectedVersion: number): Promise<void>;
317
+ setToolPolicy(
318
+ uid: string,
319
+ toolUid: string,
320
+ body: {
321
+ decision: ToolDecision | null;
322
+ expectedVersion: number;
323
+ riskConfirmed?: boolean;
324
+ },
325
+ ): Promise<void>;
326
+ update(
327
+ uid: string,
328
+ body: { description: string; expectedVersion: number; name: string },
329
+ ): Promise<void>;
330
+ };
238
331
  activity: { list(query?: PageQuery): Promise<CursorPage<Activity>> };
239
332
  reviews: { list(query?: PageQuery): Promise<CursorPage<Review>> };
240
333
  webhooks: {
package/index.js CHANGED
@@ -114,10 +114,31 @@ export class Rayrun {
114
114
  };
115
115
  this.clients = {
116
116
  list: (query) => this.request('GET', '/clients', { query }),
117
+ listTools: (uid, query) => this.request('GET', `/clients/${uid}/tools`, { query }),
118
+ setAccessProfile: (uid, profileUid, expectedVersion) =>
119
+ this.request('PATCH', `/clients/${uid}/access-profile`, {
120
+ body: { expectedVersion, profileUid },
121
+ }),
117
122
  setPolicy: (uid, mode) => this.request('PATCH', `/clients/${uid}/policy`, { body: { mode } }),
118
123
  setToolPolicy: (uid, toolUid, body) =>
119
124
  this.request('PATCH', `/clients/${uid}/tools/${toolUid}/policy`, { body }),
120
125
  };
126
+ const archiveAccessProfile = (uid, expectedVersion) =>
127
+ this.request('DELETE', `/access-profiles/${uid}`, { query: { expectedVersion } });
128
+ this.accessProfiles = {
129
+ archive: archiveAccessProfile,
130
+ create: (body) => this.request('POST', '/access-profiles', { body }),
131
+ delete: archiveAccessProfile,
132
+ list: (query) => this.request('GET', '/access-profiles', { query }),
133
+ listTools: (uid, query) => this.request('GET', `/access-profiles/${uid}/tools`, { query }),
134
+ setPolicy: (uid, mode, expectedVersion) =>
135
+ this.request('PATCH', `/access-profiles/${uid}/policy`, {
136
+ body: { expectedVersion, mode },
137
+ }),
138
+ setToolPolicy: (uid, toolUid, body) =>
139
+ this.request('PATCH', `/access-profiles/${uid}/tools/${toolUid}/policy`, { body }),
140
+ update: (uid, body) => this.request('PATCH', `/access-profiles/${uid}`, { body }),
141
+ };
121
142
  this.activity = { list: (query) => this.request('GET', '/activity', { query }) };
122
143
  this.reviews = { list: (query) => this.request('GET', '/reviews', { query }) };
123
144
  this.webhooks = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayrun/sdk",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Type-safe client for the Rayrun public API",
5
5
  "license": "MIT",
6
6
  "repository": {