UNPKG

trigger.dev

Version:

A Command-Line Interface for Trigger.dev projects

110 lines 5.04 kB
import { readFile } from "node:fs/promises"; import { isAbsolute, join, resolve as resolvePath } from "node:path"; import { copyDirectoryRecursive } from "@trigger.dev/build/internal"; import { indexWorkerManifest } from "../indexing/indexWorkerManifest.js"; import { execOptionsForRuntime } from "@trigger.dev/core/v3/build"; /** * Copy each skill's source folder to `{destinationRoot}/{id}/`. Validates * that `SKILL.md` exists and has the required frontmatter. Pure file IO — * no indexer subprocess, no env handling. * * Used by the dev path (driven by the main worker indexer's skills list) * and indirectly by the deploy path (via `bundleSkills` which discovers * skills via its own indexer pass first, then delegates here). */ export async function copySkillFolders(options) { const { skills, destinationRoot, workingDir, logger } = options; if (skills.length === 0) { return []; } for (const skill of skills) { const callerDir = skill.filePath ? resolvePath(workingDir, skill.filePath, "..") : workingDir; const sourcePath = isAbsolute(skill.sourcePath) ? skill.sourcePath : resolvePath(callerDir, skill.sourcePath); const skillMdPath = join(sourcePath, "SKILL.md"); let skillMd; try { skillMd = await readFile(skillMdPath, "utf8"); } catch { throw new Error(`Skill "${skill.id}": SKILL.md not found at ${skillMdPath}. ` + `Registered via skills.define({ id: "${skill.id}", path: "${skill.sourcePath}" }) ` + `at ${skill.filePath}.`); } if (!/^---\r?\n[\s\S]*?\r?\n---/.test(skillMd)) { throw new Error(`Skill "${skill.id}": SKILL.md at ${skillMdPath} is missing a frontmatter block.`); } if (!/\bname:\s*\S/.test(skillMd) || !/\bdescription:\s*\S/.test(skillMd)) { throw new Error(`Skill "${skill.id}": SKILL.md at ${skillMdPath} frontmatter must include both \`name\` and \`description\`.`); } const skillDest = join(destinationRoot, skill.id); logger.debug(`[copySkillFolders] Copying ${sourcePath}${skillDest}`); await copyDirectoryRecursive(sourcePath, skillDest); } return [...skills].sort((a, b) => a.id.localeCompare(b.id)); } /** * Built-in skill bundler — not an extension. Runs the indexer locally * against the bundled worker output to discover `skills.define(...)` * registrations, validates each skill's `SKILL.md`, and copies the * folder into `{outputPath}/.trigger/skills/{id}/` so the deploy image * picks it up via the existing Dockerfile `COPY`. * * Used by the deploy path. The dev path uses `copySkillFolders` directly, * driven by the main worker indexer that already runs in `BackgroundWorker.initialize` — * no duplicate indexer pass needed there. * * No `trigger.config.ts` changes required — discovery is side-effect * based, same mechanism as task/prompt registration. */ export async function bundleSkills(options) { const { buildManifest, buildManifestPath, workingDir, env, logger } = options; let skills; try { const workerManifest = await indexWorkerManifest({ runtime: buildManifest.runtime, indexWorkerPath: buildManifest.indexWorkerEntryPoint, buildManifestPath, nodeOptions: execOptionsForRuntime(buildManifest.runtime, buildManifest), env, cwd: workingDir, otelHookInclude: buildManifest.otelImportHook?.include, otelHookExclude: buildManifest.otelImportHook?.exclude, handleStdout(data) { logger.debug(`[bundleSkills] ${data}`); }, handleStderr(data) { if (!data.includes("Debugger attached")) { logger.debug(`[bundleSkills:stderr] ${data}`); } }, }); skills = workerManifest.skills ?? []; } catch (err) { // Skill discovery via the indexer is best-effort — if the user's // bundle doesn't load cleanly here the downstream full indexer will // surface the real error. Warn so the user sees what went wrong. logger.warn(`[bundleSkills] skill discovery failed, skipping skill bundling: ${err.message}`); return { buildManifest, skills: [] }; } if (skills.length === 0) { return { buildManifest, skills: [] }; } // Deploy target: the Dockerfile COPY picks up everything under outputPath // into /app, so we target {outputPath}/.trigger/skills/{id}/ and the // container's cwd (/app) resolves correctly. const destinationRoot = join(buildManifest.outputPath, ".trigger", "skills"); const sortedSkills = await copySkillFolders({ skills, destinationRoot, workingDir, logger, }); return { buildManifest: { ...buildManifest, skills: sortedSkills }, skills: sortedSkills, }; } //# sourceMappingURL=bundleSkills.js.map