@oxy-hq/sdk 0.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -435
- package/dist/index.cjs +1585 -997
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +464 -870
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +464 -870
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1571 -967
- package/dist/index.mjs.map +1 -1
- package/package.json +73 -78
- package/dist/postMessage-Cb5PCtcE.cjs +0 -233
- package/dist/postMessage-Cb5PCtcE.cjs.map +0 -1
- package/dist/postMessage-Gnhr_wnw.mjs +0 -207
- package/dist/postMessage-Gnhr_wnw.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,474 +1,103 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @oxy-hq/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React SDK for building **customer-app bundles** on the [Oxy](https://oxy.tech)
|
|
4
|
+
platform. A bundle is a normal Vite + React app that reads from its linked oxy
|
|
5
|
+
project — raw SQL, the semantic layer, agents, and procedures — through a
|
|
6
|
+
small set of hooks, plus a couple of drop-in components.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
> **v2 is a complete rewrite.** The v1 stack (`OxyClient` / `OxySDK`, the
|
|
9
|
+
> Parquet/DuckDB-WASM reader, postMessage auth) is gone. Bundles now talk to
|
|
10
|
+
> `/api/projects/:id/*` exclusively. See `CHANGELOG.md`.
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
- 📊 **Parquet Support** - Read and query Parquet files using DuckDB-WASM
|
|
9
|
-
- 🔒 **Type-Safe** - Full TypeScript support with comprehensive type definitions
|
|
10
|
-
- 🌐 **Universal** - Works in both Node.js and browser environments
|
|
11
|
-
- ⚡ **Fast** - Optimized for performance with efficient data handling
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install @oxy/sdk
|
|
17
|
-
# or
|
|
18
|
-
yarn add @oxy/sdk
|
|
19
|
-
# or
|
|
20
|
-
pnpm add @oxy/sdk
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
For Parquet file support, also install DuckDB-WASM:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npm install @duckdb/duckdb-wasm
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Quick Start
|
|
30
|
-
|
|
31
|
-
### Basic Usage
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
import { OxySDK } from "@oxy/sdk";
|
|
35
|
-
|
|
36
|
-
// Create SDK instance
|
|
37
|
-
const sdk = new OxySDK({
|
|
38
|
-
apiKey: "your-api-key",
|
|
39
|
-
projectId: "your-project-id",
|
|
40
|
-
baseUrl: "https://api.oxy.tech",
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Load parquet files and query them
|
|
44
|
-
await sdk.loadFile("data/sales.parquet", "sales");
|
|
45
|
-
await sdk.loadFile("data/customers.parquet", "customers");
|
|
46
|
-
|
|
47
|
-
// Query with SQL - supports joins across multiple tables
|
|
48
|
-
const result = await sdk.query(`
|
|
49
|
-
SELECT s.product, s.amount, c.name as customer_name
|
|
50
|
-
FROM sales s
|
|
51
|
-
JOIN customers c ON s.customer_id = c.id
|
|
52
|
-
WHERE s.amount > 1000
|
|
53
|
-
ORDER BY s.amount DESC
|
|
54
|
-
`);
|
|
55
|
-
|
|
56
|
-
console.log(result.rows);
|
|
57
|
-
await sdk.close();
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Load App Data Automatically
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
import { OxySDK, createConfig } from "@oxy/sdk";
|
|
64
|
-
|
|
65
|
-
const sdk = new OxySDK(createConfig());
|
|
66
|
-
|
|
67
|
-
// Loads all data from the app and registers tables
|
|
68
|
-
await sdk.loadAppData("dashboard.app.yml");
|
|
69
|
-
|
|
70
|
-
// Query the loaded tables
|
|
71
|
-
const result = await sdk.query("SELECT * FROM my_table LIMIT 10");
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Iframe Usage (PostMessage Authentication)
|
|
75
|
-
|
|
76
|
-
For embedding in iframes (e.g., v0.dev, sandboxed environments):
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
import { OxySDK } from "@oxy/sdk";
|
|
80
|
-
|
|
81
|
-
// SDK automatically requests API key from parent window
|
|
82
|
-
const sdk = await OxySDK.create({
|
|
83
|
-
parentOrigin: "https://app.example.com",
|
|
84
|
-
projectId: "your-project-uuid",
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
await sdk.loadAppData("dashboard.app.yml");
|
|
88
|
-
const result = await sdk.query("SELECT * FROM my_table LIMIT 10");
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
**Parent window setup:**
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
window.addEventListener("message", (event) => {
|
|
95
|
-
if (event.data.type !== "OXY_AUTH_REQUEST") return;
|
|
96
|
-
if (event.origin !== "https://your-iframe-app.com") return;
|
|
97
|
-
|
|
98
|
-
event.source.postMessage(
|
|
99
|
-
{
|
|
100
|
-
type: "OXY_AUTH_RESPONSE",
|
|
101
|
-
version: "1.0",
|
|
102
|
-
requestId: event.data.requestId,
|
|
103
|
-
apiKey: getUserApiKey(),
|
|
104
|
-
projectId: "your-project-uuid",
|
|
105
|
-
baseUrl: "https://api.oxy.tech",
|
|
106
|
-
},
|
|
107
|
-
event.origin,
|
|
108
|
-
);
|
|
109
|
-
});
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Environment Variables
|
|
12
|
+
## Install
|
|
113
13
|
|
|
114
14
|
```bash
|
|
115
|
-
|
|
116
|
-
export OXY_API_KEY="your-api-key"
|
|
117
|
-
export OXY_PROJECT_ID="your-project-uuid"
|
|
118
|
-
export OXY_BRANCH="main" # optional
|
|
15
|
+
pnpm add @oxy-hq/sdk @oxy-hq/vite-plugin
|
|
119
16
|
```
|
|
120
17
|
|
|
121
|
-
|
|
18
|
+
`react` (^19) is a peer dependency. `@oxy-hq/vite-plugin` wires the served
|
|
19
|
+
base path, copies `oxy-app.json` into the build, and injects the dev identity
|
|
20
|
+
shim — drop it into `vite.config.ts`:
|
|
122
21
|
|
|
123
|
-
```
|
|
124
|
-
import
|
|
22
|
+
```ts
|
|
23
|
+
import oxyApp from "@oxy-hq/vite-plugin";
|
|
24
|
+
import react from "@vitejs/plugin-react";
|
|
25
|
+
import { defineConfig } from "vite";
|
|
125
26
|
|
|
126
|
-
|
|
27
|
+
export default defineConfig({ plugins: [react(), oxyApp()] });
|
|
127
28
|
```
|
|
128
29
|
|
|
129
|
-
##
|
|
30
|
+
## Quick start
|
|
130
31
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
The SDK provides `OxyProvider` and `useOxy` hooks for easy integration:
|
|
32
|
+
Wrap your tree in `<OxyAppProvider>` (it resolves the app's identity), then
|
|
33
|
+
read data with hooks:
|
|
134
34
|
|
|
135
35
|
```tsx
|
|
136
|
-
import {
|
|
137
|
-
import { useEffect, useState } from "react";
|
|
138
|
-
|
|
139
|
-
// Wrap your app with OxyProvider
|
|
140
|
-
function App() {
|
|
141
|
-
return (
|
|
142
|
-
<OxyProvider config={createConfig()}>
|
|
143
|
-
<Dashboard />
|
|
144
|
-
</OxyProvider>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
36
|
+
import { OxyAppProvider, useQuery, OxyChat } from "@oxy-hq/sdk";
|
|
147
37
|
|
|
148
|
-
// Access SDK in child components
|
|
149
38
|
function Dashboard() {
|
|
150
|
-
const {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
sdk
|
|
156
|
-
.loadAppData("dashboard.app.yml")
|
|
157
|
-
.then(() => sdk.query("SELECT * FROM my_table LIMIT 100"))
|
|
158
|
-
.then(setData);
|
|
159
|
-
}
|
|
160
|
-
}, [sdk]);
|
|
161
|
-
|
|
162
|
-
if (isLoading) return <div>Initializing SDK...</div>;
|
|
163
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
164
|
-
if (!data) return <div>Loading data...</div>;
|
|
165
|
-
|
|
39
|
+
const { rows, isLoading, error } = useQuery({
|
|
40
|
+
sql: "SELECT Store, SUM(Weekly_Sales) AS sales FROM oxymart GROUP BY 1 ORDER BY 2 DESC LIMIT 5"
|
|
41
|
+
});
|
|
42
|
+
if (isLoading) return <p>Loading…</p>;
|
|
43
|
+
if (error) return <p>{error.message}</p>;
|
|
166
44
|
return (
|
|
167
|
-
|
|
168
|
-
<
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
<tr>
|
|
172
|
-
{data.columns.map((col) => (
|
|
173
|
-
<th key={col}>{col}</th>
|
|
174
|
-
))}
|
|
175
|
-
</tr>
|
|
176
|
-
</thead>
|
|
177
|
-
<tbody>
|
|
178
|
-
{data.rows.map((row, i) => (
|
|
179
|
-
<tr key={i}>
|
|
180
|
-
{row.map((cell, j) => (
|
|
181
|
-
<td key={j}>{String(cell)}</td>
|
|
182
|
-
))}
|
|
183
|
-
</tr>
|
|
184
|
-
))}
|
|
185
|
-
</tbody>
|
|
186
|
-
</table>
|
|
187
|
-
</div>
|
|
45
|
+
<>
|
|
46
|
+
<table>{rows.map((r) => <tr key={r.Store}><td>{r.Store}</td><td>{r.sales}</td></tr>)}</table>
|
|
47
|
+
<OxyChat agentId="analytics" />
|
|
48
|
+
</>
|
|
188
49
|
);
|
|
189
50
|
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Iframe with PostMessage Auth
|
|
193
51
|
|
|
194
|
-
|
|
195
|
-
import { OxyProvider, useOxySDK } from "@oxy/sdk";
|
|
196
|
-
|
|
197
|
-
function App() {
|
|
52
|
+
export function App() {
|
|
198
53
|
return (
|
|
199
|
-
<
|
|
54
|
+
<OxyAppProvider fallback={<p>Loading…</p>}>
|
|
200
55
|
<Dashboard />
|
|
201
|
-
</
|
|
56
|
+
</OxyAppProvider>
|
|
202
57
|
);
|
|
203
58
|
}
|
|
204
|
-
|
|
205
|
-
function Dashboard() {
|
|
206
|
-
const sdk = useOxySDK(); // Throws if not ready
|
|
207
|
-
const [data, setData] = useState(null);
|
|
208
|
-
|
|
209
|
-
useEffect(() => {
|
|
210
|
-
sdk
|
|
211
|
-
.loadFile("data/sales.parquet", "sales")
|
|
212
|
-
.then(() => sdk.query("SELECT * FROM sales LIMIT 100"))
|
|
213
|
-
.then(setData);
|
|
214
|
-
}, [sdk]);
|
|
215
|
-
|
|
216
|
-
return <div>{/* render data */}</div>;
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Without Context (Alternative)
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
import { OxySDK, createConfig } from '@oxy/sdk';
|
|
224
|
-
import { useEffect, useState } from 'react';
|
|
225
|
-
|
|
226
|
-
const sdk = new OxySDK(createConfig());
|
|
227
|
-
|
|
228
|
-
function Dashboard() {
|
|
229
|
-
const [data, setData] = useState(null);
|
|
230
|
-
|
|
231
|
-
useEffect(() => {
|
|
232
|
-
sdk.loadAppData('dashboard.app.yml')
|
|
233
|
-
.then(() => sdk.query('SELECT * FROM my_table LIMIT 100'))
|
|
234
|
-
.then(setData);
|
|
235
|
-
}, []);
|
|
236
|
-
|
|
237
|
-
return <div>{/* render data */}</div>;
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
## Use with v0 and Sandbox Services
|
|
242
|
-
|
|
243
|
-
**For AI Assistants (v0.dev, Cursor, etc.):** See [.v0/rules.md](.v0/rules.md) and [.cursorrules](.cursorrules) for integration guidelines.
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
import { OxySDK, createConfig } from "@oxy/sdk";
|
|
247
|
-
|
|
248
|
-
const sdk = new OxySDK(createConfig());
|
|
249
|
-
|
|
250
|
-
export async function getDashboardData() {
|
|
251
|
-
await sdk.loadAppData("dashboard.app.yml");
|
|
252
|
-
|
|
253
|
-
return await sdk.query(`
|
|
254
|
-
SELECT s.*, c.name as customer_name
|
|
255
|
-
FROM sales s
|
|
256
|
-
LEFT JOIN customers c ON s.customer_id = c.id
|
|
257
|
-
ORDER BY s.date DESC
|
|
258
|
-
LIMIT 100
|
|
259
|
-
`);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export function getChartUrl() {
|
|
263
|
-
return sdk.getClient().getFileUrl("charts/sales-overview.png");
|
|
264
|
-
}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
## API Reference
|
|
268
|
-
|
|
269
|
-
### OxySDK (Unified Interface)
|
|
270
|
-
|
|
271
|
-
The `OxySDK` class combines `OxyClient` and `ParquetReader` into a single, easy-to-use interface.
|
|
272
|
-
|
|
273
|
-
#### `constructor(config: OxyConfig)`
|
|
274
|
-
|
|
275
|
-
Creates a new SDK instance.
|
|
276
|
-
|
|
277
|
-
#### `static async create(config?: Partial<OxyConfig>): Promise<OxySDK>`
|
|
278
|
-
|
|
279
|
-
Creates an SDK instance with async configuration (supports postMessage auth).
|
|
280
|
-
|
|
281
|
-
```typescript
|
|
282
|
-
const sdk = await OxySDK.create({
|
|
283
|
-
parentOrigin: "https://app.example.com",
|
|
284
|
-
projectId: "your-project-id",
|
|
285
|
-
});
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
#### `async loadFile(filePath: string, tableName: string): Promise<void>`
|
|
289
|
-
|
|
290
|
-
Loads a Parquet file from Oxy and registers it for SQL queries.
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
await sdk.loadFile("data/sales.parquet", "sales");
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
#### `async loadFiles(files: Array<{filePath: string, tableName: string}>): Promise<void>`
|
|
297
|
-
|
|
298
|
-
Loads multiple Parquet files at once.
|
|
299
|
-
|
|
300
|
-
```typescript
|
|
301
|
-
await sdk.loadFiles([
|
|
302
|
-
{ filePath: "data/sales.parquet", tableName: "sales" },
|
|
303
|
-
{ filePath: "data/customers.parquet", tableName: "customers" },
|
|
304
|
-
]);
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
#### `async loadAppData(appPath: string): Promise<DataContainer | null>`
|
|
308
|
-
|
|
309
|
-
Loads all data from an app's data container. Uses container keys as table names.
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
312
|
-
const data = await sdk.loadAppData("dashboard.app.yml");
|
|
313
|
-
// Now query the tables using their container keys
|
|
314
|
-
const result = await sdk.query("SELECT * FROM my_table");
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
#### `async query(sql: string): Promise<QueryResult>`
|
|
318
|
-
|
|
319
|
-
Executes a SQL query against loaded data.
|
|
320
|
-
|
|
321
|
-
```typescript
|
|
322
|
-
const result = await sdk.query("SELECT * FROM sales WHERE amount > 1000");
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
#### `async getAll(tableName: string, limit?: number): Promise<QueryResult>`
|
|
326
|
-
|
|
327
|
-
Gets all data from a loaded table.
|
|
328
|
-
|
|
329
|
-
```typescript
|
|
330
|
-
const data = await sdk.getAll("sales", 100);
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
#### `async getSchema(tableName: string): Promise<QueryResult>`
|
|
334
|
-
|
|
335
|
-
Gets schema information for a loaded table.
|
|
336
|
-
|
|
337
|
-
#### `async count(tableName: string): Promise<number>`
|
|
338
|
-
|
|
339
|
-
Gets row count for a loaded table.
|
|
340
|
-
|
|
341
|
-
#### `getClient(): OxyClient`
|
|
342
|
-
|
|
343
|
-
Returns the underlying `OxyClient` for advanced operations.
|
|
344
|
-
|
|
345
|
-
```typescript
|
|
346
|
-
const apps = await sdk.getClient().listApps();
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
#### `getReader(): ParquetReader`
|
|
350
|
-
|
|
351
|
-
Returns the underlying `ParquetReader` for advanced operations.
|
|
352
|
-
|
|
353
|
-
#### `async close(): Promise<void>`
|
|
354
|
-
|
|
355
|
-
Closes and cleans up all resources.
|
|
356
|
-
|
|
357
|
-
### React Hooks
|
|
358
|
-
|
|
359
|
-
#### `OxyProvider`
|
|
360
|
-
|
|
361
|
-
Provider component that initializes and provides OxySDK to child components.
|
|
362
|
-
|
|
363
|
-
**Props:**
|
|
364
|
-
|
|
365
|
-
- `config?: Partial<OxyConfig>` - SDK configuration
|
|
366
|
-
- `useAsync?: boolean` - If true, uses async initialization (supports postMessage auth)
|
|
367
|
-
- `onReady?: (sdk: OxySDK) => void` - Called when SDK is initialized
|
|
368
|
-
- `onError?: (error: Error) => void` - Called on initialization error
|
|
369
|
-
|
|
370
|
-
```tsx
|
|
371
|
-
<OxyProvider config={createConfig()}>
|
|
372
|
-
<YourApp />
|
|
373
|
-
</OxyProvider>
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
#### `useOxy()`
|
|
377
|
-
|
|
378
|
-
Hook to access SDK, loading state, and errors.
|
|
379
|
-
|
|
380
|
-
```tsx
|
|
381
|
-
const { sdk, isLoading, error } = useOxy();
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
Returns:
|
|
385
|
-
|
|
386
|
-
- `sdk: OxySDK | null` - The SDK instance (null if not ready)
|
|
387
|
-
- `isLoading: boolean` - True while initializing
|
|
388
|
-
- `error: Error | null` - Initialization error if any
|
|
389
|
-
|
|
390
|
-
#### `useOxySDK()`
|
|
391
|
-
|
|
392
|
-
Hook that returns SDK directly or throws if not ready. Useful when you know SDK should be initialized.
|
|
393
|
-
|
|
394
|
-
```tsx
|
|
395
|
-
const sdk = useOxySDK(); // Throws if not ready
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
### Advanced: OxyClient
|
|
399
|
-
|
|
400
|
-
For advanced use cases, access the underlying client via `sdk.getClient()`:
|
|
401
|
-
|
|
402
|
-
```typescript
|
|
403
|
-
const client = sdk.getClient();
|
|
404
|
-
await client.listApps();
|
|
405
|
-
await client.getDisplays("my-app.app.yml");
|
|
406
|
-
const blob = await client.getFile("path/to/file.parquet");
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### Advanced: ParquetReader
|
|
410
|
-
|
|
411
|
-
For advanced use cases, access the underlying reader via `sdk.getReader()`:
|
|
412
|
-
|
|
413
|
-
```typescript
|
|
414
|
-
const reader = sdk.getReader();
|
|
415
|
-
await reader.registerParquet(customBlob, "custom_table");
|
|
416
59
|
```
|
|
417
60
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
```typescript
|
|
421
|
-
import { ParquetReader } from "@oxy/sdk";
|
|
61
|
+
## Identity (`oxy-app.json`)
|
|
422
62
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
await reader.registerParquet(blob2, "table2");
|
|
63
|
+
Every bundle ships an identity-only manifest at its project root (next to
|
|
64
|
+
`vite.config.ts`, **not** under `public/`):
|
|
426
65
|
|
|
427
|
-
|
|
428
|
-
|
|
66
|
+
```json
|
|
67
|
+
{ "schemaVersion": 2, "slug": "store-pulse", "orgSlug": "acme", "name": "Store Pulse" }
|
|
429
68
|
```
|
|
430
69
|
|
|
431
|
-
|
|
70
|
+
When oxy serves the bundle it injects the authoritative identity as
|
|
71
|
+
`window.__OXY_APP__`; `OxyAppProvider` reads injection first and the manifest
|
|
72
|
+
second. There is **no API key in the bundle** — requests are authorized by the
|
|
73
|
+
viewer's oxy session (same-origin cookie) or, in cross-origin local dev, a
|
|
74
|
+
bearer token the dev proxy attaches. A bundle can't read data its viewer
|
|
75
|
+
couldn't already read.
|
|
432
76
|
|
|
433
|
-
|
|
434
|
-
- `OXY_API_KEY` - API key for authentication (required)
|
|
435
|
-
- `OXY_PROJECT_ID` - Project UUID (required)
|
|
436
|
-
- `OXY_BRANCH` - Branch name (optional)
|
|
77
|
+
## API
|
|
437
78
|
|
|
438
|
-
|
|
79
|
+
| Export | What it does |
|
|
80
|
+
| --- | --- |
|
|
81
|
+
| `OxyAppProvider` | Resolves identity, provides it via context. `fallback` renders while loading; `errorFallback` gets a structured error report. |
|
|
82
|
+
| `useQuery({ sql })` | Inline SQL → rows. `SELECT`/`WITH` only, 10k-row cap. |
|
|
83
|
+
| `useSemanticQuery({ topic, dimensions, measures, … })` | Semantic-layer query compiled by airlayer. |
|
|
84
|
+
| `useAgentRun({ agentId })` | `.ask(question)` starts an analytics agent run; streams events over SSE; `.cancel()`. |
|
|
85
|
+
| `useProcedureRun({ procedureId })` | Start a long-running procedure, poll, cancel (beta). |
|
|
86
|
+
| `<OxyChat agentId="…" />` | Drop-in chat UI over `useAgentRun`. |
|
|
87
|
+
| `<OxyAnswer … />` | Renders markdown + SQL artifacts + thread link. URL schemes are allowlisted (rejects `javascript:` etc.). |
|
|
88
|
+
| `OxyApiError` | Structured `{ message, code? }` server-error envelope. |
|
|
439
89
|
|
|
440
|
-
|
|
90
|
+
Hooks fail loudly if called outside `<OxyAppProvider>`. The default fetcher
|
|
91
|
+
sends `credentials: "include"` so same-origin (served-by-oxy) calls carry the
|
|
92
|
+
session cookie automatically.
|
|
441
93
|
|
|
442
|
-
##
|
|
443
|
-
|
|
444
|
-
```bash
|
|
445
|
-
# Install dependencies
|
|
446
|
-
npm install
|
|
94
|
+
## Docs
|
|
447
95
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
npm run typecheck
|
|
453
|
-
|
|
454
|
-
# Lint
|
|
455
|
-
npm run lint
|
|
456
|
-
|
|
457
|
-
# Publish to npm (beta)
|
|
458
|
-
npm run publish:beta
|
|
459
|
-
|
|
460
|
-
# Publish to npm (latest)
|
|
461
|
-
npm run publish:latest
|
|
462
|
-
```
|
|
96
|
+
- Hands-on dev + deploy guide: `docs/local-development.md` in the
|
|
97
|
+
[`oxy-hq/customer-apps`](https://github.com/oxy-hq/customer-apps) repo.
|
|
98
|
+
- SDK flow reference: `docs/sdk-flow.md` in that repo.
|
|
99
|
+
- Platform internals: `internal-docs/customer-apps.md` in oxygen-internal.
|
|
463
100
|
|
|
464
101
|
## License
|
|
465
102
|
|
|
466
103
|
MIT
|
|
467
|
-
|
|
468
|
-
## Support
|
|
469
|
-
|
|
470
|
-
For issues and questions, please visit [GitHub Issues](https://github.com/dataframehq/oxy-internal/issues).
|
|
471
|
-
|
|
472
|
-
### Local development with v0 or cloud service
|
|
473
|
-
|
|
474
|
-
- Disable the local network access check [flag](chrome ://flags/#local-network-access-check)
|