@helmr/sdk 0.1.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/LICENSE +190 -0
- package/README.md +73 -0
- package/dist/compile.d.ts +9 -0
- package/dist/compile.js +503 -0
- package/dist/config.d.ts +8 -0
- package/dist/fuzzy.d.ts +1 -0
- package/dist/fuzzy.js +29 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +1023 -0
- package/dist/internal.d.ts +239 -0
- package/dist/internal.js +345 -0
- package/dist/runtime/client.d.ts +74 -0
- package/dist/runtime/errors.d.ts +13 -0
- package/dist/runtime/run.d.ts +195 -0
- package/dist/runtime/source.d.ts +7 -0
- package/dist/sandbox.d.ts +3 -0
- package/dist/schema/task.d.ts +20 -0
- package/dist/task.d.ts +3 -0
- package/dist/trigger.d.ts +25 -0
- package/package.json +48 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
export interface CacheMount {
|
|
2
|
+
readonly id: string;
|
|
3
|
+
}
|
|
4
|
+
export type CacheBuilder = CacheMount;
|
|
5
|
+
export interface CacheMountBinding {
|
|
6
|
+
readonly mountPath: string;
|
|
7
|
+
readonly cache: CacheMount;
|
|
8
|
+
}
|
|
9
|
+
export interface SecretMountBinding {
|
|
10
|
+
readonly mountPath: string;
|
|
11
|
+
readonly secret: string;
|
|
12
|
+
}
|
|
13
|
+
export interface SourceDirectoryOptions {
|
|
14
|
+
readonly ignore?: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
export interface WorkspaceSpec {
|
|
17
|
+
readonly kind: "github";
|
|
18
|
+
readonly repository: string;
|
|
19
|
+
readonly ref: string;
|
|
20
|
+
readonly subpath?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface SourceFileRef {
|
|
23
|
+
readonly path: string;
|
|
24
|
+
}
|
|
25
|
+
export interface SourceDirRef {
|
|
26
|
+
readonly path: string;
|
|
27
|
+
readonly ignore: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
export interface SourceCapabilities {
|
|
30
|
+
file(path: string): SourceFileRef;
|
|
31
|
+
directory(path: string, opts?: SourceDirectoryOptions): SourceDirRef;
|
|
32
|
+
}
|
|
33
|
+
export interface WorkspaceCapabilities {
|
|
34
|
+
github(repo: string, opts: {
|
|
35
|
+
readonly ref: string;
|
|
36
|
+
readonly subpath?: string;
|
|
37
|
+
}): WorkspaceSpec;
|
|
38
|
+
}
|
|
39
|
+
export declare class ApprovalTimeoutError extends Error {
|
|
40
|
+
constructor(message: string);
|
|
41
|
+
static [Symbol.hasInstance](value: unknown): boolean;
|
|
42
|
+
}
|
|
43
|
+
export declare class MessageTimeoutError extends Error {
|
|
44
|
+
constructor(message: string);
|
|
45
|
+
static [Symbol.hasInstance](value: unknown): boolean;
|
|
46
|
+
}
|
|
47
|
+
export declare class ConcurrentWaitError extends Error {
|
|
48
|
+
constructor(message: string);
|
|
49
|
+
static [Symbol.hasInstance](value: unknown): boolean;
|
|
50
|
+
}
|
|
51
|
+
export type Placement = {
|
|
52
|
+
readonly env: string;
|
|
53
|
+
} | {
|
|
54
|
+
readonly file: string;
|
|
55
|
+
readonly mode?: string;
|
|
56
|
+
readonly owner?: string;
|
|
57
|
+
} | {
|
|
58
|
+
readonly dir: string;
|
|
59
|
+
readonly mode?: string;
|
|
60
|
+
readonly owner?: string;
|
|
61
|
+
};
|
|
62
|
+
export type SecretDecls = Record<string, Placement>;
|
|
63
|
+
export declare function validateSecretName(name: string, label?: string): void;
|
|
64
|
+
export interface TaskContext {
|
|
65
|
+
readonly wait: {
|
|
66
|
+
approval(message: string, opts?: {
|
|
67
|
+
readonly timeout?: number;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
readonly approved: boolean;
|
|
70
|
+
readonly approvedBy: string;
|
|
71
|
+
readonly at: Date;
|
|
72
|
+
}>;
|
|
73
|
+
message(prompt?: string, opts?: {
|
|
74
|
+
readonly timeout?: number;
|
|
75
|
+
}): Promise<{
|
|
76
|
+
readonly text: string;
|
|
77
|
+
readonly sentBy: string;
|
|
78
|
+
readonly at: Date;
|
|
79
|
+
readonly attachments: readonly unknown[];
|
|
80
|
+
}>;
|
|
81
|
+
};
|
|
82
|
+
emit(event: EmitEvent): void;
|
|
83
|
+
readonly log: {
|
|
84
|
+
info(...args: unknown[]): void;
|
|
85
|
+
warn(...args: unknown[]): void;
|
|
86
|
+
error(...args: unknown[]): void;
|
|
87
|
+
};
|
|
88
|
+
readonly signal: AbortSignal;
|
|
89
|
+
readonly run: {
|
|
90
|
+
readonly id: string;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export type EmitContent = {
|
|
94
|
+
readonly type: "text";
|
|
95
|
+
readonly text: string;
|
|
96
|
+
} | {
|
|
97
|
+
readonly type: "image";
|
|
98
|
+
readonly data: string;
|
|
99
|
+
readonly mimeType?: string;
|
|
100
|
+
} | {
|
|
101
|
+
readonly type: "tool_use";
|
|
102
|
+
readonly name: string;
|
|
103
|
+
readonly input?: unknown;
|
|
104
|
+
} | {
|
|
105
|
+
readonly type: "tool_result";
|
|
106
|
+
readonly name?: string;
|
|
107
|
+
readonly result?: unknown;
|
|
108
|
+
} | {
|
|
109
|
+
readonly type: string;
|
|
110
|
+
readonly [key: string]: unknown;
|
|
111
|
+
};
|
|
112
|
+
export interface EmitEvent {
|
|
113
|
+
readonly type: string;
|
|
114
|
+
readonly content: readonly EmitContent[];
|
|
115
|
+
}
|
|
116
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
117
|
+
export interface TaskConfig<TPayload = unknown, TOutput = unknown, TSecrets extends SecretDecls = Record<never, never>> {
|
|
118
|
+
readonly id: string;
|
|
119
|
+
readonly sandbox: SandboxBuilder;
|
|
120
|
+
readonly maxDuration?: number;
|
|
121
|
+
readonly secrets?: TSecrets;
|
|
122
|
+
readonly run: (payload: TPayload, ctx: TaskContext) => MaybePromise<TOutput>;
|
|
123
|
+
}
|
|
124
|
+
export type Task<TPayload = unknown, TOutput = unknown, TSecrets extends SecretDecls = Record<never, never>> = TaskConfig<TPayload, TOutput, TSecrets>;
|
|
125
|
+
export type AnyTask = Task<any, any, SecretDecls>;
|
|
126
|
+
export type TaskPayload<TTask> = TTask extends Task<infer TPayload, any, any> ? TPayload : never;
|
|
127
|
+
export type TaskOutput<TTask> = TTask extends Task<any, infer TOutput, any> ? Awaited<TOutput> : never;
|
|
128
|
+
export declare const taskBrand: unique symbol;
|
|
129
|
+
export declare const taskOriginBrand: unique symbol;
|
|
130
|
+
export declare const configBrand: unique symbol;
|
|
131
|
+
export type ImageCopyInput = SourceFileRef | SourceDirRef | ImageBuilder;
|
|
132
|
+
export interface ImageRunOptions {
|
|
133
|
+
readonly cache?: readonly CacheMountBinding[];
|
|
134
|
+
readonly secrets?: readonly SecretMountBinding[];
|
|
135
|
+
}
|
|
136
|
+
export interface ImageBuilder {
|
|
137
|
+
from(ref: string): ImageBuilder;
|
|
138
|
+
run(argv: readonly string[], opts?: ImageRunOptions): ImageBuilder;
|
|
139
|
+
copy(dest: string, src: ImageCopyInput): ImageBuilder;
|
|
140
|
+
copyFrom(dest: string, src: ImageBuilder, srcPath: string): ImageBuilder;
|
|
141
|
+
workdir(path: string): ImageBuilder;
|
|
142
|
+
env(key: string, value: string): ImageBuilder;
|
|
143
|
+
user(name: string): ImageBuilder;
|
|
144
|
+
}
|
|
145
|
+
export interface SandboxBuilder {
|
|
146
|
+
image(img: ImageBuilder): SandboxBuilder;
|
|
147
|
+
workspace(mountPath?: string): SandboxBuilder;
|
|
148
|
+
resources(opts: {
|
|
149
|
+
readonly cpu?: number;
|
|
150
|
+
readonly memory?: string;
|
|
151
|
+
}): SandboxBuilder;
|
|
152
|
+
}
|
|
153
|
+
export type ImageBuildStep = {
|
|
154
|
+
readonly kind: "from";
|
|
155
|
+
readonly ref: string;
|
|
156
|
+
} | {
|
|
157
|
+
readonly kind: "run";
|
|
158
|
+
readonly argv: readonly string[];
|
|
159
|
+
readonly cache: readonly CacheMountBinding[];
|
|
160
|
+
readonly secrets: readonly SecretMountBinding[];
|
|
161
|
+
} | {
|
|
162
|
+
readonly kind: "copy";
|
|
163
|
+
readonly dest: string;
|
|
164
|
+
readonly source: ImageCopyInput;
|
|
165
|
+
} | {
|
|
166
|
+
readonly kind: "copyFrom";
|
|
167
|
+
readonly dest: string;
|
|
168
|
+
readonly source: ImageBuilder;
|
|
169
|
+
readonly srcPath: string;
|
|
170
|
+
} | {
|
|
171
|
+
readonly kind: "workdir";
|
|
172
|
+
readonly path: string;
|
|
173
|
+
} | {
|
|
174
|
+
readonly kind: "env";
|
|
175
|
+
readonly key: string;
|
|
176
|
+
readonly value: string;
|
|
177
|
+
} | {
|
|
178
|
+
readonly kind: "user";
|
|
179
|
+
readonly name: string;
|
|
180
|
+
};
|
|
181
|
+
export interface SandboxWorkspace {
|
|
182
|
+
readonly mountPath: string;
|
|
183
|
+
}
|
|
184
|
+
export interface SandboxResources {
|
|
185
|
+
readonly cpu?: number;
|
|
186
|
+
readonly memory?: string;
|
|
187
|
+
}
|
|
188
|
+
export declare function markTask<TPayload, TOutput, TSecrets extends SecretDecls>(config: TaskConfig<TPayload, TOutput, TSecrets>): Task<TPayload, Awaited<TOutput>, TSecrets>;
|
|
189
|
+
export declare function isTaskDefinition(value: unknown): value is AnyTask & {
|
|
190
|
+
readonly [taskBrand]: true;
|
|
191
|
+
};
|
|
192
|
+
export declare function taskOriginFile(value: unknown): string | null;
|
|
193
|
+
export interface HelmrConfig {
|
|
194
|
+
readonly project?: string;
|
|
195
|
+
readonly dirs: readonly string[];
|
|
196
|
+
readonly ignorePatterns?: readonly string[];
|
|
197
|
+
}
|
|
198
|
+
export declare function markConfig(config: HelmrConfig): HelmrConfig;
|
|
199
|
+
export declare function isConfigDefinition(value: unknown): value is HelmrConfig & {
|
|
200
|
+
readonly [configBrand]: true;
|
|
201
|
+
};
|
|
202
|
+
export declare class ImageBuilderImpl implements ImageBuilder {
|
|
203
|
+
readonly id: string;
|
|
204
|
+
readonly steps: readonly ImageBuildStep[];
|
|
205
|
+
constructor(id: string, steps?: readonly ImageBuildStep[]);
|
|
206
|
+
from(ref: string): ImageBuilder;
|
|
207
|
+
run(argv: readonly string[], opts?: ImageRunOptions): ImageBuilder;
|
|
208
|
+
copy(dest: string, src: ImageCopyInput): ImageBuilder;
|
|
209
|
+
copyFrom(dest: string, src: ImageBuilder, srcPath: string): ImageBuilder;
|
|
210
|
+
workdir(path: string): ImageBuilder;
|
|
211
|
+
env(key: string, value: string): ImageBuilder;
|
|
212
|
+
user(name: string): ImageBuilder;
|
|
213
|
+
}
|
|
214
|
+
export declare class SandboxBuilderImpl implements SandboxBuilder {
|
|
215
|
+
readonly id: string;
|
|
216
|
+
readonly imageBuilder: ImageBuilderImpl | undefined;
|
|
217
|
+
readonly workspaceBinding: SandboxWorkspace | undefined;
|
|
218
|
+
readonly resourceSpec: SandboxResources | undefined;
|
|
219
|
+
constructor(id: string, imageBuilder?: ImageBuilderImpl, workspaceBinding?: SandboxWorkspace, resourceSpec?: SandboxResources);
|
|
220
|
+
image(img: ImageBuilder): SandboxBuilder;
|
|
221
|
+
workspace(mountPath?: string): SandboxBuilder;
|
|
222
|
+
resources(opts: {
|
|
223
|
+
readonly cpu?: number;
|
|
224
|
+
readonly memory?: string;
|
|
225
|
+
}): SandboxBuilder;
|
|
226
|
+
}
|
|
227
|
+
export declare class SourceFileRefImpl implements SourceFileRef {
|
|
228
|
+
readonly path: string;
|
|
229
|
+
constructor(path: string);
|
|
230
|
+
}
|
|
231
|
+
export declare class SourceDirRefImpl implements SourceDirRef {
|
|
232
|
+
readonly path: string;
|
|
233
|
+
readonly ignore: readonly string[];
|
|
234
|
+
constructor(path: string, ignore: readonly string[]);
|
|
235
|
+
}
|
|
236
|
+
export declare function isImageBuilder(value: unknown): value is ImageBuilderImpl;
|
|
237
|
+
export declare function isSandboxBuilder(value: unknown): value is SandboxBuilderImpl;
|
|
238
|
+
export declare function isSourceFileRef(value: unknown): value is SourceFileRefImpl;
|
|
239
|
+
export declare function isSourceDirRef(value: unknown): value is SourceDirRefImpl;
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
// sdk/typescript/src/schema/task.ts
|
|
2
|
+
var TASK_ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$";
|
|
3
|
+
var TASK_ID_MAX_LENGTH = 128;
|
|
4
|
+
var DEFAULT_MAX_DURATION_SECONDS = 900;
|
|
5
|
+
var MIN_MAX_DURATION_SECONDS = 5;
|
|
6
|
+
var MAX_DURATION_SECONDS = 86400;
|
|
7
|
+
|
|
8
|
+
class TaskIdError extends Error {
|
|
9
|
+
name = "TaskIdError";
|
|
10
|
+
value;
|
|
11
|
+
constructor(value) {
|
|
12
|
+
super(`task id must match ${TASK_ID_PATTERN}: ${JSON.stringify(value)}`);
|
|
13
|
+
this.value = value;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function validateTaskId(value) {
|
|
17
|
+
if (!isValidTaskId(value)) {
|
|
18
|
+
throw new TaskIdError(value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function isValidTaskId(value) {
|
|
22
|
+
if (value.length === 0 || value.length > TASK_ID_MAX_LENGTH) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const first = value.charCodeAt(0);
|
|
26
|
+
if (!isAsciiAlnum(first)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
for (let index = 1;index < value.length; index += 1) {
|
|
30
|
+
const code = value.charCodeAt(index);
|
|
31
|
+
if (!(isAsciiAlnum(code) || code === 46 || code === 95 || code === 45)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class TaskMaxDurationError extends Error {
|
|
39
|
+
name = "TaskMaxDurationError";
|
|
40
|
+
value;
|
|
41
|
+
label;
|
|
42
|
+
constructor(value, label = "task maxDuration") {
|
|
43
|
+
super(`${label} must be an integer number of seconds between ${MIN_MAX_DURATION_SECONDS} and ${MAX_DURATION_SECONDS}`);
|
|
44
|
+
this.value = value;
|
|
45
|
+
this.label = label;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function readOptionalMaxDurationSeconds(value, label = "task maxDuration") {
|
|
49
|
+
if (value === undefined) {
|
|
50
|
+
return DEFAULT_MAX_DURATION_SECONDS;
|
|
51
|
+
}
|
|
52
|
+
if (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value) && value >= MIN_MAX_DURATION_SECONDS && value <= MAX_DURATION_SECONDS) {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
throw new TaskMaxDurationError(value, label);
|
|
56
|
+
}
|
|
57
|
+
function validateOptionalMaxDurationSeconds(value, label = "task maxDuration") {
|
|
58
|
+
readOptionalMaxDurationSeconds(value, label);
|
|
59
|
+
}
|
|
60
|
+
function isAsciiAlnum(code) {
|
|
61
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// sdk/typescript/src/internal.ts
|
|
65
|
+
var approvalTimeoutErrorBrand = Symbol.for("helmr.sdk.ApprovalTimeoutError");
|
|
66
|
+
var messageTimeoutErrorBrand = Symbol.for("helmr.sdk.MessageTimeoutError");
|
|
67
|
+
var concurrentWaitErrorBrand = Symbol.for("helmr.sdk.ConcurrentWaitError");
|
|
68
|
+
|
|
69
|
+
class ApprovalTimeoutError extends Error {
|
|
70
|
+
constructor(message) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.name = "ApprovalTimeoutError";
|
|
73
|
+
Object.defineProperty(this, approvalTimeoutErrorBrand, { value: true });
|
|
74
|
+
}
|
|
75
|
+
static [Symbol.hasInstance](value) {
|
|
76
|
+
return this === ApprovalTimeoutError && typeof value === "object" && value !== null && approvalTimeoutErrorBrand in value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class MessageTimeoutError extends Error {
|
|
81
|
+
constructor(message) {
|
|
82
|
+
super(message);
|
|
83
|
+
this.name = "MessageTimeoutError";
|
|
84
|
+
Object.defineProperty(this, messageTimeoutErrorBrand, { value: true });
|
|
85
|
+
}
|
|
86
|
+
static [Symbol.hasInstance](value) {
|
|
87
|
+
return this === MessageTimeoutError && typeof value === "object" && value !== null && messageTimeoutErrorBrand in value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class ConcurrentWaitError extends Error {
|
|
92
|
+
constructor(message) {
|
|
93
|
+
super(message);
|
|
94
|
+
this.name = "ConcurrentWaitError";
|
|
95
|
+
Object.defineProperty(this, concurrentWaitErrorBrand, { value: true });
|
|
96
|
+
}
|
|
97
|
+
static [Symbol.hasInstance](value) {
|
|
98
|
+
return this === ConcurrentWaitError && typeof value === "object" && value !== null && concurrentWaitErrorBrand in value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function validateSecretName(name, label = "secret name") {
|
|
102
|
+
if (name.length === 0) {
|
|
103
|
+
throw new Error(`${label} must not be empty`);
|
|
104
|
+
}
|
|
105
|
+
if (name.length > 128) {
|
|
106
|
+
throw new Error(`${label} must be at most 128 characters`);
|
|
107
|
+
}
|
|
108
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
109
|
+
throw new Error(`${label} must match /^[A-Za-z_][A-Za-z0-9_]*$/`);
|
|
110
|
+
}
|
|
111
|
+
const upper = name.toUpperCase();
|
|
112
|
+
if (upper === "CON" || upper === "PRN" || upper === "AUX" || upper === "NUL" || /^COM[1-9]$/.test(upper) || /^LPT[1-9]$/.test(upper)) {
|
|
113
|
+
throw new Error(`${label} is reserved`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function validateImageBuildSecretRef(name) {
|
|
117
|
+
validateSecretName(name, "image build secret ref");
|
|
118
|
+
return name;
|
|
119
|
+
}
|
|
120
|
+
var RESERVED_WORKSPACE_MOUNT_PATHS = [
|
|
121
|
+
"/dev",
|
|
122
|
+
"/opt/helmr",
|
|
123
|
+
"/proc",
|
|
124
|
+
"/run",
|
|
125
|
+
"/sys",
|
|
126
|
+
"/tmp",
|
|
127
|
+
"/.helmr-old-root"
|
|
128
|
+
];
|
|
129
|
+
function normalizeWorkspaceMountPath(raw) {
|
|
130
|
+
if (raw.length === 0) {
|
|
131
|
+
throw new Error("sandbox.workspace() mountPath is empty");
|
|
132
|
+
}
|
|
133
|
+
if (raw.includes("\x00")) {
|
|
134
|
+
throw new Error("sandbox.workspace() mountPath contains NUL");
|
|
135
|
+
}
|
|
136
|
+
if (!raw.startsWith("/")) {
|
|
137
|
+
throw new Error(`sandbox.workspace() mountPath must be absolute: ${raw}`);
|
|
138
|
+
}
|
|
139
|
+
const parts = [];
|
|
140
|
+
for (const part of raw.split("/")) {
|
|
141
|
+
if (part === "" || part === ".")
|
|
142
|
+
continue;
|
|
143
|
+
if (part === "..") {
|
|
144
|
+
throw new Error(`sandbox.workspace() mountPath contains unsafe path components: ${raw}`);
|
|
145
|
+
}
|
|
146
|
+
parts.push(part);
|
|
147
|
+
}
|
|
148
|
+
if (parts.length === 0) {
|
|
149
|
+
throw new Error("sandbox.workspace() mountPath cannot be /");
|
|
150
|
+
}
|
|
151
|
+
const normalized = `/${parts.join("/")}`;
|
|
152
|
+
if (RESERVED_WORKSPACE_MOUNT_PATHS.some((reserved) => normalized === reserved || normalized.startsWith(`${reserved}/`))) {
|
|
153
|
+
throw new Error(`sandbox.workspace() mountPath conflicts with reserved runtime mount paths: ${normalized}`);
|
|
154
|
+
}
|
|
155
|
+
return normalized;
|
|
156
|
+
}
|
|
157
|
+
var taskBrand = Symbol.for("helmr.sdk.Task");
|
|
158
|
+
var taskOriginBrand = Symbol.for("helmr.sdk.TaskOrigin");
|
|
159
|
+
var configBrand = Symbol.for("helmr.sdk.Config");
|
|
160
|
+
var imageBuilderBrand = Symbol.for("helmr.sdk.ImageBuilder");
|
|
161
|
+
var sandboxBuilderBrand = Symbol.for("helmr.sdk.SandboxBuilder");
|
|
162
|
+
var sourceFileRefBrand = Symbol.for("helmr.sdk.SourceFileRef");
|
|
163
|
+
var sourceDirRefBrand = Symbol.for("helmr.sdk.SourceDirRef");
|
|
164
|
+
function markTask(config) {
|
|
165
|
+
validateTaskId(config.id);
|
|
166
|
+
validateOptionalMaxDurationSeconds(config.maxDuration);
|
|
167
|
+
Object.defineProperty(config, taskBrand, { value: true });
|
|
168
|
+
Object.defineProperty(config, taskOriginBrand, { value: captureTaskOrigin() });
|
|
169
|
+
return config;
|
|
170
|
+
}
|
|
171
|
+
function isTaskDefinition(value) {
|
|
172
|
+
return hasBrand(value, taskBrand);
|
|
173
|
+
}
|
|
174
|
+
function taskOriginFile(value) {
|
|
175
|
+
if (!isTaskDefinition(value)) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const origin = value[taskOriginBrand];
|
|
179
|
+
return typeof origin === "string" && origin.length > 0 ? origin : null;
|
|
180
|
+
}
|
|
181
|
+
function markConfig(config) {
|
|
182
|
+
Object.defineProperty(config, configBrand, { value: true });
|
|
183
|
+
return config;
|
|
184
|
+
}
|
|
185
|
+
function isConfigDefinition(value) {
|
|
186
|
+
return hasBrand(value, configBrand);
|
|
187
|
+
}
|
|
188
|
+
function captureTaskOrigin() {
|
|
189
|
+
const stack = new Error().stack ?? "";
|
|
190
|
+
for (const line of stack.split(`
|
|
191
|
+
`).slice(1)) {
|
|
192
|
+
const file = stackFrameFile(line);
|
|
193
|
+
if (file === null || isSdkInternalFrame(file)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
return file;
|
|
197
|
+
}
|
|
198
|
+
return "unknown";
|
|
199
|
+
}
|
|
200
|
+
function stackFrameFile(line) {
|
|
201
|
+
const match = /\(?((?:file:\/\/)?\/[^():]+):\d+:\d+\)?$/.exec(line.trim());
|
|
202
|
+
if (!match?.[1]) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
return match[1].startsWith("file://") ? decodeURIComponent(new URL(match[1]).pathname) : match[1];
|
|
206
|
+
}
|
|
207
|
+
function isSdkInternalFrame(file) {
|
|
208
|
+
return file.includes("/sdk/typescript/src/internal.ts") || file.includes("/sdk/typescript/src/task.ts") || file.includes("/sdk/typescript/src/index.ts") || file.includes("/runtime/typescript/src/");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
class ImageBuilderImpl {
|
|
212
|
+
id;
|
|
213
|
+
steps;
|
|
214
|
+
constructor(id, steps = []) {
|
|
215
|
+
Object.defineProperty(this, imageBuilderBrand, { value: true });
|
|
216
|
+
this.id = id;
|
|
217
|
+
this.steps = steps;
|
|
218
|
+
}
|
|
219
|
+
from(ref) {
|
|
220
|
+
return new ImageBuilderImpl(this.id, [...this.steps, { kind: "from", ref }]);
|
|
221
|
+
}
|
|
222
|
+
run(argv, opts = {}) {
|
|
223
|
+
return new ImageBuilderImpl(this.id, [
|
|
224
|
+
...this.steps,
|
|
225
|
+
{
|
|
226
|
+
kind: "run",
|
|
227
|
+
argv: [...argv],
|
|
228
|
+
cache: (opts.cache ?? []).map((binding) => ({
|
|
229
|
+
mountPath: binding.mountPath,
|
|
230
|
+
cache: { id: binding.cache.id }
|
|
231
|
+
})),
|
|
232
|
+
secrets: (opts.secrets ?? []).map((binding) => ({
|
|
233
|
+
mountPath: binding.mountPath,
|
|
234
|
+
secret: validateImageBuildSecretRef(binding.secret)
|
|
235
|
+
}))
|
|
236
|
+
}
|
|
237
|
+
]);
|
|
238
|
+
}
|
|
239
|
+
copy(dest, src) {
|
|
240
|
+
return new ImageBuilderImpl(this.id, [...this.steps, { kind: "copy", dest, source: src }]);
|
|
241
|
+
}
|
|
242
|
+
copyFrom(dest, src, srcPath) {
|
|
243
|
+
if (!isImageBuilder(src)) {
|
|
244
|
+
throw new Error("image.copyFrom() requires an ImageBuilder created by image()");
|
|
245
|
+
}
|
|
246
|
+
return new ImageBuilderImpl(this.id, [
|
|
247
|
+
...this.steps,
|
|
248
|
+
{ kind: "copyFrom", dest, source: src, srcPath }
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
workdir(path) {
|
|
252
|
+
return new ImageBuilderImpl(this.id, [...this.steps, { kind: "workdir", path }]);
|
|
253
|
+
}
|
|
254
|
+
env(key, value) {
|
|
255
|
+
return new ImageBuilderImpl(this.id, [...this.steps, { kind: "env", key, value }]);
|
|
256
|
+
}
|
|
257
|
+
user(name) {
|
|
258
|
+
return new ImageBuilderImpl(this.id, [...this.steps, { kind: "user", name }]);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
class SandboxBuilderImpl {
|
|
263
|
+
id;
|
|
264
|
+
imageBuilder;
|
|
265
|
+
workspaceBinding;
|
|
266
|
+
resourceSpec;
|
|
267
|
+
constructor(id, imageBuilder, workspaceBinding, resourceSpec) {
|
|
268
|
+
Object.defineProperty(this, sandboxBuilderBrand, { value: true });
|
|
269
|
+
this.id = id;
|
|
270
|
+
this.imageBuilder = imageBuilder;
|
|
271
|
+
this.workspaceBinding = workspaceBinding;
|
|
272
|
+
this.resourceSpec = resourceSpec;
|
|
273
|
+
}
|
|
274
|
+
image(img) {
|
|
275
|
+
if (!isImageBuilder(img)) {
|
|
276
|
+
throw new Error("sandbox.image() requires an ImageBuilder created by image()");
|
|
277
|
+
}
|
|
278
|
+
return new SandboxBuilderImpl(this.id, img, this.workspaceBinding, this.resourceSpec);
|
|
279
|
+
}
|
|
280
|
+
workspace(mountPath = "/workspace") {
|
|
281
|
+
return new SandboxBuilderImpl(this.id, this.imageBuilder, { mountPath: normalizeWorkspaceMountPath(mountPath) }, this.resourceSpec);
|
|
282
|
+
}
|
|
283
|
+
resources(opts) {
|
|
284
|
+
const resourceSpec = {
|
|
285
|
+
...opts.cpu === undefined ? {} : { cpu: opts.cpu },
|
|
286
|
+
...opts.memory === undefined ? {} : { memory: opts.memory }
|
|
287
|
+
};
|
|
288
|
+
return new SandboxBuilderImpl(this.id, this.imageBuilder, this.workspaceBinding, resourceSpec);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
class SourceFileRefImpl {
|
|
293
|
+
path;
|
|
294
|
+
constructor(path) {
|
|
295
|
+
Object.defineProperty(this, sourceFileRefBrand, { value: true });
|
|
296
|
+
this.path = path;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
class SourceDirRefImpl {
|
|
301
|
+
path;
|
|
302
|
+
ignore;
|
|
303
|
+
constructor(path, ignore) {
|
|
304
|
+
Object.defineProperty(this, sourceDirRefBrand, { value: true });
|
|
305
|
+
this.path = path;
|
|
306
|
+
this.ignore = ignore;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function isImageBuilder(value) {
|
|
310
|
+
return hasBrand(value, imageBuilderBrand);
|
|
311
|
+
}
|
|
312
|
+
function isSandboxBuilder(value) {
|
|
313
|
+
return hasBrand(value, sandboxBuilderBrand);
|
|
314
|
+
}
|
|
315
|
+
function isSourceFileRef(value) {
|
|
316
|
+
return hasBrand(value, sourceFileRefBrand);
|
|
317
|
+
}
|
|
318
|
+
function isSourceDirRef(value) {
|
|
319
|
+
return hasBrand(value, sourceDirRefBrand);
|
|
320
|
+
}
|
|
321
|
+
function hasBrand(value, brand) {
|
|
322
|
+
return value !== null && typeof value === "object" && value[brand] === true;
|
|
323
|
+
}
|
|
324
|
+
export {
|
|
325
|
+
validateSecretName,
|
|
326
|
+
taskOriginFile,
|
|
327
|
+
taskOriginBrand,
|
|
328
|
+
taskBrand,
|
|
329
|
+
markTask,
|
|
330
|
+
markConfig,
|
|
331
|
+
isTaskDefinition,
|
|
332
|
+
isSourceFileRef,
|
|
333
|
+
isSourceDirRef,
|
|
334
|
+
isSandboxBuilder,
|
|
335
|
+
isImageBuilder,
|
|
336
|
+
isConfigDefinition,
|
|
337
|
+
configBrand,
|
|
338
|
+
SourceFileRefImpl,
|
|
339
|
+
SourceDirRefImpl,
|
|
340
|
+
SandboxBuilderImpl,
|
|
341
|
+
MessageTimeoutError,
|
|
342
|
+
ImageBuilderImpl,
|
|
343
|
+
ConcurrentWaitError,
|
|
344
|
+
ApprovalTimeoutError
|
|
345
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type SecretDecls, type Task, type WorkspaceSpec } from "../internal.js";
|
|
2
|
+
import { type LogSnapshot, type ListRunEventsOptions, type ListRunsOptions, type PendingApprovalWaitpoint, type PendingMessageWaitpoint, type PendingWaitpointResponse, type RetrieveRunOptions, type RunHandle, type RunEvent, type RunSnapshot, type RunSummary, type RunWaitOptions, type SubscribeRunEventsOptions, type WaitpointApprovalOptions, type WaitpointReplyOptions } from "./run.js";
|
|
3
|
+
export interface HelmrClientOptions {
|
|
4
|
+
readonly url?: string;
|
|
5
|
+
readonly apiKey?: string;
|
|
6
|
+
}
|
|
7
|
+
export type TaskTriggerOptions<TPayload, TSecrets extends SecretDecls> = {
|
|
8
|
+
/**
|
|
9
|
+
* Payload is audit data: Helmr persists it in plaintext in the `run.created`
|
|
10
|
+
* event, DB, and events stream. Do not put secret values (tokens, API keys,
|
|
11
|
+
* credentials, or PII) in payload; use `secrets:` instead. Use payload for
|
|
12
|
+
* business context such as PR numbers, repo names, ticket ids, and other
|
|
13
|
+
* identifiers.
|
|
14
|
+
*/
|
|
15
|
+
readonly payload: TPayload;
|
|
16
|
+
readonly workspace: WorkspaceSpec;
|
|
17
|
+
} & ([keyof TSecrets] extends [never] ? {
|
|
18
|
+
readonly secrets?: Record<never, never>;
|
|
19
|
+
} : {
|
|
20
|
+
readonly secrets: {
|
|
21
|
+
readonly [K in keyof TSecrets]: string;
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
export interface WaitpointsApi {
|
|
25
|
+
readonly approve: {
|
|
26
|
+
(target: PendingApprovalWaitpoint, opts?: WaitpointApprovalOptions): Promise<void>;
|
|
27
|
+
(runId: string, waitpointId: string, opts?: WaitpointApprovalOptions): Promise<void>;
|
|
28
|
+
};
|
|
29
|
+
readonly deny: {
|
|
30
|
+
(target: PendingApprovalWaitpoint, opts?: WaitpointApprovalOptions): Promise<void>;
|
|
31
|
+
(runId: string, waitpointId: string, opts?: WaitpointApprovalOptions): Promise<void>;
|
|
32
|
+
};
|
|
33
|
+
readonly reply: {
|
|
34
|
+
(target: PendingMessageWaitpoint, opts: WaitpointReplyOptions): Promise<void>;
|
|
35
|
+
(runId: string, waitpointId: string, opts: WaitpointReplyOptions): Promise<void>;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export declare class HelmrClient {
|
|
39
|
+
#private;
|
|
40
|
+
constructor(options?: HelmrClientOptions);
|
|
41
|
+
readonly tasks: {
|
|
42
|
+
trigger: <TPayload, TOutput, TSecrets extends SecretDecls>(task: Task<TPayload, TOutput, TSecrets>, opts: TaskTriggerOptions<TPayload, TSecrets>) => Promise<RunHandle<TOutput>>;
|
|
43
|
+
};
|
|
44
|
+
readonly runs: {
|
|
45
|
+
retrieve: <TOutput = unknown>(idOrHandle: string | RunHandle<TOutput>, opts?: RetrieveRunOptions) => Promise<RunSnapshot<TOutput>>;
|
|
46
|
+
wait: <TOutput = unknown>(idOrHandle: string | RunHandle<TOutput>, opts?: RunWaitOptions) => Promise<RunSnapshot<TOutput>>;
|
|
47
|
+
list: (opts?: ListRunsOptions) => Promise<RunSummary[]>;
|
|
48
|
+
logs: {
|
|
49
|
+
retrieve: <TOutput = unknown>(idOrHandle: string | RunHandle<TOutput>, opts?: {
|
|
50
|
+
readonly signal?: AbortSignal;
|
|
51
|
+
}) => Promise<LogSnapshot>;
|
|
52
|
+
};
|
|
53
|
+
events: {
|
|
54
|
+
list: <TOutput = unknown>(idOrHandle: string | RunHandle<TOutput>, opts?: ListRunEventsOptions) => Promise<RunEvent[]>;
|
|
55
|
+
subscribe: <TOutput = unknown>(idOrHandle: string | RunHandle<TOutput>, opts?: SubscribeRunEventsOptions) => Promise<AsyncIterable<RunEvent>>;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
readonly waitpoints: WaitpointsApi;
|
|
59
|
+
}
|
|
60
|
+
export interface RunResponse {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly project_id?: string;
|
|
63
|
+
readonly environment_id?: string;
|
|
64
|
+
readonly task_id: string;
|
|
65
|
+
readonly status: string;
|
|
66
|
+
readonly exit_code?: number | null;
|
|
67
|
+
readonly created_at?: string;
|
|
68
|
+
readonly updated_at?: string;
|
|
69
|
+
readonly pending_wait?: PendingWaitpointResponse | null;
|
|
70
|
+
readonly output?: unknown;
|
|
71
|
+
}
|
|
72
|
+
export interface ListRunsResponse {
|
|
73
|
+
readonly runs: readonly RunResponse[];
|
|
74
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class RunNotFoundError extends Error {
|
|
2
|
+
readonly runId: string;
|
|
3
|
+
constructor(runId: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class AuthError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class TimeoutError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class UnsupportedTransportError extends Error {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|