@rayrun/sdk 0.2.0 → 0.3.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
@@ -14,8 +14,28 @@ const { items: connections } = await rayrun.connections.list();
14
14
  Create keys in **Dashboard → Settings → API keys**. The plaintext is shown once.
15
15
 
16
16
  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.
17
+ client policies, reusable access profiles, activity, review queues, and webhooks. Safe reads retry
18
+ rate limits, server errors, and transient network failures; writes do not retry automatically.
19
+
20
+ ## Reuse one access ceiling across clients
21
+
22
+ ```js
23
+ const { profile } = await rayrun.accessProfiles.create({
24
+ name: 'Support agents',
25
+ description: 'Read by default; sensitive tools stay blocked.',
26
+ });
27
+
28
+ await rayrun.accessProfiles.setPolicy(profile.uid, 'read-only', profile.toolPolicyVersion);
29
+ await rayrun.accessProfiles.update(profile.uid, {
30
+ description: 'Read by default; sensitive tools stay blocked.',
31
+ expectedVersion: profile.toolPolicyVersion + 1,
32
+ name: 'Support agents',
33
+ });
34
+ await rayrun.clients.setAccessProfile(clientUid, profile.uid, clientToolPolicyVersion);
35
+ ```
36
+
37
+ The effective policy is always the intersection of the workspace, profile, and client rules. A
38
+ profile can narrow access but cannot grant something blocked by the workspace or client.
19
39
 
20
40
  ## Stream code run-ahead
21
41
 
@@ -73,6 +93,11 @@ const valid = verifyWebhookSignature({
73
93
 
74
94
  After verification, parse the body as `WebhookPayload`. Every event has a stable `id`, `type`, and
75
95
  `occurredAt`; its `data` shape is narrowed by `type`. Use `id` to deduplicate retries.
96
+ For `activity.call`, `arguments` and `result` contain the captured tool payloads, or `null` when
97
+ capture was disabled for that connection, the webhook did not opt in with
98
+ `forwardToolPayloads: true`, or no result existed. `payloadCaptured` distinguishes a metadata-only
99
+ webhook from a call where capture was disabled. Rayrun observes MCP tool arguments and results; it
100
+ does not receive the host's full Claude or GPT prompt, assistant response, or surrounding chat.
76
101
 
77
102
  ```ts
78
103
  import type { WebhookPayload } from '@rayrun/sdk';
package/index.d.ts CHANGED
@@ -1,8 +1,16 @@
1
1
  /* oxlint-disable perfectionist/sort-classes, perfectionist/sort-modules, perfectionist/sort-object-types, perfectionist/sort-union-types -- Public types follow the documented API resource order. */
2
2
  export type CursorPage<T> = { items: T[]; page: { nextCursor: string | null } };
3
+ export type JsonValue =
4
+ | boolean
5
+ | null
6
+ | number
7
+ | string
8
+ | JsonValue[]
9
+ | { [key: string]: JsonValue };
3
10
  export type PageQuery = { cursor?: string; limit?: number };
4
11
  export type ToolDecision = 'allow' | 'ask' | 'block';
5
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';
6
14
  export type Risk = 'read' | 'change' | 'destructive' | 'unknown';
7
15
  export type Connection = {
8
16
  authorizedAt: string | null;
@@ -17,6 +25,7 @@ export type Connection = {
17
25
  slug: string;
18
26
  status: 'connected' | 'disabled' | 'error' | 'indexing' | 'needs-authorization' | 'new';
19
27
  toolAccessMode: ToolAccessMode;
28
+ toolPolicyVersion: number;
20
29
  };
21
30
  export type CatalogEntry = {
22
31
  authType: Connection['authType'];
@@ -43,15 +52,33 @@ export type Tool = {
43
52
  title: string | null;
44
53
  };
45
54
  export type Client = {
55
+ accessProfile: Pick<AccessProfile, 'name' | 'uid'> | null;
46
56
  clientName: string;
47
57
  grantedAt: string;
48
58
  lastUsedAt: string | null;
49
59
  redirectHost: string;
50
60
  revokedAt: string | null;
51
61
  toolAccessMode: ToolAccessMode;
62
+ toolPolicyVersion: number;
52
63
  uid: string;
53
64
  userName: string;
54
65
  };
66
+ export type AccessProfile = {
67
+ assignedClientCount: number;
68
+ description: string;
69
+ name: string;
70
+ toolAccessMode: AccessProfileMode;
71
+ toolPolicyVersion: number;
72
+ uid: string;
73
+ };
74
+ export type AccessProfileTool = {
75
+ available: boolean;
76
+ decision: ToolDecision | null;
77
+ name: string;
78
+ riskLevel: Risk;
79
+ serviceName: string;
80
+ uid: string;
81
+ };
55
82
  export type Activity = {
56
83
  calledAt: string;
57
84
  clientName: string | null;
@@ -77,6 +104,7 @@ export type Webhook = {
77
104
  description: string | null;
78
105
  enabled: boolean;
79
106
  eventTypes: WebhookEvent[];
107
+ forwardToolPayloads: boolean;
80
108
  uid: string;
81
109
  url: string;
82
110
  };
@@ -103,7 +131,17 @@ export type WebhookEvent =
103
131
  | 'review.changed'
104
132
  | 'tool.definition-changed'
105
133
  | 'tool.policy-changed';
106
- export type WebhookEnvelope<T extends Exclude<WebhookEvent, '*'>, D> = {
134
+ export type WebhookDeliveryEvent = Exclude<WebhookEvent, '*'> | 'webhook.test';
135
+ export type ToolPolicyEventType =
136
+ | 'access-profile-archived'
137
+ | 'access-profile-assigned'
138
+ | 'access-profile-changed'
139
+ | 'access-profile-created'
140
+ | 'client-mode-changed'
141
+ | 'client-tool-rule-changed'
142
+ | 'workspace-mode-changed'
143
+ | 'workspace-tool-rule-changed';
144
+ export type WebhookEnvelope<T extends WebhookDeliveryEvent, D> = {
107
145
  data: D;
108
146
  id: string;
109
147
  occurredAt: string;
@@ -114,12 +152,25 @@ export type WebhookPayload =
114
152
  'activity.call',
115
153
  {
116
154
  activityId: string;
155
+ arguments: JsonValue;
117
156
  calledAt: string;
157
+ clientUid: string | null;
118
158
  connectionUid: string | null;
119
159
  errorCode: string | null;
160
+ payloadCaptured: boolean;
120
161
  policyDecision: ToolDecision | null;
162
+ result: JsonValue;
163
+ sessionUid: string;
121
164
  succeeded: boolean | null;
122
165
  toolName: string;
166
+ userUid: string | null;
167
+ }
168
+ >
169
+ | WebhookEnvelope<
170
+ 'webhook.test',
171
+ {
172
+ endpointUid: string;
173
+ message: string;
123
174
  }
124
175
  >
125
176
  | WebhookEnvelope<
@@ -159,7 +210,10 @@ export type WebhookPayload =
159
210
  {
160
211
  connectionUid: string | null;
161
212
  decision: ToolDecision | null;
162
- eventType: string;
213
+ eventType: ToolPolicyEventType;
214
+ metadata: Record<string, JsonValue>;
215
+ oauthClientConsentUid: string | null;
216
+ oauthClientName: string | null;
163
217
  toolUid: string | null;
164
218
  }
165
219
  >;
@@ -198,7 +252,7 @@ export class Rayrun {
198
252
  | { kind: 'openapi'; specificationUrl: string; baseUrl?: string; name?: string },
199
253
  ): Promise<{ connection: { uid: string } }>;
200
254
  createOAuthLink(uid: string): Promise<{ authorizationUrl: string }>;
201
- delete(uid: string): Promise<void>;
255
+ delete(uid: string, expectedVersion: number): Promise<void>;
202
256
  index(uid: string): Promise<{ enqueued: boolean }>;
203
257
  list(query?: PageQuery): Promise<CursorPage<Connection>>;
204
258
  setCredential(uid: string, body: Credential): Promise<{ enqueued: boolean }>;
@@ -218,6 +272,11 @@ export class Rayrun {
218
272
  };
219
273
  clients: {
220
274
  list(query?: PageQuery): Promise<CursorPage<Client>>;
275
+ setAccessProfile(
276
+ uid: string,
277
+ profileUid: string | null,
278
+ expectedVersion: number,
279
+ ): Promise<void>;
221
280
  setPolicy(uid: string, mode: Exclude<ToolAccessMode, 'custom'>): Promise<void>;
222
281
  setToolPolicy(
223
282
  uid: string,
@@ -225,6 +284,28 @@ export class Rayrun {
225
284
  body: { decision: ToolDecision | null; riskConfirmed?: boolean },
226
285
  ): Promise<void>;
227
286
  };
287
+ accessProfiles: {
288
+ archive(uid: string, expectedVersion: number): Promise<void>;
289
+ create(body: { description: string; name: string }): Promise<{ profile: AccessProfile }>;
290
+ /** @deprecated Use archive. */
291
+ delete(uid: string, expectedVersion: number): Promise<void>;
292
+ list(query?: PageQuery): Promise<CursorPage<AccessProfile>>;
293
+ listTools(uid: string, query?: PageQuery): Promise<CursorPage<AccessProfileTool>>;
294
+ setPolicy(uid: string, mode: AccessProfileMode, expectedVersion: number): Promise<void>;
295
+ setToolPolicy(
296
+ uid: string,
297
+ toolUid: string,
298
+ body: {
299
+ decision: ToolDecision | null;
300
+ expectedVersion: number;
301
+ riskConfirmed?: boolean;
302
+ },
303
+ ): Promise<void>;
304
+ update(
305
+ uid: string,
306
+ body: { description: string; expectedVersion: number; name: string },
307
+ ): Promise<void>;
308
+ };
228
309
  activity: { list(query?: PageQuery): Promise<CursorPage<Activity>> };
229
310
  reviews: { list(query?: PageQuery): Promise<CursorPage<Review>> };
230
311
  webhooks: {
@@ -232,6 +313,7 @@ export class Rayrun {
232
313
  url: string;
233
314
  description?: string | null;
234
315
  eventTypes: WebhookEvent[];
316
+ forwardToolPayloads?: boolean;
235
317
  }): Promise<{ uid: string; secret: string }>;
236
318
  delete(uid: string): Promise<void>;
237
319
  list(): Promise<{ items: Webhook[] }>;
package/index.js CHANGED
@@ -80,6 +80,8 @@ export const verifyWebhookSignature = ({
80
80
  };
81
81
 
82
82
  export class Rayrun {
83
+ #apiKey;
84
+
83
85
  constructor({
84
86
  apiKey,
85
87
  baseUrl = 'https://ray.run',
@@ -88,7 +90,7 @@ export class Rayrun {
88
90
  if (!apiKey) throw new TypeError('Rayrun requires an apiKey.');
89
91
  if (!fetchImplementation) throw new TypeError('Rayrun requires a fetch implementation.');
90
92
 
91
- this.apiKey = apiKey;
93
+ this.#apiKey = apiKey;
92
94
  this.baseUrl = baseUrl.replace(/\/$/u, '');
93
95
  this.fetch = fetchImplementation;
94
96
 
@@ -112,10 +114,30 @@ export class Rayrun {
112
114
  };
113
115
  this.clients = {
114
116
  list: (query) => this.request('GET', '/clients', { query }),
117
+ setAccessProfile: (uid, profileUid, expectedVersion) =>
118
+ this.request('PATCH', `/clients/${uid}/access-profile`, {
119
+ body: { expectedVersion, profileUid },
120
+ }),
115
121
  setPolicy: (uid, mode) => this.request('PATCH', `/clients/${uid}/policy`, { body: { mode } }),
116
122
  setToolPolicy: (uid, toolUid, body) =>
117
123
  this.request('PATCH', `/clients/${uid}/tools/${toolUid}/policy`, { body }),
118
124
  };
125
+ const archiveAccessProfile = (uid, expectedVersion) =>
126
+ this.request('DELETE', `/access-profiles/${uid}`, { query: { expectedVersion } });
127
+ this.accessProfiles = {
128
+ archive: archiveAccessProfile,
129
+ create: (body) => this.request('POST', '/access-profiles', { body }),
130
+ delete: archiveAccessProfile,
131
+ list: (query) => this.request('GET', '/access-profiles', { query }),
132
+ listTools: (uid, query) => this.request('GET', `/access-profiles/${uid}/tools`, { query }),
133
+ setPolicy: (uid, mode, expectedVersion) =>
134
+ this.request('PATCH', `/access-profiles/${uid}/policy`, {
135
+ body: { expectedVersion, mode },
136
+ }),
137
+ setToolPolicy: (uid, toolUid, body) =>
138
+ this.request('PATCH', `/access-profiles/${uid}/tools/${toolUid}/policy`, { body }),
139
+ update: (uid, body) => this.request('PATCH', `/access-profiles/${uid}`, { body }),
140
+ };
119
141
  this.activity = { list: (query) => this.request('GET', '/activity', { query }) };
120
142
  this.reviews = { list: (query) => this.request('GET', '/reviews', { query }) };
121
143
  this.webhooks = {
@@ -140,7 +162,7 @@ export class Rayrun {
140
162
  response = await this.fetch(url, {
141
163
  body: body === undefined ? undefined : JSON.stringify(body),
142
164
  headers: withoutUndefined({
143
- authorization: `Bearer ${this.apiKey}`,
165
+ authorization: `Bearer ${this.#apiKey}`,
144
166
  'content-type': body === undefined ? undefined : 'application/json',
145
167
  'user-agent': '@rayrun/sdk',
146
168
  }),
@@ -266,6 +288,8 @@ export class RayrunCodeRunAheadSession {
266
288
  }
267
289
 
268
290
  export class RayrunGateway {
291
+ #accessToken;
292
+
269
293
  constructor({
270
294
  accessToken,
271
295
  baseUrl = 'https://ray.run',
@@ -274,7 +298,7 @@ export class RayrunGateway {
274
298
  if (!accessToken) throw new TypeError('RayrunGateway requires an accessToken.');
275
299
  if (!fetchImplementation) throw new TypeError('RayrunGateway requires a fetch implementation.');
276
300
 
277
- this.accessToken = accessToken;
301
+ this.#accessToken = accessToken;
278
302
  this.baseUrl = baseUrl.replace(/\/$/u, '');
279
303
  this.fetch = fetchImplementation;
280
304
  this.codeRunAhead = {
@@ -298,7 +322,7 @@ export class RayrunGateway {
298
322
  const response = await this.fetch(`${this.baseUrl}/mcp/run-ahead`, {
299
323
  body: JSON.stringify(body),
300
324
  headers: {
301
- authorization: `Bearer ${this.accessToken}`,
325
+ authorization: `Bearer ${this.#accessToken}`,
302
326
  'content-type': 'application/json',
303
327
  'user-agent': '@rayrun/sdk',
304
328
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayrun/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Type-safe client for the Rayrun public API",
5
5
  "license": "MIT",
6
6
  "repository": {