bopodev-api 0.1.32 → 0.1.33
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.
- package/package.json +6 -4
- package/src/lib/agent-config.ts +58 -10
- package/src/lib/builtin-bopo-skills/bopodev-control-plane.md +123 -0
- package/src/lib/builtin-bopo-skills/bopodev-create-agent.md +90 -0
- package/src/lib/builtin-bopo-skills/index.ts +36 -0
- package/src/lib/builtin-bopo-skills/para-memory-files.md +48 -0
- package/src/lib/instance-paths.ts +5 -0
- package/src/routes/agents.ts +21 -7
- package/src/routes/observability.ts +167 -0
- package/src/services/company-file-archive-service.ts +2 -1
- package/src/services/company-skill-file-service.ts +558 -0
- package/src/services/governance-service.ts +11 -3
- package/src/services/heartbeat-service/heartbeat-run.ts +45 -8
|
@@ -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);
|