@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.
Files changed (4) hide show
  1. package/README.md +51 -1
  2. package/index.d.ts +153 -1
  3. package/index.js +19 -1
  4. 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`. Use `id` to deduplicate retries.
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
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayrun/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Type-safe client for the Rayrun public API",
5
5
  "license": "MIT",
6
6
  "repository": {