UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

94 lines 3.29 kB
/** * Skill script runtime registry (#1227). * * Resolves a runtime name from a skill's `script.runtime` declaration to * an interpreter command + args list. The CLI uses this dispatch table * to invoke skill scripts with the right interpreter, regardless of the * agent's host platform. * * Schema in SKILL.md frontmatter: * * script: * entrypoint: scripts/voice_loader.py * runtime: python3 * * No silent fallbacks: an unknown runtime name produces a clear error * with the supported list. `auto` dispatches by file extension, with * shebang as a tiebreaker for ambiguous extensions. */ import { promises as fs } from 'node:fs'; import * as path from 'node:path'; /** * Built-in runtime → interpreter mapping. Keep this small and explicit. * New runtimes go here, not in random call sites. */ const RUNTIME_TABLE = { node: { command: 'node', prefixArgs: [] }, python: { command: 'python3', prefixArgs: [] }, python3: { command: 'python3', prefixArgs: [] }, bash: { command: 'bash', prefixArgs: [] }, sh: { command: 'sh', prefixArgs: [] }, pwsh: { command: 'pwsh', prefixArgs: ['-File'] }, ruby: { command: 'ruby', prefixArgs: [] }, }; /** * The set of supported runtime names — used in error messages so users * see what they can pick from when they typo a runtime. */ export function supportedRuntimes() { return [...Object.keys(RUNTIME_TABLE), 'auto']; } /** * Resolve a runtime name (potentially `auto`) to an invocation. * * `auto` resolves by file extension first; if the extension is unknown, * it reads the entrypoint's shebang line. Returns null if neither path * yields a known runtime — callers must surface that as a user error. */ export async function resolveRuntime(runtimeName, entrypointAbsPath) { const name = runtimeName.toLowerCase().trim(); if (name !== 'auto') { return RUNTIME_TABLE[name] ?? null; } // auto: extension first const ext = path.extname(entrypointAbsPath).toLowerCase(); const byExt = { '.js': 'node', '.mjs': 'node', '.cjs': 'node', '.py': 'python3', '.sh': 'bash', '.bash': 'bash', '.ps1': 'pwsh', '.rb': 'ruby', }; const byExtName = byExt[ext]; if (byExtName) return RUNTIME_TABLE[byExtName] ?? null; // Shebang fallback try { const head = await fs.readFile(entrypointAbsPath, 'utf8'); const firstLine = head.split('\n', 1)[0] ?? ''; if (firstLine.startsWith('#!')) { if (firstLine.includes('python3')) return RUNTIME_TABLE.python3; if (firstLine.includes('python')) return RUNTIME_TABLE.python3; if (firstLine.includes('node')) return RUNTIME_TABLE.node; if (firstLine.includes('bash')) return RUNTIME_TABLE.bash; if (firstLine.match(/\bsh\b/)) return RUNTIME_TABLE.sh; if (firstLine.includes('ruby')) return RUNTIME_TABLE.ruby; if (firstLine.includes('pwsh')) return RUNTIME_TABLE.pwsh; } } catch { // unreadable — fall through to null } return null; } //# sourceMappingURL=runtime.js.map