UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

95 lines (94 loc) • 3.8 kB
import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs"; import { join, resolve, sep } from "node:path"; import { physicalPathOfExisting } from "../core/physical-path.js"; import { sanitizeScope } from "../stores/file/fs-atomic.js"; import { thrownText } from "../core/thrown-value.js"; function isPlainObject(v) { return typeof v === "object" && v !== null && !Array.isArray(v); } export function mergeWorkflowArgs(callArgs, defaultArgs) { const cloneDefaults = (v) => { try { return structuredClone(v); } catch { return v; } }; if (callArgs === undefined) return cloneDefaults(defaultArgs); if (defaultArgs === undefined) return callArgs; if (isPlainObject(callArgs) && isPlainObject(defaultArgs)) { return { ...cloneDefaults(defaultArgs), ...callArgs }; } return callArgs; } export function normalizeStringArg(callArgs, defaultArgs, key = "topic") { if (typeof callArgs === "string" && isPlainObject(defaultArgs)) return { [key]: callArgs }; return callArgs; } function safeFileStem(id) { return /^[A-Za-z0-9._-]{1,120}$/.test(id) && !id.startsWith(".") ? id : undefined; } function containedBy(root, p) { return p === root || p.startsWith(root + sep); } export function createFileWorkflowScriptStore(dir) { const root = resolve(dir); const scopeDir = (scope) => join(root, sanitizeScope(scope)); return { scopePartitioned: true, persist(runId, script, scope) { const stem = safeFileStem(runId); if (stem === undefined) throw new Error(`workflow script store: unsafe runId for a filename: ${JSON.stringify(runId)}`); const base = scopeDir(scope); mkdirSync(base, { recursive: true, mode: 0o700 }); const path = join(base, `${stem}.js`); writeFileSync(path, script, { encoding: "utf-8", mode: 0o600 }); return path; }, load(scriptPath, scope) { const base = scopeDir(scope); const full = resolve(scriptPath); if (!containedBy(base, full)) { throw new Error(`workflow script store: scriptPath is outside this scope's script partition`); } const physRoot = physicalPathOfExisting(root); const physBase = physicalPathOfExisting(base); const physFull = physicalPathOfExisting(full); const refusePhysical = () => { throw new Error(`workflow script store: scriptPath is outside this scope's script partition after symlink resolution`); }; if (!containedBy(physRoot, physBase)) refusePhysical(); if (!containedBy(physBase, physFull)) refusePhysical(); return readFileSync(physFull, "utf-8"); }, resolveName(name) { if (/^wf_/i.test(name)) return undefined; const stem = safeFileStem(name); if (stem === undefined) return undefined; const path = join(root, `${stem}.js`); if (!existsSync(path)) return undefined; const script = readFileSync(path, "utf-8"); const argsPath = join(root, `${stem}.args.json`); if (!existsSync(argsPath)) return { script }; let defaultArgs; try { defaultArgs = JSON.parse(readFileSync(argsPath, "utf-8")); } catch (err) { throw new Error(`workflow script store: invalid defaultArgs JSON in ${stem}.args.json: ${thrownText(err)}`); } return { script, defaultArgs }; }, }; }