@yhyzgn/tikeo 0.1.911

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.
@@ -0,0 +1,188 @@
1
+ syntax = "proto3";
2
+
3
+ package tikeo.worker.v1;
4
+
5
+ option go_package = "github.com/yhyzgn/tikeo/sdks/go/tikeo/internal/workerpb;workerpb";
6
+
7
+ service WorkerTunnelService {
8
+ rpc OpenTunnel(stream WorkerMessage) returns (stream ServerMessage);
9
+ rpc SubscribeTaskLogs(SubscribeTaskLogsRequest) returns (stream TaskLog);
10
+ }
11
+
12
+ message WorkerMessage {
13
+ oneof kind {
14
+ RegisterWorker register = 1;
15
+ Heartbeat heartbeat = 2;
16
+ TaskResult task_result = 3;
17
+ TaskLog task_log = 4;
18
+ UnregisterWorker unregister = 5;
19
+ TaskCheckpoint task_checkpoint = 6;
20
+ }
21
+ }
22
+
23
+ message ServerMessage {
24
+ oneof kind {
25
+ WorkerRegistered registered = 1;
26
+ Ping ping = 2;
27
+ DispatchTask dispatch_task = 3;
28
+ }
29
+ }
30
+
31
+ message RegisterWorker {
32
+ // Optional client-side stable instance hint. The tikeo still assigns the authoritative worker_id.
33
+ string client_instance_id = 1;
34
+ string app = 2;
35
+ string namespace = 3;
36
+ string cluster = 4;
37
+ string region = 5;
38
+ // Legacy free-form labels/capabilities. New routing must use structured_capabilities.
39
+ repeated string capabilities = 6;
40
+ map<string, string> labels = 7;
41
+ WorkerCapabilities structured_capabilities = 8;
42
+ WorkerClusterElection election = 9;
43
+ }
44
+
45
+ message WorkerCapabilities {
46
+ // Human/operator tags such as java or spring-boot. Not used for dispatch routing.
47
+ repeated string tags = 1;
48
+ repeated SdkProcessorCapability sdk_processors = 2;
49
+ repeated ScriptRunnerCapability script_runners = 3;
50
+ repeated PluginProcessorCapability plugin_processors = 4;
51
+ }
52
+
53
+ message SdkProcessorCapability {
54
+ string name = 1;
55
+ }
56
+
57
+ message ScriptRunnerCapability {
58
+ string language = 1;
59
+ string sandbox_backend = 2;
60
+ }
61
+
62
+ message PluginProcessorCapability {
63
+ string type = 1;
64
+ repeated string processor_names = 2;
65
+ }
66
+
67
+ message WorkerClusterElection {
68
+ bool enabled = 1;
69
+ // Stable election domain. Empty means namespace/app/cluster/region.
70
+ string domain = 2;
71
+ // Optional deterministic priority. Lower values win ties after health checks.
72
+ uint32 priority = 3;
73
+ }
74
+
75
+ message Heartbeat {
76
+ string worker_id = 1;
77
+ uint64 sequence = 2;
78
+ uint64 generation = 3;
79
+ string fencing_token = 4;
80
+ }
81
+
82
+ message WorkerRegistered {
83
+ string worker_id = 1;
84
+ uint64 lease_seconds = 2;
85
+ uint64 generation = 3;
86
+ string fencing_token = 4;
87
+ }
88
+
89
+ message UnregisterWorker {
90
+ string worker_id = 1;
91
+ uint64 generation = 2;
92
+ string fencing_token = 3;
93
+ }
94
+
95
+ message Ping {
96
+ uint64 sequence = 1;
97
+ }
98
+
99
+ message DispatchTask {
100
+ string instance_id = 1;
101
+ string job_id = 2;
102
+ bytes payload = 3;
103
+ // Explicit processor key used by SDK adapters. Defaults to job_id for backwards compatibility.
104
+ string processor_name = 4;
105
+ // Optional dynamic processor metadata. Empty for normal SDK processors.
106
+ TaskProcessorBinding processor_binding = 5;
107
+ // Server-issued opaque assignment authority bound to worker session generation/fencing.
108
+ string assignment_token = 6;
109
+ }
110
+
111
+ message TaskProcessorBinding {
112
+ oneof kind {
113
+ WasmProcessorBinding wasm = 1;
114
+ ScriptProcessorBinding script = 2;
115
+ }
116
+ }
117
+
118
+ message ScriptProcessorBinding {
119
+ string script_id = 1;
120
+ string version = 2;
121
+ string language = 3;
122
+ bytes content = 4;
123
+ string version_id = 5;
124
+ uint64 version_number = 6;
125
+ string content_sha256 = 7;
126
+ uint64 timeout_ms = 8;
127
+ uint64 max_memory_bytes = 9;
128
+ uint64 max_output_bytes = 10;
129
+ bool allow_network = 11;
130
+ repeated string allowed_env_vars = 12;
131
+ repeated string read_only_paths = 13;
132
+ repeated string writable_paths = 14;
133
+ repeated string secret_refs = 15;
134
+ repeated string allowed_network_hosts = 16;
135
+ string sandbox_backend = 17;
136
+ }
137
+
138
+ message WasmProcessorBinding {
139
+ string script_id = 1;
140
+ string version = 2;
141
+ bytes module = 3;
142
+ string runtime = 4;
143
+ string entrypoint = 5;
144
+ uint64 timeout_ms = 6;
145
+ uint64 max_memory_bytes = 7;
146
+ uint64 fuel = 8;
147
+ bool allow_network = 9;
148
+ repeated string allowed_env_vars = 10;
149
+ // Immutable script version snapshot identifier when available.
150
+ string version_id = 11;
151
+ // Monotonic script version snapshot number when available.
152
+ uint64 version_number = 12;
153
+ // Lowercase hex SHA-256 digest of module bytes. Workers must verify when present.
154
+ string module_sha256 = 13;
155
+ // Reserved signature hook for future PKI-backed module verification.
156
+ string module_signature = 14;
157
+ }
158
+
159
+ message TaskResult {
160
+ string worker_id = 1;
161
+ string instance_id = 2;
162
+ bool success = 3;
163
+ string message = 4;
164
+ string assignment_token = 5;
165
+ }
166
+
167
+ message TaskLog {
168
+ string worker_id = 1;
169
+ string instance_id = 2;
170
+ string level = 3;
171
+ string message = 4;
172
+ int64 sequence = 5;
173
+ string assignment_token = 6;
174
+ }
175
+
176
+ message TaskCheckpoint {
177
+ string worker_id = 1;
178
+ string instance_id = 2;
179
+ string checkpoint_json = 3;
180
+ int64 sequence = 4;
181
+ string assignment_token = 5;
182
+ }
183
+
184
+ message SubscribeTaskLogsRequest {
185
+ string instance_id = 1;
186
+ int64 after_sequence = 2;
187
+ bool replay_existing = 3;
188
+ }
@@ -0,0 +1,86 @@
1
+ import type { WorkerConfig } from "../config.js";
2
+ import type { TaskLogSink, TaskOutcome } from "../task.js";
3
+ import { ScriptTaskRuntimeDirs } from "./runtimeDirs.js";
4
+ export { SandboxToolResolver } from "./sandboxTools.js";
5
+ export { ScriptTaskRuntimeDirs } from "./runtimeDirs.js";
6
+ export interface ScriptRunnerTask {
7
+ scriptId: string;
8
+ versionId: string;
9
+ versionNumber: number;
10
+ language: string;
11
+ content: Uint8Array;
12
+ contentSha256: string;
13
+ timeoutMs?: number;
14
+ maxOutputBytes?: number;
15
+ allowNetwork?: boolean;
16
+ allowedEnvVars?: string[];
17
+ readOnlyPaths?: string[];
18
+ writablePaths?: string[];
19
+ secretRefs?: string[];
20
+ allowedNetworkHosts?: string[];
21
+ sandboxBackend?: string;
22
+ instanceId?: string;
23
+ jobId?: string;
24
+ log?: TaskLogSink;
25
+ }
26
+ export interface ScriptRunner {
27
+ language: string;
28
+ sandboxBackend: string;
29
+ run(task: ScriptRunnerTask): Promise<TaskOutcome> | TaskOutcome;
30
+ advertiseCapability?(): boolean;
31
+ }
32
+ export declare function normalizeScriptLanguage(language: string): string;
33
+ export declare function defaultSandboxBackend(language: string): string;
34
+ export declare function normalizeScriptSandboxBackend(backend: string, language: string): string;
35
+ export declare function defaultScriptCommand(language: string): [string, string[]];
36
+ export declare class ScriptRunnerRegistry {
37
+ private runners;
38
+ register(runner: ScriptRunner): ScriptRunnerRegistry;
39
+ get(language: string): ScriptRunner | undefined;
40
+ addCapabilities(config: WorkerConfig): void;
41
+ }
42
+ export declare class UnavailableScriptRunner implements ScriptRunner {
43
+ private reason;
44
+ language: string;
45
+ sandboxBackend: string;
46
+ constructor(language: string, sandboxBackend: string, reason: string);
47
+ advertiseCapability(): boolean;
48
+ run(task: ScriptRunnerTask): TaskOutcome;
49
+ }
50
+ export declare class LocalCommandScriptRunner implements ScriptRunner {
51
+ language: string;
52
+ sandboxBackend: string;
53
+ private command;
54
+ private args;
55
+ constructor(language: string, sandboxBackend?: string);
56
+ run(task: ScriptRunnerTask): TaskOutcome;
57
+ }
58
+ export declare class ContainerScriptRunner implements ScriptRunner {
59
+ private image;
60
+ private runtimeArgs;
61
+ language: string;
62
+ sandboxBackend: string;
63
+ constructor(language: string, runtimeCommand: string, image: string, runtimeArgs?: string[]);
64
+ run(task: ScriptRunnerTask): TaskOutcome;
65
+ private containerArgs;
66
+ }
67
+ export declare class SrtScriptRunner implements ScriptRunner {
68
+ private runtimeCommand;
69
+ private interpreter;
70
+ private extraPath;
71
+ sandboxBackend: string;
72
+ language: string;
73
+ constructor(language: string, runtimeCommand: string, interpreter: string, extraPath?: string[]);
74
+ run(task: ScriptRunnerTask): TaskOutcome;
75
+ private shellCommand;
76
+ }
77
+ export declare class DenoScriptRunner implements ScriptRunner {
78
+ private command;
79
+ sandboxBackend: string;
80
+ language: string;
81
+ constructor(language: string, command: string);
82
+ run(task: ScriptRunnerTask): TaskOutcome;
83
+ }
84
+ export declare function validateScriptTask(language: string, task: ScriptRunnerTask): void;
85
+ export declare function emitScriptCommandOutput(log: TaskLogSink | undefined, level: string, output: Buffer): void;
86
+ export declare function writeSrtSettings(task: ScriptRunnerTask, dirs: ScriptTaskRuntimeDirs, scriptFile?: string): string;
@@ -0,0 +1,316 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
3
+ import { tmpdir, homedir } from "node:os";
4
+ import { isAbsolute, join } from "node:path";
5
+ import { spawnSync } from "node:child_process";
6
+ import { failed } from "../task.js";
7
+ import { ScriptTaskRuntimeDirs, appendAllowedUnmanagedEnv } from "./runtimeDirs.js";
8
+ export { SandboxToolResolver } from "./sandboxTools.js";
9
+ export { ScriptTaskRuntimeDirs } from "./runtimeDirs.js";
10
+ export function normalizeScriptLanguage(language) {
11
+ switch (language.trim().toLowerCase()) {
12
+ case "shell":
13
+ case "sh":
14
+ case "bash": return "shell";
15
+ case "python":
16
+ case "py": return "python";
17
+ case "node":
18
+ case "nodejs":
19
+ case "javascript":
20
+ case "js": return "javascript";
21
+ case "typescript":
22
+ case "ts": return "typescript";
23
+ case "powershell":
24
+ case "pwsh": return "powershell";
25
+ case "php": return "php";
26
+ case "groovy": return "groovy";
27
+ case "rhai": return "rhai";
28
+ default: return language.trim().toLowerCase();
29
+ }
30
+ }
31
+ export function defaultSandboxBackend(language) { return ["javascript", "typescript"].includes(normalizeScriptLanguage(language)) ? "deno" : "srt"; }
32
+ export function normalizeScriptSandboxBackend(backend, language) {
33
+ let value = backend.trim().toLowerCase();
34
+ if (!value || value === "auto")
35
+ return defaultSandboxBackend(language);
36
+ const aliases = { "wasm_edge": "wasmedge", "wasm-edge": "wasmedge", "anthropic_srt": "srt", "anthropic-srt": "srt", "sandbox_runtime": "srt", "sandbox-runtime": "srt", "v8_isolate": "v8", "v8-isolate": "v8" };
37
+ value = aliases[value] ?? value;
38
+ if (!["wasmtime", "wasmedge", "srt", "deno", "v8", "docker", "podman", "custom"].includes(value))
39
+ throw new Error(`unsupported script sandbox backend: ${backend}`);
40
+ return value;
41
+ }
42
+ export function defaultScriptCommand(language) {
43
+ switch (normalizeScriptLanguage(language)) {
44
+ case "shell": return ["sh", ["-s"]];
45
+ case "python": return ["python3", ["-"]];
46
+ case "javascript":
47
+ case "typescript": return ["deno", ["run", "--no-prompt", "-"]];
48
+ case "powershell": return ["pwsh", ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "-"]];
49
+ case "php": return ["php", []];
50
+ case "groovy": return ["groovy", []];
51
+ case "rhai": return ["rhai", []];
52
+ default: return ["sh", ["-s"]];
53
+ }
54
+ }
55
+ export class ScriptRunnerRegistry {
56
+ runners = new Map();
57
+ register(runner) {
58
+ this.runners.set(normalizeScriptLanguage(runner.language), runner);
59
+ return this;
60
+ }
61
+ get(language) { return this.runners.get(normalizeScriptLanguage(language)); }
62
+ addCapabilities(config) {
63
+ for (const language of [...this.runners.keys()].sort()) {
64
+ const runner = this.runners.get(language);
65
+ if (runner.advertiseCapability?.() === false)
66
+ continue;
67
+ config.addScriptRunner(runner.language, runner.sandboxBackend);
68
+ }
69
+ }
70
+ }
71
+ export class UnavailableScriptRunner {
72
+ reason;
73
+ language;
74
+ sandboxBackend;
75
+ constructor(language, sandboxBackend, reason) {
76
+ this.reason = reason;
77
+ this.language = normalizeScriptLanguage(language);
78
+ try {
79
+ this.sandboxBackend = normalizeScriptSandboxBackend(sandboxBackend, this.language);
80
+ }
81
+ catch (error) {
82
+ this.sandboxBackend = defaultSandboxBackend(this.language);
83
+ this.reason = `${reason}; ${error.message}`;
84
+ }
85
+ }
86
+ advertiseCapability() { return false; }
87
+ run(task) {
88
+ try {
89
+ validateScriptTask(this.language, task);
90
+ }
91
+ catch (error) {
92
+ return failed(error.message);
93
+ }
94
+ return failed(`${this.language} script runner backend is unavailable: ${this.reason}`);
95
+ }
96
+ }
97
+ export class LocalCommandScriptRunner {
98
+ language;
99
+ sandboxBackend;
100
+ command;
101
+ args;
102
+ constructor(language, sandboxBackend = "custom") {
103
+ this.language = normalizeScriptLanguage(language);
104
+ this.sandboxBackend = normalizeScriptSandboxBackend(sandboxBackend, this.language);
105
+ if (this.sandboxBackend !== "custom")
106
+ throw new Error(`local command script runner must use custom sandbox backend, got ${this.sandboxBackend}`);
107
+ [this.command, this.args] = defaultScriptCommand(this.language);
108
+ }
109
+ run(task) {
110
+ try {
111
+ validateScriptTask(this.language, task);
112
+ if (task.allowNetwork || task.allowedNetworkHosts?.length)
113
+ throw new Error("local script runner rejects network access");
114
+ if (task.secretRefs?.length)
115
+ throw new Error("local script runner rejects secret refs");
116
+ if (task.readOnlyPaths?.length || task.writablePaths?.length)
117
+ throw new Error("local script runner rejects filesystem grants");
118
+ }
119
+ catch (error) {
120
+ return failed(error.message);
121
+ }
122
+ return runCommand([this.command, ...this.args], task, Buffer.from(task.content));
123
+ }
124
+ }
125
+ export class ContainerScriptRunner {
126
+ image;
127
+ runtimeArgs;
128
+ language;
129
+ sandboxBackend;
130
+ constructor(language, runtimeCommand, image, runtimeArgs = []) {
131
+ this.image = image;
132
+ this.runtimeArgs = runtimeArgs;
133
+ this.language = normalizeScriptLanguage(language);
134
+ this.sandboxBackend = normalizeScriptSandboxBackend(runtimeCommand, this.language);
135
+ if (!["docker", "podman"].includes(this.sandboxBackend))
136
+ throw new Error(`container script runner requires docker or podman backend, got ${this.sandboxBackend}`);
137
+ if (!image.trim())
138
+ throw new Error(`container script runner requires an image for ${this.language}`);
139
+ }
140
+ run(task) {
141
+ try {
142
+ validateScriptTask(this.language, task);
143
+ if (task.allowNetwork || task.allowedNetworkHosts?.length)
144
+ throw new Error("container script runner rejects network grants without host-level filtering");
145
+ if (task.secretRefs?.length)
146
+ throw new Error("container script runner rejects secret refs without a worker-local secret provider");
147
+ return runCommand([this.sandboxBackend, ...this.containerArgs(task)], task, Buffer.from(task.content));
148
+ }
149
+ catch (error) {
150
+ return failed(error.message);
151
+ }
152
+ }
153
+ containerArgs(task) {
154
+ const args = ["run", "--rm", "-i", "--network=none", "--read-only", "--tmpfs", "/tmp:rw,noexec,nosuid,size=16m", "--memory", "67108864", ...this.runtimeArgs];
155
+ for (const path of task.readOnlyPaths ?? [])
156
+ args.push("--mount", containerMount(path, true));
157
+ for (const path of task.writablePaths ?? [])
158
+ args.push("--mount", containerMount(path, false));
159
+ args.push("--env", `TIKEO_SCRIPT_ID=${task.scriptId}`, "--env", `TIKEO_SCRIPT_VERSION_ID=${task.versionId}`, "--env", `TIKEO_SCRIPT_VERSION_NUMBER=${task.versionNumber}`, this.image);
160
+ const [cmd, cmdArgs] = defaultScriptCommand(this.language);
161
+ return [...args, cmd, ...cmdArgs];
162
+ }
163
+ }
164
+ export class SrtScriptRunner {
165
+ runtimeCommand;
166
+ interpreter;
167
+ extraPath;
168
+ sandboxBackend = "srt";
169
+ language;
170
+ constructor(language, runtimeCommand, interpreter, extraPath = []) {
171
+ this.runtimeCommand = runtimeCommand;
172
+ this.interpreter = interpreter;
173
+ this.extraPath = extraPath;
174
+ this.language = normalizeScriptLanguage(language);
175
+ if (!runtimeCommand.trim() || !interpreter.trim())
176
+ throw new Error("SRT runner requires runtime and interpreter commands");
177
+ }
178
+ run(task) {
179
+ try {
180
+ validateScriptTask(this.language, task);
181
+ if (task.secretRefs?.length)
182
+ throw new Error("SRT script runner rejects secret refs without a worker-local secret provider");
183
+ }
184
+ catch (error) {
185
+ return failed(error.message);
186
+ }
187
+ const dirs = ScriptTaskRuntimeDirs.create(`tikeo-srt-${this.language}-runtime`);
188
+ let settings = "";
189
+ try {
190
+ let scriptFile = "";
191
+ if (this.language === "rhai") {
192
+ scriptFile = dirs.scriptFile("rhai");
193
+ writeFileSync(scriptFile, task.content);
194
+ }
195
+ settings = writeSrtSettings(task, dirs, scriptFile);
196
+ let env = dirs.srtEnvironment(this.extraPath);
197
+ if (this.language === "powershell")
198
+ env = dirs.powerShellEnvironment(env);
199
+ addScriptEnv(env, task);
200
+ appendAllowedUnmanagedEnv(env, task.allowedEnvVars ?? []);
201
+ return runCommand([this.runtimeCommand, "--settings", settings, "-c", this.shellCommand(Buffer.from(task.content).toString(), scriptFile)], task, undefined, dirs.workingDir(), env);
202
+ }
203
+ finally {
204
+ if (settings)
205
+ rmSync(settings, { force: true });
206
+ dirs.cleanup();
207
+ }
208
+ }
209
+ shellCommand(source, scriptFile) {
210
+ switch (this.language) {
211
+ case "shell": return source;
212
+ case "python": return heredoc(`${this.interpreter} -`, "PY", source);
213
+ case "powershell": return heredoc(`${this.interpreter} -NoLogo -NoProfile -NonInteractive -InputFormat Text -OutputFormat Text -Command -`, "PWSH", source);
214
+ case "php":
215
+ case "groovy": return heredoc(this.interpreter, this.language.toUpperCase(), source);
216
+ case "rhai": return scriptFile ? `${this.interpreter} '${scriptFile.replace(/'/g, `'\\''`)}'` : heredoc(this.interpreter, "RHAI", source);
217
+ default: return heredoc(this.interpreter, "SCRIPT", source);
218
+ }
219
+ }
220
+ }
221
+ export class DenoScriptRunner {
222
+ command;
223
+ sandboxBackend = "deno";
224
+ language;
225
+ constructor(language, command) {
226
+ this.command = command;
227
+ this.language = normalizeScriptLanguage(language);
228
+ if (!["javascript", "typescript"].includes(this.language))
229
+ throw new Error("Deno runner supports JavaScript and TypeScript only");
230
+ if (!command.trim())
231
+ throw new Error("Deno runner requires a command");
232
+ }
233
+ run(task) {
234
+ try {
235
+ validateScriptTask(this.language, task);
236
+ if (task.secretRefs?.length)
237
+ throw new Error("Deno script runner rejects secret refs without a worker-local secret provider");
238
+ }
239
+ catch (error) {
240
+ return failed(error.message);
241
+ }
242
+ const dirs = ScriptTaskRuntimeDirs.create(`tikeo-deno-${this.language}-runtime`);
243
+ try {
244
+ const args = [this.command, "run", "--no-prompt"];
245
+ if (task.allowNetwork)
246
+ args.push("--allow-net");
247
+ else if (task.allowedNetworkHosts?.length)
248
+ args.push(`--allow-net=${task.allowedNetworkHosts.join(",")}`);
249
+ if (task.allowedEnvVars?.length)
250
+ args.push(`--allow-env=${task.allowedEnvVars.join(",")}`);
251
+ if (task.readOnlyPaths?.length)
252
+ args.push(`--allow-read=${task.readOnlyPaths.join(",")}`);
253
+ const writable = [...(task.writablePaths ?? []), ...dirs.writablePaths()];
254
+ if (writable.length)
255
+ args.push(`--allow-write=${writable.join(",")}`);
256
+ args.push("-");
257
+ const env = dirs.denoEnvironment();
258
+ addScriptEnv(env, task);
259
+ appendAllowedUnmanagedEnv(env, task.allowedEnvVars ?? []);
260
+ return runCommand(args, task, Buffer.from(task.content), dirs.workingDir(), env);
261
+ }
262
+ finally {
263
+ dirs.cleanup();
264
+ }
265
+ }
266
+ }
267
+ export function validateScriptTask(language, task) {
268
+ if (normalizeScriptLanguage(task.language) !== language)
269
+ throw new Error(`script runner language mismatch: task=${task.language} runner=${language}`);
270
+ if (!task.scriptId || !task.versionNumber || !task.content?.length)
271
+ throw new Error("script runner requires a released immutable script version snapshot");
272
+ if (!task.contentSha256)
273
+ throw new Error("script runner requires a content sha256 digest");
274
+ const digest = createHash("sha256").update(task.content).digest("hex");
275
+ if (digest !== task.contentSha256.toLowerCase())
276
+ throw new Error("script content digest mismatch");
277
+ }
278
+ export function emitScriptCommandOutput(log, level, output) {
279
+ if (!log || output.length === 0)
280
+ return;
281
+ for (const line of output.toString().replace(/\r\n/g, "\n").split("\n")) {
282
+ const item = line.trim();
283
+ if (item)
284
+ log(level, `[script] ${item}`);
285
+ }
286
+ }
287
+ function runCommand(command, task, input, cwd, env) {
288
+ const result = spawnSync(command[0], command.slice(1), { input, cwd, env, encoding: "buffer", timeout: task.timeoutMs ?? 30_000 });
289
+ if (result.error)
290
+ return failed(result.error.message.includes("ETIMEDOUT") ? "script runner timed out" : result.error.message);
291
+ const stdout = result.stdout ? Buffer.from(result.stdout) : Buffer.alloc(0);
292
+ const stderr = result.stderr ? Buffer.from(result.stderr) : Buffer.alloc(0);
293
+ emitScriptCommandOutput(task.log, "info", stdout);
294
+ emitScriptCommandOutput(task.log, "error", stderr);
295
+ const message = stdout.toString().trim() || stderr.toString().trim();
296
+ if (result.status !== 0)
297
+ return failed(limitOutput(message || `script runner exited with status ${result.status}`, task.maxOutputBytes ?? 1024 * 1024));
298
+ return { success: true, message: limitOutput(message, task.maxOutputBytes ?? 1024 * 1024) };
299
+ }
300
+ export function writeSrtSettings(task, dirs, scriptFile = "") {
301
+ const allowRead = [...(task.readOnlyPaths ?? []), ...(scriptFile ? [scriptFile] : [])];
302
+ const settings = {
303
+ network: { allowUnixSocket: false, allowedDomains: task.allowedNetworkHosts ?? [], deniedDomains: [] },
304
+ filesystem: { allowRead, allowWrite: [...(task.writablePaths ?? []), ...dirs.writablePaths()], denyRead: sensitiveReadDenies(), denyWrite: [] },
305
+ };
306
+ const file = join(mkdtempSync(join(tmpdir(), "tikeo-srt-settings-dir-")), "settings.json");
307
+ writeFileSync(file, JSON.stringify(settings));
308
+ return file;
309
+ }
310
+ function sensitiveReadDenies() { return [".ssh", ".gnupg", ".aws", ".kube", ".docker", join(".config", "tikeo")].map((path) => join(homedir(), path)); }
311
+ function heredoc(command, marker, content) { let delimiter = marker; while (content.includes(delimiter))
312
+ delimiter += "_TIKEO"; return `${command} <<'${delimiter}'\n${content}\n${delimiter}`; }
313
+ function containerMount(path, readOnly) { const trimmed = path.trim(); if (!trimmed || trimmed !== path || !isAbsolute(trimmed) || trimmed.split(/[\\/]/).includes(".."))
314
+ throw new Error(`script file grant path must be clean and absolute: ${path}`); return `type=bind,src=${trimmed},dst=${trimmed}${readOnly ? ",readonly" : ""}`; }
315
+ function addScriptEnv(env, task) { env.TIKEO_SCRIPT_ID = task.scriptId; env.TIKEO_SCRIPT_VERSION_ID = task.versionId; env.TIKEO_SCRIPT_VERSION_NUMBER = String(task.versionNumber); }
316
+ function limitOutput(message, max) { return Buffer.byteLength(message) <= max ? message : Buffer.from(message).subarray(0, max).toString(); }
@@ -0,0 +1,24 @@
1
+ export declare class ScriptTaskRuntimeDirs {
2
+ root: string;
3
+ home: string;
4
+ config: string;
5
+ cache: string;
6
+ data: string;
7
+ modules: string;
8
+ dotnetHome: string;
9
+ powerShellCache: string;
10
+ tmp: string;
11
+ denoDir: string;
12
+ private constructor();
13
+ static create(prefix: string): ScriptTaskRuntimeDirs;
14
+ requiredDirectories(): string[];
15
+ writablePaths(): string[];
16
+ workingDir(): string;
17
+ scriptFile(extension: string): string;
18
+ baseEnvironment(extraPath?: string[]): NodeJS.ProcessEnv;
19
+ srtEnvironment(extraPath?: string[]): NodeJS.ProcessEnv;
20
+ powerShellEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
21
+ denoEnvironment(): NodeJS.ProcessEnv;
22
+ cleanup(): void;
23
+ }
24
+ export declare function appendAllowedUnmanagedEnv(env: NodeJS.ProcessEnv, allowed: string[]): NodeJS.ProcessEnv;