@permute/sdk 0.2.0 → 0.4.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 +224 -13
- package/dist/app.d.ts +23 -0
- package/dist/app.js +65 -0
- package/dist/client.d.ts +44 -16
- package/dist/client.js +86 -14
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +14 -0
- package/dist/generated/openapi.d.ts +642 -2
- package/dist/generated/runtime-contract.d.ts +29 -1
- package/dist/generated/runtime-contract.js +6143 -3071
- package/dist/index.d.ts +17 -16
- package/dist/index.js +17 -16
- package/dist/types.d.ts +40 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The server validates source safety and derives the dashboard data contract from
|
|
|
26
26
|
the referenced run. Clients cannot supply an authoritative contract.
|
|
27
27
|
|
|
28
28
|
```sh
|
|
29
|
-
|
|
29
|
+
bun add @permute/sdk
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
## Author data flows with external agents
|
|
@@ -101,6 +101,39 @@ and analyzing governed outputs; it does not expose code editing, draft inspectio
|
|
|
101
101
|
execution, or publication. Hosted connectors also restrict access to selected sources. Flow scheduling and standalone operation
|
|
102
102
|
creation are not part of this surface.
|
|
103
103
|
|
|
104
|
+
## Publish write syncs
|
|
105
|
+
|
|
106
|
+
Use `client.writeSyncs.destinations()` to discover writable destination objects
|
|
107
|
+
and fields, then create a paused sync with `writeSyncs.create({ definition })`.
|
|
108
|
+
For operation sources, use `client.sources.get(operationId)` to inspect tables
|
|
109
|
+
and fields, then read `client.operations.get(operationId)` and use its
|
|
110
|
+
`published.revisionId` in the definition. Publish and run the operation before
|
|
111
|
+
configuring its sync. Definition edits use `writeSyncs.update(id, {
|
|
112
|
+
definition, expectedVersion })`, with the version from the latest read.
|
|
113
|
+
|
|
114
|
+
`writeSyncs.preview(id)` queues a read-only capture, including when the sync is
|
|
115
|
+
enabled. Poll `writeSyncs.getRun(id, runId)` until `previewComplete` or
|
|
116
|
+
`previewError`, then inspect every page with `getRun(id, runId, { page })`
|
|
117
|
+
(pages start at zero) before approving delivery.
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// After reviewing every page of a completed preview:
|
|
121
|
+
const history = await client.revisions.list(writeSyncId);
|
|
122
|
+
await client.revisions.publish(writeSyncId, {
|
|
123
|
+
expectedVersion: history.version,
|
|
124
|
+
runId,
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Publishing approves that preview, starts external delivery now, and schedules
|
|
129
|
+
new captures every 24 hours under the same API key that captured the preview.
|
|
130
|
+
New captures include `definitionRevisionId`, which identifies the exact saved
|
|
131
|
+
definition used by that run; older captures may omit it.
|
|
132
|
+
For an enabled sync, `writeSyncs.execute(id, runId)` sends a selected completed
|
|
133
|
+
preview captured by that same key without refreshing its values or changing the
|
|
134
|
+
schedule. Poll `getRun` for delivery results and inspect per-record outcomes.
|
|
135
|
+
Use `writeSyncs.pause(id)` to stop future writes; requests already sent may finish.
|
|
136
|
+
|
|
104
137
|
## Configure
|
|
105
138
|
|
|
106
139
|
```ts
|
|
@@ -151,6 +184,29 @@ const client = new PermuteClient({
|
|
|
151
184
|
grant access. The selected ID is available as `client.workspaceId`. Organization
|
|
152
185
|
APIs such as `client.workspaces.list()` do not send a workspace header.
|
|
153
186
|
|
|
187
|
+
## Connect Customer Sources
|
|
188
|
+
|
|
189
|
+
Create a hosted setup link from your server after authorizing the customer's
|
|
190
|
+
workspace. Use `Providers` for provider IDs (for example, `Providers.HUBSPOT`).
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { Providers } from '@permute/sdk';
|
|
194
|
+
|
|
195
|
+
const session = await client.connectorSessions.create({
|
|
196
|
+
provider: Providers.HUBSPOT,
|
|
197
|
+
name: 'CRM',
|
|
198
|
+
});
|
|
199
|
+
// Send session.url to the authorized customer to open in a new tab or popup.
|
|
200
|
+
|
|
201
|
+
// Reconnect an existing source; its provider is inferred.
|
|
202
|
+
const reconnect = await client.connectorSessions.create({
|
|
203
|
+
connectorId: savedConnectorId,
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
See the [hosted connector setup guide](https://permute.ai/docs/connector-sessions)
|
|
208
|
+
for completion checks and return URLs.
|
|
209
|
+
|
|
154
210
|
## Upload Files
|
|
155
211
|
|
|
156
212
|
Use the one-call helper when you have file bytes in process. It creates a file
|
|
@@ -202,13 +258,13 @@ data. The SDK accepts JavaScript objects, encodes them as chunked NDJSON, and
|
|
|
202
258
|
uploads the result to a signed S3 target; callers do not need temporary files.
|
|
203
259
|
|
|
204
260
|
```ts
|
|
205
|
-
import {
|
|
261
|
+
import { Context, PermuteClient } from '@permute/sdk';
|
|
206
262
|
|
|
207
263
|
const dataset = await client.datasets.create({
|
|
208
264
|
type: 'custom',
|
|
209
265
|
key: 'quickbooks',
|
|
210
266
|
name: 'QuickBooks',
|
|
211
|
-
context: [
|
|
267
|
+
context: [Context.QUICKBOOKS],
|
|
212
268
|
});
|
|
213
269
|
|
|
214
270
|
await dataset.replace({
|
|
@@ -219,6 +275,10 @@ await dataset.replace({
|
|
|
219
275
|
console.log(dataset.sourceId, dataset.version, dataset.data.tables);
|
|
220
276
|
```
|
|
221
277
|
|
|
278
|
+
Dataset `context` accepts string identifiers, with `Context` providing named
|
|
279
|
+
constants for known contexts (such as `Context.QUICKBOOKS`). It is not restricted
|
|
280
|
+
to the connector provider list. Use `Providers` when selecting a connector.
|
|
281
|
+
|
|
222
282
|
`replace()` sends the complete dataset snapshot. Tables omitted from the call
|
|
223
283
|
are removed, while tables with the same name keep their stable identity for
|
|
224
284
|
reports and dashboards. Retrying an identical replacement is a no-op.
|
|
@@ -287,24 +347,51 @@ const evidence = await client.search({
|
|
|
287
347
|
console.log(evidence.items);
|
|
288
348
|
```
|
|
289
349
|
|
|
290
|
-
Use SQL
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
DuckDB SQL and can be queried together. Live remote
|
|
294
|
-
their native dialect
|
|
295
|
-
must be queried one at a time without other sources.
|
|
350
|
+
Use SQL when table names are known and exact rows matter. SQL requests need
|
|
351
|
+
`sourceIds`. List a source's tables first, then describe the table you plan to
|
|
352
|
+
query (materialized connector fields come from the stored SQL table). Warehouse
|
|
353
|
+
sources use DuckDB SQL and can be queried together. Live remote sources use
|
|
354
|
+
their native SQL dialect and must be queried one at a time.
|
|
296
355
|
|
|
297
356
|
```ts
|
|
298
|
-
const
|
|
357
|
+
const dataset = sources.items.find((source) => source.type === 'dataset');
|
|
358
|
+
if (!dataset) throw new Error('No dataset is available');
|
|
359
|
+
const tables = await client.sources.listTables(dataset.id);
|
|
360
|
+
const table = await client.sources.describeTable(dataset.id, tables[0].id);
|
|
299
361
|
|
|
300
362
|
const rows = await client.query({
|
|
301
|
-
sourceIds: [
|
|
302
|
-
sql: `SELECT * FROM "${
|
|
363
|
+
sourceIds: [dataset.id],
|
|
364
|
+
sql: `SELECT * FROM "${table.id}" LIMIT 10`,
|
|
303
365
|
});
|
|
304
366
|
|
|
305
|
-
console.log(rows.rows);
|
|
367
|
+
console.log(table.fields, rows.rows, rows.timings);
|
|
306
368
|
```
|
|
307
369
|
|
|
370
|
+
`timings` separates source validation, DuckDB startup, source loading, and SQL
|
|
371
|
+
execution. Compare `totalMs` with the caller's elapsed time to find delay
|
|
372
|
+
outside the query action (including transport and response handling).
|
|
373
|
+
|
|
374
|
+
## Managed databases
|
|
375
|
+
|
|
376
|
+
`client.databases` can create a database, define tables, read current records,
|
|
377
|
+
and submit atomic inserts, updates, or deletes. Use `client.query()` for filtered
|
|
378
|
+
SQL reads. Managed records include `__version`; updates and deletes need that
|
|
379
|
+
token, and a stale version returns 409. Reuse a `requestId` only when retrying
|
|
380
|
+
the same transaction after an uncertain response.
|
|
381
|
+
|
|
382
|
+
Schema definitions use `client.databases.definition(id)` and
|
|
383
|
+
`client.databases.saveDefinition(id, { definition, expectedDraft })`. Save the
|
|
384
|
+
complete table list, then publish the exact draft through `client.revisions`.
|
|
385
|
+
Published schemas only support adding tables; rows are not part of schema history.
|
|
386
|
+
|
|
387
|
+
**Upgrading from 0.3.x:** `createTable` now saves a draft. Publish the database
|
|
388
|
+
before using the new table. Direct `transact` calls require database admin
|
|
389
|
+
permission; use a business action for a delegated write.
|
|
390
|
+
|
|
391
|
+
See the [managed database guide](https://permute.ai/docs/guides/managed-database)
|
|
392
|
+
for a complete example (including provisioning, filtered reads, and conflict
|
|
393
|
+
handling).
|
|
394
|
+
|
|
308
395
|
## Error Handling
|
|
309
396
|
|
|
310
397
|
```ts
|
|
@@ -327,3 +414,127 @@ try {
|
|
|
327
414
|
## License
|
|
328
415
|
|
|
329
416
|
Apache-2.0. Copyright 2026 Permute, Inc.
|
|
417
|
+
|
|
418
|
+
## Hosted apps
|
|
419
|
+
|
|
420
|
+
Create an app with `client.apps.create({ name, key, description })`, then poll `client.apps.get(id)` until its deployment is live. Build your frontend locally with relative asset URLs (for Vite, `bun run build --base=./`).
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
const app = await client.apps.get(appId);
|
|
424
|
+
const files = await client.apps.uploadFiles(app.id, [
|
|
425
|
+
{ path: 'index.html', data: await readFile('dist/index.html') },
|
|
426
|
+
{ path: 'assets/main.js', data: await readFile('dist/assets/main.js') },
|
|
427
|
+
]);
|
|
428
|
+
const saved = await client.apps.saveSite(app.id, {
|
|
429
|
+
site: { files, spa: true },
|
|
430
|
+
expectedDraft: app.draft,
|
|
431
|
+
});
|
|
432
|
+
const state = await client.revisions.list(app.id);
|
|
433
|
+
if (state.draft?.revisionId !== saved.draft.revisionId || state.draft.sequence !== saved.draft.sequence) {
|
|
434
|
+
throw new Error('The draft changed; review before publishing');
|
|
435
|
+
}
|
|
436
|
+
await client.revisions.publish(app.id, { expectedVersion: state.version });
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Import `readFile` from `node:fs/promises`. Supply the complete build directory; the filenames above are illustrative. Uploads hash the bytes and use signed, conditional S3 PUTs without forwarding API credentials. Uploading does not create a draft. `saveSite` verifies the uploaded files and saves the complete manifest as a draft revision; `revisions.publish` makes that draft live. The limits are 200 files, 4 MiB per file, and 50 MiB total.
|
|
440
|
+
|
|
441
|
+
Identical contents reuse existing files. Changed contents at the same path are stored separately, preserving earlier versions for rollback. Each save replaces the complete manifest (including unchanged files); omitted paths disappear from the live app when published. Resolve a 409 conflict against the current draft before retrying.
|
|
442
|
+
|
|
443
|
+
Visitors sign in through Permute and need app read permission. HTML receives a revision-specific base URL; use relative URLs for bundled and public assets. Inline scripts, external requests, workers, and server runtimes are not supported. Keep API keys and secrets out of compiled browser files. Restore a historical revision with `client.revisions.restore`, then publish the resulting draft to roll back. See the [complete app guide](https://permute.ai/docs/guides/deploy-app) for creation, directory uploads, updates, and rollback.
|
|
444
|
+
|
|
445
|
+
### Inside a hosted app
|
|
446
|
+
|
|
447
|
+
Use the browser entry with the app's existing sign-in session (no API key in frontend code):
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
import { PermuteAppClient } from '@permute/sdk/app';
|
|
451
|
+
|
|
452
|
+
const app = new PermuteAppClient();
|
|
453
|
+
const result = await app.query({
|
|
454
|
+
sourceIds: ['dbas_example'],
|
|
455
|
+
sql: 'SELECT * FROM app.contacts',
|
|
456
|
+
limit: 100,
|
|
457
|
+
});
|
|
458
|
+
const request = { requestId: crypto.randomUUID(), input: { name: 'Ada' } };
|
|
459
|
+
const receipt = await app.actions.invoke('actn_example', request);
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
The client reads the hosted page's revision-pinned base URL. Save the app's `sourceIds` and
|
|
463
|
+
`actions: [{ actionId, revisionId }]` with its site manifest, then publish the app revision.
|
|
464
|
+
Queries require a declared source and the signed-in user's current read permission;
|
|
465
|
+
actions use the pinned published action revision and the user's invocation permission.
|
|
466
|
+
Read scope is source-level; client-supplied SQL is not a row-level authorization policy.
|
|
467
|
+
Keep the same request ID and input when retrying an uncertain action response. The client
|
|
468
|
+
does not retry writes automatically. A 401 requires signing in again.
|
|
469
|
+
|
|
470
|
+
## Business actions
|
|
471
|
+
|
|
472
|
+
Use `client.actions.create`, `definition`, and `saveDefinition` to author a typed
|
|
473
|
+
handler with selected database tables or connector accounts. Publish with
|
|
474
|
+
`client.revisions.publish`, then call `client.actions.invoke` with a stable
|
|
475
|
+
`requestId` and input. `list` exposes published contracts; reading definitions
|
|
476
|
+
requires admin permission. See the [business action guide](https://permute.ai/docs/guides/business-actions)
|
|
477
|
+
for the complete create, save, publish, and invoke workflow.
|
|
478
|
+
|
|
479
|
+
### Connector bindings
|
|
480
|
+
|
|
481
|
+
An action definition can bind multiple connector accounts by name alongside its database
|
|
482
|
+
schema bindings. Save and publish require admin permission on each selected resource.
|
|
483
|
+
The action runs with those delegated capabilities; callers need its invocation permission.
|
|
484
|
+
|
|
485
|
+
```ts
|
|
486
|
+
await client.actions.saveDefinition(actionId, {
|
|
487
|
+
expectedDraft: null, // Use the current draft ref when updating.
|
|
488
|
+
definition: {
|
|
489
|
+
source: handlerSource,
|
|
490
|
+
inputSchema,
|
|
491
|
+
outputSchema,
|
|
492
|
+
invokePermission: 'write',
|
|
493
|
+
sources: databaseBindings,
|
|
494
|
+
connectors: {
|
|
495
|
+
salesCrm: 'conn_aaaaaaaaaaaaaaaaaaaaaaaa',
|
|
496
|
+
partnerCrm: 'conn_bbbbbbbbbbbbbbbbbbbbbbbb',
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
Both names can refer to HubSpot accounts. Handler code uses the bound name; credentials
|
|
503
|
+
stay in the trusted proxy. The following handler creates a HubSpot deal and stages a local
|
|
504
|
+
record (the action must declare `crm_deals` in its writable database binding):
|
|
505
|
+
|
|
506
|
+
```js
|
|
507
|
+
exports.handler = async ({ input, connectors, db }) => {
|
|
508
|
+
const deal = await connectors.salesCrm.request({
|
|
509
|
+
stepId: 'create-deal',
|
|
510
|
+
method: 'POST',
|
|
511
|
+
path: '/crm/v3/objects/deals',
|
|
512
|
+
body: {
|
|
513
|
+
properties: {
|
|
514
|
+
dealname: input.name,
|
|
515
|
+
dealstage: input.hubspotStageId,
|
|
516
|
+
pipeline: input.hubspotPipelineId,
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
db.insert('crm_deals', { id: input.id, name: input.name, hubspot_id: deal.id });
|
|
521
|
+
return { id: input.id, hubspotId: deal.id };
|
|
522
|
+
};
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
`db.insert/update/delete` stages the local mutations until the handler finishes and its
|
|
526
|
+
output validates. Existing `{ mutations, output }` handlers remain supported. External
|
|
527
|
+
requests run when awaited and cannot be rolled back if the local commit later fails.
|
|
528
|
+
|
|
529
|
+
HTTP providers expose `request({ stepId, method, path, headers?, body?, service? })` using
|
|
530
|
+
provider-relative paths and current authentication. Supabase and BigQuery expose
|
|
531
|
+
`query(sql, params, { stepId })`; S3 exposes `putObject({ stepId, key, body, contentType? })`
|
|
532
|
+
for text objects in the configured bucket. The connected account's grants apply without
|
|
533
|
+
an action-level endpoint allowlist. The SEC integration has no action transport because
|
|
534
|
+
it uses a shared Permute service key.
|
|
535
|
+
|
|
536
|
+
Reuse the same invocation `requestId` and input after a failed or lost response, and keep
|
|
537
|
+
step IDs stable in code. The first attempt pins the revision; successful external steps
|
|
538
|
+
replay saved responses. Steps with uncertain outcomes block automatic resend and require
|
|
539
|
+
provider-state reconciliation. There is no automatic rollback, reconciliation API, or
|
|
540
|
+
long-running workflow runner. Connector-only actions can use `sources: []`.
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ActionRequest, ActionReceipt, QueryRequest, QueryResponse } from './types.js';
|
|
2
|
+
export { PermuteApiError } from './errors.js';
|
|
3
|
+
export type { ActionRequest, ActionReceipt, QueryRequest, QueryResponse } from './types.js';
|
|
4
|
+
export interface PermuteAppClientOptions {
|
|
5
|
+
/** Defaults to the hosted page's revision-pinned document.baseURI. */
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
}
|
|
9
|
+
export interface AppRequestOptions {
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
/** Browser client authenticated by the hosted app's HttpOnly session cookie. */
|
|
13
|
+
export declare class PermuteAppClient {
|
|
14
|
+
private readonly baseUrl;
|
|
15
|
+
private readonly fetcher;
|
|
16
|
+
constructor(options?: PermuteAppClientOptions);
|
|
17
|
+
readonly actions: {
|
|
18
|
+
/** Keep requestId and input unchanged when retrying an uncertain result. */
|
|
19
|
+
invoke: (actionId: string, request: ActionRequest, options?: AppRequestOptions) => Promise<ActionReceipt>;
|
|
20
|
+
};
|
|
21
|
+
query(request: QueryRequest, options?: AppRequestOptions): Promise<QueryResponse>;
|
|
22
|
+
private request;
|
|
23
|
+
}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { PermuteApiError } from './errors.js';
|
|
2
|
+
export { PermuteApiError } from './errors.js';
|
|
3
|
+
/** Browser client authenticated by the hosted app's HttpOnly session cookie. */
|
|
4
|
+
export class PermuteAppClient {
|
|
5
|
+
baseUrl;
|
|
6
|
+
fetcher;
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
const base = options.baseUrl ?? (typeof document !== 'undefined' ? document.baseURI : undefined);
|
|
9
|
+
if (!base)
|
|
10
|
+
throw new Error('A hosted app revision URL is required.');
|
|
11
|
+
const url = new URL(base);
|
|
12
|
+
const revision = /^\/__revisions\/(rvsn_[a-z0-9]+)\//.exec(url.pathname);
|
|
13
|
+
if (!revision || !['https:', 'http:'].includes(url.protocol) || url.username || url.password)
|
|
14
|
+
throw new Error('Use a revision-pinned hosted app URL.');
|
|
15
|
+
if (typeof location !== 'undefined' && url.origin !== location.origin)
|
|
16
|
+
throw new Error('App requests must use the current origin.');
|
|
17
|
+
this.baseUrl = `${url.origin}${revision[0]}`;
|
|
18
|
+
this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
19
|
+
}
|
|
20
|
+
actions = {
|
|
21
|
+
/** Keep requestId and input unchanged when retrying an uncertain result. */
|
|
22
|
+
invoke: (actionId, request, options) => this.request(`__actions/${encodeURIComponent(actionId)}/invoke`, request, options),
|
|
23
|
+
};
|
|
24
|
+
query(request, options) {
|
|
25
|
+
return this.request('__queries', request, options);
|
|
26
|
+
}
|
|
27
|
+
async request(path, body, options) {
|
|
28
|
+
const response = await this.fetcher(`${this.baseUrl}${path}`, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
credentials: 'same-origin',
|
|
31
|
+
mode: 'same-origin',
|
|
32
|
+
redirect: 'error',
|
|
33
|
+
headers: { 'Content-Type': 'application/json' },
|
|
34
|
+
body: JSON.stringify(body),
|
|
35
|
+
signal: options?.signal,
|
|
36
|
+
});
|
|
37
|
+
const text = await response.text();
|
|
38
|
+
let payload;
|
|
39
|
+
try {
|
|
40
|
+
payload = JSON.parse(text);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* Gateways can return plain-text errors. */
|
|
44
|
+
}
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
const error = payload?.error;
|
|
47
|
+
throw new PermuteApiError({
|
|
48
|
+
status: response.status,
|
|
49
|
+
code: error?.code ?? (response.status === 401 ? 'unauthenticated' : 'request_failed'),
|
|
50
|
+
message: (typeof error === 'string' ? error : error?.message) ??
|
|
51
|
+
(response.headers.get('content-type')?.startsWith('text/plain') ? text : null) ??
|
|
52
|
+
`App request failed (${response.status}).`,
|
|
53
|
+
details: error?.details,
|
|
54
|
+
requestId: error?.requestId,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (!payload || typeof payload !== 'object' || !Object.hasOwn(payload, 'data'))
|
|
58
|
+
throw new PermuteApiError({
|
|
59
|
+
status: response.status,
|
|
60
|
+
code: 'invalid_response',
|
|
61
|
+
message: 'Invalid app response.',
|
|
62
|
+
});
|
|
63
|
+
return payload.data;
|
|
64
|
+
}
|
|
65
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
readonly code: string;
|
|
5
|
-
readonly details?: unknown;
|
|
6
|
-
readonly requestId?: string;
|
|
7
|
-
constructor(input: {
|
|
8
|
-
status: number;
|
|
9
|
-
code: string;
|
|
10
|
-
message: string;
|
|
11
|
-
details?: unknown;
|
|
12
|
-
requestId?: string;
|
|
13
|
-
});
|
|
14
|
-
}
|
|
1
|
+
export { PermuteApiError } from './errors.js';
|
|
2
|
+
import type { BusinessAction, BusinessActionList, ActionCreateRequest, ActionDefinition, ActionDefinitionSaveRequest, ActionDefinitionSaved, ActionRequest, ActionReceipt } from './types.js';
|
|
3
|
+
import type { ConnectorSession, ConnectorSessionCreated, ConnectorSessionCreateRequest, Database, AppDraft, AppDraftList, AppCreateRequest, AppUpdateRequest, AppDeleted, AppUploadsRequest, AppUploads, AppSiteSaveRequest, AppUploadFile, DatabaseList, DatabaseCreateRequest, DatabaseDefinition, DatabaseDefinitionSaveRequest, DatabaseDefinitionSaved, DatabaseTables, DatabaseTableCreateRequest, DatabaseTableCreated, DatabaseRecords, DatabaseRecordsQuery, DatabaseTransactionRequest, DatabaseTransactionReceipt, WriteSync, WriteSyncCreateRequest, WriteSyncUpdateRequest, WriteSyncDetail, WriteSyncList, WriteSyncDeleted, WriteSyncDestinations, WriteSyncRun, WriteSyncRunList, WriteSyncRunQuery, WriteSyncExecution, ResourceVersionState, ResourceRevisionList, ResourceRevision, ResourceVersionRequest, ResourcePublishRequest, Fact, FactList, FactWriteRequest, SkillList, SkillContent, SkillWriteRequest, KnowledgeWriteResponse, OperationRunGroup, OperationRunQuery, Operation, OperationList, OperationListQuery, OperationSource, OperationSourceUpdateRequest, OperationUpdateRequest, OperationRunRequest, OperationRunStarted, OperationRunList, OperationRunOutput, DataFlow, DataFlowCreateRequest, DataFlowCommandRequest, DataFlowCommandResponse, Workspace, WorkspaceCreateRequest, WorkspaceDeleteResponse, WorkspaceList, WorkspaceUpdateRequest, AnswerRequest, AnswerJobResponse, AnswerResponse, PermuteClientOptions, PermuteRequestOptions, QueryRequest, QueryResponse, SearchRequest, SearchResponse, SourceDetail, CatalogTable, SourceList, CustomDatasetCreateRequest, DatasetDeleteResponse, DatasetResponse, FileDatasetCreateRequest, FileDatasetResponse, DatasetRows, CustomDatasetResponse, DashboardSource, DashboardSourceUpdateRequest } from './types.js';
|
|
15
4
|
type DatasetWriteInput = {
|
|
16
5
|
tables: Record<string, DatasetRows>;
|
|
17
6
|
mode: 'replace';
|
|
@@ -71,7 +60,7 @@ export declare class PermuteClient {
|
|
|
71
60
|
list(resourceId: string, options?: PermuteRequestOptions): Promise<ResourceRevisionList>;
|
|
72
61
|
get(resourceId: string, revisionId: string, options?: PermuteRequestOptions): Promise<ResourceRevision>;
|
|
73
62
|
restore(resourceId: string, revisionId: string, request: ResourceVersionRequest, options?: PermuteRequestOptions): Promise<ResourceVersionState>;
|
|
74
|
-
publish(resourceId: string, request:
|
|
63
|
+
publish(resourceId: string, request: ResourcePublishRequest, options?: PermuteRequestOptions): Promise<ResourceVersionState>;
|
|
75
64
|
};
|
|
76
65
|
readonly facts: {
|
|
77
66
|
list(options?: PermuteRequestOptions): Promise<FactList>;
|
|
@@ -82,16 +71,54 @@ export declare class PermuteClient {
|
|
|
82
71
|
};
|
|
83
72
|
readonly skills: SkillsClient;
|
|
84
73
|
readonly organizationSkills: SkillsClient;
|
|
74
|
+
readonly actions: {
|
|
75
|
+
list(options?: PermuteRequestOptions): Promise<BusinessActionList>;
|
|
76
|
+
create(body: ActionCreateRequest, options?: PermuteRequestOptions): Promise<BusinessAction>;
|
|
77
|
+
get(actionId: string, options?: PermuteRequestOptions): Promise<BusinessAction>;
|
|
78
|
+
delete(actionId: string, options?: PermuteRequestOptions): Promise<BusinessAction>;
|
|
79
|
+
definition(actionId: string, options?: PermuteRequestOptions): Promise<ActionDefinition>;
|
|
80
|
+
saveDefinition(actionId: string, body: ActionDefinitionSaveRequest, options?: PermuteRequestOptions): Promise<ActionDefinitionSaved>;
|
|
81
|
+
invoke(actionId: string, body: ActionRequest, options?: PermuteRequestOptions): Promise<ActionReceipt>;
|
|
82
|
+
};
|
|
85
83
|
readonly databases: {
|
|
86
84
|
list(options?: PermuteRequestOptions): Promise<DatabaseList>;
|
|
87
85
|
create(body: DatabaseCreateRequest, options?: PermuteRequestOptions): Promise<Database>;
|
|
88
86
|
get(databaseId: string, options?: PermuteRequestOptions): Promise<Database>;
|
|
89
87
|
delete(databaseId: string, options?: PermuteRequestOptions): Promise<Database>;
|
|
88
|
+
definition(databaseId: string, options?: PermuteRequestOptions): Promise<DatabaseDefinition>;
|
|
89
|
+
saveDefinition(databaseId: string, body: DatabaseDefinitionSaveRequest, options?: PermuteRequestOptions): Promise<DatabaseDefinitionSaved>;
|
|
90
90
|
tables(databaseId: string, options?: PermuteRequestOptions): Promise<DatabaseTables>;
|
|
91
91
|
createTable(databaseId: string, body: DatabaseTableCreateRequest, options?: PermuteRequestOptions): Promise<DatabaseTableCreated>;
|
|
92
92
|
records(databaseId: string, table: string, query?: Partial<DatabaseRecordsQuery>, options?: PermuteRequestOptions): Promise<DatabaseRecords>;
|
|
93
93
|
transact(databaseId: string, body: DatabaseTransactionRequest, options?: PermuteRequestOptions): Promise<DatabaseTransactionReceipt>;
|
|
94
94
|
};
|
|
95
|
+
readonly apps: {
|
|
96
|
+
list(options?: PermuteRequestOptions): Promise<AppDraftList>;
|
|
97
|
+
create(body: AppCreateRequest, options?: PermuteRequestOptions): Promise<AppDraft>;
|
|
98
|
+
get(applicationId: string, options?: PermuteRequestOptions): Promise<AppDraft>;
|
|
99
|
+
update(applicationId: string, body: AppUpdateRequest, options?: PermuteRequestOptions): Promise<AppDraft>;
|
|
100
|
+
delete(applicationId: string, options?: PermuteRequestOptions): Promise<AppDeleted>;
|
|
101
|
+
prepareUploads(applicationId: string, body: AppUploadsRequest, options?: PermuteRequestOptions): Promise<AppUploads>;
|
|
102
|
+
uploadFiles(applicationId: string, files: AppUploadFile[], options?: PermuteRequestOptions): Promise<AppUploadsRequest['files']>;
|
|
103
|
+
saveSite(applicationId: string, body: AppSiteSaveRequest, options?: PermuteRequestOptions): Promise<AppDraft>;
|
|
104
|
+
};
|
|
105
|
+
readonly connectorSessions: {
|
|
106
|
+
create(request: ConnectorSessionCreateRequest, options?: PermuteRequestOptions): Promise<ConnectorSessionCreated>;
|
|
107
|
+
get(sessionId: string, options?: PermuteRequestOptions): Promise<ConnectorSession>;
|
|
108
|
+
};
|
|
109
|
+
readonly writeSyncs: {
|
|
110
|
+
list(options?: PermuteRequestOptions): Promise<WriteSyncList>;
|
|
111
|
+
create(request: WriteSyncCreateRequest, options?: PermuteRequestOptions): Promise<WriteSync>;
|
|
112
|
+
get(writeSyncId: string, options?: PermuteRequestOptions): Promise<WriteSyncDetail>;
|
|
113
|
+
update(writeSyncId: string, request: WriteSyncUpdateRequest, options?: PermuteRequestOptions): Promise<WriteSync>;
|
|
114
|
+
delete(writeSyncId: string, options?: PermuteRequestOptions): Promise<WriteSyncDeleted>;
|
|
115
|
+
destinations(options?: PermuteRequestOptions): Promise<WriteSyncDestinations>;
|
|
116
|
+
preview(writeSyncId: string, options?: PermuteRequestOptions): Promise<WriteSyncRun>;
|
|
117
|
+
pause(writeSyncId: string, options?: PermuteRequestOptions): Promise<WriteSync>;
|
|
118
|
+
listRuns(writeSyncId: string, options?: PermuteRequestOptions): Promise<WriteSyncRunList>;
|
|
119
|
+
getRun(writeSyncId: string, runId: string, query?: WriteSyncRunQuery, options?: PermuteRequestOptions): Promise<WriteSyncRun>;
|
|
120
|
+
execute(writeSyncId: string, runId: string, options?: PermuteRequestOptions): Promise<WriteSyncExecution>;
|
|
121
|
+
};
|
|
95
122
|
readonly workspaces: {
|
|
96
123
|
list: (options?: PermuteRequestOptions) => Promise<WorkspaceList>;
|
|
97
124
|
create: (request: WorkspaceCreateRequest, options?: PermuteRequestOptions) => Promise<Workspace>;
|
|
@@ -102,6 +129,8 @@ export declare class PermuteClient {
|
|
|
102
129
|
readonly sources: {
|
|
103
130
|
list: (options?: PermuteRequestOptions) => Promise<SourceList>;
|
|
104
131
|
get: (sourceId: string, options?: PermuteRequestOptions) => Promise<SourceDetail>;
|
|
132
|
+
listTables: (sourceId: string, options?: PermuteRequestOptions) => Promise<CatalogTable[]>;
|
|
133
|
+
describeTable: (sourceId: string, tableId: string, options?: PermuteRequestOptions) => Promise<CatalogTable>;
|
|
105
134
|
};
|
|
106
135
|
readonly operations: {
|
|
107
136
|
list: (query?: OperationListQuery, options?: PermuteRequestOptions) => Promise<OperationList>;
|
|
@@ -152,4 +181,3 @@ export declare class PermuteClient {
|
|
|
152
181
|
private validateRequestBody;
|
|
153
182
|
private validateRequestQuery;
|
|
154
183
|
}
|
|
155
|
-
export {};
|
package/dist/client.js
CHANGED
|
@@ -1,22 +1,10 @@
|
|
|
1
|
+
import { PermuteApiError } from './errors.js';
|
|
2
|
+
export { PermuteApiError } from './errors.js';
|
|
1
3
|
import { Ajv } from 'ajv';
|
|
2
4
|
import { createHash } from 'node:crypto';
|
|
3
5
|
import { publicApiRoutes } from './generated/runtime-contract.js';
|
|
4
6
|
const DEFAULT_BASE_URL = 'https://api.permute.ai';
|
|
5
7
|
const DEFAULT_POLL_TIMEOUT_MS = 20 * 60 * 1000;
|
|
6
|
-
export class PermuteApiError extends Error {
|
|
7
|
-
status;
|
|
8
|
-
code;
|
|
9
|
-
details;
|
|
10
|
-
requestId;
|
|
11
|
-
constructor(input) {
|
|
12
|
-
super(input.message);
|
|
13
|
-
this.name = 'PermuteApiError';
|
|
14
|
-
this.status = input.status;
|
|
15
|
-
this.code = input.code;
|
|
16
|
-
this.details = input.details;
|
|
17
|
-
this.requestId = input.requestId;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
8
|
function routeFor(operationId) {
|
|
21
9
|
const route = publicApiRoutes[operationId];
|
|
22
10
|
if (!route) {
|
|
@@ -266,7 +254,11 @@ export class PermuteClient {
|
|
|
266
254
|
facts;
|
|
267
255
|
skills;
|
|
268
256
|
organizationSkills;
|
|
257
|
+
actions;
|
|
269
258
|
databases;
|
|
259
|
+
apps;
|
|
260
|
+
connectorSessions;
|
|
261
|
+
writeSyncs;
|
|
270
262
|
workspaces;
|
|
271
263
|
sources;
|
|
272
264
|
operations;
|
|
@@ -321,16 +313,88 @@ export class PermuteClient {
|
|
|
321
313
|
});
|
|
322
314
|
this.skills = skillsClient('skills');
|
|
323
315
|
this.organizationSkills = skillsClient('organizationSkills');
|
|
316
|
+
this.actions = {
|
|
317
|
+
list: (options) => this.request('actionsList', {}, options),
|
|
318
|
+
create: (body, options) => this.request('actionsCreate', { body }, options),
|
|
319
|
+
get: (actionId, options) => this.request('actionsGet', { pathParams: { actionId } }, options),
|
|
320
|
+
delete: (actionId, options) => this.request('actionsDelete', { pathParams: { actionId } }, options),
|
|
321
|
+
definition: (actionId, options) => this.request('actionDefinitionGet', { pathParams: { actionId } }, options),
|
|
322
|
+
saveDefinition: (actionId, body, options) => this.request('actionDefinitionSave', { pathParams: { actionId }, body }, options),
|
|
323
|
+
invoke: (actionId, body, options) => this.request('actionsInvoke', { pathParams: { actionId }, body }, options),
|
|
324
|
+
};
|
|
324
325
|
this.databases = {
|
|
325
326
|
list: (options) => this.request('databasesList', {}, options),
|
|
326
327
|
create: (body, options) => this.request('databasesCreate', { body }, options),
|
|
327
328
|
get: (databaseId, options) => this.request('databasesGet', { pathParams: { databaseId } }, options),
|
|
328
329
|
delete: (databaseId, options) => this.request('databasesDelete', { pathParams: { databaseId } }, options),
|
|
330
|
+
definition: (databaseId, options) => this.request('databaseDefinitionGet', { pathParams: { databaseId } }, options),
|
|
331
|
+
saveDefinition: (databaseId, body, options) => this.request('databaseDefinitionSave', { pathParams: { databaseId }, body }, options),
|
|
329
332
|
tables: (databaseId, options) => this.request('databaseTablesList', { pathParams: { databaseId } }, options),
|
|
330
333
|
createTable: (databaseId, body, options) => this.request('databaseTablesCreate', { pathParams: { databaseId }, body }, options),
|
|
331
334
|
records: (databaseId, table, query = {}, options) => this.request('databaseRecordsList', { pathParams: { databaseId, table }, query }, options),
|
|
332
335
|
transact: (databaseId, body, options) => this.request('databaseTransactionsCreate', { pathParams: { databaseId }, body }, options),
|
|
333
336
|
};
|
|
337
|
+
this.apps = {
|
|
338
|
+
prepareUploads: (applicationId, body, options) => this.request('appsUploadsCreate', { pathParams: { applicationId }, body }, options),
|
|
339
|
+
uploadFiles: async (applicationId, files, options) => {
|
|
340
|
+
const contents = await Promise.all(files.map(async (file) => ({
|
|
341
|
+
path: file.path,
|
|
342
|
+
bytes: typeof file.data === 'string'
|
|
343
|
+
? Buffer.from(file.data)
|
|
344
|
+
: file.data instanceof Blob
|
|
345
|
+
? Buffer.from(await file.data.arrayBuffer())
|
|
346
|
+
: Buffer.from(file.data),
|
|
347
|
+
})));
|
|
348
|
+
const manifest = contents.map(({ path, bytes }) => ({
|
|
349
|
+
path,
|
|
350
|
+
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
351
|
+
size: bytes.length,
|
|
352
|
+
}));
|
|
353
|
+
const { uploads } = await this.apps.prepareUploads(applicationId, { files: manifest }, options);
|
|
354
|
+
for (const target of uploads) {
|
|
355
|
+
const file = contents.find((item) => item.path === target.path);
|
|
356
|
+
if (!file)
|
|
357
|
+
throw new Error('Unexpected app upload path');
|
|
358
|
+
const uploaded = await this.fetchImpl(target.url, {
|
|
359
|
+
method: 'PUT',
|
|
360
|
+
headers: target.headers,
|
|
361
|
+
body: new Blob([new Uint8Array(file.bytes)]),
|
|
362
|
+
signal: options?.signal,
|
|
363
|
+
});
|
|
364
|
+
// A retry or another identical file can win the conditional PUT. Save verifies the stored checksum.
|
|
365
|
+
if (!uploaded.ok && uploaded.status !== 412)
|
|
366
|
+
throw new PermuteApiError({
|
|
367
|
+
status: uploaded.status,
|
|
368
|
+
code: 'upload_failed',
|
|
369
|
+
message: `App upload failed for ${target.path}`,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
return manifest;
|
|
373
|
+
},
|
|
374
|
+
saveSite: (applicationId, body, options) => this.request('appsSiteUpdate', { pathParams: { applicationId }, body }, options),
|
|
375
|
+
list: (options) => this.request('appsList', {}, options),
|
|
376
|
+
create: (body, options) => this.request('appsCreate', { body }, options),
|
|
377
|
+
get: (applicationId, options) => this.request('appsGet', { pathParams: { applicationId } }, options),
|
|
378
|
+
update: (applicationId, body, options) => this.request('appsUpdate', { pathParams: { applicationId }, body }, options),
|
|
379
|
+
delete: (applicationId, options) => this.request('appsDelete', { pathParams: { applicationId } }, options),
|
|
380
|
+
};
|
|
381
|
+
this.connectorSessions = {
|
|
382
|
+
create: (body, options) => this.request('connectorSessionsCreate', { body }, options),
|
|
383
|
+
get: (sessionId, options) => this.request('connectorSessionsGet', { pathParams: { sessionId } }, options),
|
|
384
|
+
};
|
|
385
|
+
this.writeSyncs = {
|
|
386
|
+
list: (options) => this.request('writeSyncsList', {}, options),
|
|
387
|
+
create: (body, options) => this.request('writeSyncsCreate', { body }, options),
|
|
388
|
+
get: (writeSyncId, options) => this.request('writeSyncsGet', { pathParams: { writeSyncId } }, options),
|
|
389
|
+
update: (writeSyncId, body, options) => this.request('writeSyncsUpdate', { pathParams: { writeSyncId }, body }, options),
|
|
390
|
+
delete: (writeSyncId, options) => this.request('writeSyncsDelete', { pathParams: { writeSyncId } }, options),
|
|
391
|
+
destinations: (options) => this.request('writeSyncsDestinations', {}, options),
|
|
392
|
+
preview: (writeSyncId, options) => this.request('writeSyncsPreview', { pathParams: { writeSyncId } }, options),
|
|
393
|
+
pause: (writeSyncId, options) => this.request('writeSyncsPause', { pathParams: { writeSyncId } }, options),
|
|
394
|
+
listRuns: (writeSyncId, options) => this.request('writeSyncsListRuns', { pathParams: { writeSyncId } }, options),
|
|
395
|
+
getRun: (writeSyncId, runId, query = {}, options) => this.request('writeSyncsGetRun', { pathParams: { writeSyncId, runId }, query }, options),
|
|
396
|
+
execute: (writeSyncId, runId, options) => this.request('writeSyncsExecute', { pathParams: { writeSyncId, runId } }, options),
|
|
397
|
+
};
|
|
334
398
|
this.workspaces = {
|
|
335
399
|
list: (requestOptions) => this.request('workspacesList', {}, requestOptions),
|
|
336
400
|
create: (request, requestOptions) => this.request('workspacesCreate', { body: request }, requestOptions),
|
|
@@ -341,6 +405,14 @@ export class PermuteClient {
|
|
|
341
405
|
this.sources = {
|
|
342
406
|
list: (requestOptions) => this.request('sourcesList', {}, requestOptions),
|
|
343
407
|
get: (sourceId, requestOptions) => this.request('sourcesGet', { pathParams: { sourceId } }, requestOptions),
|
|
408
|
+
listTables: async (sourceId, requestOptions) => (await this.request('sourcesGet', {
|
|
409
|
+
pathParams: { sourceId },
|
|
410
|
+
query: { includeFields: 'false' },
|
|
411
|
+
}, requestOptions)).tables,
|
|
412
|
+
describeTable: async (sourceId, tableId, requestOptions) => (await this.request('sourcesGet', {
|
|
413
|
+
pathParams: { sourceId },
|
|
414
|
+
query: { tableId },
|
|
415
|
+
}, requestOptions)).tables[0],
|
|
344
416
|
};
|
|
345
417
|
this.operations = {
|
|
346
418
|
list: (query = {}, options) => this.request('operationsList', { query }, options),
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class PermuteApiError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
readonly code: string;
|
|
4
|
+
readonly details?: unknown;
|
|
5
|
+
readonly requestId?: string;
|
|
6
|
+
constructor(input: {
|
|
7
|
+
status: number;
|
|
8
|
+
code: string;
|
|
9
|
+
message: string;
|
|
10
|
+
details?: unknown;
|
|
11
|
+
requestId?: string;
|
|
12
|
+
});
|
|
13
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class PermuteApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
code;
|
|
4
|
+
details;
|
|
5
|
+
requestId;
|
|
6
|
+
constructor(input) {
|
|
7
|
+
super(input.message);
|
|
8
|
+
this.name = 'PermuteApiError';
|
|
9
|
+
this.status = input.status;
|
|
10
|
+
this.code = input.code;
|
|
11
|
+
this.details = input.details;
|
|
12
|
+
this.requestId = input.requestId;
|
|
13
|
+
}
|
|
14
|
+
}
|