@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/README.md CHANGED
@@ -78,9 +78,16 @@ interface UseBrixelTaskOptions {
78
78
  onInputsUpdate?: (inputs) => void; // Callback when inputs change
79
79
  onDestroy?: () => void; // Callback before cleanup
80
80
  debug?: boolean; // Enable debug logging
81
+ autoResize?: boolean; // Auto-send BRIXEL_RESIZE based on content height (default: false)
81
82
  }
82
83
  ```
83
84
 
85
+ ### Full Parent Space vs `setHeight`
86
+
87
+ If your SDK components must always fill the space allocated by the parent, keep `autoResize` disabled (default) and use CSS layout like `height: 100%` in your component root.
88
+
89
+ Use `setHeight` (or `autoResize: true`) only when the iframe height must be content-driven.
90
+
84
91
  ## Development Mode
85
92
 
86
93
  Test your UI Task locally without Brixel:
@@ -97,24 +104,6 @@ if (import.meta.env.DEV) {
97
104
  }
98
105
  ```
99
106
 
100
- ### Mock Host for Testing
101
-
102
- ```tsx
103
- import { createMockBrixelHost } from "@brixel_ai/artifact-sdk";
104
-
105
- const host = createMockBrixelHost({
106
- onComplete: (output) => console.debug("Completed:", output),
107
- onCancel: (reason) => console.debug("Cancelled:", reason),
108
- onResize: (height) => console.debug("Resize:", height),
109
- });
110
-
111
- // Send init
112
- host.init({ title: "Test" });
113
-
114
- // Later: cleanup
115
- host.destroy();
116
- ```
117
-
118
107
  ## Render Modes
119
108
 
120
109
  ### Display Mode
@@ -179,7 +168,7 @@ function MyUITask() {
179
168
 
180
169
  const handleExecuteTask = async () => {
181
170
  const result = await executeTask({
182
- taskUuid: "78c2482f-b47d-461c-9fd0-509476687be9",
171
+ taskId: "78c2482f-b47d-461c-9fd0-509476687be9",
183
172
  inputs: { name: "value" },
184
173
  });
185
174
 
@@ -196,10 +185,7 @@ function MyUITask() {
196
185
 
197
186
  ### Authentication
198
187
 
199
- The `executeTask` function supports two authentication methods (in priority order):
200
-
201
- 1. **API Token via postMessage** (RECOMMENDED): The parent interface passes the token via the INIT message
202
- 2. **Cookies fallback**: Uses `credentials: 'include'` if no token provided
188
+ The SDK uses Bearer token authentication. The parent interface should pass `apiToken` via the INIT message context.
203
189
 
204
190
  #### Passing Token from Parent
205
191
 
@@ -213,6 +199,7 @@ iframe.contentWindow.postMessage({
213
199
  runId: 'run-123',
214
200
  inputs: { /* ... */ },
215
201
  context: {
202
+ organizationId: 'org-123',
216
203
  apiToken: authToken,
217
204
  // ... other context fields
218
205
  }
@@ -227,7 +214,7 @@ const { executeTask, context } = useBrixelArtifact();
227
214
 
228
215
  // Token from context is automatically used
229
216
  await executeTask({
230
- taskUuid: "task-uuid",
217
+ taskId: "task-uuid",
231
218
  inputs: {}
232
219
  });
233
220
 
@@ -237,16 +224,90 @@ await executeTask({
237
224
 
238
225
  The SDK automatically detects your environment and uses the appropriate API:
239
226
 
240
- - **Development** (localhost): `http://localhost:8000/backoffice/ui-components`
241
- - **Production**: `https://api.brixel.ai/backoffice/ui-components`
227
+ - **Development** (localhost): `http://localhost:8000`
228
+ - **Production**: `https://platform.brixel.ai`
242
229
 
243
230
  When running on localhost, you'll see:
244
231
  ```
245
- [Brixel SDK] Using API URL: http://localhost:8000/backoffice/ui-components
232
+ [Brixel SDK] Using API URL: http://localhost:8000
246
233
  ```
247
234
 
248
235
  See [DEVELOPMENT.md](./DEVELOPMENT.md) for details on local development setup.
249
236
 
237
+ ## Uploading Files
238
+
239
+ The SDK provides an `uploadFile` function to upload files to Brixel using:
240
+
241
+ - Endpoint: `POST /v1/files/`
242
+ - Form fields:
243
+ - `file`
244
+ - `visibility` (always forced to `"user"` by the SDK)
245
+ - Header (optional): `X-Organization-Id`
246
+
247
+ ```tsx
248
+ import { useBrixelArtifact } from "@brixel_ai/artifact-sdk";
249
+
250
+ function MyUITask() {
251
+ const { uploadFile } = useBrixelArtifact();
252
+
253
+ const handleUpload = async (file: File) => {
254
+ const result = await uploadFile({
255
+ file,
256
+ });
257
+
258
+ if (!result.success) {
259
+ console.error("Upload error:", result.error);
260
+ return;
261
+ }
262
+
263
+ console.debug("Uploaded file:", result.data);
264
+ };
265
+
266
+ return <input type="file" onChange={(e) => e.target.files?.[0] && handleUpload(e.target.files[0])} />;
267
+ }
268
+ ```
269
+
270
+ ## Getting File Content
271
+
272
+ The SDK provides `getFileContent` to retrieve inline file content:
273
+
274
+ - Endpoint: `GET /v1/files/{brixel_file_id}/content`
275
+ - Headers (optional):
276
+ - `X-Organization-Id`
277
+ - `X-Conversation-Id`
278
+ - `If-None-Match`
279
+ - `If-Modified-Since`
280
+
281
+ ```tsx
282
+ import { useBrixelArtifact } from "@brixel_ai/artifact-sdk";
283
+
284
+ function MyUITask() {
285
+ const { getFileContent } = useBrixelArtifact();
286
+
287
+ const handlePreview = async (brixelFileId: string) => {
288
+ const result = await getFileContent({
289
+ brixelFileId,
290
+ });
291
+
292
+ if (!result.success) {
293
+ console.error("Get content error:", result.error);
294
+ return;
295
+ }
296
+
297
+ if (result.notModified) {
298
+ console.debug("File not modified");
299
+ return;
300
+ }
301
+
302
+ if (!result.data) return;
303
+ const url = URL.createObjectURL(result.data.blob);
304
+ console.debug("Preview URL:", url, "content-type:", result.data.contentType);
305
+ };
306
+
307
+ return <button onClick={() => handlePreview("your-brixel-file-id")}>Preview</button>;
308
+ }
309
+ ```
310
+
250
311
  ## PostMessage Protocol
251
312
 
252
313
  The SDK handles the following message types automatically:
package/dist/index.d.mts CHANGED
@@ -198,7 +198,18 @@ interface UseBrixelTaskResult<TInputs, TOutput> {
198
198
  /** Whether running inside Brixel iframe */
199
199
  isEmbedded: boolean;
200
200
  /** Execute another UI Task (bound to current context) */
201
- executeTask: <TTaskOutput = unknown>(params: Omit<ExecuteTaskParams, "conversationId" | "apiToken" | "apiBaseUrl">) => Promise<ExecuteTaskResponse<TTaskOutput>>;
201
+ executeTask: <TTaskOutput = unknown>(params: Omit<ExecuteTaskParams, "conversationId" | "apiToken" | "apiBaseUrl"> & {
202
+ organizationId?: string;
203
+ }) => Promise<ExecuteTaskResponse<TTaskOutput>>;
204
+ /** Upload a file to Brixel (visibility is always forced to "user") */
205
+ uploadFile: <TFileOutput = InternalFilePublicOut>(params: Omit<UploadFileParams, "organizationId" | "apiToken" | "apiBaseUrl"> & {
206
+ organizationId?: string;
207
+ }) => Promise<UploadFileResponse<TFileOutput>>;
208
+ /** Get inline content of a Brixel file by id */
209
+ getFileContent: <TContentOutput = FileContentData>(params: Omit<GetFileContentParams, "organizationId" | "conversationId" | "apiToken" | "apiBaseUrl"> & {
210
+ organizationId?: string;
211
+ conversationId?: string;
212
+ }) => Promise<GetFileContentResponse<TContentOutput>>;
202
213
  }
203
214
  interface UseBrixelTaskOptions {
204
215
  /** Target origin for postMessage (default: "*", should be restricted in production) */
@@ -209,18 +220,22 @@ interface UseBrixelTaskOptions {
209
220
  onDestroy?: () => void;
210
221
  /** Enable debug logging */
211
222
  debug?: boolean;
223
+ /** Auto-send BRIXEL_RESIZE based on document.body size changes */
224
+ autoResize?: boolean;
212
225
  }
213
226
  /**
214
227
  * Parameters for executing a UI Task via the API
215
228
  */
216
229
  interface ExecuteTaskParams {
217
- /** UUID of the task to execute */
218
- taskUuid: string;
230
+ /** ID of the organization owning the task */
231
+ organizationId: string;
232
+ /** ID of the task to execute */
233
+ taskId: string;
219
234
  /** Input values for the task */
220
235
  inputs: Record<string, unknown>;
221
236
  /** Optional conversation ID for x-conversation-id header */
222
237
  conversationId?: string;
223
- /** Optional API token (if not provided, uses credentials: 'include' as fallback) */
238
+ /** API token used as Bearer token for API requests */
224
239
  apiToken?: string;
225
240
  /** Optional custom API base URL (auto-detects dev/prod if not provided) */
226
241
  apiBaseUrl?: string;
@@ -237,137 +252,127 @@ interface ExecuteTaskResponse<TOutput = unknown> {
237
252
  details?: unknown;
238
253
  };
239
254
  }
240
-
241
255
  /**
242
- * Main hook for building Brixel UI Tasks
243
- *
244
- * @example
245
- * ```tsx
246
- * import { useBrixelArtifact } from "@brixel/artifact-sdk";
247
- *
248
- * interface Inputs {
249
- * title: string;
250
- * options: string[];
251
- * }
252
- *
253
- * interface Output {
254
- * selectedOption: string;
255
- * }
256
- *
257
- * function MyUITask() {
258
- * const { inputs, complete, cancel, context } = useBrixelArtifact<Inputs, Output>();
259
- *
260
- * if (!inputs) return <div>Loading...</div>;
261
- *
262
- * return (
263
- * <div>
264
- * <h1>{inputs.title}</h1>
265
- * {inputs.options.map(opt => (
266
- * <button key={opt} onClick={() => complete({ selectedOption: opt })}>
267
- * {opt}
268
- * </button>
269
- * ))}
270
- * <button onClick={() => cancel()}>Cancel</button>
271
- * </div>
272
- * );
273
- * }
274
- * ```
256
+ * Parameters for uploading a file to Brixel
275
257
  */
276
- declare function useBrixelArtifact<TInputs = unknown, TOutput = unknown>(options?: UseBrixelTaskOptions): UseBrixelTaskResult<TInputs, TOutput>;
277
-
258
+ interface UploadFileParams {
259
+ /** File to upload */
260
+ file: File;
261
+ /** Optional organization ID sent as X-Organization-Id header */
262
+ organizationId?: string;
263
+ /** API token used as Bearer token for API requests */
264
+ apiToken?: string;
265
+ /** Optional custom API base URL (auto-detects dev/prod if not provided) */
266
+ apiBaseUrl?: string;
267
+ }
278
268
  /**
279
- * Execute a UI Task via the Brixel API
280
- *
281
- * This function allows UI Tasks to programmatically execute other UI Tasks.
282
- *
283
- * **API URL auto-detection:**
284
- * - Development (localhost): http://localhost:8000/backoffice/ui-components
285
- * - Production: https://api.brixel.ai/backoffice/ui-components
286
- * - Can be overridden via `apiBaseUrl` parameter
287
- *
288
- * **Authentication strategy (in order of priority):**
289
- * 1. **API Token from context** (RECOMMENDED): Passed via postMessage from parent
290
- * - More secure and explicit
291
- * - Parent has full control over the token
292
- * 2. **Cookies fallback**: Uses credentials: 'include' if no token provided
293
- * - Works for same-domain scenarios (*.brixel.ai)
294
- *
295
- * @example
296
- * ```tsx
297
- * import { executeTask } from "@brixel/artifact-sdk";
298
- *
299
- * // Recommended: Use token from context (passed by parent via postMessage)
300
- * const result = await executeTask({
301
- * taskUuid: "task-123-456",
302
- * inputs: { name: "John", email: "john@example.com" },
303
- * apiToken: context?.apiToken, // Token passed by parent
304
- * });
305
- *
306
- * // Or let the hook bind it automatically
307
- * const { executeTask } = useBrixelArtifact();
308
- * const result = await executeTask({
309
- * taskUuid: "task-123-456",
310
- * inputs: { name: "John", email: "john@example.com" },
311
- * });
312
- *
313
- * if (result.success) {
314
- * console.debug("Task executed:", result.data);
315
- * } else {
316
- * console.error("Error:", result.error);
317
- * }
318
- * ```
319
- *
320
- * @param params - Parameters for executing the task
321
- * @returns Promise with the execution result
269
+ * Mime type returned by Brixel for internal files.
270
+ * Kept as `string` because backend enum values may evolve.
322
271
  */
323
- declare function executeTask<TOutput = unknown>(params: ExecuteTaskParams): Promise<ExecuteTaskResponse<TOutput>>;
272
+ type InternalFileMimeType = string;
324
273
  /**
325
- * Create an executeTask function bound to a specific context
326
- *
327
- * This is useful when you want to reuse the same auth context
328
- * for multiple task executions.
329
- *
330
- * @example
331
- * ```tsx
332
- * const { context } = useBrixelArtifact();
333
- * const boundExecuteTask = createExecuteTask({
334
- * apiToken: context?.apiToken,
335
- * conversationId: context?.conversationId
336
- * });
337
- *
338
- * // Now you can call it without passing auth each time
339
- * const result = await boundExecuteTask({
340
- * taskUuid: "task-123",
341
- * inputs: { foo: "bar" }
342
- * });
343
- * ```
344
- *
345
- * @example
346
- * ```tsx
347
- * // Or use the executeTask from the hook directly (recommended)
348
- * const { executeTask } = useBrixelArtifact();
349
- *
350
- * const result = await executeTask({
351
- * taskUuid: "task-123",
352
- * inputs: { foo: "bar" }
353
- * });
354
- * ```
274
+ * Public schema for internal file output.
275
+ * Mirrors backend `InternalFilePublicOut`.
276
+ */
277
+ interface InternalFilePublicOut {
278
+ brixel_file_id: string;
279
+ name: string;
280
+ mime_type: InternalFileMimeType;
281
+ size: number;
282
+ expires_at: string | null;
283
+ }
284
+ /**
285
+ * Response from the upload file API
286
+ */
287
+ interface UploadFileResponse<TOutput = InternalFilePublicOut> {
288
+ success: boolean;
289
+ data?: TOutput;
290
+ error?: {
291
+ code: string;
292
+ message: string;
293
+ details?: unknown;
294
+ };
295
+ }
296
+ /**
297
+ * Returned file content payload
298
+ */
299
+ interface FileContentData {
300
+ blob: Blob;
301
+ contentType: string | null;
302
+ contentLength?: number;
303
+ etag?: string | null;
304
+ lastModified?: string | null;
305
+ }
306
+ /**
307
+ * Parameters for getting file content from Brixel
355
308
  */
309
+ interface GetFileContentParams {
310
+ /** Brixel file id */
311
+ brixelFileId: string;
312
+ /** Optional organization ID sent as X-Organization-Id header */
313
+ organizationId?: string;
314
+ /** Optional conversation ID sent as X-Conversation-Id header */
315
+ conversationId?: string;
316
+ /** API token used as Bearer token for API requests */
317
+ apiToken?: string;
318
+ /** Optional custom API base URL (auto-detects dev/prod if not provided) */
319
+ apiBaseUrl?: string;
320
+ /** Optional conditional request header */
321
+ ifNoneMatch?: string;
322
+ /** Optional conditional request header */
323
+ ifModifiedSince?: string;
324
+ }
325
+ /**
326
+ * Response from the get file content API
327
+ */
328
+ interface GetFileContentResponse<TOutput = FileContentData> {
329
+ success: boolean;
330
+ notModified?: boolean;
331
+ data?: TOutput;
332
+ error?: {
333
+ code: string;
334
+ message: string;
335
+ details?: unknown;
336
+ };
337
+ }
338
+
339
+ declare function useBrixelArtifact<TInputs = unknown, TOutput = unknown>(options?: UseBrixelTaskOptions): UseBrixelTaskResult<TInputs, TOutput>;
340
+
341
+ declare function executeTask<TOutput = unknown>(params: ExecuteTaskParams): Promise<ExecuteTaskResponse<TOutput>>;
356
342
  declare function createExecuteTask(contextAuth?: {
343
+ organizationId?: string;
344
+ apiToken?: string;
345
+ conversationId?: string;
346
+ apiBaseUrl?: string;
347
+ }): <TOutput = unknown>(params: Omit<ExecuteTaskParams, "conversationId" | "apiToken" | "apiBaseUrl"> & {
348
+ organizationId?: string;
349
+ }) => Promise<ExecuteTaskResponse<TOutput>>;
350
+
351
+ declare function uploadFile<TOutput = InternalFilePublicOut>(params: UploadFileParams): Promise<UploadFileResponse<TOutput>>;
352
+ declare function createUploadFile(contextAuth?: {
353
+ organizationId?: string;
357
354
  apiToken?: string;
355
+ apiBaseUrl?: string;
356
+ }): <TOutput = InternalFilePublicOut>(params: Omit<UploadFileParams, "organizationId" | "apiToken" | "apiBaseUrl"> & {
357
+ organizationId?: string;
358
+ }) => Promise<UploadFileResponse<TOutput>>;
359
+
360
+ declare function getFileContent<TOutput = FileContentData>(params: GetFileContentParams): Promise<GetFileContentResponse<TOutput>>;
361
+ declare function createGetFileContent(contextAuth?: {
362
+ organizationId?: string;
358
363
  conversationId?: string;
364
+ apiToken?: string;
359
365
  apiBaseUrl?: string;
360
- }): <TOutput = unknown>(params: Omit<ExecuteTaskParams, "conversationId" | "apiToken" | "apiBaseUrl">) => Promise<ExecuteTaskResponse<TOutput>>;
366
+ }): <TOutput = FileContentData>(params: Omit<GetFileContentParams, "organizationId" | "conversationId" | "apiToken" | "apiBaseUrl"> & {
367
+ organizationId?: string;
368
+ conversationId?: string;
369
+ }) => Promise<GetFileContentResponse<TOutput>>;
361
370
 
362
371
  /**
363
372
  * Development tools for testing UI Tasks outside of Brixel
364
373
  *
365
374
  * These utilities help simulate the Brixel host environment during development
366
375
  */
367
- /**
368
- * Default mock context for development
369
- */
370
- declare const mockContext: BrixelContext;
371
376
  /**
372
377
  * Simulate the Brixel host sending an INIT message
373
378
  *
@@ -404,40 +409,5 @@ declare function simulateBrixelInit<TInputs = unknown>(inputs: TInputs, options?
404
409
  * ```
405
410
  */
406
411
  declare function listenToUITaskMessages(callback: (message: unknown) => void): () => void;
407
- /**
408
- * Create a mock Brixel host for development
409
- *
410
- * @example
411
- * ```tsx
412
- * const host = createMockBrixelHost({
413
- * onComplete: (output) => console.debug("Completed:", output),
414
- * onCancel: (reason) => console.debug("Cancelled:", reason),
415
- * });
416
- *
417
- * // Send init
418
- * host.init({ title: "Test" });
419
- *
420
- * // Cleanup
421
- * host.destroy();
422
- * ```
423
- */
424
- declare function createMockBrixelHost<TInputs = unknown, TOutput = unknown>(options: {
425
- onReady?: (version: string) => void;
426
- onComplete?: (output: TOutput) => void;
427
- onCancel?: (reason?: string) => void;
428
- onResize?: (height: number | "auto") => void;
429
- onLog?: (level: string, message: string, data?: unknown) => void;
430
- onError?: (error: {
431
- code: string;
432
- message: string;
433
- details?: unknown;
434
- }) => void;
435
- }): {
436
- init(inputs: TInputs, runId?: string, renderMode?: RenderMode): void;
437
- updateInputs(inputs: Partial<TInputs>): void;
438
- destroy(): void;
439
- updateTheme(theme: BrixelContext["theme"]): void;
440
- updateLocale(locale: string): void;
441
- };
442
412
 
443
- export { type ArtifactManifest, type BrixelContext, type CancelMessage, type CompleteMessage, type DestroyMessage, type ErrorMessage, type ExecuteTaskParams, type ExecuteTaskResponse, type HostToIframeMessage, type IframeToHostMessage, type InitMessage, type LogMessage, type ReadyMessage, type RenderMode, type ResizeMessage, type TaskStatus, type UpdateInputsMessage, type UpdateLocaleMessage, type UpdateThemeMessage, type UseBrixelTaskOptions, type UseBrixelTaskResult, createExecuteTask, createMockBrixelHost, executeTask, listenToUITaskMessages, mockContext, simulateBrixelInit, useBrixelArtifact };
413
+ export { type ArtifactManifest, type BrixelContext, type CancelMessage, type CompleteMessage, type DestroyMessage, type ErrorMessage, type ExecuteTaskParams, type ExecuteTaskResponse, type FileContentData, type GetFileContentParams, type GetFileContentResponse, type HostToIframeMessage, type IframeToHostMessage, type InitMessage, type InternalFileMimeType, type InternalFilePublicOut, type LogMessage, type ReadyMessage, type RenderMode, type ResizeMessage, type TaskStatus, type UpdateInputsMessage, type UpdateLocaleMessage, type UpdateThemeMessage, type UploadFileParams, type UploadFileResponse, type UseBrixelTaskOptions, type UseBrixelTaskResult, createExecuteTask, createGetFileContent, createUploadFile, executeTask, getFileContent, listenToUITaskMessages, simulateBrixelInit, uploadFile, useBrixelArtifact };