@brixel_ai/artifact-sdk 1.0.0 → 1.0.2

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/dist/index.mjs CHANGED
@@ -1,23 +1,77 @@
1
1
  // src/useBrixelArtifact.ts
2
- import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
 
4
- // src/executeTask.ts
5
- function getApiBaseUrl() {
6
- if (typeof window !== "undefined") {
7
- const hostname = window.location.hostname;
8
- if (hostname === "localhost" || hostname === "127.0.0.1") {
9
- return "http://localhost:8000/backoffice/ui-components";
10
- }
4
+ // src/http.ts
5
+ var LOCAL_API_BASE_URL = "http://localhost:8000";
6
+ var PROD_API_BASE_URL = "https://platform.brixel.ai";
7
+ function isBrowserLocalhost() {
8
+ if (typeof window === "undefined") {
9
+ return false;
11
10
  }
12
- return "https://api.brixel.ai/backoffice/ui-components";
11
+ const { hostname } = window.location;
12
+ return hostname === "localhost" || hostname === "127.0.0.1";
13
13
  }
14
- async function executeTask(params) {
15
- const { taskUuid, inputs, conversationId, apiToken, apiBaseUrl } = params;
16
- const baseUrl = apiBaseUrl || getApiBaseUrl();
17
- if (typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")) {
14
+ function resolveApiBaseUrl(apiBaseUrl) {
15
+ if (apiBaseUrl) {
16
+ return apiBaseUrl;
17
+ }
18
+ return isBrowserLocalhost() ? LOCAL_API_BASE_URL : PROD_API_BASE_URL;
19
+ }
20
+ function debugApiBaseUrl(baseUrl) {
21
+ if (isBrowserLocalhost()) {
18
22
  console.debug("[Brixel SDK] Using API URL:", baseUrl);
19
23
  }
24
+ }
25
+ async function parseJsonOrText(response) {
26
+ const responseText = await response.text();
27
+ if (!responseText) {
28
+ return null;
29
+ }
20
30
  try {
31
+ return JSON.parse(responseText);
32
+ } catch {
33
+ return { raw: responseText };
34
+ }
35
+ }
36
+ function toHttpError(response, payload) {
37
+ return {
38
+ code: payload?.code || `HTTP_${response.status}`,
39
+ message: payload?.message || `Request failed with status ${response.status}`,
40
+ details: payload?.details || payload
41
+ };
42
+ }
43
+ function toNetworkError(error) {
44
+ return {
45
+ code: "NETWORK_ERROR",
46
+ message: error instanceof Error ? error.message : "Unknown error occurred",
47
+ details: error
48
+ };
49
+ }
50
+
51
+ // src/executeTask.ts
52
+ async function executeTask(params) {
53
+ const { organizationId, taskId, inputs, conversationId, apiToken, apiBaseUrl } = params;
54
+ const baseUrl = resolveApiBaseUrl(apiBaseUrl);
55
+ debugApiBaseUrl(baseUrl);
56
+ try {
57
+ if (!organizationId) {
58
+ return {
59
+ success: false,
60
+ error: {
61
+ code: "MISSING_ORGANIZATION_ID",
62
+ message: "organizationId is required to execute a task"
63
+ }
64
+ };
65
+ }
66
+ if (!taskId) {
67
+ return {
68
+ success: false,
69
+ error: {
70
+ code: "MISSING_TASK_ID",
71
+ message: "taskId is required to execute a task"
72
+ }
73
+ };
74
+ }
21
75
  const headers = {
22
76
  "Content-Type": "application/json"
23
77
  };
@@ -27,33 +81,21 @@ async function executeTask(params) {
27
81
  if (conversationId) {
28
82
  headers["x-conversation-id"] = conversationId;
29
83
  }
30
- const response = await fetch(`${baseUrl}/execute_task`, {
31
- method: "POST",
32
- headers,
33
- // Include cookies as fallback if no explicit token provided
34
- credentials: apiToken ? "same-origin" : "include",
35
- body: JSON.stringify({
36
- task_uuid: taskUuid,
37
- inputs
38
- })
39
- });
40
- const responseText = await response.text();
41
- let data = null;
42
- if (responseText) {
43
- try {
44
- data = JSON.parse(responseText);
45
- } catch {
46
- data = { raw: responseText };
84
+ const orgIdEncoded = encodeURIComponent(organizationId);
85
+ const taskIdEncoded = encodeURIComponent(taskId);
86
+ const response = await fetch(
87
+ `${baseUrl}/v1/organizations/${orgIdEncoded}/tasks/${taskIdEncoded}/execute`,
88
+ {
89
+ method: "POST",
90
+ headers,
91
+ body: JSON.stringify({ inputs })
47
92
  }
48
- }
93
+ );
94
+ const data = await parseJsonOrText(response);
49
95
  if (!response.ok) {
50
96
  return {
51
97
  success: false,
52
- error: {
53
- code: data?.code || `HTTP_${response.status}`,
54
- message: data?.message || `Request failed with status ${response.status}`,
55
- details: data?.details || data
56
- }
98
+ error: toHttpError(response, data)
57
99
  };
58
100
  }
59
101
  return {
@@ -63,18 +105,16 @@ async function executeTask(params) {
63
105
  } catch (error) {
64
106
  return {
65
107
  success: false,
66
- error: {
67
- code: "NETWORK_ERROR",
68
- message: error instanceof Error ? error.message : "Unknown error occurred",
69
- details: error
70
- }
108
+ error: toNetworkError(error)
71
109
  };
72
110
  }
73
111
  }
74
112
  function createExecuteTask(contextAuth) {
75
113
  return (params) => {
114
+ const { organizationId, ...restParams } = params;
76
115
  return executeTask({
77
- ...params,
116
+ ...restParams,
117
+ organizationId: organizationId ?? contextAuth?.organizationId ?? "",
78
118
  apiToken: contextAuth?.apiToken,
79
119
  conversationId: contextAuth?.conversationId,
80
120
  apiBaseUrl: contextAuth?.apiBaseUrl
@@ -82,6 +122,158 @@ function createExecuteTask(contextAuth) {
82
122
  };
83
123
  }
84
124
 
125
+ // src/uploadFile.ts
126
+ async function uploadFile(params) {
127
+ const { file, organizationId, apiToken, apiBaseUrl } = params;
128
+ const baseUrl = resolveApiBaseUrl(apiBaseUrl);
129
+ debugApiBaseUrl(baseUrl);
130
+ try {
131
+ if (!file) {
132
+ return {
133
+ success: false,
134
+ error: {
135
+ code: "MISSING_FILE",
136
+ message: "file is required for upload"
137
+ }
138
+ };
139
+ }
140
+ const headers = {};
141
+ if (apiToken) {
142
+ headers["Authorization"] = `Bearer ${apiToken}`;
143
+ }
144
+ if (organizationId) {
145
+ headers["X-Organization-Id"] = organizationId;
146
+ }
147
+ const formData = new FormData();
148
+ formData.append("file", file);
149
+ formData.append("visibility", "user");
150
+ const response = await fetch(`${baseUrl}/v1/files/`, {
151
+ method: "POST",
152
+ headers,
153
+ body: formData
154
+ });
155
+ const data = await parseJsonOrText(response);
156
+ if (!response.ok) {
157
+ return {
158
+ success: false,
159
+ error: toHttpError(response, data)
160
+ };
161
+ }
162
+ return {
163
+ success: true,
164
+ data: data ?? void 0
165
+ };
166
+ } catch (error) {
167
+ return {
168
+ success: false,
169
+ error: toNetworkError(error)
170
+ };
171
+ }
172
+ }
173
+ function createUploadFile(contextAuth) {
174
+ return (params) => {
175
+ const { organizationId, ...restParams } = params;
176
+ return uploadFile({
177
+ ...restParams,
178
+ organizationId: organizationId ?? contextAuth?.organizationId,
179
+ apiToken: contextAuth?.apiToken,
180
+ apiBaseUrl: contextAuth?.apiBaseUrl
181
+ });
182
+ };
183
+ }
184
+
185
+ // src/getFileContent.ts
186
+ async function getFileContent(params) {
187
+ const {
188
+ brixelFileId,
189
+ organizationId,
190
+ conversationId,
191
+ apiToken,
192
+ apiBaseUrl,
193
+ ifNoneMatch,
194
+ ifModifiedSince
195
+ } = params;
196
+ const baseUrl = resolveApiBaseUrl(apiBaseUrl);
197
+ debugApiBaseUrl(baseUrl);
198
+ try {
199
+ if (!brixelFileId) {
200
+ return {
201
+ success: false,
202
+ error: {
203
+ code: "MISSING_FILE_ID",
204
+ message: "brixelFileId is required"
205
+ }
206
+ };
207
+ }
208
+ const headers = {};
209
+ if (apiToken) {
210
+ headers["Authorization"] = `Bearer ${apiToken}`;
211
+ }
212
+ if (organizationId) {
213
+ headers["X-Organization-Id"] = organizationId;
214
+ }
215
+ if (conversationId) {
216
+ headers["X-Conversation-Id"] = conversationId;
217
+ }
218
+ if (ifNoneMatch) {
219
+ headers["If-None-Match"] = ifNoneMatch;
220
+ }
221
+ if (ifModifiedSince) {
222
+ headers["If-Modified-Since"] = ifModifiedSince;
223
+ }
224
+ const fileIdEncoded = encodeURIComponent(brixelFileId);
225
+ const response = await fetch(`${baseUrl}/v1/files/${fileIdEncoded}/content`, {
226
+ method: "GET",
227
+ headers
228
+ });
229
+ if (response.status === 304) {
230
+ return {
231
+ success: true,
232
+ notModified: true
233
+ };
234
+ }
235
+ if (!response.ok) {
236
+ const data = await parseJsonOrText(response);
237
+ return {
238
+ success: false,
239
+ error: toHttpError(response, data)
240
+ };
241
+ }
242
+ const blob = await response.blob();
243
+ const contentType = response.headers.get("Content-Type");
244
+ const contentLengthRaw = response.headers.get("Content-Length");
245
+ const contentLength = contentLengthRaw ? Number(contentLengthRaw) : void 0;
246
+ const fileData = {
247
+ blob,
248
+ contentType,
249
+ contentLength: Number.isFinite(contentLength) ? contentLength : void 0,
250
+ etag: response.headers.get("ETag"),
251
+ lastModified: response.headers.get("Last-Modified")
252
+ };
253
+ return {
254
+ success: true,
255
+ data: fileData
256
+ };
257
+ } catch (error) {
258
+ return {
259
+ success: false,
260
+ error: toNetworkError(error)
261
+ };
262
+ }
263
+ }
264
+ function createGetFileContent(contextAuth) {
265
+ return (params) => {
266
+ const { organizationId, conversationId, ...restParams } = params;
267
+ return getFileContent({
268
+ ...restParams,
269
+ organizationId: organizationId ?? contextAuth?.organizationId,
270
+ conversationId: conversationId ?? contextAuth?.conversationId,
271
+ apiToken: contextAuth?.apiToken,
272
+ apiBaseUrl: contextAuth?.apiBaseUrl
273
+ });
274
+ };
275
+ }
276
+
85
277
  // src/useBrixelArtifact.ts
86
278
  var SDK_VERSION = "1.0.0";
87
279
  function isInIframe() {
@@ -92,7 +284,13 @@ function isInIframe() {
92
284
  }
93
285
  }
94
286
  function useBrixelArtifact(options = {}) {
95
- const { targetOrigin = "*", onInputsUpdate, onDestroy, debug = false } = options;
287
+ const {
288
+ targetOrigin = "*",
289
+ onInputsUpdate,
290
+ onDestroy,
291
+ debug = false,
292
+ autoResize = false
293
+ } = options;
96
294
  const [inputs, setInputs] = useState(null);
97
295
  const [context, setContext] = useState(null);
98
296
  const [status, setStatus] = useState("initializing");
@@ -120,45 +318,35 @@ function useBrixelArtifact(options = {}) {
120
318
  },
121
319
  [targetOrigin, debugLog]
122
320
  );
123
- const complete = useCallback(
124
- (output) => {
321
+ const completeTask = useCallback(
322
+ (type, payload) => {
125
323
  if (hasCompleted.current) {
126
- debugLog("Already completed, ignoring duplicate complete call");
324
+ debugLog(`Already completed, ignoring ${type} call`);
127
325
  return;
128
326
  }
129
327
  if (!runId) {
130
- console.error("[BrixelSDK] Cannot complete - no runId");
328
+ console.error(`[BrixelSDK] Cannot send ${type} - no runId`);
131
329
  return;
132
330
  }
133
331
  hasCompleted.current = true;
134
- setStatus("completed");
135
- postToParent({
136
- type: "BRIXEL_COMPLETE",
137
- payload: { runId, output }
138
- });
139
- debugLog("Task completed with output:", output);
332
+ setStatus(type === "BRIXEL_COMPLETE" ? "completed" : "cancelled");
333
+ postToParent({ type, payload: { runId, ...payload } });
140
334
  },
141
335
  [runId, postToParent, debugLog]
142
336
  );
337
+ const complete = useCallback(
338
+ (output) => {
339
+ completeTask("BRIXEL_COMPLETE", { output });
340
+ debugLog("Task completed with output:", output);
341
+ },
342
+ [completeTask, debugLog]
343
+ );
143
344
  const cancel = useCallback(
144
345
  (reason) => {
145
- if (hasCompleted.current) {
146
- debugLog("Already completed, ignoring cancel call");
147
- return;
148
- }
149
- if (!runId) {
150
- console.error("[BrixelSDK] Cannot cancel - no runId");
151
- return;
152
- }
153
- hasCompleted.current = true;
154
- setStatus("cancelled");
155
- postToParent({
156
- type: "BRIXEL_CANCEL",
157
- payload: { runId, reason }
158
- });
346
+ completeTask("BRIXEL_CANCEL", { reason });
159
347
  debugLog("Task cancelled:", reason);
160
348
  },
161
- [runId, postToParent, debugLog]
349
+ [completeTask, debugLog]
162
350
  );
163
351
  const setHeight = useCallback(
164
352
  (height) => {
@@ -244,7 +432,7 @@ function useBrixelArtifact(options = {}) {
244
432
  };
245
433
  }, [debugLog, postToParent, onInputsUpdate, onDestroy]);
246
434
  useEffect(() => {
247
- if (!runId || !isEmbedded.current) return;
435
+ if (!runId || !isEmbedded.current || !autoResize) return;
248
436
  const resizeObserver = new ResizeObserver((entries) => {
249
437
  for (const entry of entries) {
250
438
  const height = entry.contentRect.height;
@@ -255,14 +443,32 @@ function useBrixelArtifact(options = {}) {
255
443
  return () => {
256
444
  resizeObserver.disconnect();
257
445
  };
258
- }, [runId, setHeight]);
259
- const executeTask2 = useCallback(
260
- createExecuteTask({
446
+ }, [runId, autoResize, setHeight]);
447
+ const executeTask2 = useMemo(
448
+ () => createExecuteTask({
449
+ organizationId: context?.organizationId,
261
450
  apiToken: context?.apiToken,
262
451
  conversationId: context?.conversationId,
263
452
  apiBaseUrl: context?.apiBaseUrl
264
453
  }),
265
- [context?.apiToken, context?.conversationId, context?.apiBaseUrl]
454
+ [context?.organizationId, context?.apiToken, context?.conversationId, context?.apiBaseUrl]
455
+ );
456
+ const uploadFile2 = useMemo(
457
+ () => createUploadFile({
458
+ organizationId: context?.organizationId,
459
+ apiToken: context?.apiToken,
460
+ apiBaseUrl: context?.apiBaseUrl
461
+ }),
462
+ [context?.organizationId, context?.apiToken, context?.apiBaseUrl]
463
+ );
464
+ const getFileContent2 = useMemo(
465
+ () => createGetFileContent({
466
+ organizationId: context?.organizationId,
467
+ conversationId: context?.conversationId,
468
+ apiToken: context?.apiToken,
469
+ apiBaseUrl: context?.apiBaseUrl
470
+ }),
471
+ [context?.organizationId, context?.conversationId, context?.apiToken, context?.apiBaseUrl]
266
472
  );
267
473
  return {
268
474
  inputs,
@@ -275,20 +481,13 @@ function useBrixelArtifact(options = {}) {
275
481
  setHeight,
276
482
  log,
277
483
  isEmbedded: isEmbedded.current,
278
- executeTask: executeTask2
484
+ executeTask: executeTask2,
485
+ uploadFile: uploadFile2,
486
+ getFileContent: getFileContent2
279
487
  };
280
488
  }
281
489
 
282
490
  // src/devTools.ts
283
- var mockContext = {
284
- runId: "dev-run-001",
285
- stepId: "dev-step-001",
286
- userId: "dev-user-001",
287
- organizationId: "dev-org-001",
288
- theme: "light",
289
- locale: "en-US",
290
- conversationId: "dev-conversation-001"
291
- };
292
491
  function simulateBrixelInit(inputs, options = {}) {
293
492
  const { runId = "dev-run-001", renderMode = "interaction", context = {}, delay = 100 } = options;
294
493
  const message = {
@@ -296,7 +495,7 @@ function simulateBrixelInit(inputs, options = {}) {
296
495
  payload: {
297
496
  runId,
298
497
  inputs,
299
- context: { ...mockContext, ...context, runId },
498
+ context: { ...context, runId },
300
499
  renderMode
301
500
  }
302
501
  };
@@ -317,112 +516,14 @@ function listenToUITaskMessages(callback) {
317
516
  window.removeEventListener("message", handleMessage);
318
517
  };
319
518
  }
320
- function createMockBrixelHost(options) {
321
- const { onReady, onComplete, onCancel, onResize, onLog, onError } = options;
322
- let currentRunId = null;
323
- const handleMessage = (event) => {
324
- const message = event.data;
325
- if (!message || typeof message !== "object" || !message.type?.startsWith("BRIXEL_")) {
326
- return;
327
- }
328
- switch (message.type) {
329
- case "BRIXEL_READY":
330
- onReady?.(message.payload?.version);
331
- break;
332
- case "BRIXEL_COMPLETE":
333
- onComplete?.(message.payload?.output);
334
- break;
335
- case "BRIXEL_CANCEL":
336
- onCancel?.(message.payload?.reason);
337
- break;
338
- case "BRIXEL_RESIZE":
339
- onResize?.(message.payload?.height);
340
- break;
341
- case "BRIXEL_LOG":
342
- onLog?.(message.payload?.level, message.payload?.message, message.payload?.data);
343
- break;
344
- case "BRIXEL_ERROR":
345
- onError?.(message.payload?.error);
346
- break;
347
- }
348
- };
349
- window.addEventListener("message", handleMessage);
350
- return {
351
- init(inputs, runId = "mock-run-001", renderMode = "interaction") {
352
- currentRunId = runId;
353
- window.postMessage(
354
- {
355
- type: "BRIXEL_INIT",
356
- payload: {
357
- runId,
358
- inputs,
359
- context: { ...mockContext, runId },
360
- renderMode
361
- }
362
- },
363
- "*"
364
- );
365
- },
366
- updateInputs(inputs) {
367
- if (!currentRunId) {
368
- console.warn("[MockHost] Cannot update inputs - no active run");
369
- return;
370
- }
371
- window.postMessage(
372
- {
373
- type: "BRIXEL_UPDATE_INPUTS",
374
- payload: { runId: currentRunId, inputs }
375
- },
376
- "*"
377
- );
378
- },
379
- destroy() {
380
- if (currentRunId) {
381
- window.postMessage(
382
- {
383
- type: "BRIXEL_DESTROY",
384
- payload: { runId: currentRunId }
385
- },
386
- "*"
387
- );
388
- }
389
- window.removeEventListener("message", handleMessage);
390
- currentRunId = null;
391
- },
392
- updateTheme(theme) {
393
- if (!currentRunId) {
394
- console.warn("[MockHost] Cannot update theme - no active run");
395
- return;
396
- }
397
- window.postMessage(
398
- {
399
- type: "BRIXEL_UPDATE_THEME",
400
- payload: { runId: currentRunId, theme }
401
- },
402
- "*"
403
- );
404
- },
405
- updateLocale(locale) {
406
- if (!currentRunId) {
407
- console.warn("[MockHost] Cannot update locale - no active run");
408
- return;
409
- }
410
- window.postMessage(
411
- {
412
- type: "BRIXEL_UPDATE_LOCALE",
413
- payload: { runId: currentRunId, locale }
414
- },
415
- "*"
416
- );
417
- }
418
- };
419
- }
420
519
  export {
421
520
  createExecuteTask,
422
- createMockBrixelHost,
521
+ createGetFileContent,
522
+ createUploadFile,
423
523
  executeTask,
524
+ getFileContent,
424
525
  listenToUITaskMessages,
425
- mockContext,
426
526
  simulateBrixelInit,
527
+ uploadFile,
427
528
  useBrixelArtifact
428
529
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brixel_ai/artifact-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "SDK for building Brixel Artifacts - interactive React components that integrate with Brixel workflows",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",