import { Router } from "express"; import type { Db } from "@paperclipai/db"; import { companySkillCreateSchema, companySkillFileUpdateSchema, companySkillImportSchema, companySkillProjectScanRequestSchema, } from "@paperclipai/shared"; import { validate } from "../middleware/validate.js"; import { companySkillService, logActivity } from "../services/index.js"; import { assertCompanyAccess, getActorInfo } from "./authz.js"; export function companySkillRoutes(db: Db) { const router = Router(); const svc = companySkillService(db); router.get("/companies/:companyId/skills", async (req, res) => { const companyId = req.params.companyId as string; assertCompanyAccess(req, companyId); const result = await svc.list(companyId); res.json(result); }); router.get("/companies/:companyId/skills/:skillId", async (req, res) => { const companyId = req.params.companyId as string; const skillId = req.params.skillId as string; assertCompanyAccess(req, companyId); const result = await svc.detail(companyId, skillId); if (!result) { res.status(404).json({ error: "Skill not found" }); return; } res.json(result); }); router.get("/companies/:companyId/skills/:skillId/update-status", async (req, res) => { const companyId = req.params.companyId as string; const skillId = req.params.skillId as string; assertCompanyAccess(req, companyId); const result = await svc.updateStatus(companyId, skillId); if (!result) { res.status(404).json({ error: "Skill not found" }); return; } res.json(result); }); router.get("/companies/:companyId/skills/:skillId/files", async (req, res) => { const companyId = req.params.companyId as string; const skillId = req.params.skillId as string; const relativePath = String(req.query.path ?? "SKILL.md"); assertCompanyAccess(req, companyId); const result = await svc.readFile(companyId, skillId, relativePath); if (!result) { res.status(404).json({ error: "Skill not found" }); return; } res.json(result); }); router.post( "/companies/:companyId/skills", validate(companySkillCreateSchema), async (req, res) => { const companyId = req.params.companyId as string; assertCompanyAccess(req, companyId); const result = await svc.createLocalSkill(companyId, req.body); const actor = getActorInfo(req); await logActivity(db, { companyId, actorType: actor.actorType, actorId: actor.actorId, agentId: actor.agentId, runId: actor.runId, action: "company.skill_created", entityType: "company_skill", entityId: result.id, details: { slug: result.slug, name: result.name, }, }); res.status(201).json(result); }, ); router.patch( "/companies/:companyId/skills/:skillId/files", validate(companySkillFileUpdateSchema), async (req, res) => { const companyId = req.params.companyId as string; const skillId = req.params.skillId as string; assertCompanyAccess(req, companyId); const result = await svc.updateFile( companyId, skillId, String(req.body.path ?? ""), String(req.body.content ?? ""), ); const actor = getActorInfo(req); await logActivity(db, { companyId, actorType: actor.actorType, actorId: actor.actorId, agentId: actor.agentId, runId: actor.runId, action: "company.skill_file_updated", entityType: "company_skill", entityId: skillId, details: { path: result.path, markdown: result.markdown, }, }); res.json(result); }, ); router.post( "/companies/:companyId/skills/import", validate(companySkillImportSchema), async (req, res) => { const companyId = req.params.companyId as string; assertCompanyAccess(req, companyId); const source = String(req.body.source ?? ""); const result = await svc.importFromSource(companyId, source); const actor = getActorInfo(req); await logActivity(db, { companyId, actorType: actor.actorType, actorId: actor.actorId, agentId: actor.agentId, runId: actor.runId, action: "company.skills_imported", entityType: "company", entityId: companyId, details: { source, importedCount: result.imported.length, importedSlugs: result.imported.map((skill) => skill.slug), warningCount: result.warnings.length, }, }); res.status(201).json(result); }, ); router.post( "/companies/:companyId/skills/scan-projects", validate(companySkillProjectScanRequestSchema), async (req, res) => { const companyId = req.params.companyId as string; assertCompanyAccess(req, companyId); const result = await svc.scanProjectWorkspaces(companyId, req.body); const actor = getActorInfo(req); await logActivity(db, { companyId, actorType: actor.actorType, actorId: actor.actorId, agentId: actor.agentId, runId: actor.runId, action: "company.skills_scanned", entityType: "company", entityId: companyId, details: { scannedProjects: result.scannedProjects, scannedWorkspaces: result.scannedWorkspaces, discovered: result.discovered, importedCount: result.imported.length, updatedCount: result.updated.length, conflictCount: result.conflicts.length, warningCount: result.warnings.length, }, }); res.json(result); }, ); router.post("/companies/:companyId/skills/:skillId/install-update", async (req, res) => { const companyId = req.params.companyId as string; const skillId = req.params.skillId as string; assertCompanyAccess(req, companyId); const result = await svc.installUpdate(companyId, skillId); if (!result) { res.status(404).json({ error: "Skill not found" }); return; } const actor = getActorInfo(req); await logActivity(db, { companyId, actorType: actor.actorType, actorId: actor.actorId, agentId: actor.agentId, runId: actor.runId, action: "company.skill_update_installed", entityType: "company_skill", entityId: result.id, details: { slug: result.slug, sourceRef: result.sourceRef, }, }); res.json(result); }); return router; }