import type { OpenClawPluginApi } from "../api.js"; import { resolveDefaultAgentId } from "../api.js"; import type { SkillWorkshopConfig } from "./config.js"; import { applyProposalToWorkspace, prepareProposalWrite } from "./skills.js"; import { SkillWorkshopStore } from "./store.js"; import type { SkillProposal } from "./types.js"; type ToolContext = { workspaceDir?: string; agentId?: string; }; export function resolveWorkspaceDir(params: { api: OpenClawPluginApi; ctx?: ToolContext }): string { return ( params.ctx?.workspaceDir || params.api.runtime.agent.resolveAgentWorkspaceDir( params.api.config, params.ctx?.agentId ?? resolveDefaultAgentId(params.api.config), ) ); } export function createStoreForContext(params: { api: OpenClawPluginApi; ctx?: ToolContext; config: SkillWorkshopConfig; }): SkillWorkshopStore { const workspaceDir = resolveWorkspaceDir(params); return new SkillWorkshopStore({ stateDir: params.api.runtime.state.resolveStateDir(), workspaceDir, }); } export async function applyOrStoreProposal(params: { proposal: SkillProposal; store: SkillWorkshopStore; config: SkillWorkshopConfig; workspaceDir: string; skipAutoApply?: boolean; }): Promise<{ status: "pending" | "applied" | "quarantined"; skillPath?: string; proposal: SkillProposal; }> { const prepared = await prepareProposalWrite({ proposal: params.proposal, maxSkillBytes: params.config.maxSkillBytes, }); const critical = prepared.findings.find((finding) => finding.severity === "critical"); if (critical) { const stored = await params.store.add( { ...params.proposal, status: "quarantined", updatedAt: Date.now(), scanFindings: prepared.findings, quarantineReason: critical.message, }, params.config.maxPending, ); return { status: "quarantined", proposal: stored }; } if (params.config.approvalPolicy === "auto" && !params.skipAutoApply) { const applied = await applyProposalToWorkspace({ proposal: params.proposal, maxSkillBytes: params.config.maxSkillBytes, }); const stored = await params.store.add( { ...params.proposal, status: "applied", updatedAt: Date.now(), scanFindings: applied.findings, }, params.config.maxPending, ); return { status: "applied", skillPath: applied.skillPath, proposal: stored }; } const stored = await params.store.add( { ...params.proposal, scanFindings: prepared.findings }, params.config.maxPending, ); return { status: "pending", proposal: stored }; }