@rayrun/sdk 0.4.0 → 0.6.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.
Files changed (4) hide show
  1. package/README.md +190 -13
  2. package/index.d.ts +497 -10
  3. package/index.js +88 -8
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -15,8 +15,8 @@ const { items: matchingTools } = await rayrun.tools.list({ query: 'create issue'
15
15
  Create keys in **Dashboard → Settings → API keys**. The plaintext is shown once.
16
16
 
17
17
  The client covers catalog search, connection and credential setup, OAuth links, indexing, tool and
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.
18
+ client policies, access profiles, Workspace Skills, Activity, review queues, and webhooks. Safe
19
+ reads retry transient failures; writes do not retry automatically.
20
20
 
21
21
  Inspect the effective policy applied to one connected client without reproducing policy logic in
22
22
  your application:
@@ -38,17 +38,160 @@ const { profile } = await rayrun.accessProfiles.create({
38
38
  description: 'Read by default; sensitive tools stay blocked.',
39
39
  });
40
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,
41
+ const policy = await rayrun.accessProfiles.setPolicy(
42
+ profile.uid,
43
+ 'read-only',
44
+ profile.toolPolicyVersion,
45
+ );
46
+ const details = await rayrun.accessProfiles.update(profile.uid, {
47
+ description: 'Read-only support access; sensitive tools stay blocked.',
48
+ expectedVersion: policy.toolPolicyVersion,
45
49
  name: 'Support agents',
46
50
  });
47
51
  await rayrun.clients.setAccessProfile(clientUid, profile.uid, clientToolPolicyVersion);
52
+
53
+ const profileHistory = await rayrun.accessProfiles.listConfigurationVersions(profile.uid);
54
+ const original = await rayrun.accessProfiles.getConfigurationVersion(
55
+ profile.uid,
56
+ profileHistory.items.at(-1).uid,
57
+ );
58
+ await rayrun.accessProfiles.restoreConfigurationVersion(profile.uid, original.version.uid, {
59
+ expectedVersion: details.toolPolicyVersion,
60
+ reason: 'Restore the reviewed access ceiling',
61
+ riskConfirmed: true,
62
+ });
48
63
  ```
49
64
 
50
65
  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.
66
+ profile can narrow access but cannot grant something blocked by the workspace or client. History
67
+ versions profile details and rules together; assignments and archival stay outside the snapshot.
68
+ Restore refuses missing tools and requires `riskConfirmed` when it would allow a Destructive or
69
+ Unknown tool. Profile history is limited to 64 MiB per workspace.
70
+
71
+ ## Manage connection configuration history
72
+
73
+ Connection names, slugs, descriptions, timeouts, enablement, and payload-capture preferences are
74
+ saved as one immutable configuration snapshot. Credentials, headers, OAuth state, identity and tool
75
+ policy, health, and index state are deliberately excluded.
76
+
77
+ ```js
78
+ const { items: connections } = await rayrun.connections.list();
79
+ const connection = connections[0];
80
+
81
+ const updated = await rayrun.connections.setEnabled(connection.uid, {
82
+ enabled: false,
83
+ expectedVersion: connection.configurationVersion,
84
+ });
85
+
86
+ const history = await rayrun.connections.listConfigurationVersions(connection.uid, { limit: 25 });
87
+ const selected = await rayrun.connections.getConfigurationVersion(
88
+ connection.uid,
89
+ history.items.at(-1).uid,
90
+ );
91
+
92
+ await rayrun.connections.restoreConfigurationVersion(connection.uid, selected.version.uid, {
93
+ expectedVersion: updated.connection.configurationVersion,
94
+ reason: 'Restore the reviewed service configuration',
95
+ });
96
+ ```
97
+
98
+ Every material update records the user or API key, channel, time, and optional reason. Identical
99
+ saves are no-ops. Restore creates a new attributed version and fails with `version_conflict` if the
100
+ connection changed after it was read.
101
+
102
+ ## Manage hosted tool hooks
103
+
104
+ Hooks are versioned TypeScript adapters hosted and sandboxed by Rayrun. Pull the generated types and
105
+ draft, test it without a live upstream call, then create an immutable shadow or active revision.
106
+
107
+ ```js
108
+ const { hook } = await rayrun.hooks.get(connectionUid, toolUid);
109
+
110
+ const tested = await rayrun.hooks.test(connectionUid, toolUid, {
111
+ source: hook.draftSource,
112
+ config: hook.draftConfig,
113
+ arguments: { query: 'release' },
114
+ mockResult: { items: [] },
115
+ });
116
+
117
+ const saved = await rayrun.hooks.saveDraft(connectionUid, toolUid, {
118
+ source: hook.draftSource,
119
+ config: hook.draftConfig,
120
+ expectedVersion: hook.version,
121
+ reason: 'Normalize upstream release results',
122
+ });
123
+
124
+ const deployed = await rayrun.hooks.deploy(connectionUid, toolUid, {
125
+ expectedVersion: saved.hook.version,
126
+ mode: 'shadow',
127
+ });
128
+
129
+ const runs = await rayrun.hooks.listRuns(connectionUid, toolUid, { limit: 25 });
130
+
131
+ const history = await rayrun.hooks.listDraftVersions(connectionUid, toolUid, { limit: 25 });
132
+ const prior = await rayrun.hooks.getDraftVersion(connectionUid, toolUid, history.items.at(-1).uid);
133
+ await rayrun.hooks.restoreDraft(connectionUid, toolUid, prior.version.uid, {
134
+ expectedVersion: deployed.hook.version,
135
+ reason: 'Restore the previous argument mapping',
136
+ });
137
+ ```
138
+
139
+ `setDeployment` moves an active or shadow pointer to an existing compatible revision, or deactivates
140
+ it with `revisionUid: null`. Every mutation uses `expectedVersion`; fetch and reconcile instead of
141
+ blindly retrying a `version_conflict`. Use a full-control API key for hook writes. Read-only keys can
142
+ inspect source, generated declarations, revision history, and deployed-run metadata; captured logs
143
+ and errors require `hooks:write`. `RayrunApiError.diagnostics` carries compiler line and column
144
+ details for editor and CI output.
145
+
146
+ Draft history is immutable and separate from deployment revisions: every material save records the
147
+ API key or user, optional change note, source, and non-secret config. Re-saving identical content is
148
+ a no-op. Restore always creates a new attributed draft head; it never rewrites the selected version.
149
+ Source plus config history is limited to 64 MiB per workspace. Export required versions, then call
150
+ `hooks.reset` with the current hook UID and version to permanently remove that hook's draft history
151
+ and deployment revisions. Rayrun blocks connection deletion while hook history remains.
152
+
153
+ ## Publish Workspace Skills
154
+
155
+ Encode each file as a package-relative path and base64 content. Validate before creating when CI
156
+ needs the parsed name, frontmatter, digests, and warnings before it mutates workspace state.
157
+
158
+ ```js
159
+ const files = [
160
+ {
161
+ path: 'SKILL.md',
162
+ contentBase64: Buffer.from(
163
+ `---
164
+ name: incident-response
165
+ description: Investigate and communicate production incidents.
166
+ ---
167
+
168
+ # Incident response
169
+ `,
170
+ ).toString('base64'),
171
+ },
172
+ ];
173
+
174
+ const validation = await rayrun.skills.validate(files);
175
+ const created = await rayrun.skills.create({ files, source: 'api' });
176
+
177
+ await rayrun.skills.publish(created.skillUid, {
178
+ deliveryMode: 'both',
179
+ expectedVersion: created.configurationVersion,
180
+ profileUids: [],
181
+ riskConfirmed: validation.riskFlags.length > 0,
182
+ });
183
+ ```
184
+
185
+ `saveDraft` creates an immutable revision without changing the published pointer. `updateAudience`
186
+ changes Code/Direct delivery and access-profile targeting without republishing. `setEnabled`,
187
+ `archive`, `export`, `exportVersion`, and `restoreVersion` cover the reversible lifecycle;
188
+ `delete` permanently removes retained revisions and delivery summaries after name and version
189
+ confirmation. The name remains reserved.
190
+
191
+ Use `skills:read` for inspection and export and add `skills:write` deliberately for publication
192
+ automation. The ordinary workspace-management key preset excludes `skills:write`. Rayrun stores
193
+ Skill bytes encrypted under the workspace key and serves them only after reauthorizing the MCP
194
+ client on each read. `allowed-tools` is guidance, not access policy.
52
195
 
53
196
  ## Stream code run-ahead
54
197
 
@@ -83,13 +226,47 @@ try {
83
226
  `open()` returns `null` when this optional optimization is rate-limited or temporarily unavailable.
84
227
  `feedArguments()` coalesces snapshots while one request is in flight; call `flush()` once before the
85
228
  final MCP request. It throws synchronously when a snapshot is not a string, so TypeScript users get
86
- the mistake at the call site. If an active session becomes unavailable, `meta` becomes an empty object. In
87
- either case, send the final MCP call normally. Rayrun only runs ahead tools that are explicitly
88
- allowed, approved as Read by a workspace owner, and advertised by their server as read-only and
89
- idempotent. The final MCP call rechecks the token, complete program, policy, definition, arguments,
90
- and upstream identity before it can use an early result.
229
+ the mistake at the call site. If a session becomes unavailable, send the final MCP call normally.
230
+ Rayrun runs ahead only allowed tools approved as Read and advertised as read-only and idempotent.
231
+ The final call rechecks identity, policy, definition, arguments, and program before using an early
232
+ result.
233
+
234
+ ## Manage webhook configuration history
235
+
236
+ Webhook destination, description, event selection, delivery mode, batch size, enablement, and
237
+ payload forwarding are saved together as immutable configuration versions. Signing secrets,
238
+ delivery attempts, retry state, and health stay outside the snapshot.
239
+
240
+ ```js
241
+ const { items: webhooks } = await rayrun.webhooks.list();
242
+ const webhook = webhooks[0];
243
+
244
+ const updated = await rayrun.webhooks.update(webhook.uid, {
245
+ enabled: false,
246
+ expectedVersion: webhook.configurationVersion,
247
+ reason: 'Pause deliveries during maintenance',
248
+ });
249
+
250
+ const history = await rayrun.webhooks.listConfigurationVersions(webhook.uid, { limit: 25 });
251
+ const selected = await rayrun.webhooks.getConfigurationVersion(
252
+ webhook.uid,
253
+ history.items.at(-1).uid,
254
+ );
255
+
256
+ await rayrun.webhooks.restoreConfigurationVersion(webhook.uid, selected.version.uid, {
257
+ expectedVersion: updated.configurationVersion,
258
+ reason: 'Restore the reviewed destination',
259
+ });
260
+ ```
261
+
262
+ Every material update records the user or API key, channel, time, and optional reason. Restore
263
+ creates a new version, revalidates the historical destination URL, and preserves the endpoint’s
264
+ current signing secret and delivery state. Identical saves are no-ops, stale expected versions fail,
265
+ and immutable webhook configuration history is limited to 64 MiB per workspace. Destination paths
266
+ and query strings can contain provider tokens, so read-only keys receive `null` URLs; keys with
267
+ `webhooks:write` receive the full current and historical destination.
91
268
 
92
- ## Verify webhooks
269
+ ## Verify webhook signatures
93
270
 
94
271
  Pass the exact request body and the `Rayrun-Signature` header before parsing the event. Verification
95
272
  also rejects timestamps outside a five-minute replay window by default.
package/index.d.ts CHANGED
@@ -12,10 +12,38 @@ export type ToolDecision = 'allow' | 'ask' | 'block';
12
12
  export type ToolAccessMode = 'read-only' | 'ask-before-changes' | 'allow-all' | 'custom';
13
13
  export type AccessProfileMode = 'read-only' | 'ask-before-changes' | 'allow-all' | 'block-all';
14
14
  export type Risk = 'read' | 'change' | 'destructive' | 'unknown';
15
+ export type ConfigurationVersionSummary = {
16
+ actor: { name: string; type: 'api-key' | 'system' | 'user'; uid: string };
17
+ changeReason: string | null;
18
+ changeType: 'restore' | 'save';
19
+ channel: 'api' | 'browser' | 'legacy';
20
+ createdAt: string;
21
+ isCurrent: boolean;
22
+ restoredFromVersionUid: string | null;
23
+ revisionNumber: number;
24
+ uid: string;
25
+ };
26
+ export type ConnectionConfiguration = {
27
+ captureToolPayloads: boolean;
28
+ description: string | null;
29
+ displayName: string;
30
+ enabled: boolean;
31
+ requestTimeoutMs: number;
32
+ slug: string;
33
+ };
34
+ export type ConnectionConfigurationVersion = ConfigurationVersionSummary & {
35
+ previous: {
36
+ revisionNumber: number;
37
+ snapshot: ConnectionConfiguration;
38
+ uid: string;
39
+ } | null;
40
+ snapshot: ConnectionConfiguration;
41
+ };
15
42
  export type Connection = {
16
43
  authorizedAt: string | null;
17
44
  authType: 'api-key' | 'basic' | 'none' | 'oauth2';
18
45
  createdAt: string;
46
+ configurationVersion: number;
19
47
  uid: string;
20
48
  displayName: string;
21
49
  enabled: boolean;
@@ -51,6 +79,116 @@ export type Tool = {
51
79
  inputSchema: Record<string, unknown>;
52
80
  title: string | null;
53
81
  };
82
+ export type ToolHookPublicContract = {
83
+ description?: string;
84
+ inputSchema?: Record<string, unknown>;
85
+ outputSchema?: Record<string, unknown>;
86
+ };
87
+ export type ToolHookRevision = {
88
+ config: Record<string, JsonValue>;
89
+ createdAt: string;
90
+ publicContract: ToolHookPublicContract | null;
91
+ revisionNumber: number;
92
+ source: string;
93
+ sourceHash: string;
94
+ uid: string;
95
+ upstreamDefinitionHash: string;
96
+ };
97
+ export type ToolHook = {
98
+ activeRevisionStale: boolean;
99
+ activeRevisionUid: string | null;
100
+ currentDraftRevisionNumber: number;
101
+ draftConfig: Record<string, JsonValue>;
102
+ draftSource: string;
103
+ hookUid: string | null;
104
+ revisions: ToolHookRevision[];
105
+ shadowRevisionStale: boolean;
106
+ shadowRevisionUid: string | null;
107
+ target: {
108
+ available: boolean;
109
+ definitionHash: string | null;
110
+ inputSchema: Record<string, unknown>;
111
+ name: string;
112
+ outputSchema: Record<string, unknown> | null;
113
+ toolUid: string;
114
+ };
115
+ types: string;
116
+ version: number;
117
+ };
118
+ export type ToolHookDraftVersionSummary = {
119
+ actor: {
120
+ name: string;
121
+ type: 'api-key' | 'user';
122
+ uid: string;
123
+ };
124
+ changeReason: string | null;
125
+ changeType: 'restore' | 'save';
126
+ channel: 'api' | 'browser' | 'legacy';
127
+ createdAt: string;
128
+ isCurrent: boolean;
129
+ restoredFromVersionUid: string | null;
130
+ revisionNumber: number;
131
+ uid: string;
132
+ };
133
+ export type ToolHookDraftVersion = ToolHookDraftVersionSummary & {
134
+ config: Record<string, JsonValue>;
135
+ previous: {
136
+ config: Record<string, JsonValue>;
137
+ revisionNumber: number;
138
+ source: string;
139
+ uid: string;
140
+ } | null;
141
+ source: string;
142
+ };
143
+ export type ToolHookLogEntry = {
144
+ data?: JsonValue;
145
+ level: 'debug' | 'error' | 'info' | 'warn';
146
+ message: string;
147
+ };
148
+ export type ToolHookTestFailure = {
149
+ durationMs: number;
150
+ error: {
151
+ code: 'invalid_arguments' | 'invalid_outcome' | 'invalid_output' | 'runtime_error' | 'timeout';
152
+ message: string;
153
+ };
154
+ logs: ToolHookLogEntry[];
155
+ status: 'failed';
156
+ };
157
+ export type ToolHookBeforeTestResult =
158
+ | {
159
+ durationMs: number;
160
+ logs: ToolHookLogEntry[];
161
+ outcome:
162
+ | { action: 'continue'; arguments: JsonValue }
163
+ | { action: 'reject'; message: string; reason: string }
164
+ | { action: 'require_approval'; arguments: JsonValue; reason: string };
165
+ status: 'completed';
166
+ }
167
+ | ToolHookTestFailure;
168
+ export type ToolHookAfterTestResult =
169
+ | {
170
+ durationMs: number;
171
+ logs: ToolHookLogEntry[];
172
+ outcome:
173
+ | { action: 'return'; result: JsonValue }
174
+ | { action: 'fail'; message: string; reason: string };
175
+ status: 'completed';
176
+ }
177
+ | ToolHookTestFailure;
178
+ export type ToolHookRun = {
179
+ createdAt: string;
180
+ differsFromActive: boolean | null;
181
+ durationMs: number;
182
+ errorMessage: string | null;
183
+ id: string;
184
+ logs: ToolHookLogEntry[] | null;
185
+ outcome: 'approval-required' | 'continued' | 'failed' | 'rejected' | 'returned';
186
+ requestId: string;
187
+ revisionUid: string;
188
+ shadow: boolean;
189
+ stage: 'after' | 'before';
190
+ uid: string;
191
+ };
54
192
  export type Client = {
55
193
  accessProfile: Pick<AccessProfile, 'name' | 'uid'> | null;
56
194
  clientName: string;
@@ -86,6 +224,7 @@ export type ClientTool = {
86
224
  };
87
225
  export type AccessProfile = {
88
226
  assignedClientCount: number;
227
+ assignedSkillCount: number;
89
228
  description: string;
90
229
  name: string;
91
230
  toolAccessMode: AccessProfileMode;
@@ -100,6 +239,24 @@ export type AccessProfileTool = {
100
239
  serviceName: string;
101
240
  uid: string;
102
241
  };
242
+ export type AccessProfileConfiguration = {
243
+ description: string;
244
+ name: string;
245
+ toolAccessMode: AccessProfileMode;
246
+ toolRules: Array<{
247
+ connectionUid: string;
248
+ decision: ToolDecision;
249
+ toolUid: string;
250
+ }>;
251
+ };
252
+ export type AccessProfileConfigurationVersion = ConfigurationVersionSummary & {
253
+ previous: {
254
+ revisionNumber: number;
255
+ snapshot: AccessProfileConfiguration;
256
+ uid: string;
257
+ } | null;
258
+ snapshot: AccessProfileConfiguration;
259
+ };
103
260
  export type Activity = {
104
261
  calledAt: string;
105
262
  clientName: string | null;
@@ -120,15 +277,109 @@ export type Review = {
120
277
  toolName: string;
121
278
  uid: string;
122
279
  };
123
- export type Webhook = {
280
+ export type SkillFileInput = { contentBase64: string; path: string };
281
+ export type SkillRiskFlag = 'binary-files' | 'possible-secrets' | 'scripts';
282
+ export type SkillDeliveryMode = 'both' | 'code' | 'direct';
283
+ export type Skill = {
284
+ archivedAt: string | null;
285
+ byteCount: number;
286
+ configurationVersion: number;
287
+ createdAt: string;
288
+ currentDraftRevisionNumber: number;
289
+ currentDraftVersionUid: string;
290
+ deliveryMode: SkillDeliveryMode;
291
+ description: string;
292
+ enabled: boolean;
293
+ fetchCount: number;
294
+ fileCount: number;
295
+ frontmatter: Record<string, JsonValue>;
296
+ lastFetchedAt: string | null;
297
+ name: string;
298
+ profiles: Array<{ name: string; uid: string }>;
299
+ publishedRevisionNumber: number | null;
300
+ publishedVersionUid: string | null;
301
+ riskFlags: SkillRiskFlag[];
302
+ state: 'archived' | 'disabled' | 'draft' | 'published';
303
+ uid: string;
304
+ updatedAt: string;
305
+ uri: string;
306
+ };
307
+ export type SkillVersion = {
308
+ actorName: string;
309
+ actorType: 'api-key' | 'user';
310
+ actorUid: string;
311
+ byteCount: number;
312
+ changeReason: string | null;
124
313
  createdAt: string;
314
+ fileCount: number;
315
+ isCurrentDraft: boolean;
316
+ isPublished: boolean;
317
+ restoredFromVersionUid: string | null;
318
+ revisionNumber: number;
319
+ riskFlags: SkillRiskFlag[];
320
+ source: 'api' | 'cli' | 'dashboard-folder' | 'dashboard-zip' | 'restore';
321
+ uid: string;
322
+ };
323
+ export type SkillDetail = {
324
+ deliveries: Array<{
325
+ clientName: string;
326
+ fetchCount: number;
327
+ firstFetchedAt: string;
328
+ lastFetchedAt: string;
329
+ method: 'resources/directory/read' | 'resources/read' | 'skills/get';
330
+ resourceUri: string;
331
+ userName: string | null;
332
+ versionUid: string;
333
+ }>;
334
+ files: Array<{ digest: string; mimeType: string; path: string; size: number; uri: string }>;
335
+ skill: Skill;
336
+ versions: SkillVersion[];
337
+ };
338
+ export type SkillMutationResult = {
339
+ configurationVersion: number;
340
+ skillUid: string;
341
+ unchanged: boolean;
342
+ versionUid: string | null;
343
+ };
344
+ export type SkillValidationResult = {
345
+ byteCount: number;
346
+ fileCount: number;
347
+ frontmatter: Record<string, JsonValue>;
348
+ manifestDigest: string;
349
+ name: string;
350
+ riskFlags: SkillRiskFlag[];
351
+ };
352
+ export type WebhookConfiguration = {
353
+ batchMaxEvents: number;
354
+ deliveryMode: 'batch' | 'single';
125
355
  description: string | null;
126
356
  enabled: boolean;
127
357
  eventTypes: WebhookEvent[];
128
358
  forwardToolPayloads: boolean;
129
- uid: string;
130
359
  url: string;
131
360
  };
361
+ export type WebhookConfigurationVersion = ConfigurationVersionSummary & {
362
+ previous: {
363
+ revisionNumber: number;
364
+ snapshot: WebhookConfigurationHistorySnapshot;
365
+ uid: string;
366
+ } | null;
367
+ snapshot: WebhookConfigurationHistorySnapshot;
368
+ };
369
+ export type WebhookConfigurationHistorySnapshot = Omit<WebhookConfiguration, 'url'> & {
370
+ url: string | null;
371
+ };
372
+ export type Webhook = Omit<WebhookConfiguration, 'url'> & {
373
+ configurationVersion: number;
374
+ createdAt: string;
375
+ health: 'delayed' | 'failing' | 'healthy' | 'idle' | 'pending';
376
+ lastDeliveredAt: string | null;
377
+ lastFailedAt: string | null;
378
+ oldestPendingAt: string | null;
379
+ pendingDeliveryCount: number;
380
+ uid: string;
381
+ url: string | null;
382
+ };
132
383
  export type Credential =
133
384
  | {
134
385
  authType: 'api-key';
@@ -150,6 +401,7 @@ export type WebhookEvent =
150
401
  | 'connection.changed'
151
402
  | 'connection.indexed'
152
403
  | 'review.changed'
404
+ | 'skill.changed'
153
405
  | 'tool.definition-changed'
154
406
  | 'tool.policy-changed';
155
407
  export type WebhookDeliveryEvent = Exclude<WebhookEvent, '*'> | 'webhook.test';
@@ -222,6 +474,28 @@ export type WebhookPayload =
222
474
  toolUid: string;
223
475
  }
224
476
  >
477
+ | WebhookEnvelope<
478
+ 'skill.changed',
479
+ {
480
+ deliveryMode?: SkillDeliveryMode;
481
+ eventType:
482
+ | 'skill-archived'
483
+ | 'skill-audience-changed'
484
+ | 'skill-deleted'
485
+ | 'skill-disabled'
486
+ | 'skill-draft-created'
487
+ | 'skill-enabled'
488
+ | 'skill-published'
489
+ | 'skill-restored';
490
+ eventUid: string;
491
+ name?: string;
492
+ profileUids?: string[];
493
+ restoredFromVersionUid?: string;
494
+ revisionNumber?: number;
495
+ skillUid: string;
496
+ versionUid?: string;
497
+ }
498
+ >
225
499
  | WebhookEnvelope<
226
500
  'tool.definition-changed',
227
501
  { approved: boolean; connectionUid: string; kind: 'added' | 'changed'; toolUid: string }
@@ -243,6 +517,7 @@ export const CODE_RUN_AHEAD_META_KEY: 'io.rayrun/run-ahead-session';
243
517
 
244
518
  export class RayrunApiError extends Error {
245
519
  code: string;
520
+ diagnostics: Array<{ column?: number; line?: number; message: string }>;
246
521
  requestId: string | null;
247
522
  status: number;
248
523
  }
@@ -273,14 +548,39 @@ export class Rayrun {
273
548
  | { kind: 'openapi'; specificationUrl: string; baseUrl?: string; name?: string },
274
549
  ): Promise<{ connection: { uid: string } }>;
275
550
  createOAuthLink(uid: string): Promise<{ authorizationUrl: string }>;
276
- delete(uid: string, expectedVersion: number): Promise<void>;
551
+ delete(uid: string): Promise<void>;
277
552
  index(uid: string): Promise<{ enqueued: boolean }>;
278
553
  list(query?: PageQuery): Promise<CursorPage<Connection>>;
554
+ getConfigurationVersion(
555
+ uid: string,
556
+ versionUid: string,
557
+ ): Promise<{ version: ConnectionConfigurationVersion }>;
558
+ listConfigurationVersions(
559
+ uid: string,
560
+ query?: PageQuery,
561
+ ): Promise<CursorPage<ConfigurationVersionSummary>>;
562
+ restoreConfigurationVersion(
563
+ uid: string,
564
+ versionUid: string,
565
+ body: { expectedVersion: number; reason?: string },
566
+ ): Promise<{
567
+ configuration: ConnectionConfiguration;
568
+ configurationVersion: number;
569
+ versionUid: string;
570
+ }>;
279
571
  setCredential(uid: string, body: Credential): Promise<{ enqueued: boolean }>;
280
572
  setEnabled(
281
573
  uid: string,
282
574
  enabled: boolean,
283
- ): Promise<{ connection: Pick<Connection, 'displayName' | 'enabled' | 'uid'> }>;
575
+ ): Promise<{
576
+ connection: Pick<Connection, 'configurationVersion' | 'displayName' | 'enabled' | 'uid'>;
577
+ }>;
578
+ setEnabled(
579
+ uid: string,
580
+ body: { enabled: boolean; expectedVersion?: number },
581
+ ): Promise<{
582
+ connection: Pick<Connection, 'configurationVersion' | 'displayName' | 'enabled' | 'uid'>;
583
+ }>;
284
584
  setPolicy(uid: string, mode: ToolAccessMode): Promise<void>;
285
585
  };
286
586
  tools: {
@@ -291,6 +591,81 @@ export class Rayrun {
291
591
  body: { decision: ToolDecision | null; riskConfirmed?: boolean; riskOverride?: Risk | null },
292
592
  ): Promise<void>;
293
593
  };
594
+ hooks: {
595
+ deploy(
596
+ connectionUid: string,
597
+ toolUid: string,
598
+ body: { expectedVersion: number; mode: 'active' | 'shadow' },
599
+ ): Promise<{ hook: ToolHook }>;
600
+ get(connectionUid: string, toolUid: string): Promise<{ hook: ToolHook }>;
601
+ getDraftVersion(
602
+ connectionUid: string,
603
+ toolUid: string,
604
+ versionUid: string,
605
+ ): Promise<{ version: ToolHookDraftVersion }>;
606
+ listDraftVersions(
607
+ connectionUid: string,
608
+ toolUid: string,
609
+ query?: PageQuery,
610
+ ): Promise<CursorPage<ToolHookDraftVersionSummary>>;
611
+ listRuns(
612
+ connectionUid: string,
613
+ toolUid: string,
614
+ query?: PageQuery,
615
+ ): Promise<CursorPage<ToolHookRun>>;
616
+ reset(
617
+ connectionUid: string,
618
+ toolUid: string,
619
+ body: { confirmationHookUid: string; expectedVersion: number; reason: string },
620
+ ): Promise<void>;
621
+ saveDraft(
622
+ connectionUid: string,
623
+ toolUid: string,
624
+ body: {
625
+ config?: Record<string, JsonValue>;
626
+ expectedVersion: number;
627
+ reason?: string;
628
+ source: string;
629
+ },
630
+ ): Promise<{
631
+ draftVersion: { revisionNumber: number; uid: string; unchanged: boolean };
632
+ hook: ToolHook;
633
+ }>;
634
+ restoreDraft(
635
+ connectionUid: string,
636
+ toolUid: string,
637
+ versionUid: string,
638
+ body: { expectedVersion: number; reason?: string },
639
+ ): Promise<{
640
+ draftVersion: { revisionNumber: number; uid: string };
641
+ hook: ToolHook;
642
+ restoredVersionUid: string;
643
+ }>;
644
+ setDeployment(
645
+ connectionUid: string,
646
+ toolUid: string,
647
+ body: {
648
+ expectedVersion: number;
649
+ mode: 'active' | 'shadow';
650
+ revisionUid: string | null;
651
+ },
652
+ ): Promise<{ hook: ToolHook }>;
653
+ test(
654
+ connectionUid: string,
655
+ toolUid: string,
656
+ body: {
657
+ arguments: JsonValue;
658
+ config?: Record<string, JsonValue>;
659
+ mockResult?: JsonValue;
660
+ source: string;
661
+ },
662
+ ): Promise<{
663
+ after?: ToolHookAfterTestResult;
664
+ before: ToolHookBeforeTestResult;
665
+ publicContract: ToolHookPublicContract | null;
666
+ sourceHash: string;
667
+ }>;
668
+ };
294
669
  clients: {
295
670
  list(query?: PageQuery): Promise<CursorPage<Client>>;
296
671
  listTools(uid: string, query?: PageQuery & { query?: string }): Promise<CursorPage<ClientTool>>;
@@ -308,37 +683,149 @@ export class Rayrun {
308
683
  };
309
684
  accessProfiles: {
310
685
  archive(uid: string, expectedVersion: number): Promise<void>;
311
- create(body: { description: string; name: string }): Promise<{ profile: AccessProfile }>;
686
+ create(body: {
687
+ description: string;
688
+ name: string;
689
+ reason?: string;
690
+ }): Promise<{ profile: AccessProfile }>;
312
691
  /** @deprecated Use archive. */
313
692
  delete(uid: string, expectedVersion: number): Promise<void>;
693
+ getConfigurationVersion(
694
+ uid: string,
695
+ versionUid: string,
696
+ ): Promise<{ version: AccessProfileConfigurationVersion }>;
314
697
  list(query?: PageQuery): Promise<CursorPage<AccessProfile>>;
698
+ listConfigurationVersions(
699
+ uid: string,
700
+ query?: PageQuery,
701
+ ): Promise<CursorPage<ConfigurationVersionSummary>>;
315
702
  listTools(uid: string, query?: PageQuery): Promise<CursorPage<AccessProfileTool>>;
316
- setPolicy(uid: string, mode: AccessProfileMode, expectedVersion: number): Promise<void>;
703
+ restoreConfigurationVersion(
704
+ uid: string,
705
+ versionUid: string,
706
+ body: { expectedVersion: number; reason?: string; riskConfirmed?: boolean },
707
+ ): Promise<{
708
+ configuration: AccessProfileConfiguration;
709
+ toolPolicyVersion: number;
710
+ versionUid: string;
711
+ }>;
712
+ setPolicy(
713
+ uid: string,
714
+ mode: AccessProfileMode,
715
+ expectedVersion: number,
716
+ reason?: string,
717
+ ): Promise<{ toolPolicyVersion: number; unchanged: boolean }>;
317
718
  setToolPolicy(
318
719
  uid: string,
319
720
  toolUid: string,
320
721
  body: {
321
722
  decision: ToolDecision | null;
322
723
  expectedVersion: number;
724
+ reason?: string;
323
725
  riskConfirmed?: boolean;
324
726
  },
325
- ): Promise<void>;
727
+ ): Promise<{ toolPolicyVersion: number; unchanged: boolean }>;
326
728
  update(
327
729
  uid: string,
328
- body: { description: string; expectedVersion: number; name: string },
329
- ): Promise<void>;
730
+ body: { description: string; expectedVersion: number; name: string; reason?: string },
731
+ ): Promise<{ toolPolicyVersion: number; unchanged: boolean }>;
330
732
  };
331
733
  activity: { list(query?: PageQuery): Promise<CursorPage<Activity>> };
332
734
  reviews: { list(query?: PageQuery): Promise<CursorPage<Review>> };
735
+ skills: {
736
+ archive(uid: string, expectedVersion: number): Promise<SkillMutationResult>;
737
+ create(body: {
738
+ changeReason?: string;
739
+ files: SkillFileInput[];
740
+ source?: 'api' | 'cli';
741
+ }): Promise<SkillMutationResult>;
742
+ delete(
743
+ uid: string,
744
+ body: { confirmationName: string; expectedVersion: number },
745
+ ): Promise<SkillMutationResult>;
746
+ export(uid: string): Promise<{ files: SkillFileInput[]; name: string }>;
747
+ exportVersion(uid: string, versionUid: string): Promise<{ files: SkillFileInput[] }>;
748
+ get(uid: string): Promise<SkillDetail>;
749
+ list(): Promise<{
750
+ items: Skill[];
751
+ usage: { archivedSkillCount: number; liveSkillCount: number; storedByteCount: number };
752
+ }>;
753
+ publish(
754
+ uid: string,
755
+ body: {
756
+ changeReason?: string;
757
+ deliveryMode: SkillDeliveryMode;
758
+ expectedVersion: number;
759
+ profileUids: string[];
760
+ riskConfirmed?: boolean;
761
+ },
762
+ ): Promise<SkillMutationResult>;
763
+ restoreVersion(
764
+ uid: string,
765
+ versionUid: string,
766
+ body: { changeReason?: string; expectedVersion: number },
767
+ ): Promise<SkillMutationResult>;
768
+ saveDraft(
769
+ uid: string,
770
+ body: {
771
+ changeReason?: string;
772
+ expectedVersion: number;
773
+ files: SkillFileInput[];
774
+ source?: 'api' | 'cli';
775
+ },
776
+ ): Promise<SkillMutationResult>;
777
+ setEnabled(
778
+ uid: string,
779
+ enabled: boolean,
780
+ expectedVersion: number,
781
+ ): Promise<SkillMutationResult>;
782
+ updateAudience(
783
+ uid: string,
784
+ body: {
785
+ deliveryMode: SkillDeliveryMode;
786
+ expectedVersion: number;
787
+ profileUids: string[];
788
+ },
789
+ ): Promise<SkillMutationResult>;
790
+ validate(files: SkillFileInput[]): Promise<SkillValidationResult>;
791
+ };
333
792
  webhooks: {
334
793
  create(body: {
335
- url: string;
794
+ batchMaxEvents?: number;
795
+ deliveryMode?: 'batch' | 'single';
336
796
  description?: string | null;
337
797
  eventTypes: WebhookEvent[];
338
798
  forwardToolPayloads?: boolean;
799
+ url: string;
339
800
  }): Promise<{ uid: string; secret: string }>;
340
801
  delete(uid: string): Promise<void>;
802
+ getConfigurationVersion(
803
+ uid: string,
804
+ versionUid: string,
805
+ ): Promise<{ version: WebhookConfigurationVersion }>;
341
806
  list(): Promise<{ items: Webhook[] }>;
807
+ listConfigurationVersions(
808
+ uid: string,
809
+ query?: PageQuery,
810
+ ): Promise<CursorPage<ConfigurationVersionSummary>>;
811
+ restoreConfigurationVersion(
812
+ uid: string,
813
+ versionUid: string,
814
+ body: { expectedVersion: number; reason?: string },
815
+ ): Promise<{
816
+ configuration: WebhookConfiguration;
817
+ configurationVersion: number;
818
+ versionUid: string;
819
+ }>;
820
+ update(
821
+ uid: string,
822
+ body: Partial<WebhookConfiguration> & { expectedVersion: number; reason?: string },
823
+ ): Promise<{
824
+ configuration: WebhookConfiguration;
825
+ configurationVersion: number;
826
+ unchanged: boolean;
827
+ versionUid: string;
828
+ }>;
342
829
  verify: typeof verifyWebhookSignature;
343
830
  };
344
831
  request(
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
  }
@@ -99,11 +100,19 @@ export class Rayrun {
99
100
  create: (body) => this.request('POST', '/connections', { body }),
100
101
  createOAuthLink: (uid) => this.request('POST', `/connections/${uid}/oauth-link`),
101
102
  delete: (uid) => this.request('DELETE', `/connections/${uid}`),
103
+ getConfigurationVersion: (uid, versionUid) =>
104
+ this.request('GET', `/connections/${uid}/history/${versionUid}`),
102
105
  index: (uid) => this.request('POST', `/connections/${uid}/index`),
103
106
  list: (query) => this.request('GET', '/connections', { query }),
107
+ listConfigurationVersions: (uid, query) =>
108
+ this.request('GET', `/connections/${uid}/history`, { query }),
109
+ restoreConfigurationVersion: (uid, versionUid, body) =>
110
+ this.request('POST', `/connections/${uid}/history/${versionUid}/restore`, { body }),
104
111
  setCredential: (uid, body) => this.request('PUT', `/connections/${uid}/credential`, { body }),
105
- setEnabled: (uid, enabled) =>
106
- this.request('PATCH', `/connections/${uid}`, { body: { enabled } }),
112
+ setEnabled: (uid, input) =>
113
+ this.request('PATCH', `/connections/${uid}`, {
114
+ body: typeof input === 'boolean' ? { enabled: input } : input,
115
+ }),
107
116
  setPolicy: (uid, mode) =>
108
117
  this.request('PATCH', `/connections/${uid}/policy`, { body: { mode } }),
109
118
  };
@@ -112,6 +121,33 @@ export class Rayrun {
112
121
  setPolicy: (connectionUid, toolUid, body) =>
113
122
  this.request('PATCH', `/connections/${connectionUid}/tools/${toolUid}/policy`, { body }),
114
123
  };
124
+ const toolHookPath = (connectionUid, toolUid) =>
125
+ `/connections/${connectionUid}/tools/${toolUid}/hook`;
126
+ this.hooks = {
127
+ deploy: (connectionUid, toolUid, body) =>
128
+ this.request('POST', `${toolHookPath(connectionUid, toolUid)}/deploy`, { body }),
129
+ get: (connectionUid, toolUid) => this.request('GET', toolHookPath(connectionUid, toolUid)),
130
+ getDraftVersion: (connectionUid, toolUid, versionUid) =>
131
+ this.request('GET', `${toolHookPath(connectionUid, toolUid)}/history/${versionUid}`),
132
+ listDraftVersions: (connectionUid, toolUid, query) =>
133
+ this.request('GET', `${toolHookPath(connectionUid, toolUid)}/history`, { query }),
134
+ listRuns: (connectionUid, toolUid, query) =>
135
+ this.request('GET', `${toolHookPath(connectionUid, toolUid)}/runs`, { query }),
136
+ reset: (connectionUid, toolUid, body) =>
137
+ this.request('DELETE', toolHookPath(connectionUid, toolUid), { body }),
138
+ restoreDraft: (connectionUid, toolUid, versionUid, body) =>
139
+ this.request(
140
+ 'POST',
141
+ `${toolHookPath(connectionUid, toolUid)}/history/${versionUid}/restore`,
142
+ { body },
143
+ ),
144
+ saveDraft: (connectionUid, toolUid, body) =>
145
+ this.request('PUT', toolHookPath(connectionUid, toolUid), { body }),
146
+ setDeployment: (connectionUid, toolUid, body) =>
147
+ this.request('POST', `${toolHookPath(connectionUid, toolUid)}/deployment`, { body }),
148
+ test: (connectionUid, toolUid, body) =>
149
+ this.request('POST', `${toolHookPath(connectionUid, toolUid)}/test`, { body }),
150
+ };
115
151
  this.clients = {
116
152
  list: (query) => this.request('GET', '/clients', { query }),
117
153
  listTools: (uid, query) => this.request('GET', `/clients/${uid}/tools`, { query }),
@@ -129,27 +165,69 @@ export class Rayrun {
129
165
  archive: archiveAccessProfile,
130
166
  create: (body) => this.request('POST', '/access-profiles', { body }),
131
167
  delete: archiveAccessProfile,
168
+ getConfigurationVersion: (uid, versionUid) =>
169
+ this.request('GET', `/access-profiles/${uid}/history/${versionUid}`),
132
170
  list: (query) => this.request('GET', '/access-profiles', { query }),
171
+ listConfigurationVersions: (uid, query) =>
172
+ this.request('GET', `/access-profiles/${uid}/history`, { query }),
133
173
  listTools: (uid, query) => this.request('GET', `/access-profiles/${uid}/tools`, { query }),
134
- setPolicy: (uid, mode, expectedVersion) =>
174
+ restoreConfigurationVersion: (uid, versionUid, body) =>
175
+ this.request('POST', `/access-profiles/${uid}/history/${versionUid}/restore`, { body }),
176
+ setPolicy: (uid, mode, expectedVersion, reason) =>
135
177
  this.request('PATCH', `/access-profiles/${uid}/policy`, {
136
- body: { expectedVersion, mode },
178
+ body: withoutUndefined({ expectedVersion, mode, reason }),
179
+ headers: { prefer: 'return=representation' },
137
180
  }),
138
181
  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 }),
182
+ this.request('PATCH', `/access-profiles/${uid}/tools/${toolUid}/policy`, {
183
+ body,
184
+ headers: { prefer: 'return=representation' },
185
+ }),
186
+ update: (uid, body) =>
187
+ this.request('PATCH', `/access-profiles/${uid}`, {
188
+ body,
189
+ headers: { prefer: 'return=representation' },
190
+ }),
141
191
  };
142
192
  this.activity = { list: (query) => this.request('GET', '/activity', { query }) };
143
193
  this.reviews = { list: (query) => this.request('GET', '/reviews', { query }) };
194
+ this.skills = {
195
+ archive: (uid, expectedVersion) =>
196
+ this.request('POST', `/skills/${uid}/archive`, { body: { expectedVersion } }),
197
+ create: (body) => this.request('POST', '/skills', { body }),
198
+ delete: (uid, body) => this.request('DELETE', `/skills/${uid}`, { body }),
199
+ export: (uid) => this.request('GET', `/skills/${uid}/export`),
200
+ exportVersion: (uid, versionUid) =>
201
+ this.request('GET', `/skills/${uid}/versions/${versionUid}/export`),
202
+ get: (uid) => this.request('GET', `/skills/${uid}`),
203
+ list: () => this.request('GET', '/skills'),
204
+ publish: (uid, body) => this.request('POST', `/skills/${uid}/publish`, { body }),
205
+ restoreVersion: (uid, versionUid, body) =>
206
+ this.request('POST', `/skills/${uid}/versions/${versionUid}/restore`, { body }),
207
+ saveDraft: (uid, body) => this.request('PUT', `/skills/${uid}/draft`, { body }),
208
+ setEnabled: (uid, enabled, expectedVersion) =>
209
+ this.request('PATCH', `/skills/${uid}/enabled`, {
210
+ body: { enabled, expectedVersion },
211
+ }),
212
+ updateAudience: (uid, body) => this.request('PATCH', `/skills/${uid}/audience`, { body }),
213
+ validate: (files) => this.request('POST', '/skills/validate', { body: { files } }),
214
+ };
144
215
  this.webhooks = {
145
216
  create: (body) => this.request('POST', '/webhooks', { body }),
146
217
  delete: (uid) => this.request('DELETE', `/webhooks/${uid}`),
218
+ getConfigurationVersion: (uid, versionUid) =>
219
+ this.request('GET', `/webhooks/${uid}/history/${versionUid}`),
147
220
  list: () => this.request('GET', '/webhooks'),
221
+ listConfigurationVersions: (uid, query) =>
222
+ this.request('GET', `/webhooks/${uid}/history`, { query }),
223
+ restoreConfigurationVersion: (uid, versionUid, body) =>
224
+ this.request('POST', `/webhooks/${uid}/history/${versionUid}/restore`, { body }),
225
+ update: (uid, body) => this.request('PATCH', `/webhooks/${uid}`, { body }),
148
226
  verify: verifyWebhookSignature,
149
227
  };
150
228
  }
151
229
 
152
- async request(method, path, { body, query, signal } = {}) {
230
+ async request(method, path, { body, headers, query, signal } = {}) {
153
231
  const url = new URL(`${this.baseUrl}/v1${path}`);
154
232
  for (const [name, value] of Object.entries(withoutUndefined(query ?? {}))) {
155
233
  url.searchParams.set(name, String(value));
@@ -163,6 +241,7 @@ export class Rayrun {
163
241
  response = await this.fetch(url, {
164
242
  body: body === undefined ? undefined : JSON.stringify(body),
165
243
  headers: withoutUndefined({
244
+ ...headers,
166
245
  authorization: `Bearer ${this.#apiKey}`,
167
246
  'content-type': body === undefined ? undefined : 'application/json',
168
247
  'user-agent': '@rayrun/sdk',
@@ -189,6 +268,7 @@ export class Rayrun {
189
268
  problem?.error?.message ?? `Rayrun API request failed with HTTP ${response.status}.`,
190
269
  {
191
270
  code: problem?.error?.code ?? 'request_failed',
271
+ diagnostics: Array.isArray(problem?.diagnostics) ? problem.diagnostics : [],
192
272
  requestId: problem?.error?.requestId ?? response.headers.get('rayrun-request-id'),
193
273
  status: response.status,
194
274
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayrun/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Type-safe client for the Rayrun public API",
5
5
  "license": "MIT",
6
6
  "repository": {