bopodev-api 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,6 +24,17 @@ import {
24
24
  readAgentOperatingFile,
25
25
  writeAgentOperatingFile
26
26
  } from "../services/agent-operating-file-service";
27
+ import { BUILTIN_BOPO_SKILLS } from "../lib/builtin-bopo-skills";
28
+ import {
29
+ createCompanySkillPackage,
30
+ deleteCompanySkillPackage,
31
+ linkCompanySkillFromUrl,
32
+ listCompanySkillFiles,
33
+ listCompanySkillPackages,
34
+ refreshCompanySkillFromUrl,
35
+ readCompanySkillFile,
36
+ writeCompanySkillFile
37
+ } from "../services/company-skill-file-service";
27
38
  import {
28
39
  listAgentMemoryFiles,
29
40
  listCompanyMemoryFiles,
@@ -512,6 +523,162 @@ export function createObservabilityRouter(ctx: AppContext) {
512
523
  }
513
524
  });
514
525
 
526
+ router.get("/builtin-skills", async (_req, res) => {
527
+ return sendOk(
528
+ res,
529
+ BUILTIN_BOPO_SKILLS.map((row) => ({
530
+ id: row.id,
531
+ title: row.title,
532
+ content: row.content
533
+ }))
534
+ );
535
+ });
536
+
537
+ router.get("/company-skills", async (req, res) => {
538
+ const companyId = req.companyId!;
539
+ try {
540
+ const { items: packages } = await listCompanySkillPackages({ companyId, maxSkills: 80 });
541
+ const items = await Promise.all(
542
+ packages.map(async (pack) => {
543
+ const { relativePaths, hasLocalSkillMd } = await listCompanySkillFiles({
544
+ companyId,
545
+ skillId: pack.skillId,
546
+ maxFiles: 200
547
+ });
548
+ return {
549
+ skillId: pack.skillId,
550
+ linkedUrl: pack.linkedUrl,
551
+ linkLastFetchedAt: pack.linkLastFetchedAt,
552
+ hasLocalSkillMd,
553
+ files: relativePaths.map((relativePath) => ({ relativePath }))
554
+ };
555
+ })
556
+ );
557
+ return sendOk(res, { items });
558
+ } catch (error) {
559
+ return sendError(res, String(error), 422);
560
+ }
561
+ });
562
+
563
+ router.get("/company-skills/file", async (req, res) => {
564
+ const companyId = req.companyId!;
565
+ const skillId = typeof req.query.skillId === "string" ? req.query.skillId.trim() : "";
566
+ const relativePath = typeof req.query.path === "string" ? req.query.path.trim() : "";
567
+ if (!skillId || !relativePath) {
568
+ return sendError(res, "Query parameters 'skillId' and 'path' are required.", 422);
569
+ }
570
+ try {
571
+ const file = await readCompanySkillFile({ companyId, skillId, relativePath });
572
+ return sendOk(res, { content: file.content });
573
+ } catch (error) {
574
+ return sendError(res, String(error), 422);
575
+ }
576
+ });
577
+
578
+ router.put("/company-skills/file", async (req, res) => {
579
+ if (!enforcePermission(req, res, "agents:write")) {
580
+ return;
581
+ }
582
+ const companyId = req.companyId!;
583
+ const skillId = typeof req.query.skillId === "string" ? req.query.skillId.trim() : "";
584
+ const relativePath = typeof req.query.path === "string" ? req.query.path.trim() : "";
585
+ if (!skillId || !relativePath) {
586
+ return sendError(res, "Query parameters 'skillId' and 'path' are required.", 422);
587
+ }
588
+ const body = req.body as { content?: unknown };
589
+ if (typeof body?.content !== "string") {
590
+ return sendError(res, "Expected JSON body with string 'content'.", 422);
591
+ }
592
+ try {
593
+ const result = await writeCompanySkillFile({
594
+ companyId,
595
+ skillId,
596
+ relativePath,
597
+ content: body.content
598
+ });
599
+ return sendOk(res, result);
600
+ } catch (error) {
601
+ return sendError(res, String(error), 422);
602
+ }
603
+ });
604
+
605
+ router.post("/company-skills/create", async (req, res) => {
606
+ if (!enforcePermission(req, res, "agents:write")) {
607
+ return;
608
+ }
609
+ const companyId = req.companyId!;
610
+ const body = req.body as { skillId?: unknown };
611
+ if (typeof body?.skillId !== "string" || !body.skillId.trim()) {
612
+ return sendError(res, "Expected JSON body with string 'skillId'.", 422);
613
+ }
614
+ try {
615
+ const result = await createCompanySkillPackage({ companyId, skillId: body.skillId });
616
+ return sendOk(res, result);
617
+ } catch (error) {
618
+ return sendError(res, String(error), 422);
619
+ }
620
+ });
621
+
622
+ router.post("/company-skills/link-url", async (req, res) => {
623
+ if (!enforcePermission(req, res, "agents:write")) {
624
+ return;
625
+ }
626
+ const companyId = req.companyId!;
627
+ const body = req.body as { url?: unknown; skillId?: unknown };
628
+ if (typeof body?.url !== "string" || !body.url.trim()) {
629
+ return sendError(res, "Expected JSON body with string 'url'.", 422);
630
+ }
631
+ const optionalSkillId =
632
+ typeof body.skillId === "string" && body.skillId.trim() ? body.skillId.trim() : undefined;
633
+ try {
634
+ const result = await linkCompanySkillFromUrl({
635
+ companyId,
636
+ url: body.url,
637
+ ...(optionalSkillId ? { skillId: optionalSkillId } : {})
638
+ });
639
+ return sendOk(res, result);
640
+ } catch (error) {
641
+ return sendError(res, String(error), 422);
642
+ }
643
+ });
644
+
645
+ router.post("/company-skills/refresh-from-url", async (req, res) => {
646
+ if (!enforcePermission(req, res, "agents:write")) {
647
+ return;
648
+ }
649
+ const companyId = req.companyId!;
650
+ const body = req.body as { skillId?: unknown };
651
+ if (typeof body?.skillId !== "string" || !body.skillId.trim()) {
652
+ return sendError(res, "Expected JSON body with string 'skillId'.", 422);
653
+ }
654
+ try {
655
+ const result = await refreshCompanySkillFromUrl({
656
+ companyId,
657
+ skillId: body.skillId.trim()
658
+ });
659
+ return sendOk(res, result);
660
+ } catch (error) {
661
+ return sendError(res, String(error), 422);
662
+ }
663
+ });
664
+
665
+ router.delete("/company-skills", async (req, res) => {
666
+ if (!enforcePermission(req, res, "agents:write")) {
667
+ return;
668
+ }
669
+ const companyId = req.companyId!;
670
+ const skillId = typeof req.query.skillId === "string" ? req.query.skillId.trim() : "";
671
+ if (!skillId) {
672
+ return sendError(res, "Query parameter 'skillId' is required.", 422);
673
+ }
674
+ try {
675
+ const result = await deleteCompanySkillPackage({ companyId, skillId });
676
+ return sendOk(res, result);
677
+ } catch (error) {
678
+ return sendError(res, String(error), 422);
679
+ }
680
+ });
681
+
515
682
  router.get("/memory/:agentId/context-preview", async (req, res) => {
516
683
  const companyId = req.companyId!;
517
684
  const agentId = req.params.agentId;
@@ -10,6 +10,7 @@ import {
10
10
  resolveAgentOperatingPath,
11
11
  resolveCompanyProjectsWorkspacePath
12
12
  } from "../lib/instance-paths";
13
+ import { SKILL_LINK_BASENAME } from "./company-skill-file-service";
13
14
  import { listWorkLoopTriggers, listWorkLoops } from "./work-loop-service/work-loop-service";
14
15
 
15
16
  const EXPORT_SCHEMA = "bopo/company-export/v1";
@@ -68,7 +69,7 @@ async function walkTextFilesUnder(rootAbs: string, budget: { n: number }): Promi
68
69
  return;
69
70
  }
70
71
  const name = ent.name;
71
- if (name.startsWith(".")) {
72
+ if (name.startsWith(".") && name !== SKILL_LINK_BASENAME) {
72
73
  continue;
73
74
  }
74
75
  const full = join(dir, name);