@permute/sdk 0.3.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 +195 -11
- package/dist/app.d.ts +23 -0
- package/dist/app.js +65 -0
- package/dist/client.d.ts +40 -16
- package/dist/client.js +81 -14
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +14 -0
- package/dist/generated/openapi.d.ts +597 -5
- package/dist/generated/runtime-contract.d.ts +3 -1
- package/dist/generated/runtime-contract.js +6614 -3753
- package/dist/types.d.ts +36 -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
|
|
@@ -314,24 +347,51 @@ const evidence = await client.search({
|
|
|
314
347
|
console.log(evidence.items);
|
|
315
348
|
```
|
|
316
349
|
|
|
317
|
-
Use SQL
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
DuckDB SQL and can be queried together. Live remote
|
|
321
|
-
their native dialect
|
|
322
|
-
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.
|
|
323
355
|
|
|
324
356
|
```ts
|
|
325
|
-
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);
|
|
326
361
|
|
|
327
362
|
const rows = await client.query({
|
|
328
|
-
sourceIds: [
|
|
329
|
-
sql: `SELECT * FROM "${
|
|
363
|
+
sourceIds: [dataset.id],
|
|
364
|
+
sql: `SELECT * FROM "${table.id}" LIMIT 10`,
|
|
330
365
|
});
|
|
331
366
|
|
|
332
|
-
console.log(rows.rows);
|
|
367
|
+
console.log(table.fields, rows.rows, rows.timings);
|
|
333
368
|
```
|
|
334
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
|
+
|
|
335
395
|
## Error Handling
|
|
336
396
|
|
|
337
397
|
```ts
|
|
@@ -354,3 +414,127 @@ try {
|
|
|
354
414
|
## License
|
|
355
415
|
|
|
356
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,20 +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
|
+
};
|
|
95
105
|
readonly connectorSessions: {
|
|
96
106
|
create(request: ConnectorSessionCreateRequest, options?: PermuteRequestOptions): Promise<ConnectorSessionCreated>;
|
|
97
107
|
get(sessionId: string, options?: PermuteRequestOptions): Promise<ConnectorSession>;
|
|
98
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
|
+
};
|
|
99
122
|
readonly workspaces: {
|
|
100
123
|
list: (options?: PermuteRequestOptions) => Promise<WorkspaceList>;
|
|
101
124
|
create: (request: WorkspaceCreateRequest, options?: PermuteRequestOptions) => Promise<Workspace>;
|
|
@@ -106,6 +129,8 @@ export declare class PermuteClient {
|
|
|
106
129
|
readonly sources: {
|
|
107
130
|
list: (options?: PermuteRequestOptions) => Promise<SourceList>;
|
|
108
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>;
|
|
109
134
|
};
|
|
110
135
|
readonly operations: {
|
|
111
136
|
list: (query?: OperationListQuery, options?: PermuteRequestOptions) => Promise<OperationList>;
|
|
@@ -156,4 +181,3 @@ export declare class PermuteClient {
|
|
|
156
181
|
private validateRequestBody;
|
|
157
182
|
private validateRequestQuery;
|
|
158
183
|
}
|
|
159
|
-
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,8 +254,11 @@ export class PermuteClient {
|
|
|
266
254
|
facts;
|
|
267
255
|
skills;
|
|
268
256
|
organizationSkills;
|
|
257
|
+
actions;
|
|
269
258
|
databases;
|
|
259
|
+
apps;
|
|
270
260
|
connectorSessions;
|
|
261
|
+
writeSyncs;
|
|
271
262
|
workspaces;
|
|
272
263
|
sources;
|
|
273
264
|
operations;
|
|
@@ -322,20 +313,88 @@ export class PermuteClient {
|
|
|
322
313
|
});
|
|
323
314
|
this.skills = skillsClient('skills');
|
|
324
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
|
+
};
|
|
325
325
|
this.databases = {
|
|
326
326
|
list: (options) => this.request('databasesList', {}, options),
|
|
327
327
|
create: (body, options) => this.request('databasesCreate', { body }, options),
|
|
328
328
|
get: (databaseId, options) => this.request('databasesGet', { pathParams: { databaseId } }, options),
|
|
329
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),
|
|
330
332
|
tables: (databaseId, options) => this.request('databaseTablesList', { pathParams: { databaseId } }, options),
|
|
331
333
|
createTable: (databaseId, body, options) => this.request('databaseTablesCreate', { pathParams: { databaseId }, body }, options),
|
|
332
334
|
records: (databaseId, table, query = {}, options) => this.request('databaseRecordsList', { pathParams: { databaseId, table }, query }, options),
|
|
333
335
|
transact: (databaseId, body, options) => this.request('databaseTransactionsCreate', { pathParams: { databaseId }, body }, options),
|
|
334
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
|
+
};
|
|
335
381
|
this.connectorSessions = {
|
|
336
382
|
create: (body, options) => this.request('connectorSessionsCreate', { body }, options),
|
|
337
383
|
get: (sessionId, options) => this.request('connectorSessionsGet', { pathParams: { sessionId } }, options),
|
|
338
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
|
+
};
|
|
339
398
|
this.workspaces = {
|
|
340
399
|
list: (requestOptions) => this.request('workspacesList', {}, requestOptions),
|
|
341
400
|
create: (request, requestOptions) => this.request('workspacesCreate', { body: request }, requestOptions),
|
|
@@ -346,6 +405,14 @@ export class PermuteClient {
|
|
|
346
405
|
this.sources = {
|
|
347
406
|
list: (requestOptions) => this.request('sourcesList', {}, requestOptions),
|
|
348
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],
|
|
349
416
|
};
|
|
350
417
|
this.operations = {
|
|
351
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
|
+
}
|