@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
package/dist/compile.js
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
// sdk/typescript/src/compile.ts
|
|
2
|
+
import { create, toBinary } from "@bufbuild/protobuf";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import {
|
|
5
|
+
BundleSchema,
|
|
6
|
+
CacheMountBindingSchema,
|
|
7
|
+
CopyFromImageSchema,
|
|
8
|
+
CopySourceDirSchema,
|
|
9
|
+
CopySourceFileSchema,
|
|
10
|
+
DirPlacementSchema,
|
|
11
|
+
EnvSchema,
|
|
12
|
+
EnvPlacementSchema,
|
|
13
|
+
FilePlacementSchema,
|
|
14
|
+
FromSchema,
|
|
15
|
+
ImageSpecSchema,
|
|
16
|
+
ImageStepSchema,
|
|
17
|
+
PlacementSchema as BundlePlacementSchema,
|
|
18
|
+
PlatformSchema,
|
|
19
|
+
ResourcesSchema,
|
|
20
|
+
RunSchema,
|
|
21
|
+
SandboxSpecSchema,
|
|
22
|
+
SecretPlacementSchema as BundleSecretPlacementSchema,
|
|
23
|
+
SecretMountBindingSchema,
|
|
24
|
+
SecretRefSchema,
|
|
25
|
+
SourceDirRefSchema,
|
|
26
|
+
SourceFileRefSchema,
|
|
27
|
+
TaskSpecSchema,
|
|
28
|
+
UserSchema,
|
|
29
|
+
WorkdirSchema,
|
|
30
|
+
WorkspaceRuntimeBindingSchema
|
|
31
|
+
} from "@helmr/proto";
|
|
32
|
+
|
|
33
|
+
// sdk/typescript/src/schema/task.ts
|
|
34
|
+
var DEFAULT_MAX_DURATION_SECONDS = 900;
|
|
35
|
+
var MIN_MAX_DURATION_SECONDS = 5;
|
|
36
|
+
var MAX_DURATION_SECONDS = 86400;
|
|
37
|
+
class TaskMaxDurationError extends Error {
|
|
38
|
+
name = "TaskMaxDurationError";
|
|
39
|
+
value;
|
|
40
|
+
label;
|
|
41
|
+
constructor(value, label = "task maxDuration") {
|
|
42
|
+
super(`${label} must be an integer number of seconds between ${MIN_MAX_DURATION_SECONDS} and ${MAX_DURATION_SECONDS}`);
|
|
43
|
+
this.value = value;
|
|
44
|
+
this.label = label;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function readOptionalMaxDurationSeconds(value, label = "task maxDuration") {
|
|
48
|
+
if (value === undefined) {
|
|
49
|
+
return DEFAULT_MAX_DURATION_SECONDS;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value) && value >= MIN_MAX_DURATION_SECONDS && value <= MAX_DURATION_SECONDS) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
throw new TaskMaxDurationError(value, label);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// sdk/typescript/src/internal.ts
|
|
58
|
+
var approvalTimeoutErrorBrand = Symbol.for("helmr.sdk.ApprovalTimeoutError");
|
|
59
|
+
var messageTimeoutErrorBrand = Symbol.for("helmr.sdk.MessageTimeoutError");
|
|
60
|
+
var concurrentWaitErrorBrand = Symbol.for("helmr.sdk.ConcurrentWaitError");
|
|
61
|
+
function validateSecretName(name, label = "secret name") {
|
|
62
|
+
if (name.length === 0) {
|
|
63
|
+
throw new Error(`${label} must not be empty`);
|
|
64
|
+
}
|
|
65
|
+
if (name.length > 128) {
|
|
66
|
+
throw new Error(`${label} must be at most 128 characters`);
|
|
67
|
+
}
|
|
68
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
69
|
+
throw new Error(`${label} must match /^[A-Za-z_][A-Za-z0-9_]*$/`);
|
|
70
|
+
}
|
|
71
|
+
const upper = name.toUpperCase();
|
|
72
|
+
if (upper === "CON" || upper === "PRN" || upper === "AUX" || upper === "NUL" || /^COM[1-9]$/.test(upper) || /^LPT[1-9]$/.test(upper)) {
|
|
73
|
+
throw new Error(`${label} is reserved`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
var taskBrand = Symbol.for("helmr.sdk.Task");
|
|
77
|
+
var taskOriginBrand = Symbol.for("helmr.sdk.TaskOrigin");
|
|
78
|
+
var configBrand = Symbol.for("helmr.sdk.Config");
|
|
79
|
+
var imageBuilderBrand = Symbol.for("helmr.sdk.ImageBuilder");
|
|
80
|
+
var sandboxBuilderBrand = Symbol.for("helmr.sdk.SandboxBuilder");
|
|
81
|
+
var sourceFileRefBrand = Symbol.for("helmr.sdk.SourceFileRef");
|
|
82
|
+
var sourceDirRefBrand = Symbol.for("helmr.sdk.SourceDirRef");
|
|
83
|
+
function isImageBuilder(value) {
|
|
84
|
+
return hasBrand(value, imageBuilderBrand);
|
|
85
|
+
}
|
|
86
|
+
function isSandboxBuilder(value) {
|
|
87
|
+
return hasBrand(value, sandboxBuilderBrand);
|
|
88
|
+
}
|
|
89
|
+
function isSourceFileRef(value) {
|
|
90
|
+
return hasBrand(value, sourceFileRefBrand);
|
|
91
|
+
}
|
|
92
|
+
function isSourceDirRef(value) {
|
|
93
|
+
return hasBrand(value, sourceDirRefBrand);
|
|
94
|
+
}
|
|
95
|
+
function hasBrand(value, brand) {
|
|
96
|
+
return value !== null && typeof value === "object" && value[brand] === true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// sdk/typescript/src/compile.ts
|
|
100
|
+
var IMAGE_FORMAT_VERSION = 1;
|
|
101
|
+
var IMAGE_KEY_DOMAIN = `helmr.image.v1
|
|
102
|
+
`;
|
|
103
|
+
function compile(opts) {
|
|
104
|
+
const task = opts.task;
|
|
105
|
+
if (!isSandboxBuilder(task.sandbox)) {
|
|
106
|
+
throw new Error(`task "${task.id}" must declare sandbox: sandbox(...)`);
|
|
107
|
+
}
|
|
108
|
+
const compiler = new BundleCompiler;
|
|
109
|
+
const imageSpec = compiler.compileSandboxImage(task.sandbox);
|
|
110
|
+
const subImages = compiler.compileSubImages(imageSpec);
|
|
111
|
+
const workspace = compiler.compileWorkspace(task.sandbox);
|
|
112
|
+
const resources = task.sandbox.resourceSpec;
|
|
113
|
+
const maxDurationSeconds = readOptionalMaxDurationSeconds(task.maxDuration, `task "${task.id}" maxDuration`);
|
|
114
|
+
const sandboxSpec = create(SandboxSpecSchema, {
|
|
115
|
+
id: task.sandbox.id,
|
|
116
|
+
workspace,
|
|
117
|
+
...resources ? {
|
|
118
|
+
resources: create(ResourcesSchema, {
|
|
119
|
+
...resources.cpu === undefined ? {} : { cpu: resources.cpu },
|
|
120
|
+
...resources.memory === undefined ? {} : { memory: resources.memory }
|
|
121
|
+
})
|
|
122
|
+
} : {}
|
|
123
|
+
});
|
|
124
|
+
return create(BundleSchema, {
|
|
125
|
+
image: imageSpec,
|
|
126
|
+
sandbox: sandboxSpec,
|
|
127
|
+
subImages,
|
|
128
|
+
task: create(TaskSpecSchema, {
|
|
129
|
+
id: task.id,
|
|
130
|
+
sandboxId: task.sandbox.id,
|
|
131
|
+
modulePath: opts.modulePath,
|
|
132
|
+
exportName: opts.exportName ?? "default",
|
|
133
|
+
maxDurationSeconds,
|
|
134
|
+
secrets: Object.entries(readSecretDecls(task.secrets)).map(([name, placement]) => create(BundleSecretPlacementSchema, {
|
|
135
|
+
name,
|
|
136
|
+
placement: compilePlacement(placement)
|
|
137
|
+
}))
|
|
138
|
+
})
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function compilePlacement(placement) {
|
|
142
|
+
if ("env" in placement) {
|
|
143
|
+
return create(BundlePlacementSchema, {
|
|
144
|
+
kind: {
|
|
145
|
+
case: "env",
|
|
146
|
+
value: create(EnvPlacementSchema, {
|
|
147
|
+
name: placement.env
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if ("file" in placement) {
|
|
153
|
+
return create(BundlePlacementSchema, {
|
|
154
|
+
kind: {
|
|
155
|
+
case: "file",
|
|
156
|
+
value: create(FilePlacementSchema, {
|
|
157
|
+
path: placement.file,
|
|
158
|
+
...placement.mode === undefined ? {} : { mode: placement.mode },
|
|
159
|
+
...placement.owner === undefined ? {} : { owner: placement.owner }
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return create(BundlePlacementSchema, {
|
|
165
|
+
kind: {
|
|
166
|
+
case: "dir",
|
|
167
|
+
value: create(DirPlacementSchema, {
|
|
168
|
+
path: placement.dir,
|
|
169
|
+
...placement.mode === undefined ? {} : { mode: placement.mode },
|
|
170
|
+
...placement.owner === undefined ? {} : { owner: placement.owner }
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
class BundleCompiler {
|
|
177
|
+
imageSpecs = new Map;
|
|
178
|
+
compileSandboxImage(sandbox) {
|
|
179
|
+
const image = sandbox.imageBuilder;
|
|
180
|
+
if (!image) {
|
|
181
|
+
throw new Error(`sandbox "${sandbox.id}" must declare image(...)`);
|
|
182
|
+
}
|
|
183
|
+
return this.compileImage(image);
|
|
184
|
+
}
|
|
185
|
+
compileWorkspace(sandbox) {
|
|
186
|
+
const workspace = sandbox.workspaceBinding ?? {
|
|
187
|
+
mountPath: "/workspace"
|
|
188
|
+
};
|
|
189
|
+
return create(WorkspaceRuntimeBindingSchema, {
|
|
190
|
+
mountPath: workspace.mountPath
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
compileImage(image) {
|
|
194
|
+
const existing = this.imageSpecs.get(image);
|
|
195
|
+
if (existing) {
|
|
196
|
+
return existing;
|
|
197
|
+
}
|
|
198
|
+
if (image.steps.length === 0) {
|
|
199
|
+
throw new Error(`image "${image.id}" must contain at least one operation`);
|
|
200
|
+
}
|
|
201
|
+
const spec = create(ImageSpecSchema, {
|
|
202
|
+
formatVersion: IMAGE_FORMAT_VERSION,
|
|
203
|
+
platform: create(PlatformSchema, { os: "linux", architecture: currentArchitecture() }),
|
|
204
|
+
steps: image.steps.map((step) => this.compileBuildStep(step))
|
|
205
|
+
});
|
|
206
|
+
this.imageSpecs.set(image, spec);
|
|
207
|
+
return spec;
|
|
208
|
+
}
|
|
209
|
+
compileSubImages(root) {
|
|
210
|
+
const values = {};
|
|
211
|
+
for (const spec of this.imageSpecs.values()) {
|
|
212
|
+
if (spec === root) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
values[compileProvisionalImageKey(spec)] = spec;
|
|
216
|
+
}
|
|
217
|
+
return values;
|
|
218
|
+
}
|
|
219
|
+
compileBuildStep(step) {
|
|
220
|
+
switch (step.kind) {
|
|
221
|
+
case "from":
|
|
222
|
+
return create(ImageStepSchema, {
|
|
223
|
+
kind: { case: "from", value: create(FromSchema, { ref: step.ref }) }
|
|
224
|
+
});
|
|
225
|
+
case "run":
|
|
226
|
+
return create(ImageStepSchema, {
|
|
227
|
+
kind: {
|
|
228
|
+
case: "run",
|
|
229
|
+
value: create(RunSchema, {
|
|
230
|
+
argv: [...step.argv],
|
|
231
|
+
cacheMounts: step.cache.map((binding) => create(CacheMountBindingSchema, {
|
|
232
|
+
dst: binding.mountPath,
|
|
233
|
+
cacheId: binding.cache.id,
|
|
234
|
+
sharing: "locked"
|
|
235
|
+
})),
|
|
236
|
+
secretMounts: step.secrets.map((binding) => create(SecretMountBindingSchema, {
|
|
237
|
+
dst: binding.mountPath,
|
|
238
|
+
secretRef: create(SecretRefSchema, { name: binding.secret })
|
|
239
|
+
}))
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
case "copy":
|
|
244
|
+
return this.compileCopyStep(step.dest, step.source);
|
|
245
|
+
case "copyFrom":
|
|
246
|
+
return create(ImageStepSchema, {
|
|
247
|
+
kind: {
|
|
248
|
+
case: "copyFromImage",
|
|
249
|
+
value: create(CopyFromImageSchema, {
|
|
250
|
+
dst: step.dest,
|
|
251
|
+
srcImageKey: compileProvisionalImageKey(this.compileImage(step.source)),
|
|
252
|
+
srcPath: step.srcPath
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
case "workdir":
|
|
257
|
+
return create(ImageStepSchema, {
|
|
258
|
+
kind: { case: "workdir", value: create(WorkdirSchema, { path: step.path }) }
|
|
259
|
+
});
|
|
260
|
+
case "env":
|
|
261
|
+
return create(ImageStepSchema, {
|
|
262
|
+
kind: { case: "env", value: create(EnvSchema, { key: step.key, value: step.value }) }
|
|
263
|
+
});
|
|
264
|
+
case "user":
|
|
265
|
+
return create(ImageStepSchema, {
|
|
266
|
+
kind: { case: "user", value: create(UserSchema, { name: step.name }) }
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
compileCopyStep(dest, source) {
|
|
271
|
+
if (isSourceFileRef(source)) {
|
|
272
|
+
return create(ImageStepSchema, {
|
|
273
|
+
kind: {
|
|
274
|
+
case: "copySourceFile",
|
|
275
|
+
value: create(CopySourceFileSchema, {
|
|
276
|
+
dst: dest,
|
|
277
|
+
srcRef: create(SourceFileRefSchema, { path: source.path })
|
|
278
|
+
})
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (isSourceDirRef(source)) {
|
|
283
|
+
return create(ImageStepSchema, {
|
|
284
|
+
kind: {
|
|
285
|
+
case: "copySourceDir",
|
|
286
|
+
value: create(CopySourceDirSchema, {
|
|
287
|
+
dst: dest,
|
|
288
|
+
srcRef: this.compileSourceDirRef(source),
|
|
289
|
+
ignore: [...source.ignore]
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (isImageBuilder(source)) {
|
|
295
|
+
return create(ImageStepSchema, {
|
|
296
|
+
kind: {
|
|
297
|
+
case: "copyFromImage",
|
|
298
|
+
value: create(CopyFromImageSchema, {
|
|
299
|
+
dst: dest,
|
|
300
|
+
srcImageKey: compileProvisionalImageKey(this.compileImage(source)),
|
|
301
|
+
srcPath: "/"
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
throw new Error("image.copy() source must be source.file(), source.directory(), or image()");
|
|
307
|
+
}
|
|
308
|
+
compileSourceDirRef(ref) {
|
|
309
|
+
return create(SourceDirRefSchema, { path: ref.path, ignore: [...ref.ignore] });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function currentArchitecture() {
|
|
313
|
+
switch (process.arch) {
|
|
314
|
+
case "arm64":
|
|
315
|
+
return "arm64";
|
|
316
|
+
case "x64":
|
|
317
|
+
return "amd64";
|
|
318
|
+
default:
|
|
319
|
+
return process.arch;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function compileProvisionalImageKey(image) {
|
|
323
|
+
return canonicalImageKey(image);
|
|
324
|
+
}
|
|
325
|
+
function canonicalImageKey(image) {
|
|
326
|
+
const hash = createHash("sha256");
|
|
327
|
+
hash.update(IMAGE_KEY_DOMAIN);
|
|
328
|
+
hash.update(u32be(image.formatVersion));
|
|
329
|
+
hashLenPrefixedBytes(hash, image.platform ? toBinary(PlatformSchema, image.platform) : new Uint8Array);
|
|
330
|
+
hashLenPrefixedBytes(hash, encodeImageSteps(image.steps));
|
|
331
|
+
hashLenPrefixedBytes(hash, encodeDigestList(sourceInputDigests(image.steps)));
|
|
332
|
+
hashLenPrefixedBytes(hash, encodeDigestList(subImageKeys(image.steps)));
|
|
333
|
+
return `sha256:${hash.digest("hex")}`;
|
|
334
|
+
}
|
|
335
|
+
function encodeImageSteps(steps) {
|
|
336
|
+
const chunks = [u64be(steps.length)];
|
|
337
|
+
for (const step of steps) {
|
|
338
|
+
chunks.push(lenPrefixedBytes(toBinary(ImageStepSchema, step)));
|
|
339
|
+
}
|
|
340
|
+
return concatBytes(chunks);
|
|
341
|
+
}
|
|
342
|
+
function encodeDigestList(values) {
|
|
343
|
+
const chunks = [u64be(values.length)];
|
|
344
|
+
for (const value of values) {
|
|
345
|
+
chunks.push(lenPrefixedBytes(new TextEncoder().encode(value)));
|
|
346
|
+
}
|
|
347
|
+
return concatBytes(chunks);
|
|
348
|
+
}
|
|
349
|
+
function sourceInputDigests(steps) {
|
|
350
|
+
const values = [];
|
|
351
|
+
for (const step of steps) {
|
|
352
|
+
switch (step.kind.case) {
|
|
353
|
+
case "copySourceFile":
|
|
354
|
+
values.push(step.kind.value.digest);
|
|
355
|
+
break;
|
|
356
|
+
case "copySourceDir":
|
|
357
|
+
values.push(step.kind.value.treeDigest);
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return values;
|
|
362
|
+
}
|
|
363
|
+
function subImageKeys(steps) {
|
|
364
|
+
const values = [];
|
|
365
|
+
for (const step of steps) {
|
|
366
|
+
if (step.kind.case === "copyFromImage") {
|
|
367
|
+
values.push(step.kind.value.srcImageKey);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return values;
|
|
371
|
+
}
|
|
372
|
+
function hashLenPrefixedBytes(hash, bytes) {
|
|
373
|
+
hash.update(u64be(bytes.byteLength));
|
|
374
|
+
hash.update(bytes);
|
|
375
|
+
}
|
|
376
|
+
function lenPrefixedBytes(bytes) {
|
|
377
|
+
return concatBytes([u64be(bytes.byteLength), bytes]);
|
|
378
|
+
}
|
|
379
|
+
function u32be(value) {
|
|
380
|
+
const buffer = Buffer.alloc(4);
|
|
381
|
+
buffer.writeUInt32BE(value);
|
|
382
|
+
return buffer;
|
|
383
|
+
}
|
|
384
|
+
function u64be(value) {
|
|
385
|
+
const buffer = Buffer.alloc(8);
|
|
386
|
+
buffer.writeBigUInt64BE(BigInt(value));
|
|
387
|
+
return buffer;
|
|
388
|
+
}
|
|
389
|
+
function concatBytes(chunks) {
|
|
390
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
391
|
+
const out = new Uint8Array(total);
|
|
392
|
+
let offset = 0;
|
|
393
|
+
for (const chunk of chunks) {
|
|
394
|
+
out.set(chunk, offset);
|
|
395
|
+
offset += chunk.byteLength;
|
|
396
|
+
}
|
|
397
|
+
return out;
|
|
398
|
+
}
|
|
399
|
+
function readSecretDecls(value) {
|
|
400
|
+
if (value === undefined) {
|
|
401
|
+
return {};
|
|
402
|
+
}
|
|
403
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
404
|
+
throw new Error("task secrets must be an object literal");
|
|
405
|
+
}
|
|
406
|
+
const record = value;
|
|
407
|
+
return Object.fromEntries(Object.entries(record).map(([name, placement]) => {
|
|
408
|
+
validateSecretName(name, `task secrets.${name}`);
|
|
409
|
+
return [name, readPlacement(placement, `task secrets.${name}`)];
|
|
410
|
+
}));
|
|
411
|
+
}
|
|
412
|
+
function readPlacement(value, label) {
|
|
413
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
414
|
+
throw new Error(`${label} must be a placement object`);
|
|
415
|
+
}
|
|
416
|
+
const record = value;
|
|
417
|
+
if ("env" in record) {
|
|
418
|
+
const env = record["env"];
|
|
419
|
+
if (Object.keys(record).length !== 1 || !isNonEmptyPlacementString(env)) {
|
|
420
|
+
throw new Error(`${label} must be { env: string }`);
|
|
421
|
+
}
|
|
422
|
+
return { env };
|
|
423
|
+
}
|
|
424
|
+
if ("file" in record) {
|
|
425
|
+
const file = record["file"];
|
|
426
|
+
const mode = readOptionalPlacementString(record, "mode");
|
|
427
|
+
const owner = readOptionalPlacementString(record, "owner");
|
|
428
|
+
if (!hasOnlyKeys(record, ["file", "mode", "owner"]) || !isNonEmptyPlacementString(file)) {
|
|
429
|
+
throw new Error(`${label} must be { file: string, mode?: string, owner?: string }`);
|
|
430
|
+
}
|
|
431
|
+
if (mode === INVALID_PLACEMENT_STRING || owner === INVALID_PLACEMENT_STRING) {
|
|
432
|
+
throw new Error(`${label} must be { file: string, mode?: string, owner?: string }`);
|
|
433
|
+
}
|
|
434
|
+
validatePlacementPath(file, `${label}.file`);
|
|
435
|
+
validatePlacementMode(mode, `${label}.mode`);
|
|
436
|
+
return {
|
|
437
|
+
file,
|
|
438
|
+
...mode === undefined ? {} : { mode },
|
|
439
|
+
...owner === undefined ? {} : { owner }
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
if ("dir" in record) {
|
|
443
|
+
const dir = record["dir"];
|
|
444
|
+
const mode = readOptionalPlacementString(record, "mode");
|
|
445
|
+
const owner = readOptionalPlacementString(record, "owner");
|
|
446
|
+
if (!hasOnlyKeys(record, ["dir", "mode", "owner"]) || !isNonEmptyPlacementString(dir)) {
|
|
447
|
+
throw new Error(`${label} must be { dir: string, mode?: string, owner?: string }`);
|
|
448
|
+
}
|
|
449
|
+
if (mode === INVALID_PLACEMENT_STRING || owner === INVALID_PLACEMENT_STRING) {
|
|
450
|
+
throw new Error(`${label} must be { dir: string, mode?: string, owner?: string }`);
|
|
451
|
+
}
|
|
452
|
+
validatePlacementPath(dir, `${label}.dir`);
|
|
453
|
+
validatePlacementMode(mode, `${label}.mode`);
|
|
454
|
+
return {
|
|
455
|
+
dir,
|
|
456
|
+
...mode === undefined ? {} : { mode },
|
|
457
|
+
...owner === undefined ? {} : { owner }
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
throw new Error(`${label} must be one of { env }, { file, mode?, owner? }, or { dir, mode?, owner? }`);
|
|
461
|
+
}
|
|
462
|
+
var INVALID_PLACEMENT_STRING = Symbol("invalid placement string");
|
|
463
|
+
function isNonEmptyPlacementString(value) {
|
|
464
|
+
return typeof value === "string" && value.trim() !== "";
|
|
465
|
+
}
|
|
466
|
+
function readOptionalPlacementString(record, key) {
|
|
467
|
+
const value = record[key];
|
|
468
|
+
if (value === undefined) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
return typeof value === "string" ? value : INVALID_PLACEMENT_STRING;
|
|
472
|
+
}
|
|
473
|
+
function validatePlacementMode(mode, label) {
|
|
474
|
+
if (mode === undefined) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const normalized = mode.trim().replace(/^0[oO]/, "");
|
|
478
|
+
if (!/^[0-7]+$/.test(normalized)) {
|
|
479
|
+
throw new Error(`${label} must be an octal permission mode`);
|
|
480
|
+
}
|
|
481
|
+
if (Number.parseInt(normalized, 8) > 511) {
|
|
482
|
+
throw new Error(`${label} must only contain permission bits`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function validatePlacementPath(path, label) {
|
|
486
|
+
const normalized = path.trim().replaceAll("\\", "/");
|
|
487
|
+
if (path !== path.trim()) {
|
|
488
|
+
throw new Error(`${label} must not contain leading or trailing whitespace`);
|
|
489
|
+
}
|
|
490
|
+
if (normalized === "." || normalized === "/") {
|
|
491
|
+
throw new Error(`${label} must target a file or directory`);
|
|
492
|
+
}
|
|
493
|
+
if (normalized.split("/").includes("..")) {
|
|
494
|
+
throw new Error(`${label} must not contain parent components`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function hasOnlyKeys(record, allowed) {
|
|
498
|
+
return Object.keys(record).every((key) => allowed.includes(key));
|
|
499
|
+
}
|
|
500
|
+
export {
|
|
501
|
+
compile,
|
|
502
|
+
IMAGE_FORMAT_VERSION
|
|
503
|
+
};
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type HelmrConfig } from "./internal.js";
|
|
2
|
+
export interface HelmrConfigInput {
|
|
3
|
+
readonly project?: string;
|
|
4
|
+
readonly dirs: readonly string[];
|
|
5
|
+
readonly ignorePatterns?: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function defineConfig(config: HelmrConfigInput): HelmrConfig;
|
|
8
|
+
export type { HelmrConfig };
|
package/dist/fuzzy.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function levenshteinDistance(left: string, right: string): number;
|
package/dist/fuzzy.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// sdk/typescript/src/fuzzy.ts
|
|
2
|
+
function levenshteinDistance(left, right) {
|
|
3
|
+
if (left === right) {
|
|
4
|
+
return 0;
|
|
5
|
+
}
|
|
6
|
+
if (left.length === 0) {
|
|
7
|
+
return right.length;
|
|
8
|
+
}
|
|
9
|
+
if (right.length === 0) {
|
|
10
|
+
return left.length;
|
|
11
|
+
}
|
|
12
|
+
const previous = new Array(right.length + 1);
|
|
13
|
+
const current = new Array(right.length + 1);
|
|
14
|
+
for (let column = 0;column <= right.length; column += 1) {
|
|
15
|
+
previous[column] = column;
|
|
16
|
+
}
|
|
17
|
+
for (let row = 1;row <= left.length; row += 1) {
|
|
18
|
+
current[0] = row;
|
|
19
|
+
for (let column = 1;column <= right.length; column += 1) {
|
|
20
|
+
const cost = left[row - 1] === right[column - 1] ? 0 : 1;
|
|
21
|
+
current[column] = Math.min(current[column - 1] + 1, previous[column] + 1, previous[column - 1] + cost);
|
|
22
|
+
}
|
|
23
|
+
previous.splice(0, previous.length, ...current);
|
|
24
|
+
}
|
|
25
|
+
return previous[right.length] ?? right.length;
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
levenshteinDistance
|
|
29
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ApprovalTimeoutError, ConcurrentWaitError, MessageTimeoutError, validateSecretName, type CacheBuilder, type CacheMount, type CacheMountBinding, type EmitContent, type EmitEvent, type ImageBuilder, type ImageRunOptions, type Placement, type SandboxBuilder, type SecretDecls, type SecretMountBinding, type SourceCapabilities, type SourceDirRef, type SourceDirectoryOptions, type SourceFileRef, type TaskContext, type TaskOutput, type TaskPayload, type WorkspaceCapabilities, type WorkspaceSpec } from "./internal.js";
|
|
2
|
+
import { HelmrClient } from "./runtime/client.js";
|
|
3
|
+
import { sandbox } from "./sandbox.js";
|
|
4
|
+
import { defineConfig, type HelmrConfig, type HelmrConfigInput } from "./config.js";
|
|
5
|
+
import { task, type Task, type TaskConfig } from "./task.js";
|
|
6
|
+
import { tasks } from "./trigger.js";
|
|
7
|
+
export { AuthError, RunNotFoundError, TimeoutError, UnsupportedTransportError } from "./runtime/errors.js";
|
|
8
|
+
export { ApprovalTimeoutError, ConcurrentWaitError, MessageTimeoutError };
|
|
9
|
+
export { type LogSnapshot, type ListRunEventsOptions, type ListRunsOptions, type PendingApprovalWaitpoint, type PendingMessageWaitpoint, type PendingWaitpoint, type RetrieveRunOptions, type RunEvent, type RunEventRecord, type RunEventRecordPage, type RunHandle, type RunSnapshot, type RunStatus, type RunSummary, type RunWaitOptions, type SubscribeRunEventsOptions, type WaitpointApprovalOptions, type WaitpointRef, type WaitpointReplyOptions, } from "./runtime/run.js";
|
|
10
|
+
export { defineConfig, sandbox, task, tasks };
|
|
11
|
+
export type { HelmrConfig, HelmrConfigInput, Placement, SecretDecls, Task, TaskConfig, TaskOutput, TaskPayload, };
|
|
12
|
+
export declare const image: (id: string) => ImageBuilder;
|
|
13
|
+
export declare const cache: (id: string) => CacheBuilder;
|
|
14
|
+
export declare const source: SourceCapabilities;
|
|
15
|
+
export declare const workspace: WorkspaceCapabilities;
|
|
16
|
+
export { HelmrClient };
|
|
17
|
+
export { validateSecretName };
|
|
18
|
+
export declare const runs: {
|
|
19
|
+
retrieve: <TOutput = unknown>(idOrHandle: string | import("./index.js").RunHandle<TOutput>, opts?: import("./index.js").RetrieveRunOptions) => Promise<import("./index.js").RunSnapshot<TOutput>>;
|
|
20
|
+
wait: <TOutput = unknown>(idOrHandle: string | import("./index.js").RunHandle<TOutput>, opts?: import("./index.js").RunWaitOptions) => Promise<import("./index.js").RunSnapshot<TOutput>>;
|
|
21
|
+
list: (opts?: import("./index.js").ListRunsOptions) => Promise<import("./index.js").RunSummary[]>;
|
|
22
|
+
logs: {
|
|
23
|
+
retrieve: <TOutput = unknown>(idOrHandle: string | import("./index.js").RunHandle<TOutput>, opts?: {
|
|
24
|
+
readonly signal?: AbortSignal;
|
|
25
|
+
}) => Promise<import("./index.js").LogSnapshot>;
|
|
26
|
+
};
|
|
27
|
+
events: {
|
|
28
|
+
list: <TOutput = unknown>(idOrHandle: string | import("./index.js").RunHandle<TOutput>, opts?: import("./index.js").ListRunEventsOptions) => Promise<import("./index.js").RunEvent[]>;
|
|
29
|
+
subscribe: <TOutput = unknown>(idOrHandle: string | import("./index.js").RunHandle<TOutput>, opts?: import("./index.js").SubscribeRunEventsOptions) => Promise<AsyncIterable<import("./index.js").RunEvent>>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export declare const waitpoints: import("./runtime/client.js").WaitpointsApi;
|
|
33
|
+
export type { CacheBuilder, CacheMount, CacheMountBinding, EmitContent, EmitEvent, ImageBuilder, ImageRunOptions, SandboxBuilder, SecretMountBinding, SourceCapabilities, SourceDirRef, SourceDirectoryOptions, SourceFileRef, WorkspaceCapabilities, WorkspaceSpec, TaskContext, };
|