@permute/sdk 0.5.0 → 0.7.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 +229 -20
- package/dist/app.d.ts +16 -2
- package/dist/app.js +25 -5
- package/dist/bridge.d.ts +10 -0
- package/dist/bridge.js +62 -0
- package/dist/client.d.ts +123 -5
- package/dist/client.js +110 -100
- package/dist/deploy.d.ts +77 -5
- package/dist/deploy.js +137 -63
- package/dist/generated/openapi.d.ts +354 -13
- package/dist/generated/runtime-contract.d.ts +2 -1
- package/dist/generated/runtime-contract.js +100 -8558
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/method.d.ts +10 -0
- package/dist/method.js +21 -0
- package/dist/sourceFiles.d.ts +16 -0
- package/dist/sourceFiles.js +114 -0
- package/dist/types.d.ts +19 -0
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/method.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PublicApiOperationId } from './generated/runtime-contract.js';
|
|
2
|
+
type SdkMethod = (...args: never[]) => unknown;
|
|
3
|
+
/** Keep route metadata on the SDK functions that execute it, without a second method registry. */
|
|
4
|
+
export declare class SdkMethodFactory {
|
|
5
|
+
private static readonly operations;
|
|
6
|
+
static create<Method extends SdkMethod>(operationId: PublicApiOperationId, method: Method): Method;
|
|
7
|
+
static compose<Method extends SdkMethod>(dependencies: readonly SdkMethod[], method: Method): Method;
|
|
8
|
+
static operationsFor(method: SdkMethod): readonly PublicApiOperationId[] | undefined;
|
|
9
|
+
}
|
|
10
|
+
export {};
|
package/dist/method.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Keep route metadata on the SDK functions that execute it, without a second method registry. */
|
|
2
|
+
export class SdkMethodFactory {
|
|
3
|
+
static operations = new WeakMap();
|
|
4
|
+
static create(operationId, method) {
|
|
5
|
+
this.operations.set(method, [operationId]);
|
|
6
|
+
return method;
|
|
7
|
+
}
|
|
8
|
+
static compose(dependencies, method) {
|
|
9
|
+
const operations = dependencies.flatMap((dependency) => {
|
|
10
|
+
const selected = this.operations.get(dependency);
|
|
11
|
+
if (!selected)
|
|
12
|
+
throw new Error('SDK method has no route binding.');
|
|
13
|
+
return selected;
|
|
14
|
+
});
|
|
15
|
+
this.operations.set(method, [...new Set(operations)]);
|
|
16
|
+
return method;
|
|
17
|
+
}
|
|
18
|
+
static operationsFor(method) {
|
|
19
|
+
return this.operations.get(method);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Read a bounded snapshot; never follow a file symlink or silently accept a size change. */
|
|
2
|
+
export declare function readLocalFile(absolutePath: string, size: number, signal?: AbortSignal): Promise<Buffer>;
|
|
3
|
+
/** Project ignore rules, independent of Git's index and each developer's global configuration. */
|
|
4
|
+
export declare class SourceFileSelection {
|
|
5
|
+
private readonly root;
|
|
6
|
+
private readonly outputDirectory;
|
|
7
|
+
private readonly signal?;
|
|
8
|
+
private readonly scopes;
|
|
9
|
+
constructor(root: string, outputDirectory: string, signal?: AbortSignal | undefined);
|
|
10
|
+
static excluded(path: string): boolean;
|
|
11
|
+
blocked(path: string): boolean;
|
|
12
|
+
ignored(path: string, directory: boolean, absolutePath?: string): Promise<boolean>;
|
|
13
|
+
resolveLink(absolutePath: string): Promise<string>;
|
|
14
|
+
ignoreContents(path: string): Buffer | undefined;
|
|
15
|
+
private scope;
|
|
16
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { lstat, open, realpath } from 'node:fs/promises';
|
|
2
|
+
import { constants } from 'node:fs';
|
|
3
|
+
import { join, relative, sep } from 'node:path';
|
|
4
|
+
import ignore from 'ignore';
|
|
5
|
+
/** Read a bounded snapshot; never follow a file symlink or silently accept a size change. */
|
|
6
|
+
export async function readLocalFile(absolutePath, size, signal) {
|
|
7
|
+
const file = await open(absolutePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
8
|
+
try {
|
|
9
|
+
const stat = await file.stat();
|
|
10
|
+
if (!stat.isFile() || stat.size !== size)
|
|
11
|
+
throw new Error(`App file changed: ${absolutePath}`);
|
|
12
|
+
const data = Buffer.alloc(size + 1);
|
|
13
|
+
let length = 0;
|
|
14
|
+
while (length < data.length) {
|
|
15
|
+
signal?.throwIfAborted();
|
|
16
|
+
const { bytesRead } = await file.read(data, length, data.length - length, null);
|
|
17
|
+
if (!bytesRead)
|
|
18
|
+
break;
|
|
19
|
+
length += bytesRead;
|
|
20
|
+
}
|
|
21
|
+
if (length !== size)
|
|
22
|
+
throw new Error(`App file changed: ${absolutePath}`);
|
|
23
|
+
return data.subarray(0, length);
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
await file.close();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Project ignore rules, independent of Git's index and each developer's global configuration. */
|
|
30
|
+
export class SourceFileSelection {
|
|
31
|
+
root;
|
|
32
|
+
outputDirectory;
|
|
33
|
+
signal;
|
|
34
|
+
scopes = new Map();
|
|
35
|
+
constructor(root, outputDirectory, signal) {
|
|
36
|
+
this.root = root;
|
|
37
|
+
this.outputDirectory = outputDirectory;
|
|
38
|
+
this.signal = signal;
|
|
39
|
+
}
|
|
40
|
+
static excluded(path) {
|
|
41
|
+
return (path
|
|
42
|
+
.split('/')
|
|
43
|
+
.some((part) => /^(?:\.git|node_modules|\.aws|\.ssh|\.sst|\.cache|\.next|\.turbo|coverage|\.DS_Store)$/i.test(part) ||
|
|
44
|
+
/^(?:\.npmrc|\.netrc|CLAUDE\.local\.md)$/i.test(part) ||
|
|
45
|
+
(/^\.env(?:\..*)?$/i.test(part) && !/\.(?:example|sample|template)$/i.test(part)) ||
|
|
46
|
+
/\.(?:pem|key|p12|pfx)$/i.test(part)) ||
|
|
47
|
+
/(?:^|\/)\.claude\/(?:settings\.local\.json|worktrees|projects)(?:\/|$)/i.test(path) ||
|
|
48
|
+
/(?:^|\/)\.codex\/(?:auth\.json|history\.jsonl|session_index\.jsonl|sessions|logs)(?:\/|$)/i.test(path) ||
|
|
49
|
+
/(?:^|\/)\.gemini\/(?:oauth_creds\.json|google_accounts\.json|tmp)(?:\/|$)/i.test(path));
|
|
50
|
+
}
|
|
51
|
+
blocked(path) {
|
|
52
|
+
const output = this.outputDirectory.toLowerCase();
|
|
53
|
+
return (SourceFileSelection.excluded(path) || path.toLowerCase() === output || path.toLowerCase().startsWith(`${output}/`));
|
|
54
|
+
}
|
|
55
|
+
async ignored(path, directory, absolutePath = join(this.root, path)) {
|
|
56
|
+
const physical = relative(this.root, absolutePath).split(sep).join('/');
|
|
57
|
+
if (physical !== path && (this.blocked(physical) || (await this.ignored(physical, directory))))
|
|
58
|
+
return true;
|
|
59
|
+
// Retain visited ignore files so checkout inherits the same project selection rules.
|
|
60
|
+
const parts = path.split('/');
|
|
61
|
+
const scopes = [];
|
|
62
|
+
for (let depth = 0; depth < parts.length; depth++) {
|
|
63
|
+
const prefix = parts.slice(0, depth).join('/');
|
|
64
|
+
scopes.push({ prefix, rules: (await this.scope(prefix)).rules });
|
|
65
|
+
if (depth === parts.length - 1 && parts[depth] === '.gitignore')
|
|
66
|
+
return false;
|
|
67
|
+
const candidate = parts.slice(0, depth + 1).join('/');
|
|
68
|
+
const suffix = depth < parts.length - 1 || directory ? '/' : '';
|
|
69
|
+
let ignored = false;
|
|
70
|
+
for (const scope of scopes) {
|
|
71
|
+
const result = scope.rules.test(candidate.slice(scope.prefix ? scope.prefix.length + 1 : 0) + suffix);
|
|
72
|
+
if (result.ignored || result.unignored)
|
|
73
|
+
ignored = result.ignored;
|
|
74
|
+
}
|
|
75
|
+
if (ignored)
|
|
76
|
+
return true; // A child rule cannot reinclude a pruned parent directory.
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
async resolveLink(absolutePath) {
|
|
81
|
+
const target = await realpath(absolutePath);
|
|
82
|
+
const path = relative(this.root, target).split(sep).join('/');
|
|
83
|
+
if (!path || path === '..' || path.startsWith('../') || path.startsWith('/') || /^[A-Za-z]:/.test(path)) {
|
|
84
|
+
throw new Error('Source symlinks must target files or directories inside the project.');
|
|
85
|
+
}
|
|
86
|
+
if (this.blocked(path) || (await this.ignored(path, (await lstat(target)).isDirectory()))) {
|
|
87
|
+
throw new Error(`Source symlink target is excluded: ${path}`);
|
|
88
|
+
}
|
|
89
|
+
return target;
|
|
90
|
+
}
|
|
91
|
+
ignoreContents(path) {
|
|
92
|
+
const parts = path.split('/');
|
|
93
|
+
if (parts.pop() !== '.gitignore')
|
|
94
|
+
return undefined;
|
|
95
|
+
return this.scopes.get(parts.join('/'))?.contents;
|
|
96
|
+
}
|
|
97
|
+
async scope(prefix) {
|
|
98
|
+
const existing = this.scopes.get(prefix);
|
|
99
|
+
if (existing)
|
|
100
|
+
return existing;
|
|
101
|
+
const path = join(this.root, prefix, '.gitignore');
|
|
102
|
+
const stat = await lstat(path).catch((error) => {
|
|
103
|
+
if (error.code === 'ENOENT')
|
|
104
|
+
return null;
|
|
105
|
+
throw error;
|
|
106
|
+
});
|
|
107
|
+
if (stat && (!stat.isFile() || stat.size > 4 * 1024 * 1024))
|
|
108
|
+
throw new Error('Invalid .gitignore file.');
|
|
109
|
+
const contents = stat ? await readLocalFile(path, stat.size, this.signal) : undefined;
|
|
110
|
+
const value = { rules: ignore({ ignorecase: false }).add(contents?.toString('utf8') ?? ''), contents };
|
|
111
|
+
this.scopes.set(prefix, value);
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { components } from './generated/openapi.js';
|
|
2
2
|
type PublicApiSchema<Name extends keyof components['schemas']> = components['schemas'][Name];
|
|
3
|
+
export type AppUser = PublicApiSchema<'AppUser'>;
|
|
4
|
+
export type AppGrants = PublicApiSchema<'AppGrants'>;
|
|
5
|
+
export type AppAccess = PublicApiSchema<'AppAccess'>;
|
|
6
|
+
export type AppAddressSettings = PublicApiSchema<'AppAddressSettings'>;
|
|
7
|
+
export type AppAddressSetRequest = PublicApiSchema<'AppAddressSetRequest'>;
|
|
8
|
+
export type AppAccessSetRequest = PublicApiSchema<'AppAccessSetRequest'>;
|
|
9
|
+
export type AppGrantSetRequest = PublicApiSchema<'AppGrantSetRequest'>;
|
|
3
10
|
export type BusinessAction = PublicApiSchema<'BusinessAction'>;
|
|
4
11
|
export type BusinessActionList = PublicApiSchema<'BusinessActionList'>;
|
|
5
12
|
export type ActionCreateRequest = PublicApiSchema<'ActionCreateRequest'>;
|
|
@@ -41,6 +48,12 @@ export type FileDatasetCreateRequest = Omit<Extract<DatasetApiCreateRequest, {
|
|
|
41
48
|
sizeBytes?: number;
|
|
42
49
|
headers?: Record<string, string>;
|
|
43
50
|
};
|
|
51
|
+
export type FileDatasetMetadataCreateRequest = Extract<DatasetApiCreateRequest, {
|
|
52
|
+
type: 'file';
|
|
53
|
+
}> & {
|
|
54
|
+
data?: undefined;
|
|
55
|
+
headers?: undefined;
|
|
56
|
+
};
|
|
44
57
|
export type DatasetCreateRequest = CustomDatasetCreateRequest | FileDatasetCreateRequest;
|
|
45
58
|
export type DatasetWriteTablesRequest = PublicApiSchema<'DatasetWriteTablesRequest'>;
|
|
46
59
|
export type DatasetWriteTablesResponse = PublicApiSchema<'DatasetWriteTablesResponse'>;
|
|
@@ -106,6 +119,9 @@ export type AppDeleted = PublicApiSchema<'AppDeleted'>;
|
|
|
106
119
|
export type AppUploadsRequest = PublicApiSchema<'AppUploadsRequest'>;
|
|
107
120
|
export type AppUploads = PublicApiSchema<'AppUploads'>;
|
|
108
121
|
export type AppSiteSaveRequest = PublicApiSchema<'AppSiteSaveRequest'>;
|
|
122
|
+
export type AppSource = AppSiteSaveRequest['source'];
|
|
123
|
+
export type AppSourceFileQuery = PublicApiSchema<'AppSourceFileQuery'>;
|
|
124
|
+
export type AppSourceFileContent = PublicApiSchema<'AppSourceFileContent'>;
|
|
109
125
|
export type AppUploadFile = {
|
|
110
126
|
path: string;
|
|
111
127
|
data: string | Blob | Uint8Array;
|
|
@@ -134,4 +150,7 @@ export type WriteSyncRun = PublicApiSchema<'WriteSyncRun'>;
|
|
|
134
150
|
export type WriteSyncRunList = PublicApiSchema<'WriteSyncRunList'>;
|
|
135
151
|
export type WriteSyncRunQuery = PublicApiSchema<'WriteSyncRunQuery'>;
|
|
136
152
|
export type WriteSyncExecution = PublicApiSchema<'WriteSyncExecution'>;
|
|
153
|
+
export type ChatSendRequest = PublicApiSchema<'ChatSendRequest'>;
|
|
154
|
+
export type ChatDecisionRequest = PublicApiSchema<'ChatDecisionRequest'>;
|
|
155
|
+
export type ChatResponse = PublicApiSchema<'ChatResponse'>;
|
|
137
156
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@permute/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "TypeScript client for the Permute public API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"typecheck": "bun run scripts/typecheck.ts"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"ajv": "^8.20.0"
|
|
57
|
+
"ajv": "^8.20.0",
|
|
58
|
+
"ignore": "7.0.5"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@types/bun": "1.3.13",
|