@palettelab/sdk 0.1.0 → 0.1.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 ADDED
@@ -0,0 +1,163 @@
1
+ # @palettelab/sdk
2
+
3
+ Frontend SDK for building Palette plugins and internal apps.
4
+
5
+ Use this package from plugin frontends created with `@palettelab/cli`. It provides typed platform context, authenticated API helpers, React hooks for common Palette resources, iframe sandbox bridge helpers, install configuration helpers, and test utilities.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @palettelab/sdk react react-dom
11
+ ```
12
+
13
+ `react` and `react-dom` are peer dependencies. Plugin bundles should externalize React and `@palettelab/sdk`; the Palette CLI handles this for standard templates.
14
+
15
+ ## Exports
16
+
17
+ ```ts
18
+ import {
19
+ PluginProvider,
20
+ usePlatform,
21
+ usePluginTasks,
22
+ usePluginDataRooms,
23
+ usePluginChat,
24
+ apiFetch,
25
+ apiUpload,
26
+ createSandboxBridge,
27
+ getInstallConfig,
28
+ updateInstallConfig,
29
+ } from "@palettelab/sdk"
30
+ ```
31
+
32
+ Subpath exports are also available:
33
+
34
+ ```ts
35
+ import { usePlatform } from "@palettelab/sdk/hooks"
36
+ import type { PluginManifest } from "@palettelab/sdk/types"
37
+ import { PluginProvider } from "@palettelab/sdk/components"
38
+ ```
39
+
40
+ ## Plugin Root
41
+
42
+ The platform passes plugin runtime context into your root component. Wrap your UI with `PluginProvider` so hooks can read it.
43
+
44
+ ```tsx
45
+ import { PluginProvider, usePlatform, type PluginComponentProps } from "@palettelab/sdk"
46
+
47
+ function App() {
48
+ const { user, organizationId, pluginId } = usePlatform()
49
+
50
+ return (
51
+ <main>
52
+ <h1>{pluginId}</h1>
53
+ <p>{user?.email}</p>
54
+ <p>Organization: {organizationId}</p>
55
+ </main>
56
+ )
57
+ }
58
+
59
+ export default function PluginRoot(props: PluginComponentProps) {
60
+ return (
61
+ <PluginProvider value={props}>
62
+ <App />
63
+ </PluginProvider>
64
+ )
65
+ }
66
+ ```
67
+
68
+ ## API Helpers
69
+
70
+ Use `apiFetch` for authenticated platform API calls. It includes credentials and performs the platform refresh flow for normal portal runtime.
71
+
72
+ ```ts
73
+ import { apiFetch } from "@palettelab/sdk"
74
+
75
+ const res = await apiFetch("/api/v1/tasks")
76
+ const tasks = await res.json()
77
+ ```
78
+
79
+ Use `apiUpload` for multipart uploads.
80
+
81
+ ```ts
82
+ import { apiUpload } from "@palettelab/sdk"
83
+
84
+ await apiUpload("/api/v1/resources/upload", file)
85
+ ```
86
+
87
+ ## Resource Hooks
88
+
89
+ The SDK includes hooks for common Palette resources:
90
+
91
+ - `usePluginTasks`
92
+ - `usePluginDataRooms`
93
+ - `usePluginChat`
94
+
95
+ ```tsx
96
+ import { usePluginTasks } from "@palettelab/sdk"
97
+
98
+ export function TaskList() {
99
+ const { tasks, loading, error, refetch } = usePluginTasks()
100
+
101
+ if (loading) return <p>Loading...</p>
102
+ if (error) return <p>{error}</p>
103
+
104
+ return (
105
+ <ul>
106
+ {tasks.map((task) => (
107
+ <li key={task.id}>{task.title}</li>
108
+ ))}
109
+ </ul>
110
+ )
111
+ }
112
+ ```
113
+
114
+ ## Install Configuration
115
+
116
+ Plugins can read and update their installation configuration through helper APIs.
117
+
118
+ ```ts
119
+ import { getInstallConfig, updateInstallConfig } from "@palettelab/sdk"
120
+
121
+ const config = await getInstallConfig()
122
+ await updateInstallConfig({ ...config, enabled: true })
123
+ ```
124
+
125
+ ## Sandbox Bridge
126
+
127
+ Appstore plugin frontends run inside an iframe sandbox by default. Use `createSandboxBridge` when you need to explicitly call host-proxied APIs from sandbox runtime code.
128
+
129
+ ```ts
130
+ import { createSandboxBridge, isSandboxRuntime } from "@palettelab/sdk"
131
+
132
+ if (isSandboxRuntime()) {
133
+ const bridge = createSandboxBridge()
134
+ const result = await bridge.apiFetch("/api/v1/tasks")
135
+ console.log(result)
136
+ }
137
+ ```
138
+
139
+ For standard React plugin templates, the runtime wiring is already handled by the platform and CLI.
140
+
141
+ ## Testing
142
+
143
+ Use the SDK test utilities to render plugin components with a mock platform context.
144
+
145
+ ```tsx
146
+ import { createMockPlatformContext, withPluginProvider } from "@palettelab/sdk"
147
+
148
+ const ctx = createMockPlatformContext({
149
+ pluginId: "hello-sdk",
150
+ organizationId: 1,
151
+ })
152
+
153
+ const Wrapped = withPluginProvider(MyPluginComponent, ctx)
154
+ ```
155
+
156
+ ## Package Contents
157
+
158
+ The npm package ships compiled CommonJS, ESM, and TypeScript declarations from `dist/`.
159
+
160
+ ## Related Packages
161
+
162
+ - `@palettelab/cli` provides the `pltt` command for scaffolding, local dev, validation, packaging, and publishing.
163
+ - `palette-sdk` is the Python backend SDK for FastAPI plugin routes, permissions, tools, DB helpers, RLS, and test helpers.
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import react__default from 'react';
3
- import { P as PlatformContext } from '../plugin-CQH23f5N.mjs';
3
+ import { P as PlatformContext } from '../plugin-DzSTKgkz.mjs';
4
4
 
5
5
  /**
6
6
  * Provider that wraps plugin components with the platform context.
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import react__default from 'react';
3
- import { P as PlatformContext } from '../plugin-CQH23f5N.js';
3
+ import { P as PlatformContext } from '../plugin-DzSTKgkz.js';
4
4
 
5
5
  /**
6
6
  * Provider that wraps plugin components with the platform context.
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { P as PlatformContext } from '../plugin-CQH23f5N.mjs';
2
+ import { P as PlatformContext } from '../plugin-DzSTKgkz.mjs';
3
3
  import { T as Task, i as TaskStats, g as TaskCreatePayload, l as TaskUpdatePayload, D as DataRoom, d as DataRoomFolder, c as DataRoomFile, b as ChatThread, a as ChatMessage } from '../data-room-BP0PjYoe.mjs';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { P as PlatformContext } from '../plugin-CQH23f5N.js';
2
+ import { P as PlatformContext } from '../plugin-DzSTKgkz.js';
3
3
  import { T as Task, i as TaskStats, g as TaskCreatePayload, l as TaskUpdatePayload, D as DataRoom, d as DataRoomFolder, c as DataRoomFile, b as ChatThread, a as ChatMessage } from '../data-room-BP0PjYoe.js';
4
4
 
5
5
  /**
package/dist/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
- export { A as Agent, a as AgentConfig, b as AppCategory, c as AuthContextValue, O as OrgSummary, P as PlatformContext, d as PluginAgentDefinition, e as PluginComponentProps, f as PluginManifest, g as PluginToolDefinition, U as User } from './plugin-CQH23f5N.mjs';
1
+ import { P as PlatformContext } from './plugin-DzSTKgkz.mjs';
2
+ export { A as Agent, a as AgentConfig, b as AppCategory, c as AuthContextValue, O as OrgSummary, d as PluginAgentDefinition, e as PluginComponentProps, f as PluginManifest, g as PluginToolDefinition, U as User } from './plugin-DzSTKgkz.mjs';
2
3
  export { C as ChatAttachment, a as ChatMessage, b as ChatThread, D as DataRoom, c as DataRoomFile, d as DataRoomFolder, e as DataRoomPermission, T as Task, f as TaskAgentSnippet, g as TaskCreatePayload, h as TaskPriority, i as TaskStats, j as TaskStatus, k as TaskType, l as TaskUpdatePayload } from './data-room-BP0PjYoe.mjs';
3
4
  export { AgentResource, ResourcesByGroup } from './types/index.mjs';
5
+ import { ReactElement } from 'react';
4
6
  export { PlatformCtx, usePlatform, usePluginChat, usePluginDataRooms, usePluginTasks } from './hooks/index.mjs';
5
7
  export { PluginProvider } from './components/index.mjs';
6
- import 'react';
7
8
  import 'react/jsx-runtime';
8
9
 
9
10
  /**
@@ -28,4 +29,28 @@ declare function apiFetch(path: string, init?: RequestInit): Promise<Response>;
28
29
  */
29
30
  declare function apiUpload(path: string, file: File, fieldName?: string, extraFields?: Record<string, string>): Promise<Response>;
30
31
 
31
- export { apiFetch, apiUpload, getBaseUrl, setBaseUrl };
32
+ declare class PaletteApiError extends Error {
33
+ status: number;
34
+ detail: unknown;
35
+ constructor(message: string, status?: number, detail?: unknown);
36
+ }
37
+ declare function errorFromResponse(response: Response): Promise<PaletteApiError>;
38
+ declare function isPaletteApiError(error: unknown): error is PaletteApiError;
39
+
40
+ type ToastType = "success" | "error" | "info";
41
+ type SandboxBridge = {
42
+ apiFetch: PlatformContext["apiFetch"];
43
+ navigate: (path: string) => void;
44
+ showToast: (message: string, type?: ToastType) => void;
45
+ };
46
+ declare function createSandboxBridge(platform: Pick<PlatformContext, "apiFetch" | "navigate" | "showToast">): SandboxBridge;
47
+ declare function isSandboxRuntime(): boolean;
48
+
49
+ type InstallConfig = Record<string, unknown>;
50
+ declare function getInstallConfig(pluginId: string): Promise<InstallConfig>;
51
+ declare function updateInstallConfig(pluginId: string, config: InstallConfig): Promise<InstallConfig>;
52
+
53
+ declare function createMockPlatformContext(overrides?: Partial<PlatformContext>): PlatformContext;
54
+ declare function withPluginProvider(element: ReactElement, platform?: Partial<PlatformContext>): ReactElement;
55
+
56
+ export { type InstallConfig, PaletteApiError, PlatformContext, type SandboxBridge, apiFetch, apiUpload, createMockPlatformContext, createSandboxBridge, errorFromResponse, getBaseUrl, getInstallConfig, isPaletteApiError, isSandboxRuntime, setBaseUrl, updateInstallConfig, withPluginProvider };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- export { A as Agent, a as AgentConfig, b as AppCategory, c as AuthContextValue, O as OrgSummary, P as PlatformContext, d as PluginAgentDefinition, e as PluginComponentProps, f as PluginManifest, g as PluginToolDefinition, U as User } from './plugin-CQH23f5N.js';
1
+ import { P as PlatformContext } from './plugin-DzSTKgkz.js';
2
+ export { A as Agent, a as AgentConfig, b as AppCategory, c as AuthContextValue, O as OrgSummary, d as PluginAgentDefinition, e as PluginComponentProps, f as PluginManifest, g as PluginToolDefinition, U as User } from './plugin-DzSTKgkz.js';
2
3
  export { C as ChatAttachment, a as ChatMessage, b as ChatThread, D as DataRoom, c as DataRoomFile, d as DataRoomFolder, e as DataRoomPermission, T as Task, f as TaskAgentSnippet, g as TaskCreatePayload, h as TaskPriority, i as TaskStats, j as TaskStatus, k as TaskType, l as TaskUpdatePayload } from './data-room-BP0PjYoe.js';
3
4
  export { AgentResource, ResourcesByGroup } from './types/index.js';
5
+ import { ReactElement } from 'react';
4
6
  export { PlatformCtx, usePlatform, usePluginChat, usePluginDataRooms, usePluginTasks } from './hooks/index.js';
5
7
  export { PluginProvider } from './components/index.js';
6
- import 'react';
7
8
  import 'react/jsx-runtime';
8
9
 
9
10
  /**
@@ -28,4 +29,28 @@ declare function apiFetch(path: string, init?: RequestInit): Promise<Response>;
28
29
  */
29
30
  declare function apiUpload(path: string, file: File, fieldName?: string, extraFields?: Record<string, string>): Promise<Response>;
30
31
 
31
- export { apiFetch, apiUpload, getBaseUrl, setBaseUrl };
32
+ declare class PaletteApiError extends Error {
33
+ status: number;
34
+ detail: unknown;
35
+ constructor(message: string, status?: number, detail?: unknown);
36
+ }
37
+ declare function errorFromResponse(response: Response): Promise<PaletteApiError>;
38
+ declare function isPaletteApiError(error: unknown): error is PaletteApiError;
39
+
40
+ type ToastType = "success" | "error" | "info";
41
+ type SandboxBridge = {
42
+ apiFetch: PlatformContext["apiFetch"];
43
+ navigate: (path: string) => void;
44
+ showToast: (message: string, type?: ToastType) => void;
45
+ };
46
+ declare function createSandboxBridge(platform: Pick<PlatformContext, "apiFetch" | "navigate" | "showToast">): SandboxBridge;
47
+ declare function isSandboxRuntime(): boolean;
48
+
49
+ type InstallConfig = Record<string, unknown>;
50
+ declare function getInstallConfig(pluginId: string): Promise<InstallConfig>;
51
+ declare function updateInstallConfig(pluginId: string, config: InstallConfig): Promise<InstallConfig>;
52
+
53
+ declare function createMockPlatformContext(overrides?: Partial<PlatformContext>): PlatformContext;
54
+ declare function withPluginProvider(element: ReactElement, platform?: Partial<PlatformContext>): ReactElement;
55
+
56
+ export { type InstallConfig, PaletteApiError, PlatformContext, type SandboxBridge, apiFetch, apiUpload, createMockPlatformContext, createSandboxBridge, errorFromResponse, getBaseUrl, getInstallConfig, isPaletteApiError, isSandboxRuntime, setBaseUrl, updateInstallConfig, withPluginProvider };
package/dist/index.js CHANGED
@@ -20,16 +20,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
+ PaletteApiError: () => PaletteApiError,
23
24
  PlatformCtx: () => PlatformCtx,
24
25
  PluginProvider: () => PluginProvider,
25
26
  apiFetch: () => apiFetch,
26
27
  apiUpload: () => apiUpload,
28
+ createMockPlatformContext: () => createMockPlatformContext,
29
+ createSandboxBridge: () => createSandboxBridge,
30
+ errorFromResponse: () => errorFromResponse,
27
31
  getBaseUrl: () => getBaseUrl,
32
+ getInstallConfig: () => getInstallConfig,
33
+ isPaletteApiError: () => isPaletteApiError,
34
+ isSandboxRuntime: () => isSandboxRuntime,
28
35
  setBaseUrl: () => setBaseUrl,
36
+ updateInstallConfig: () => updateInstallConfig,
29
37
  usePlatform: () => usePlatform,
30
38
  usePluginChat: () => usePluginChat,
31
39
  usePluginDataRooms: () => usePluginDataRooms,
32
- usePluginTasks: () => usePluginTasks
40
+ usePluginTasks: () => usePluginTasks,
41
+ withPluginProvider: () => withPluginProvider
33
42
  });
34
43
  module.exports = __toCommonJS(src_exports);
35
44
 
@@ -92,6 +101,53 @@ async function apiUpload(path, file, fieldName = "file", extraFields) {
92
101
  return res;
93
102
  }
94
103
 
104
+ // src/errors.ts
105
+ var PaletteApiError = class extends Error {
106
+ constructor(message, status = 0, detail) {
107
+ super(message);
108
+ this.name = "PaletteApiError";
109
+ this.status = status;
110
+ this.detail = detail;
111
+ }
112
+ };
113
+ async function errorFromResponse(response) {
114
+ const body = await response.json().catch(() => null);
115
+ const message = body && typeof body === "object" && "detail" in body ? String(body.detail) : response.statusText;
116
+ return new PaletteApiError(message, response.status, body);
117
+ }
118
+ function isPaletteApiError(error) {
119
+ return error instanceof PaletteApiError;
120
+ }
121
+
122
+ // src/sandbox-bridge.ts
123
+ function createSandboxBridge(platform) {
124
+ return {
125
+ apiFetch: platform.apiFetch,
126
+ navigate: platform.navigate,
127
+ showToast: platform.showToast
128
+ };
129
+ }
130
+ function isSandboxRuntime() {
131
+ if (typeof window === "undefined") return false;
132
+ return Boolean(window.__palettePluginHost);
133
+ }
134
+
135
+ // src/install-config.ts
136
+ async function getInstallConfig(pluginId) {
137
+ const res = await apiFetch(`/api/v1/app-installs/${encodeURIComponent(pluginId)}/config`);
138
+ return res.json();
139
+ }
140
+ async function updateInstallConfig(pluginId, config) {
141
+ const res = await apiFetch(`/api/v1/app-installs/${encodeURIComponent(pluginId)}/config`, {
142
+ method: "PATCH",
143
+ body: JSON.stringify({ values: config })
144
+ });
145
+ return res.json();
146
+ }
147
+
148
+ // src/test-utils.tsx
149
+ var import_react2 = require("react");
150
+
95
151
  // src/hooks/use-platform.ts
96
152
  var import_react = require("react");
97
153
  var PlatformCtx = (0, import_react.createContext)(null);
@@ -105,14 +161,65 @@ function usePlatform() {
105
161
  return ctx;
106
162
  }
107
163
 
164
+ // src/components/plugin-provider.tsx
165
+ var import_jsx_runtime = require("react/jsx-runtime");
166
+ function PluginProvider({
167
+ value,
168
+ children
169
+ }) {
170
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformCtx.Provider, { value, children });
171
+ }
172
+
173
+ // src/test-utils.tsx
174
+ var mockUser = {
175
+ id: "test-user",
176
+ email: "test@example.com",
177
+ name: "Test User",
178
+ is_active: true,
179
+ created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
180
+ onboarding_completed: true,
181
+ company_name: null,
182
+ company_type: null,
183
+ org_theme: null,
184
+ org_logo: null,
185
+ org_enabled_menu_items: null,
186
+ org_menu_labels: null,
187
+ org_enabled_apps: null,
188
+ org_app_store_enabled: true,
189
+ org_enabled_agent_ids: null,
190
+ organization_id: 1,
191
+ org_role: "owner"
192
+ };
193
+ function createMockPlatformContext(overrides = {}) {
194
+ return {
195
+ user: mockUser,
196
+ organizationId: 1,
197
+ orgRole: "owner",
198
+ orgs: [],
199
+ agents: [],
200
+ apiFetch: async () => new Response(JSON.stringify({}), { status: 200 }),
201
+ navigate: () => {
202
+ },
203
+ showToast: () => {
204
+ },
205
+ ...overrides
206
+ };
207
+ }
208
+ function withPluginProvider(element, platform = {}) {
209
+ return (0, import_react2.createElement)(
210
+ PluginProvider,
211
+ { value: createMockPlatformContext(platform), children: element }
212
+ );
213
+ }
214
+
108
215
  // src/hooks/use-plugin-tasks.ts
109
- var import_react2 = require("react");
216
+ var import_react3 = require("react");
110
217
  function usePluginTasks(agentId) {
111
218
  const { apiFetch: apiFetch2 } = usePlatform();
112
- const [tasks, setTasks] = (0, import_react2.useState)([]);
113
- const [stats, setStats] = (0, import_react2.useState)(null);
114
- const [loading, setLoading] = (0, import_react2.useState)(true);
115
- const fetchTasks = (0, import_react2.useCallback)(async () => {
219
+ const [tasks, setTasks] = (0, import_react3.useState)([]);
220
+ const [stats, setStats] = (0, import_react3.useState)(null);
221
+ const [loading, setLoading] = (0, import_react3.useState)(true);
222
+ const fetchTasks = (0, import_react3.useCallback)(async () => {
116
223
  try {
117
224
  const params = new URLSearchParams();
118
225
  if (agentId) params.set("agent_id", String(agentId));
@@ -121,18 +228,18 @@ function usePluginTasks(agentId) {
121
228
  } catch {
122
229
  }
123
230
  }, [apiFetch2, agentId]);
124
- const fetchStats = (0, import_react2.useCallback)(async () => {
231
+ const fetchStats = (0, import_react3.useCallback)(async () => {
125
232
  try {
126
233
  const res = await apiFetch2("/api/v1/tasks/stats");
127
234
  setStats(await res.json());
128
235
  } catch {
129
236
  }
130
237
  }, [apiFetch2]);
131
- (0, import_react2.useEffect)(() => {
238
+ (0, import_react3.useEffect)(() => {
132
239
  setLoading(true);
133
240
  Promise.all([fetchTasks(), fetchStats()]).finally(() => setLoading(false));
134
241
  }, [fetchTasks, fetchStats]);
135
- const createTask = (0, import_react2.useCallback)(async (payload) => {
242
+ const createTask = (0, import_react3.useCallback)(async (payload) => {
136
243
  const res = await apiFetch2("/api/v1/tasks", {
137
244
  method: "POST",
138
245
  body: JSON.stringify(payload)
@@ -141,7 +248,7 @@ function usePluginTasks(agentId) {
141
248
  await Promise.all([fetchTasks(), fetchStats()]);
142
249
  return task;
143
250
  }, [apiFetch2, fetchTasks, fetchStats]);
144
- const updateTask = (0, import_react2.useCallback)(async (taskId, payload) => {
251
+ const updateTask = (0, import_react3.useCallback)(async (taskId, payload) => {
145
252
  const res = await apiFetch2(`/api/v1/tasks/${taskId}`, {
146
253
  method: "PATCH",
147
254
  body: JSON.stringify(payload)
@@ -150,7 +257,7 @@ function usePluginTasks(agentId) {
150
257
  await Promise.all([fetchTasks(), fetchStats()]);
151
258
  return task;
152
259
  }, [apiFetch2, fetchTasks, fetchStats]);
153
- const deleteTask = (0, import_react2.useCallback)(async (taskId) => {
260
+ const deleteTask = (0, import_react3.useCallback)(async (taskId) => {
154
261
  await apiFetch2(`/api/v1/tasks/${taskId}`, { method: "DELETE" });
155
262
  await Promise.all([fetchTasks(), fetchStats()]);
156
263
  }, [apiFetch2, fetchTasks, fetchStats]);
@@ -158,23 +265,23 @@ function usePluginTasks(agentId) {
158
265
  }
159
266
 
160
267
  // src/hooks/use-plugin-data-rooms.ts
161
- var import_react3 = require("react");
268
+ var import_react4 = require("react");
162
269
  function usePluginDataRooms() {
163
270
  const { apiFetch: apiFetch2 } = usePlatform();
164
- const [rooms, setRooms] = (0, import_react3.useState)([]);
165
- const [loading, setLoading] = (0, import_react3.useState)(true);
166
- const fetchRooms = (0, import_react3.useCallback)(async () => {
271
+ const [rooms, setRooms] = (0, import_react4.useState)([]);
272
+ const [loading, setLoading] = (0, import_react4.useState)(true);
273
+ const fetchRooms = (0, import_react4.useCallback)(async () => {
167
274
  try {
168
275
  const res = await apiFetch2("/api/v1/data-rooms");
169
276
  setRooms(await res.json());
170
277
  } catch {
171
278
  }
172
279
  }, [apiFetch2]);
173
- (0, import_react3.useEffect)(() => {
280
+ (0, import_react4.useEffect)(() => {
174
281
  setLoading(true);
175
282
  fetchRooms().finally(() => setLoading(false));
176
283
  }, [fetchRooms]);
177
- const fetchFolder = (0, import_react3.useCallback)(async (roomId, folderId) => {
284
+ const fetchFolder = (0, import_react4.useCallback)(async (roomId, folderId) => {
178
285
  const path = folderId ? `/api/v1/data-rooms/${roomId}/folders/${folderId}` : `/api/v1/data-rooms/${roomId}`;
179
286
  const res = await apiFetch2(path);
180
287
  return res.json();
@@ -183,19 +290,19 @@ function usePluginDataRooms() {
183
290
  }
184
291
 
185
292
  // src/hooks/use-plugin-chat.ts
186
- var import_react4 = require("react");
293
+ var import_react5 = require("react");
187
294
  function usePluginChat(agentId) {
188
295
  const { apiFetch: apiFetch2 } = usePlatform();
189
- const [threads, setThreads] = (0, import_react4.useState)([]);
190
- const [messages, setMessages] = (0, import_react4.useState)([]);
191
- const [streaming, setStreaming] = (0, import_react4.useState)(false);
192
- const [activeThreadId, setActiveThreadId] = (0, import_react4.useState)(null);
193
- const abortRef = (0, import_react4.useRef)(null);
194
- const fetchThreads = (0, import_react4.useCallback)(async () => {
296
+ const [threads, setThreads] = (0, import_react5.useState)([]);
297
+ const [messages, setMessages] = (0, import_react5.useState)([]);
298
+ const [streaming, setStreaming] = (0, import_react5.useState)(false);
299
+ const [activeThreadId, setActiveThreadId] = (0, import_react5.useState)(null);
300
+ const abortRef = (0, import_react5.useRef)(null);
301
+ const fetchThreads = (0, import_react5.useCallback)(async () => {
195
302
  const res = await apiFetch2(`/api/v1/chat/${agentId}/threads`);
196
303
  setThreads(await res.json());
197
304
  }, [apiFetch2, agentId]);
198
- const createThread = (0, import_react4.useCallback)(async () => {
305
+ const createThread = (0, import_react5.useCallback)(async () => {
199
306
  const res = await apiFetch2(`/api/v1/chat/${agentId}/threads`, { method: "POST" });
200
307
  const thread = await res.json();
201
308
  setActiveThreadId(thread.id);
@@ -203,13 +310,13 @@ function usePluginChat(agentId) {
203
310
  await fetchThreads();
204
311
  return thread;
205
312
  }, [apiFetch2, agentId, fetchThreads]);
206
- const fetchMessages = (0, import_react4.useCallback)(async (threadId) => {
313
+ const fetchMessages = (0, import_react5.useCallback)(async (threadId) => {
207
314
  const res = await apiFetch2(`/api/v1/chat/threads/${threadId}/messages`);
208
315
  const msgs = await res.json();
209
316
  setMessages(msgs);
210
317
  setActiveThreadId(threadId);
211
318
  }, [apiFetch2]);
212
- const sendMessage = (0, import_react4.useCallback)(async (threadId, content) => {
319
+ const sendMessage = (0, import_react5.useCallback)(async (threadId, content) => {
213
320
  setStreaming(true);
214
321
  abortRef.current = new AbortController();
215
322
  const userMsg = {
@@ -277,7 +384,7 @@ function usePluginChat(agentId) {
277
384
  abortRef.current = null;
278
385
  }
279
386
  }, []);
280
- const stopStreaming = (0, import_react4.useCallback)(() => {
387
+ const stopStreaming = (0, import_react5.useCallback)(() => {
281
388
  abortRef.current?.abort();
282
389
  }, []);
283
390
  return {
@@ -292,25 +399,25 @@ function usePluginChat(agentId) {
292
399
  stopStreaming
293
400
  };
294
401
  }
295
-
296
- // src/components/plugin-provider.tsx
297
- var import_jsx_runtime = require("react/jsx-runtime");
298
- function PluginProvider({
299
- value,
300
- children
301
- }) {
302
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformCtx.Provider, { value, children });
303
- }
304
402
  // Annotate the CommonJS export names for ESM import in node:
305
403
  0 && (module.exports = {
404
+ PaletteApiError,
306
405
  PlatformCtx,
307
406
  PluginProvider,
308
407
  apiFetch,
309
408
  apiUpload,
409
+ createMockPlatformContext,
410
+ createSandboxBridge,
411
+ errorFromResponse,
310
412
  getBaseUrl,
413
+ getInstallConfig,
414
+ isPaletteApiError,
415
+ isSandboxRuntime,
311
416
  setBaseUrl,
417
+ updateInstallConfig,
312
418
  usePlatform,
313
419
  usePluginChat,
314
420
  usePluginDataRooms,
315
- usePluginTasks
421
+ usePluginTasks,
422
+ withPluginProvider
316
423
  });
package/dist/index.mjs CHANGED
@@ -57,6 +57,53 @@ async function apiUpload(path, file, fieldName = "file", extraFields) {
57
57
  return res;
58
58
  }
59
59
 
60
+ // src/errors.ts
61
+ var PaletteApiError = class extends Error {
62
+ constructor(message, status = 0, detail) {
63
+ super(message);
64
+ this.name = "PaletteApiError";
65
+ this.status = status;
66
+ this.detail = detail;
67
+ }
68
+ };
69
+ async function errorFromResponse(response) {
70
+ const body = await response.json().catch(() => null);
71
+ const message = body && typeof body === "object" && "detail" in body ? String(body.detail) : response.statusText;
72
+ return new PaletteApiError(message, response.status, body);
73
+ }
74
+ function isPaletteApiError(error) {
75
+ return error instanceof PaletteApiError;
76
+ }
77
+
78
+ // src/sandbox-bridge.ts
79
+ function createSandboxBridge(platform) {
80
+ return {
81
+ apiFetch: platform.apiFetch,
82
+ navigate: platform.navigate,
83
+ showToast: platform.showToast
84
+ };
85
+ }
86
+ function isSandboxRuntime() {
87
+ if (typeof window === "undefined") return false;
88
+ return Boolean(window.__palettePluginHost);
89
+ }
90
+
91
+ // src/install-config.ts
92
+ async function getInstallConfig(pluginId) {
93
+ const res = await apiFetch(`/api/v1/app-installs/${encodeURIComponent(pluginId)}/config`);
94
+ return res.json();
95
+ }
96
+ async function updateInstallConfig(pluginId, config) {
97
+ const res = await apiFetch(`/api/v1/app-installs/${encodeURIComponent(pluginId)}/config`, {
98
+ method: "PATCH",
99
+ body: JSON.stringify({ values: config })
100
+ });
101
+ return res.json();
102
+ }
103
+
104
+ // src/test-utils.tsx
105
+ import { createElement } from "react";
106
+
60
107
  // src/hooks/use-platform.ts
61
108
  import { createContext, useContext } from "react";
62
109
  var PlatformCtx = createContext(null);
@@ -70,6 +117,57 @@ function usePlatform() {
70
117
  return ctx;
71
118
  }
72
119
 
120
+ // src/components/plugin-provider.tsx
121
+ import { jsx } from "react/jsx-runtime";
122
+ function PluginProvider({
123
+ value,
124
+ children
125
+ }) {
126
+ return /* @__PURE__ */ jsx(PlatformCtx.Provider, { value, children });
127
+ }
128
+
129
+ // src/test-utils.tsx
130
+ var mockUser = {
131
+ id: "test-user",
132
+ email: "test@example.com",
133
+ name: "Test User",
134
+ is_active: true,
135
+ created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
136
+ onboarding_completed: true,
137
+ company_name: null,
138
+ company_type: null,
139
+ org_theme: null,
140
+ org_logo: null,
141
+ org_enabled_menu_items: null,
142
+ org_menu_labels: null,
143
+ org_enabled_apps: null,
144
+ org_app_store_enabled: true,
145
+ org_enabled_agent_ids: null,
146
+ organization_id: 1,
147
+ org_role: "owner"
148
+ };
149
+ function createMockPlatformContext(overrides = {}) {
150
+ return {
151
+ user: mockUser,
152
+ organizationId: 1,
153
+ orgRole: "owner",
154
+ orgs: [],
155
+ agents: [],
156
+ apiFetch: async () => new Response(JSON.stringify({}), { status: 200 }),
157
+ navigate: () => {
158
+ },
159
+ showToast: () => {
160
+ },
161
+ ...overrides
162
+ };
163
+ }
164
+ function withPluginProvider(element, platform = {}) {
165
+ return createElement(
166
+ PluginProvider,
167
+ { value: createMockPlatformContext(platform), children: element }
168
+ );
169
+ }
170
+
73
171
  // src/hooks/use-plugin-tasks.ts
74
172
  import { useCallback, useEffect, useState } from "react";
75
173
  function usePluginTasks(agentId) {
@@ -257,24 +355,24 @@ function usePluginChat(agentId) {
257
355
  stopStreaming
258
356
  };
259
357
  }
260
-
261
- // src/components/plugin-provider.tsx
262
- import { jsx } from "react/jsx-runtime";
263
- function PluginProvider({
264
- value,
265
- children
266
- }) {
267
- return /* @__PURE__ */ jsx(PlatformCtx.Provider, { value, children });
268
- }
269
358
  export {
359
+ PaletteApiError,
270
360
  PlatformCtx,
271
361
  PluginProvider,
272
362
  apiFetch,
273
363
  apiUpload,
364
+ createMockPlatformContext,
365
+ createSandboxBridge,
366
+ errorFromResponse,
274
367
  getBaseUrl,
368
+ getInstallConfig,
369
+ isPaletteApiError,
370
+ isSandboxRuntime,
275
371
  setBaseUrl,
372
+ updateInstallConfig,
276
373
  usePlatform,
277
374
  usePluginChat,
278
375
  usePluginDataRooms,
279
- usePluginTasks
376
+ usePluginTasks,
377
+ withPluginProvider
280
378
  };
@@ -71,6 +71,7 @@ interface AgentConfig {
71
71
  type AppCategory = "All" | "Productivity" | "Design" | "Marketing" | "Analytics";
72
72
  /** Plugin manifest — corresponds to palette-plugin.json */
73
73
  interface PluginManifest {
74
+ manifest_version: "1";
74
75
  id: string;
75
76
  name: string;
76
77
  version: string;
@@ -83,8 +84,26 @@ interface PluginManifest {
83
84
  bg: string;
84
85
  text: string;
85
86
  };
87
+ sdk?: {
88
+ frontend?: string;
89
+ backend?: string;
90
+ };
91
+ platform?: {
92
+ min_version?: string;
93
+ max_version?: string;
94
+ };
95
+ capabilities?: {
96
+ frontend?: boolean;
97
+ backend?: boolean;
98
+ database?: boolean;
99
+ webhooks?: boolean;
100
+ scheduled_jobs?: boolean;
101
+ file_uploads?: boolean;
102
+ external_network?: string[];
103
+ };
86
104
  frontend?: {
87
105
  entry: string;
106
+ sandbox?: boolean;
88
107
  };
89
108
  backend?: {
90
109
  entry: string;
@@ -93,6 +112,10 @@ interface PluginManifest {
93
112
  agents?: PluginAgentDefinition[];
94
113
  tools?: PluginToolDefinition[];
95
114
  permissions?: string[];
115
+ public_routes?: string[];
116
+ rate_limit?: {
117
+ per_minute?: number;
118
+ };
96
119
  rating?: number;
97
120
  reviews?: number;
98
121
  featured?: boolean;
@@ -71,6 +71,7 @@ interface AgentConfig {
71
71
  type AppCategory = "All" | "Productivity" | "Design" | "Marketing" | "Analytics";
72
72
  /** Plugin manifest — corresponds to palette-plugin.json */
73
73
  interface PluginManifest {
74
+ manifest_version: "1";
74
75
  id: string;
75
76
  name: string;
76
77
  version: string;
@@ -83,8 +84,26 @@ interface PluginManifest {
83
84
  bg: string;
84
85
  text: string;
85
86
  };
87
+ sdk?: {
88
+ frontend?: string;
89
+ backend?: string;
90
+ };
91
+ platform?: {
92
+ min_version?: string;
93
+ max_version?: string;
94
+ };
95
+ capabilities?: {
96
+ frontend?: boolean;
97
+ backend?: boolean;
98
+ database?: boolean;
99
+ webhooks?: boolean;
100
+ scheduled_jobs?: boolean;
101
+ file_uploads?: boolean;
102
+ external_network?: string[];
103
+ };
86
104
  frontend?: {
87
105
  entry: string;
106
+ sandbox?: boolean;
88
107
  };
89
108
  backend?: {
90
109
  entry: string;
@@ -93,6 +112,10 @@ interface PluginManifest {
93
112
  agents?: PluginAgentDefinition[];
94
113
  tools?: PluginToolDefinition[];
95
114
  permissions?: string[];
115
+ public_routes?: string[];
116
+ rate_limit?: {
117
+ per_minute?: number;
118
+ };
96
119
  rating?: number;
97
120
  reviews?: number;
98
121
  featured?: boolean;
@@ -1,4 +1,4 @@
1
- export { A as Agent, a as AgentConfig, b as AppCategory, c as AuthContextValue, O as OrgSummary, P as PlatformContext, d as PluginAgentDefinition, e as PluginComponentProps, f as PluginManifest, g as PluginToolDefinition, U as User } from '../plugin-CQH23f5N.mjs';
1
+ export { A as Agent, a as AgentConfig, b as AppCategory, c as AuthContextValue, O as OrgSummary, P as PlatformContext, d as PluginAgentDefinition, e as PluginComponentProps, f as PluginManifest, g as PluginToolDefinition, U as User } from '../plugin-DzSTKgkz.mjs';
2
2
  export { C as ChatAttachment, a as ChatMessage, b as ChatThread, D as DataRoom, c as DataRoomFile, d as DataRoomFolder, e as DataRoomPermission, T as Task, f as TaskAgentSnippet, g as TaskCreatePayload, h as TaskPriority, i as TaskStats, j as TaskStatus, k as TaskType, l as TaskUpdatePayload } from '../data-room-BP0PjYoe.mjs';
3
3
 
4
4
  interface AgentResource {
@@ -1,4 +1,4 @@
1
- export { A as Agent, a as AgentConfig, b as AppCategory, c as AuthContextValue, O as OrgSummary, P as PlatformContext, d as PluginAgentDefinition, e as PluginComponentProps, f as PluginManifest, g as PluginToolDefinition, U as User } from '../plugin-CQH23f5N.js';
1
+ export { A as Agent, a as AgentConfig, b as AppCategory, c as AuthContextValue, O as OrgSummary, P as PlatformContext, d as PluginAgentDefinition, e as PluginComponentProps, f as PluginManifest, g as PluginToolDefinition, U as User } from '../plugin-DzSTKgkz.js';
2
2
  export { C as ChatAttachment, a as ChatMessage, b as ChatThread, D as DataRoom, c as DataRoomFile, d as DataRoomFolder, e as DataRoomPermission, T as Task, f as TaskAgentSnippet, g as TaskCreatePayload, h as TaskPriority, i as TaskStats, j as TaskStatus, k as TaskType, l as TaskUpdatePayload } from '../data-room-BP0PjYoe.js';
3
3
 
4
4
  interface AgentResource {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@palettelab/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Palette Platform SDK for building plugins and apps",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,16 +27,16 @@
27
27
  }
28
28
  },
29
29
  "files": [
30
- "dist"
30
+ "dist",
31
+ "README.md"
31
32
  ],
32
33
  "publishConfig": {
33
- "registry": "https://registry.npmjs.org/",
34
- "access": "public"
34
+ "registry": "https://npm.pkg.github.com"
35
35
  },
36
36
  "license": "MIT",
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/palette-lab/virtual-organisation.git",
39
+ "url": "git+https://github.com/palette-lab/virtual-organisation.git",
40
40
  "directory": "sdk/frontend"
41
41
  },
42
42
  "scripts": {