@cadraos/sdk 0.3.14-p46.4 → 0.3.15
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 +104 -53
- package/dist/ai-functions/react/index.cjs +176 -0
- package/dist/ai-functions/react/index.cjs.map +1 -0
- package/dist/ai-functions/react/index.d.cts +129 -0
- package/dist/ai-functions/react/index.d.ts +129 -0
- package/dist/ai-functions/react/index.js +153 -0
- package/dist/ai-functions/react/index.js.map +1 -0
- package/dist/ai-functions/server/index.cjs +296 -0
- package/dist/ai-functions/server/index.cjs.map +1 -0
- package/dist/ai-functions/server/index.d.cts +334 -0
- package/dist/ai-functions/server/index.d.ts +334 -0
- package/dist/ai-functions/server/index.js +284 -0
- package/dist/ai-functions/server/index.js.map +1 -0
- package/dist/chat/index.cjs +303 -17
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +211 -0
- package/dist/chat/index.d.ts +211 -0
- package/dist/chat/index.js +303 -17
- package/dist/chat/index.js.map +1 -1
- package/dist/index.cjs +225 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +279 -1
- package/dist/index.d.ts +279 -1
- package/dist/index.js +219 -4
- package/dist/index.js.map +1 -1
- package/dist/kanban/index.cjs +218 -3
- package/dist/kanban/index.cjs.map +1 -1
- package/dist/kanban/index.d.cts +211 -0
- package/dist/kanban/index.d.ts +211 -0
- package/dist/kanban/index.js +218 -3
- package/dist/kanban/index.js.map +1 -1
- package/dist/teams/index.cjs +218 -3
- package/dist/teams/index.cjs.map +1 -1
- package/dist/teams/index.d.cts +211 -0
- package/dist/teams/index.d.ts +211 -0
- package/dist/teams/index.js +218 -3
- package/dist/teams/index.js.map +1 -1
- package/package.json +21 -1
package/README.md
CHANGED
|
@@ -2,36 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Official SDK for the CadraOS platform - programmatic access to agents, skills, tools, and artifacts.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// After (recommended)
|
|
20
|
-
import '@cadraos/react/styles.css';
|
|
21
|
-
import { CadraCopilot } from '@cadraos/react';
|
|
22
|
-
|
|
23
|
-
function App() {
|
|
24
|
-
return (
|
|
25
|
-
<CadraCopilot
|
|
26
|
-
apiKey="cdr_live_xxx"
|
|
27
|
-
orgId={123}
|
|
28
|
-
agentId="agent-uuid"
|
|
29
|
-
/>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
For full migration instructions, see the [migration guide](https://github.com/cadraos/cadra-sdk/blob/main/packages/react/MIGRATION.md).
|
|
5
|
+
## Entry Points
|
|
6
|
+
|
|
7
|
+
| Import path | Contents |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `@cadraos/sdk` | `CadraClient`, resource APIs, types, error classes, image-stream helpers |
|
|
10
|
+
| `@cadraos/sdk/chat` | React chat components and hooks (`AgentChat`, `ChatConfigProvider`, adapters) — the canonical chat surface |
|
|
11
|
+
| `@cadraos/sdk/teams` | React components for visualizing agent team structures |
|
|
12
|
+
| `@cadraos/sdk/kanban` | Agent-first kanban: React board + data adapter |
|
|
13
|
+
| `@cadraos/sdk/kanban/engine` | Server-side board engine (host backend only, never the browser) |
|
|
14
|
+
| `@cadraos/sdk/board-host` | Board-host mapping types + `<BoardHostMapping/>` editor widget (see [Board Host](#board-host-run-a-cadra-agentic-board-over-your-own-tables)) |
|
|
15
|
+
| `@cadraos/sdk/board-host/server` | Server board-host kit (read compiler) + `runBoardHostConformance` |
|
|
16
|
+
| `@cadraos/sdk/ai-functions/server` | Server-only handlers for host-app AI-function slots |
|
|
17
|
+
| `@cadraos/sdk/ai-functions/react` | `<AiFunctionSlotsPanel/>` client component (React is an optional peer dep) |
|
|
35
18
|
|
|
36
19
|
## Installation
|
|
37
20
|
|
|
@@ -50,7 +33,7 @@ import { CadraClient } from '@cadraos/sdk';
|
|
|
50
33
|
|
|
51
34
|
// Initialize the client
|
|
52
35
|
const client = new CadraClient({
|
|
53
|
-
apiKey: process.env.CADRA_API_KEY!, //
|
|
36
|
+
apiKey: process.env.CADRA_API_KEY!, // <api-key>
|
|
54
37
|
orgId: Number(process.env.CADRA_ORG_ID),
|
|
55
38
|
});
|
|
56
39
|
|
|
@@ -65,7 +48,7 @@ const execution = await client.agents.executeSync(agents[0].uuid, {
|
|
|
65
48
|
console.log('Result:', execution.output);
|
|
66
49
|
```
|
|
67
50
|
|
|
68
|
-
> **Note**: `AISaaSClient`
|
|
51
|
+
> **Note**: `AISaaSClient` / `AISaaSError` are deprecated aliases for `CadraClient` / `CadraError`.
|
|
69
52
|
|
|
70
53
|
## Authentication
|
|
71
54
|
|
|
@@ -74,8 +57,8 @@ The SDK supports three authentication methods:
|
|
|
74
57
|
### API Key (recommended for server-side)
|
|
75
58
|
|
|
76
59
|
```typescript
|
|
77
|
-
const client = new
|
|
78
|
-
apiKey: '
|
|
60
|
+
const client = new CadraClient({
|
|
61
|
+
apiKey: '<api-key>',
|
|
79
62
|
orgId: 123,
|
|
80
63
|
});
|
|
81
64
|
```
|
|
@@ -83,8 +66,8 @@ const client = new AISaaSClient({
|
|
|
83
66
|
### OAuth Token
|
|
84
67
|
|
|
85
68
|
```typescript
|
|
86
|
-
const client = new
|
|
87
|
-
token: '
|
|
69
|
+
const client = new CadraClient({
|
|
70
|
+
token: '<oauth-token>',
|
|
88
71
|
orgId: 123,
|
|
89
72
|
});
|
|
90
73
|
```
|
|
@@ -92,7 +75,7 @@ const client = new AISaaSClient({
|
|
|
92
75
|
### Token Provider (for automatic refresh)
|
|
93
76
|
|
|
94
77
|
```typescript
|
|
95
|
-
const client = new
|
|
78
|
+
const client = new CadraClient({
|
|
96
79
|
tokenProvider: async () => myAuth.getAccessToken(),
|
|
97
80
|
orgId: 123,
|
|
98
81
|
});
|
|
@@ -132,6 +115,8 @@ stream.on('log', (log) => console.log(log.title));
|
|
|
132
115
|
stream.on('toolCall', ({ toolName }) => console.log(`Calling: ${toolName}`));
|
|
133
116
|
stream.on('complete', ({ execution }) => console.log(execution.output));
|
|
134
117
|
|
|
118
|
+
// agents.stream() does NOT connect — register handlers first, then connect once
|
|
119
|
+
await stream.connect();
|
|
135
120
|
const result = await stream.wait();
|
|
136
121
|
|
|
137
122
|
// Get execution result
|
|
@@ -143,6 +128,14 @@ const updated = await client.agents.update('agent-uuid', {
|
|
|
143
128
|
description: 'New description',
|
|
144
129
|
});
|
|
145
130
|
|
|
131
|
+
// Update agent config
|
|
132
|
+
await client.agents.updateConfig('agent-uuid', { /* AgentConfigUpdateInput */ });
|
|
133
|
+
|
|
134
|
+
// Attach to an existing execution (register handlers, then connect once)
|
|
135
|
+
const live = client.agents.streamExecution('execution-uuid');
|
|
136
|
+
live.on('complete', ({ execution }) => console.log(execution.output));
|
|
137
|
+
await live.connect();
|
|
138
|
+
|
|
146
139
|
// Cancel execution
|
|
147
140
|
await client.agents.cancelExecution('execution-uuid');
|
|
148
141
|
```
|
|
@@ -280,7 +273,7 @@ const execution = await client.sessions.sendMessageSync('session-id', {
|
|
|
280
273
|
message: 'Tell me more about that',
|
|
281
274
|
});
|
|
282
275
|
|
|
283
|
-
// Stream message
|
|
276
|
+
// Stream message (sessions.stream() connects for you)
|
|
284
277
|
const stream = await client.sessions.stream('session-id', {
|
|
285
278
|
message: 'Explain in detail',
|
|
286
279
|
});
|
|
@@ -296,6 +289,66 @@ history.messages.forEach(msg => {
|
|
|
296
289
|
await client.sessions.delete('session-id');
|
|
297
290
|
```
|
|
298
291
|
|
|
292
|
+
### Attachments
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// 1. Get a presigned upload URL
|
|
296
|
+
const { attachmentUuid, uploadUrl } = await client.attachments.getUploadUrl({
|
|
297
|
+
filename: 'photo.png',
|
|
298
|
+
mimeType: 'image/png',
|
|
299
|
+
size: file.size,
|
|
300
|
+
});
|
|
301
|
+
// 2. PUT the bytes to uploadUrl, then confirm
|
|
302
|
+
await client.attachments.confirm(attachmentUuid);
|
|
303
|
+
// 3. Reference it in an execution
|
|
304
|
+
await client.agents.execute('agent-uuid', { task: 'Describe this', attachmentIds: [attachmentUuid] });
|
|
305
|
+
|
|
306
|
+
// Download URL
|
|
307
|
+
const url = await client.attachments.getDownloadUrl(attachmentUuid);
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Teams
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
const { data: teams } = await client.teams.list({ isActive: true });
|
|
314
|
+
const team = await client.teams.get('team-uuid'); // includeMembers defaults to true
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Kanban
|
|
318
|
+
|
|
319
|
+
`client.kanban` drives an agentic board: `getBoard()`, `listCards()`, `createCard()`,
|
|
320
|
+
`getCard()`, `updateCard()`, `transitionCard({ cardId, ... })`, `getCardRun(executionId)`,
|
|
321
|
+
and `streamCardRun(executionId)` (attach `.on(...)` handlers, then call `.connect()` once).
|
|
322
|
+
See the [Kanban HTTP adapter guide](./docs/guides/kanban-http-adapter.md).
|
|
323
|
+
|
|
324
|
+
### Remote Agents & Runners
|
|
325
|
+
|
|
326
|
+
`client.remoteAgents`: `list()`, `get()`, `create()`, `update()`, `delete()`, `deploy()`,
|
|
327
|
+
`execute()`, `stream()`. `client.remoteRunners`: `list()`.
|
|
328
|
+
|
|
329
|
+
### AI Functions
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// orgId must be set on the client config — never in run() options
|
|
333
|
+
const fns = await client.aiFunctions.list();
|
|
334
|
+
const descriptor = await client.aiFunctions.get('function_key');
|
|
335
|
+
|
|
336
|
+
// Shorthand: run(functionKey, parameters, opts?)
|
|
337
|
+
const result = await client.aiFunctions.run('function_key', { topic: 'AI' });
|
|
338
|
+
console.log(result.output, result.usage);
|
|
339
|
+
|
|
340
|
+
// Object form: run(ref, { variables, version?, idempotencyKey?, params?, validate? })
|
|
341
|
+
await client.aiFunctions.run({ functionKey: 'function_key' }, { variables: { topic: 'AI' } });
|
|
342
|
+
|
|
343
|
+
// Read a run back
|
|
344
|
+
const run = await client.aiFunctions.getRun(result.executionId);
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Paid runs are never auto-retried (an idempotency key is generated when omitted).
|
|
348
|
+
Errors: `AiFunctionParameterError` (client-side check failed, nothing sent),
|
|
349
|
+
`AiFunctionDuplicateError` (409), `AiFunctionChargedError` (metered but output failed).
|
|
350
|
+
See the [AI Functions reference](./docs/api-reference/ai-functions.md).
|
|
351
|
+
|
|
299
352
|
## Streaming
|
|
300
353
|
|
|
301
354
|
The SDK supports real-time streaming for agent executions:
|
|
@@ -333,6 +386,9 @@ stream.on('connectionChange', ({ connected, usingFallback }) => {
|
|
|
333
386
|
}
|
|
334
387
|
});
|
|
335
388
|
|
|
389
|
+
// Connect AFTER registering handlers (agents.stream() does not auto-connect)
|
|
390
|
+
await stream.connect();
|
|
391
|
+
|
|
336
392
|
// Wait for completion
|
|
337
393
|
const execution = await stream.wait();
|
|
338
394
|
|
|
@@ -346,7 +402,7 @@ The SDK provides typed error classes for different error scenarios:
|
|
|
346
402
|
|
|
347
403
|
```typescript
|
|
348
404
|
import {
|
|
349
|
-
|
|
405
|
+
CadraError,
|
|
350
406
|
AuthenticationError,
|
|
351
407
|
AuthorizationError,
|
|
352
408
|
NotFoundError,
|
|
@@ -370,7 +426,7 @@ try {
|
|
|
370
426
|
error.errors.forEach(e => {
|
|
371
427
|
console.log(`${e.field}: ${e.message}`);
|
|
372
428
|
});
|
|
373
|
-
} else if (error instanceof
|
|
429
|
+
} else if (error instanceof CadraError) {
|
|
374
430
|
console.log(`Error [${error.code}]: ${error.message}`);
|
|
375
431
|
console.log('Request ID:', error.requestId);
|
|
376
432
|
}
|
|
@@ -380,18 +436,19 @@ try {
|
|
|
380
436
|
## Configuration Options
|
|
381
437
|
|
|
382
438
|
```typescript
|
|
383
|
-
const client = new
|
|
439
|
+
const client = new CadraClient({
|
|
384
440
|
// Required
|
|
385
|
-
apiKey: '
|
|
441
|
+
apiKey: '<api-key>', // or token/tokenProvider
|
|
386
442
|
orgId: 123,
|
|
387
443
|
|
|
388
444
|
// Optional
|
|
389
|
-
baseUrl: 'https://api.
|
|
445
|
+
baseUrl: '<base-url>', // Origin only; SDK adds /api/v1 (default: https://api.cadraos.com)
|
|
390
446
|
timeout: 30000, // Request timeout (ms)
|
|
391
447
|
maxRetries: 3, // Retry attempts
|
|
392
448
|
headers: { // Custom headers
|
|
393
449
|
'X-Custom-Header': 'value',
|
|
394
450
|
},
|
|
451
|
+
fetch: customFetch, // Optional fetch impl (e.g. expo/fetch for React Native streaming)
|
|
395
452
|
});
|
|
396
453
|
```
|
|
397
454
|
|
|
@@ -413,16 +470,10 @@ import type {
|
|
|
413
470
|
|
|
414
471
|
## Browser Support
|
|
415
472
|
|
|
416
|
-
The SDK works in modern browsers
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
const agents = await client.agents.list();
|
|
421
|
-
const execution = await client.agents.executeSync(uuid, { task: '...' });
|
|
422
|
-
|
|
423
|
-
// Node.js only (v1)
|
|
424
|
-
const stream = await client.agents.stream(uuid, { task: '...' });
|
|
425
|
-
```
|
|
473
|
+
The SDK works in Node.js >= 18 and modern browsers, including streaming: SSE is
|
|
474
|
+
read with `fetch` + `ReadableStream` and authenticates via headers. For React
|
|
475
|
+
Native streaming, pass a streaming-capable `fetch` (e.g. `expo/fetch`) in the
|
|
476
|
+
client options.
|
|
426
477
|
|
|
427
478
|
## Board Host (run a Cadra agentic board over your own tables)
|
|
428
479
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
function _interopNamespace(e) {
|
|
7
|
+
if (e && e.__esModule) return e;
|
|
8
|
+
var n = Object.create(null);
|
|
9
|
+
if (e) {
|
|
10
|
+
Object.keys(e).forEach(function (k) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return e[k]; }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
25
|
+
|
|
26
|
+
var DEFAULT_AI_FUNCTION_SLOTS_PANEL_LABELS = {
|
|
27
|
+
slot: "Slot",
|
|
28
|
+
usedFor: "Used for",
|
|
29
|
+
boundTo: "Bound to",
|
|
30
|
+
actions: "Actions",
|
|
31
|
+
notSet: "Not set",
|
|
32
|
+
reset: "Reset",
|
|
33
|
+
retry: "Retry",
|
|
34
|
+
loading: "Loading\u2026",
|
|
35
|
+
empty: "No AI function slots are declared.",
|
|
36
|
+
loadFailed: "Could not load AI function slots",
|
|
37
|
+
saveFailed: "Could not save",
|
|
38
|
+
incompatible: "Incompatible",
|
|
39
|
+
unavailable: "unavailable",
|
|
40
|
+
version: (n) => `v${n}`
|
|
41
|
+
};
|
|
42
|
+
function messageOf(error) {
|
|
43
|
+
if (error && typeof error === "object" && typeof error.message === "string") {
|
|
44
|
+
return error.message;
|
|
45
|
+
}
|
|
46
|
+
return typeof error === "string" ? error : "";
|
|
47
|
+
}
|
|
48
|
+
function AiFunctionSlotsPanel(props) {
|
|
49
|
+
const { api, className, classNames = {} } = props;
|
|
50
|
+
const L = { ...DEFAULT_AI_FUNCTION_SLOTS_PANEL_LABELS, ...props.labels };
|
|
51
|
+
const [state, setState] = React__namespace.useState({ status: "loading" });
|
|
52
|
+
const [pendingKey, setPendingKey] = React__namespace.useState(null);
|
|
53
|
+
const [rowErrors, setRowErrors] = React__namespace.useState({});
|
|
54
|
+
const mounted = React__namespace.useRef(true);
|
|
55
|
+
const load = React__namespace.useCallback(async () => {
|
|
56
|
+
try {
|
|
57
|
+
const rows2 = await api.listSlots();
|
|
58
|
+
if (mounted.current) setState({ status: "ready", rows: rows2 });
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (mounted.current) setState({ status: "error", message: messageOf(error) });
|
|
61
|
+
}
|
|
62
|
+
}, [api]);
|
|
63
|
+
React__namespace.useEffect(() => {
|
|
64
|
+
mounted.current = true;
|
|
65
|
+
void load();
|
|
66
|
+
return () => {
|
|
67
|
+
mounted.current = false;
|
|
68
|
+
};
|
|
69
|
+
}, [load]);
|
|
70
|
+
const mutate = async (key, action) => {
|
|
71
|
+
setPendingKey(key);
|
|
72
|
+
setRowErrors((e) => {
|
|
73
|
+
const next = { ...e };
|
|
74
|
+
delete next[key];
|
|
75
|
+
return next;
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
await action();
|
|
79
|
+
await load();
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (mounted.current) {
|
|
82
|
+
const msg = messageOf(error);
|
|
83
|
+
setRowErrors((e) => ({ ...e, [key]: msg ? `${L.saveFailed}: ${msg}` : L.saveFailed }));
|
|
84
|
+
}
|
|
85
|
+
} finally {
|
|
86
|
+
if (mounted.current) setPendingKey(null);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const rootClass = [classNames.root, className].filter(Boolean).join(" ") || void 0;
|
|
90
|
+
if (state.status === "loading") {
|
|
91
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: rootClass, "aria-busy": "true", "data-state": "loading", children: L.loading });
|
|
92
|
+
}
|
|
93
|
+
if (state.status === "error") {
|
|
94
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: rootClass, "data-state": "error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: classNames.banner, children: [
|
|
95
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: state.message ? `${L.loadFailed}: ${state.message}` : L.loadFailed }),
|
|
96
|
+
" ",
|
|
97
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
98
|
+
"button",
|
|
99
|
+
{
|
|
100
|
+
type: "button",
|
|
101
|
+
className: classNames.button,
|
|
102
|
+
onClick: () => {
|
|
103
|
+
setState({ status: "loading" });
|
|
104
|
+
void load();
|
|
105
|
+
},
|
|
106
|
+
children: L.retry
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
] }) });
|
|
110
|
+
}
|
|
111
|
+
const { rows } = state;
|
|
112
|
+
if (rows.length === 0) {
|
|
113
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: rootClass, "data-state": "empty", children: L.empty });
|
|
114
|
+
}
|
|
115
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: rootClass, "data-state": "ready", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: classNames.table, children: [
|
|
116
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: classNames.headerRow, children: [
|
|
117
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: classNames.headerCell, children: L.slot }),
|
|
118
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: classNames.headerCell, children: L.usedFor }),
|
|
119
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: classNames.headerCell, children: L.boundTo }),
|
|
120
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: classNames.headerCell, children: L.actions })
|
|
121
|
+
] }) }),
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.map((row) => {
|
|
123
|
+
const bound = row.binding?.functionKey ?? "";
|
|
124
|
+
const boundIsOption = row.options.some((o) => o.functionKey === bound);
|
|
125
|
+
const busy = pendingKey === row.key;
|
|
126
|
+
const incompatible = row.compat && !row.compat.compatible;
|
|
127
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: classNames.row, "data-slot": row.key, children: [
|
|
128
|
+
/* @__PURE__ */ jsxRuntime.jsxs("td", { className: classNames.cell, children: [
|
|
129
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames.slotLabel, children: row.label }),
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames.slotKey, children: row.key })
|
|
131
|
+
] }),
|
|
132
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: classNames.cell, children: row.usedFor ?? "" }),
|
|
133
|
+
/* @__PURE__ */ jsxRuntime.jsxs("td", { className: classNames.cell, children: [
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
135
|
+
"select",
|
|
136
|
+
{
|
|
137
|
+
"aria-label": `${L.boundTo}: ${row.label}`,
|
|
138
|
+
className: classNames.select,
|
|
139
|
+
value: bound,
|
|
140
|
+
disabled: busy,
|
|
141
|
+
onChange: (e) => {
|
|
142
|
+
const functionKey = e.target.value;
|
|
143
|
+
if (functionKey && functionKey !== bound) {
|
|
144
|
+
void mutate(row.key, () => api.setSlot({ key: row.key, functionKey }));
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
children: [
|
|
148
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: Boolean(bound), children: L.notSet }),
|
|
149
|
+
bound && !boundIsOption ? /* @__PURE__ */ jsxRuntime.jsx("option", { value: bound, disabled: true, children: `${row.boundFunction?.title ?? bound} (${incompatible ? L.incompatible : L.unavailable})` }) : null,
|
|
150
|
+
row.options.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.functionKey, children: `${o.title} \xB7 ${L.version(o.liveVersion)}` }, o.functionKey))
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
row.boundFunction ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: classNames.meta, children: `${row.boundFunction.functionKey} \xB7 ${L.version(row.boundFunction.liveVersion)}` }) : null,
|
|
155
|
+
incompatible ? /* @__PURE__ */ jsxRuntime.jsx("ul", { role: "alert", className: classNames.issues, "aria-label": L.incompatible, children: row.compat.issues.map((issue, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: issue.message }, i)) }) : null,
|
|
156
|
+
rowErrors[row.key] ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", className: classNames.rowError, children: rowErrors[row.key] }) : null
|
|
157
|
+
] }),
|
|
158
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: classNames.cell, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
159
|
+
"button",
|
|
160
|
+
{
|
|
161
|
+
type: "button",
|
|
162
|
+
className: classNames.button,
|
|
163
|
+
disabled: busy || !row.binding,
|
|
164
|
+
onClick: () => void mutate(row.key, () => api.resetSlot({ key: row.key })),
|
|
165
|
+
children: L.reset
|
|
166
|
+
}
|
|
167
|
+
) })
|
|
168
|
+
] }, row.key);
|
|
169
|
+
}) })
|
|
170
|
+
] }) });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
exports.AiFunctionSlotsPanel = AiFunctionSlotsPanel;
|
|
174
|
+
exports.DEFAULT_AI_FUNCTION_SLOTS_PANEL_LABELS = DEFAULT_AI_FUNCTION_SLOTS_PANEL_LABELS;
|
|
175
|
+
//# sourceMappingURL=index.cjs.map
|
|
176
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/ai-functions/react/AiFunctionSlotsPanel.tsx"],"names":["React","rows","jsx","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * <AiFunctionSlotsPanel/> — admin table for a host app's AI function slots\n * (p93 AIF-017, specs D28).\n *\n * Table: Slot | Used for | Bound to (select of compatible functions + live\n * version) | Reset. Zero-dependency and unstyled: every element takes a class\n * from `classNames` so the host styles it with its own tokens. No toasts — a\n * failed load shows an error banner (never an empty table) and a failed write\n * shows inline on its row. All visible text can be replaced via `labels`.\n */\n\nimport * as React from 'react';\nimport type { AiFunctionSetSlotResult, AiFunctionSlotRow } from '../server/types.js';\n\nexport interface AiFunctionSlotsPanelApi {\n listSlots(): Promise<AiFunctionSlotRow[]>;\n setSlot(input: { key: string; functionKey: string }): Promise<AiFunctionSetSlotResult | unknown>;\n resetSlot(input: { key: string }): Promise<unknown>;\n}\n\nexport interface AiFunctionSlotsPanelLabels {\n slot: string;\n usedFor: string;\n boundTo: string;\n actions: string;\n notSet: string;\n reset: string;\n retry: string;\n loading: string;\n empty: string;\n loadFailed: string;\n saveFailed: string;\n incompatible: string;\n unavailable: string;\n version: (n: number) => string;\n}\n\nexport const DEFAULT_AI_FUNCTION_SLOTS_PANEL_LABELS: AiFunctionSlotsPanelLabels = {\n slot: 'Slot',\n usedFor: 'Used for',\n boundTo: 'Bound to',\n actions: 'Actions',\n notSet: 'Not set',\n reset: 'Reset',\n retry: 'Retry',\n loading: 'Loading…',\n empty: 'No AI function slots are declared.',\n loadFailed: 'Could not load AI function slots',\n saveFailed: 'Could not save',\n incompatible: 'Incompatible',\n unavailable: 'unavailable',\n version: (n) => `v${n}`,\n};\n\nexport type AiFunctionSlotsPanelPart =\n | 'root'\n | 'banner'\n | 'table'\n | 'headerRow'\n | 'headerCell'\n | 'row'\n | 'cell'\n | 'slotLabel'\n | 'slotKey'\n | 'select'\n | 'meta'\n | 'issues'\n | 'rowError'\n | 'button';\n\nexport interface AiFunctionSlotsPanelProps {\n api: AiFunctionSlotsPanelApi;\n /** Class on the root element. */\n className?: string;\n /** Classes per part, for host styling. */\n classNames?: Partial<Record<AiFunctionSlotsPanelPart, string>>;\n /** Replace any visible text. */\n labels?: Partial<AiFunctionSlotsPanelLabels>;\n}\n\ntype LoadState =\n | { status: 'loading' }\n | { status: 'ready'; rows: AiFunctionSlotRow[] }\n | { status: 'error'; message: string };\n\nfunction messageOf(error: unknown): string {\n if (error && typeof error === 'object' && typeof (error as { message?: unknown }).message === 'string') {\n return (error as { message: string }).message;\n }\n return typeof error === 'string' ? error : '';\n}\n\nexport function AiFunctionSlotsPanel(props: AiFunctionSlotsPanelProps): React.ReactElement {\n const { api, className, classNames = {} } = props;\n const L: AiFunctionSlotsPanelLabels = { ...DEFAULT_AI_FUNCTION_SLOTS_PANEL_LABELS, ...props.labels };\n\n const [state, setState] = React.useState<LoadState>({ status: 'loading' });\n const [pendingKey, setPendingKey] = React.useState<string | null>(null);\n const [rowErrors, setRowErrors] = React.useState<Record<string, string>>({});\n const mounted = React.useRef(true);\n\n const load = React.useCallback(async () => {\n try {\n const rows = await api.listSlots();\n if (mounted.current) setState({ status: 'ready', rows });\n } catch (error) {\n if (mounted.current) setState({ status: 'error', message: messageOf(error) });\n }\n }, [api]);\n\n React.useEffect(() => {\n mounted.current = true;\n void load();\n return () => {\n mounted.current = false;\n };\n }, [load]);\n\n const mutate = async (key: string, action: () => Promise<unknown>) => {\n setPendingKey(key);\n setRowErrors((e) => {\n const next = { ...e };\n delete next[key];\n return next;\n });\n try {\n await action();\n await load();\n } catch (error) {\n if (mounted.current) {\n const msg = messageOf(error);\n setRowErrors((e) => ({ ...e, [key]: msg ? `${L.saveFailed}: ${msg}` : L.saveFailed }));\n }\n } finally {\n if (mounted.current) setPendingKey(null);\n }\n };\n\n const rootClass = [classNames.root, className].filter(Boolean).join(' ') || undefined;\n\n if (state.status === 'loading') {\n return (\n <div className={rootClass} aria-busy=\"true\" data-state=\"loading\">\n {L.loading}\n </div>\n );\n }\n\n if (state.status === 'error') {\n return (\n <div className={rootClass} data-state=\"error\">\n <div role=\"alert\" className={classNames.banner}>\n <span>{state.message ? `${L.loadFailed}: ${state.message}` : L.loadFailed}</span>{' '}\n <button\n type=\"button\"\n className={classNames.button}\n onClick={() => {\n setState({ status: 'loading' });\n void load();\n }}\n >\n {L.retry}\n </button>\n </div>\n </div>\n );\n }\n\n const { rows } = state;\n if (rows.length === 0) {\n return (\n <div className={rootClass} data-state=\"empty\">\n {L.empty}\n </div>\n );\n }\n\n return (\n <div className={rootClass} data-state=\"ready\">\n <table className={classNames.table}>\n <thead>\n <tr className={classNames.headerRow}>\n <th scope=\"col\" className={classNames.headerCell}>{L.slot}</th>\n <th scope=\"col\" className={classNames.headerCell}>{L.usedFor}</th>\n <th scope=\"col\" className={classNames.headerCell}>{L.boundTo}</th>\n <th scope=\"col\" className={classNames.headerCell}>{L.actions}</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => {\n const bound = row.binding?.functionKey ?? '';\n const boundIsOption = row.options.some((o) => o.functionKey === bound);\n const busy = pendingKey === row.key;\n const incompatible = row.compat && !row.compat.compatible;\n return (\n <tr key={row.key} className={classNames.row} data-slot={row.key}>\n <td className={classNames.cell}>\n <div className={classNames.slotLabel}>{row.label}</div>\n <div className={classNames.slotKey}>{row.key}</div>\n </td>\n <td className={classNames.cell}>{row.usedFor ?? ''}</td>\n <td className={classNames.cell}>\n <select\n aria-label={`${L.boundTo}: ${row.label}`}\n className={classNames.select}\n value={bound}\n disabled={busy}\n onChange={(e) => {\n const functionKey = e.target.value;\n if (functionKey && functionKey !== bound) {\n void mutate(row.key, () => api.setSlot({ key: row.key, functionKey }));\n }\n }}\n >\n <option value=\"\" disabled={Boolean(bound)}>\n {L.notSet}\n </option>\n {bound && !boundIsOption ? (\n <option value={bound} disabled>\n {`${row.boundFunction?.title ?? bound} (${incompatible ? L.incompatible : L.unavailable})`}\n </option>\n ) : null}\n {row.options.map((o) => (\n <option key={o.functionKey} value={o.functionKey}>\n {`${o.title} · ${L.version(o.liveVersion)}`}\n </option>\n ))}\n </select>\n {row.boundFunction ? (\n <div className={classNames.meta}>\n {`${row.boundFunction.functionKey} · ${L.version(row.boundFunction.liveVersion)}`}\n </div>\n ) : null}\n {incompatible ? (\n <ul role=\"alert\" className={classNames.issues} aria-label={L.incompatible}>\n {row.compat!.issues.map((issue, i) => (\n <li key={i}>{issue.message}</li>\n ))}\n </ul>\n ) : null}\n {rowErrors[row.key] ? (\n <div role=\"alert\" className={classNames.rowError}>\n {rowErrors[row.key]}\n </div>\n ) : null}\n </td>\n <td className={classNames.cell}>\n <button\n type=\"button\"\n className={classNames.button}\n disabled={busy || !row.binding}\n onClick={() => void mutate(row.key, () => api.resetSlot({ key: row.key }))}\n >\n {L.reset}\n </button>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AI function types (p93 — specs §4 run contract, D27 discovery, §6.5 run reads).
|
|
5
|
+
*
|
|
6
|
+
* An AI function is a published prompt in the Cadra Prompts library that makes
|
|
7
|
+
* ONE direct AI-provider call (never an agent). Callers reference it by its
|
|
8
|
+
* `functionKey` (unique per org), by prompt uuid, or — for platform features —
|
|
9
|
+
* by slot key.
|
|
10
|
+
*/
|
|
11
|
+
/** Output kind of an AI function. */
|
|
12
|
+
type AiFunctionOutputKind = 'text' | 'json' | 'image';
|
|
13
|
+
/** Parameter (prompt variable) types — specs D10 + D26d. */
|
|
14
|
+
type AiFunctionParameterType = 'string' | 'text' | 'number' | 'boolean' | 'enum' | 'image';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Host-app AI function slots — shared types (p93 AIF-017, specs D28).
|
|
18
|
+
*
|
|
19
|
+
* Type-only module: imported by both `@cadraos/sdk/ai-functions/server` and
|
|
20
|
+
* `@cadraos/sdk/ai-functions/react` (the panel renders the rows the server
|
|
21
|
+
* handlers return), so it must stay free of runtime code and of React.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** One parameter a host slot passes when it calls its function. */
|
|
25
|
+
interface AiFunctionSlotParameter {
|
|
26
|
+
name: string;
|
|
27
|
+
type: AiFunctionParameterType;
|
|
28
|
+
required: boolean;
|
|
29
|
+
maxLength?: number;
|
|
30
|
+
options?: string[];
|
|
31
|
+
}
|
|
32
|
+
/** What a slot is bound to: a function of the host's Cadra org. */
|
|
33
|
+
interface AiFunctionSlotBinding {
|
|
34
|
+
functionKey: string;
|
|
35
|
+
}
|
|
36
|
+
/** Why a function does not fit a slot (D16 rule applied to a D27 descriptor). */
|
|
37
|
+
type AiFunctionSlotCompatIssueCode = 'function_unavailable' | 'output_kind' | 'parameter_missing' | 'parameter_type' | 'parameter_required' | 'options' | 'max_length';
|
|
38
|
+
interface AiFunctionSlotCompatIssue {
|
|
39
|
+
code: AiFunctionSlotCompatIssueCode;
|
|
40
|
+
/** The parameter concerned, when there is one. */
|
|
41
|
+
parameter?: string;
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
interface AiFunctionSlotCompatVerdict {
|
|
45
|
+
compatible: boolean;
|
|
46
|
+
issues: AiFunctionSlotCompatIssue[];
|
|
47
|
+
}
|
|
48
|
+
/** A function offered in a slot's picker. */
|
|
49
|
+
interface AiFunctionSlotOption {
|
|
50
|
+
functionKey: string;
|
|
51
|
+
title: string;
|
|
52
|
+
liveVersion: number;
|
|
53
|
+
}
|
|
54
|
+
/** One row of `listSlots()` — catalog + binding + live descriptor + verdict. */
|
|
55
|
+
interface AiFunctionSlotRow {
|
|
56
|
+
key: string;
|
|
57
|
+
label: string;
|
|
58
|
+
usedFor: string | null;
|
|
59
|
+
outputKind: AiFunctionOutputKind;
|
|
60
|
+
parameters: AiFunctionSlotParameter[];
|
|
61
|
+
/** The stored binding, or null when the slot is unset. */
|
|
62
|
+
binding: AiFunctionSlotBinding | null;
|
|
63
|
+
/** The bound function's live descriptor summary; null when unset or unavailable. */
|
|
64
|
+
boundFunction: (AiFunctionSlotOption & {
|
|
65
|
+
outputKind: AiFunctionOutputKind;
|
|
66
|
+
}) | null;
|
|
67
|
+
/** Compatibility of the bound function; null when the slot is unset. */
|
|
68
|
+
compat: AiFunctionSlotCompatVerdict | null;
|
|
69
|
+
/** Functions compatible with this slot (the picker's choices). */
|
|
70
|
+
options: AiFunctionSlotOption[];
|
|
71
|
+
}
|
|
72
|
+
/** Result of a successful `setSlot`. */
|
|
73
|
+
interface AiFunctionSetSlotResult {
|
|
74
|
+
key: string;
|
|
75
|
+
functionKey: string;
|
|
76
|
+
liveVersion: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* <AiFunctionSlotsPanel/> — admin table for a host app's AI function slots
|
|
81
|
+
* (p93 AIF-017, specs D28).
|
|
82
|
+
*
|
|
83
|
+
* Table: Slot | Used for | Bound to (select of compatible functions + live
|
|
84
|
+
* version) | Reset. Zero-dependency and unstyled: every element takes a class
|
|
85
|
+
* from `classNames` so the host styles it with its own tokens. No toasts — a
|
|
86
|
+
* failed load shows an error banner (never an empty table) and a failed write
|
|
87
|
+
* shows inline on its row. All visible text can be replaced via `labels`.
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
interface AiFunctionSlotsPanelApi {
|
|
91
|
+
listSlots(): Promise<AiFunctionSlotRow[]>;
|
|
92
|
+
setSlot(input: {
|
|
93
|
+
key: string;
|
|
94
|
+
functionKey: string;
|
|
95
|
+
}): Promise<AiFunctionSetSlotResult | unknown>;
|
|
96
|
+
resetSlot(input: {
|
|
97
|
+
key: string;
|
|
98
|
+
}): Promise<unknown>;
|
|
99
|
+
}
|
|
100
|
+
interface AiFunctionSlotsPanelLabels {
|
|
101
|
+
slot: string;
|
|
102
|
+
usedFor: string;
|
|
103
|
+
boundTo: string;
|
|
104
|
+
actions: string;
|
|
105
|
+
notSet: string;
|
|
106
|
+
reset: string;
|
|
107
|
+
retry: string;
|
|
108
|
+
loading: string;
|
|
109
|
+
empty: string;
|
|
110
|
+
loadFailed: string;
|
|
111
|
+
saveFailed: string;
|
|
112
|
+
incompatible: string;
|
|
113
|
+
unavailable: string;
|
|
114
|
+
version: (n: number) => string;
|
|
115
|
+
}
|
|
116
|
+
declare const DEFAULT_AI_FUNCTION_SLOTS_PANEL_LABELS: AiFunctionSlotsPanelLabels;
|
|
117
|
+
type AiFunctionSlotsPanelPart = 'root' | 'banner' | 'table' | 'headerRow' | 'headerCell' | 'row' | 'cell' | 'slotLabel' | 'slotKey' | 'select' | 'meta' | 'issues' | 'rowError' | 'button';
|
|
118
|
+
interface AiFunctionSlotsPanelProps {
|
|
119
|
+
api: AiFunctionSlotsPanelApi;
|
|
120
|
+
/** Class on the root element. */
|
|
121
|
+
className?: string;
|
|
122
|
+
/** Classes per part, for host styling. */
|
|
123
|
+
classNames?: Partial<Record<AiFunctionSlotsPanelPart, string>>;
|
|
124
|
+
/** Replace any visible text. */
|
|
125
|
+
labels?: Partial<AiFunctionSlotsPanelLabels>;
|
|
126
|
+
}
|
|
127
|
+
declare function AiFunctionSlotsPanel(props: AiFunctionSlotsPanelProps): React.ReactElement;
|
|
128
|
+
|
|
129
|
+
export { type AiFunctionSetSlotResult, type AiFunctionSlotCompatVerdict, type AiFunctionSlotOption, type AiFunctionSlotRow, AiFunctionSlotsPanel, type AiFunctionSlotsPanelApi, type AiFunctionSlotsPanelLabels, type AiFunctionSlotsPanelPart, type AiFunctionSlotsPanelProps, DEFAULT_AI_FUNCTION_SLOTS_PANEL_LABELS };
|