bopodev-api 0.1.34 → 0.1.36
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 +5 -5
- package/src/app.ts +4 -2
- package/src/assets/starter-packs/customer-support-excellence.zip +0 -0
- package/src/assets/starter-packs/devrel-growth.zip +0 -0
- package/src/assets/starter-packs/product-delivery-trio.zip +0 -0
- package/src/assets/starter-packs/revenue-gtm-b2b.zip +0 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/.bopo.yaml +129 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/COMPANY.md +7 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/README.md +3 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/agents/founder-ceo/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/agents/support-lead/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/agents/support-specialist/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/projects/knowledge-base/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/projects/quality/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/projects/queue/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/skills/kb-article-skeleton/SKILL.md +17 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/skills/ticket-response-playbook/SKILL.md +15 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/tasks/daily-queue-standup/TASK.md +11 -0
- package/src/assets/starter-packs/sources/customer-support-excellence/tasks/kb-gap-sweep/TASK.md +11 -0
- package/src/assets/starter-packs/sources/devrel-growth/.bopo.yaml +128 -0
- package/src/assets/starter-packs/sources/devrel-growth/COMPANY.md +7 -0
- package/src/assets/starter-packs/sources/devrel-growth/README.md +3 -0
- package/src/assets/starter-packs/sources/devrel-growth/agents/content-producer/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/devrel-growth/agents/devrel-lead/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/devrel-growth/agents/founder-ceo/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/devrel-growth/projects/community/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/devrel-growth/projects/docs-education/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/devrel-growth/projects/partners/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/devrel-growth/skills/changelog-to-post/SKILL.md +14 -0
- package/src/assets/starter-packs/sources/devrel-growth/skills/tutorial-outline/SKILL.md +15 -0
- package/src/assets/starter-packs/sources/devrel-growth/tasks/community-health-review/TASK.md +11 -0
- package/src/assets/starter-packs/sources/devrel-growth/tasks/weekly-content-plan/TASK.md +11 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/.bopo.yaml +138 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/COMPANY.md +7 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/README.md +9 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/agents/engineer-ic/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/agents/founder-ceo/HEARTBEAT.md +6 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/agents/product-lead/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/projects/delivery/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/projects/quality/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/projects/strategy/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/skills/issue-triage/SKILL.md +21 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/skills/rca-template/SKILL.md +16 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/tasks/release-hygiene/TASK.md +11 -0
- package/src/assets/starter-packs/sources/product-delivery-trio/tasks/weekly-leadership-sync/TASK.md +11 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/.bopo.yaml +132 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/COMPANY.md +7 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/README.md +3 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/agents/founder-ceo/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/agents/gtm-lead/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/agents/pipeline-owner/HEARTBEAT.md +5 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/projects/customer-success/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/projects/deals/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/projects/pipeline/PROJECT.md +7 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/skills/discovery-call-brief/SKILL.md +14 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/skills/icp-scoring/SKILL.md +20 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/tasks/pipeline-hygiene/TASK.md +11 -0
- package/src/assets/starter-packs/sources/revenue-gtm-b2b/tasks/weekly-revenue-review/TASK.md +11 -0
- package/src/lib/agent-issue-permissions.ts +56 -0
- package/src/lib/builtin-bopo-skills/bopodev-control-plane.md +7 -0
- package/src/lib/instance-paths.ts +5 -0
- package/src/realtime/office-space.ts +7 -0
- package/src/routes/agents.ts +23 -1
- package/src/routes/assistant.ts +40 -1
- package/src/routes/companies.ts +227 -15
- package/src/routes/issues.ts +82 -3
- package/src/routes/observability.ts +222 -0
- package/src/routes/plugins.ts +393 -103
- package/src/routes/{loops.ts → routines.ts} +72 -76
- package/src/scripts/onboard-seed.ts +2 -0
- package/src/server.ts +3 -1
- package/src/services/company-assistant-context-snapshot.ts +4 -2
- package/src/services/company-assistant-service.ts +17 -15
- package/src/services/company-file-archive-service.ts +81 -6
- package/src/services/company-file-import-service.ts +221 -31
- package/src/services/company-knowledge-file-service.ts +361 -0
- package/src/services/company-skill-file-service.ts +151 -2
- package/src/services/governance-service.ts +58 -3
- package/src/services/heartbeat-service/heartbeat-run.ts +7 -0
- package/src/services/plugin-artifact-installer.ts +115 -0
- package/src/services/plugin-artifact-store.ts +28 -0
- package/src/services/plugin-capability-policy.ts +31 -0
- package/src/services/plugin-jobs-service.ts +74 -0
- package/src/services/plugin-manifest-loader.ts +78 -3
- package/src/services/plugin-rpc.ts +102 -0
- package/src/services/plugin-runtime.ts +240 -209
- package/src/services/plugin-worker-host.ts +167 -0
- package/src/services/starter-pack-registry.ts +68 -0
- package/src/services/template-apply-service.ts +3 -1
- package/src/services/template-catalog.ts +29 -0
- package/src/services/work-loop-service/work-loop-service.ts +18 -18
- package/src/shutdown/graceful-shutdown.ts +3 -1
- package/src/validation/issue-routes.ts +19 -2
- package/src/worker/scheduler.ts +21 -1
- package/src/services/company-export-service.ts +0 -63
|
@@ -4,7 +4,7 @@ import { unzipSync } from "fflate";
|
|
|
4
4
|
import { parse as yamlParse } from "yaml";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import type { BopoDb } from "bopodev-db";
|
|
7
|
-
import { createAgent, createCompany, createProject } from "bopodev-db";
|
|
7
|
+
import { createAgent, createCompany, createGoal, createProject, updateAgent, updateGoal } from "bopodev-db";
|
|
8
8
|
import { normalizeRuntimeConfig, runtimeConfigToDb, runtimeConfigToStateBlobPatch } from "../lib/agent-config";
|
|
9
9
|
import {
|
|
10
10
|
resolveAgentMemoryRootPath,
|
|
@@ -16,16 +16,22 @@ import { ensureBuiltinPluginsRegistered } from "./plugin-runtime";
|
|
|
16
16
|
import { ensureCompanyBuiltinTemplateDefaults } from "./template-catalog";
|
|
17
17
|
import { addWorkLoopTrigger, createWorkLoop } from "./work-loop-service/work-loop-service";
|
|
18
18
|
|
|
19
|
-
const EXPORT_SCHEMA = "bopo/company-export/v1";
|
|
19
|
+
export const EXPORT_SCHEMA = "bopo/company-export/v1";
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const goalLevelSchema = z.enum(["company", "project", "agent"]);
|
|
22
|
+
const goalStatusSchema = z.enum(["draft", "active", "completed", "archived"]);
|
|
23
|
+
|
|
24
|
+
export const BopoExportYamlSchema = z.object({
|
|
22
25
|
schema: z.string(),
|
|
23
26
|
company: z.object({
|
|
24
27
|
name: z.string().min(1),
|
|
25
28
|
mission: z.string().nullable().optional(),
|
|
26
29
|
slug: z.string().optional()
|
|
27
30
|
}),
|
|
28
|
-
projects: z.record(
|
|
31
|
+
projects: z.record(
|
|
32
|
+
z.string(),
|
|
33
|
+
z.object({ name: z.string().min(1), description: z.string().nullable().optional(), status: z.string().optional() })
|
|
34
|
+
),
|
|
29
35
|
agents: z.record(
|
|
30
36
|
z.string(),
|
|
31
37
|
z.object({
|
|
@@ -37,9 +43,27 @@ const BopoExportYamlSchema = z.object({
|
|
|
37
43
|
managerSlug: z.string().nullable().optional(),
|
|
38
44
|
providerType: z.string().min(1),
|
|
39
45
|
heartbeatCron: z.string().min(1),
|
|
40
|
-
canHireAgents: z.boolean().optional()
|
|
46
|
+
canHireAgents: z.boolean().optional(),
|
|
47
|
+
canAssignAgents: z.boolean().optional(),
|
|
48
|
+
canCreateIssues: z.boolean().optional(),
|
|
49
|
+
bootstrapPrompt: z.string().nullable().optional(),
|
|
50
|
+
monthlyBudgetUsd: z.union([z.string(), z.number()]).optional()
|
|
41
51
|
})
|
|
42
52
|
),
|
|
53
|
+
goals: z
|
|
54
|
+
.record(
|
|
55
|
+
z.string(),
|
|
56
|
+
z.object({
|
|
57
|
+
level: goalLevelSchema,
|
|
58
|
+
title: z.string().min(1),
|
|
59
|
+
description: z.string().nullable().optional(),
|
|
60
|
+
status: goalStatusSchema.optional(),
|
|
61
|
+
projectSlug: z.string().nullable().optional(),
|
|
62
|
+
parentGoalSlug: z.string().nullable().optional(),
|
|
63
|
+
ownerAgentSlug: z.string().nullable().optional()
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
.optional(),
|
|
43
67
|
routines: z
|
|
44
68
|
.record(
|
|
45
69
|
z.string(),
|
|
@@ -62,6 +86,13 @@ const BopoExportYamlSchema = z.object({
|
|
|
62
86
|
.optional()
|
|
63
87
|
});
|
|
64
88
|
|
|
89
|
+
export type BopoExportDoc = z.infer<typeof BopoExportYamlSchema>;
|
|
90
|
+
|
|
91
|
+
export type ParsedCompanyPackage = {
|
|
92
|
+
doc: BopoExportDoc;
|
|
93
|
+
entries: Record<string, string>;
|
|
94
|
+
};
|
|
95
|
+
|
|
65
96
|
export class CompanyFileImportError extends Error {
|
|
66
97
|
constructor(message: string) {
|
|
67
98
|
super(message);
|
|
@@ -77,7 +108,7 @@ function normalizeZipPath(key: string): string | null {
|
|
|
77
108
|
return t;
|
|
78
109
|
}
|
|
79
110
|
|
|
80
|
-
function decodeZipEntries(buffer: Buffer): Record<string, string> {
|
|
111
|
+
export function decodeZipEntries(buffer: Buffer): Record<string, string> {
|
|
81
112
|
let raw: Record<string, Uint8Array>;
|
|
82
113
|
try {
|
|
83
114
|
raw = unzipSync(new Uint8Array(buffer));
|
|
@@ -99,6 +130,30 @@ function decodeZipEntries(buffer: Buffer): Record<string, string> {
|
|
|
99
130
|
return out;
|
|
100
131
|
}
|
|
101
132
|
|
|
133
|
+
/** Parse and validate a company zip; throws CompanyFileImportError on failure. */
|
|
134
|
+
export function parseCompanyZipBuffer(buffer: Buffer): ParsedCompanyPackage {
|
|
135
|
+
const entries = decodeZipEntries(buffer);
|
|
136
|
+
const yamlText = entries[".bopo.yaml"] ?? entries["bopo.yaml"];
|
|
137
|
+
if (!yamlText?.trim()) {
|
|
138
|
+
throw new CompanyFileImportError('Zip must contain a ".bopo.yaml" manifest at the archive root.');
|
|
139
|
+
}
|
|
140
|
+
let parsedYaml: unknown;
|
|
141
|
+
try {
|
|
142
|
+
parsedYaml = yamlParse(yamlText);
|
|
143
|
+
} catch {
|
|
144
|
+
throw new CompanyFileImportError(".bopo.yaml is not valid YAML.");
|
|
145
|
+
}
|
|
146
|
+
const parsed = BopoExportYamlSchema.safeParse(parsedYaml);
|
|
147
|
+
if (!parsed.success) {
|
|
148
|
+
throw new CompanyFileImportError(`Invalid export manifest: ${parsed.error.message}`);
|
|
149
|
+
}
|
|
150
|
+
const doc = parsed.data;
|
|
151
|
+
if (doc.schema !== EXPORT_SCHEMA) {
|
|
152
|
+
throw new CompanyFileImportError(`Unsupported export schema '${doc.schema}' (expected ${EXPORT_SCHEMA}).`);
|
|
153
|
+
}
|
|
154
|
+
return { doc, entries };
|
|
155
|
+
}
|
|
156
|
+
|
|
102
157
|
const PROVIDER_TYPES = new Set([
|
|
103
158
|
"claude_code",
|
|
104
159
|
"codex",
|
|
@@ -118,36 +173,60 @@ function coerceProviderType(raw: string): AgentProvider {
|
|
|
118
173
|
return PROVIDER_TYPES.has(raw) ? (raw as AgentProvider) : "shell";
|
|
119
174
|
}
|
|
120
175
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (!yamlText?.trim()) {
|
|
125
|
-
throw new CompanyFileImportError('Zip must contain a ".bopo.yaml" manifest at the archive root.');
|
|
176
|
+
function formatMonthlyBudgetUsdFromManifest(raw: string | number | undefined): string {
|
|
177
|
+
if (raw === undefined) {
|
|
178
|
+
return "100.0000";
|
|
126
179
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
parsedYaml = yamlParse(yamlText);
|
|
130
|
-
} catch {
|
|
131
|
-
throw new CompanyFileImportError(".bopo.yaml is not valid YAML.");
|
|
180
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
181
|
+
return raw.toFixed(4);
|
|
132
182
|
}
|
|
133
|
-
const
|
|
134
|
-
if (!
|
|
135
|
-
|
|
183
|
+
const s = String(raw).trim();
|
|
184
|
+
if (!s) {
|
|
185
|
+
return "100.0000";
|
|
136
186
|
}
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
187
|
+
const n = Number(s);
|
|
188
|
+
return Number.isFinite(n) ? n.toFixed(4) : "100.0000";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function sortGoalSlugsForImport(
|
|
192
|
+
goals: Record<string, { parentGoalSlug?: string | null | undefined }>
|
|
193
|
+
): string[] {
|
|
194
|
+
const slugs = Object.keys(goals);
|
|
195
|
+
const slugSet = new Set(slugs);
|
|
196
|
+
const remaining = new Set(slugs);
|
|
197
|
+
const order: string[] = [];
|
|
198
|
+
while (remaining.size > 0) {
|
|
199
|
+
const ready = [...remaining].filter((slug) => {
|
|
200
|
+
const p = goals[slug]?.parentGoalSlug?.trim();
|
|
201
|
+
if (!p) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
if (!slugSet.has(p)) {
|
|
205
|
+
throw new CompanyFileImportError(`Goal '${slug}' references unknown parent goal slug '${p}'.`);
|
|
206
|
+
}
|
|
207
|
+
return !remaining.has(p);
|
|
208
|
+
});
|
|
209
|
+
if (ready.length === 0) {
|
|
210
|
+
throw new CompanyFileImportError("Circular goal parent chain in manifest.");
|
|
211
|
+
}
|
|
212
|
+
ready.sort((a, b) => a.localeCompare(b));
|
|
213
|
+
for (const s of ready) {
|
|
214
|
+
order.push(s);
|
|
215
|
+
remaining.delete(s);
|
|
216
|
+
}
|
|
140
217
|
}
|
|
218
|
+
return order;
|
|
219
|
+
}
|
|
141
220
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Seeds projects, agents, workspace files, goals, and routines for an existing company.
|
|
223
|
+
* Does not create the company row or call ensureCompanyBuiltinTemplateDefaults.
|
|
224
|
+
*/
|
|
225
|
+
export async function seedOperationalDataFromPackage(db: BopoDb, companyId: string, parsed: ParsedCompanyPackage): Promise<void> {
|
|
226
|
+
const { doc, entries } = parsed;
|
|
147
227
|
const cwd = await resolveDefaultRuntimeCwdForCompany(db, companyId);
|
|
148
228
|
await mkdir(cwd, { recursive: true });
|
|
149
229
|
await ensureBuiltinPluginsRegistered(db, [companyId]);
|
|
150
|
-
await ensureCompanyBuiltinTemplateDefaults(db, companyId);
|
|
151
230
|
|
|
152
231
|
const projectSlugToId = new Map<string, string>();
|
|
153
232
|
const projectStatuses = new Set(["planned", "active", "paused", "blocked", "completed", "archived"]);
|
|
@@ -183,7 +262,11 @@ export async function importCompanyFromZipBuffer(db: BopoDb, buffer: Buffer): Pr
|
|
|
183
262
|
}
|
|
184
263
|
const defaultRt = normalizeRuntimeConfig({
|
|
185
264
|
defaultRuntimeCwd: cwd,
|
|
186
|
-
runtimeConfig: {
|
|
265
|
+
runtimeConfig: {
|
|
266
|
+
runtimeModel: undefined,
|
|
267
|
+
runtimeEnv: {},
|
|
268
|
+
bootstrapPrompt: a.bootstrapPrompt?.trim() || undefined
|
|
269
|
+
}
|
|
187
270
|
});
|
|
188
271
|
const createdAgent = await createAgent(db, {
|
|
189
272
|
companyId,
|
|
@@ -195,8 +278,10 @@ export async function importCompanyFromZipBuffer(db: BopoDb, buffer: Buffer): Pr
|
|
|
195
278
|
name: a.name,
|
|
196
279
|
providerType: coerceProviderType(a.providerType),
|
|
197
280
|
heartbeatCron: a.heartbeatCron,
|
|
198
|
-
monthlyBudgetUsd:
|
|
281
|
+
monthlyBudgetUsd: formatMonthlyBudgetUsdFromManifest(a.monthlyBudgetUsd),
|
|
199
282
|
canHireAgents: a.canHireAgents ?? false,
|
|
283
|
+
canAssignAgents: a.canAssignAgents ?? true,
|
|
284
|
+
canCreateIssues: a.canCreateIssues ?? true,
|
|
200
285
|
...runtimeConfigToDb(defaultRt),
|
|
201
286
|
initialState: runtimeConfigToStateBlobPatch(defaultRt)
|
|
202
287
|
});
|
|
@@ -243,6 +328,60 @@ export async function importCompanyFromZipBuffer(db: BopoDb, buffer: Buffer): Pr
|
|
|
243
328
|
const dest = join(companyRoot, path);
|
|
244
329
|
await mkdir(dirname(dest), { recursive: true });
|
|
245
330
|
await writeFile(dest, text, "utf8");
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (path.startsWith("knowledge/")) {
|
|
334
|
+
const dest = join(companyRoot, path);
|
|
335
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
336
|
+
await writeFile(dest, text, "utf8");
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const goalsManifest = doc.goals ?? {};
|
|
341
|
+
const goalSlugToId = new Map<string, string>();
|
|
342
|
+
for (const slug of sortGoalSlugsForImport(goalsManifest)) {
|
|
343
|
+
const g = goalsManifest[slug]!;
|
|
344
|
+
const level = g.level;
|
|
345
|
+
const projectSlug = g.projectSlug?.trim() || null;
|
|
346
|
+
const projectId = projectSlug ? projectSlugToId.get(projectSlug) ?? null : null;
|
|
347
|
+
if (level === "project" && !projectId) {
|
|
348
|
+
throw new CompanyFileImportError(`Goal '${slug}' (project level) references unknown project slug '${projectSlug ?? ""}'.`);
|
|
349
|
+
}
|
|
350
|
+
if (level === "company" && projectId) {
|
|
351
|
+
throw new CompanyFileImportError(`Goal '${slug}' is company-level but specifies a project.`);
|
|
352
|
+
}
|
|
353
|
+
const parentSlug = g.parentGoalSlug?.trim() || null;
|
|
354
|
+
const parentGoalId = parentSlug ? goalSlugToId.get(parentSlug) ?? null : null;
|
|
355
|
+
if (parentSlug && !parentGoalId) {
|
|
356
|
+
throw new CompanyFileImportError(`Goal '${slug}' references unknown parent goal slug '${parentSlug}'.`);
|
|
357
|
+
}
|
|
358
|
+
const ownerSlug = g.ownerAgentSlug?.trim() || null;
|
|
359
|
+
const ownerAgentId = ownerSlug ? agentSlugToId.get(ownerSlug) ?? null : null;
|
|
360
|
+
if (ownerSlug && !ownerAgentId) {
|
|
361
|
+
throw new CompanyFileImportError(`Goal '${slug}' references unknown owner agent slug '${ownerSlug}'.`);
|
|
362
|
+
}
|
|
363
|
+
const agentLevelProjectId = level === "agent" ? projectId : null;
|
|
364
|
+
if (level === "agent" && g.projectSlug?.trim() && !projectId) {
|
|
365
|
+
throw new CompanyFileImportError(`Goal '${slug}' (agent level) references unknown project slug '${g.projectSlug.trim()}'.`);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const created = await createGoal(db, {
|
|
369
|
+
companyId,
|
|
370
|
+
projectId: level === "project" ? projectId : agentLevelProjectId,
|
|
371
|
+
parentGoalId,
|
|
372
|
+
ownerAgentId,
|
|
373
|
+
level,
|
|
374
|
+
title: g.title,
|
|
375
|
+
description: g.description?.trim() || undefined
|
|
376
|
+
});
|
|
377
|
+
goalSlugToId.set(slug, created.id);
|
|
378
|
+
const st = g.status?.trim();
|
|
379
|
+
if (st && st !== "draft") {
|
|
380
|
+
await updateGoal(db, {
|
|
381
|
+
companyId,
|
|
382
|
+
id: created.id,
|
|
383
|
+
status: st
|
|
384
|
+
});
|
|
246
385
|
}
|
|
247
386
|
}
|
|
248
387
|
|
|
@@ -266,7 +405,7 @@ export async function importCompanyFromZipBuffer(db: BopoDb, buffer: Buffer): Pr
|
|
|
266
405
|
for (const t of r.triggers) {
|
|
267
406
|
await addWorkLoopTrigger(db, {
|
|
268
407
|
companyId,
|
|
269
|
-
|
|
408
|
+
routineId: loop.id,
|
|
270
409
|
cronExpression: t.cronExpression,
|
|
271
410
|
timezone: t.timezone?.trim() || "UTC",
|
|
272
411
|
label: t.label ?? null,
|
|
@@ -274,6 +413,57 @@ export async function importCompanyFromZipBuffer(db: BopoDb, buffer: Buffer): Pr
|
|
|
274
413
|
});
|
|
275
414
|
}
|
|
276
415
|
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function assertManifestHasCeoAgent(doc: BopoExportDoc): void {
|
|
419
|
+
const hasCeo = Object.values(doc.agents).some((a) => (a.roleKey ?? "").trim().toLowerCase() === "ceo");
|
|
420
|
+
if (!hasCeo) {
|
|
421
|
+
throw new CompanyFileImportError("Company package must include an agent with roleKey 'ceo'.");
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function summarizeCompanyPackageForPreview(parsed: ParsedCompanyPackage): {
|
|
426
|
+
companyName: string;
|
|
427
|
+
counts: {
|
|
428
|
+
projects: number;
|
|
429
|
+
agents: number;
|
|
430
|
+
goals: number;
|
|
431
|
+
routines: number;
|
|
432
|
+
skillFiles: number;
|
|
433
|
+
knowledgeFiles: number;
|
|
434
|
+
};
|
|
435
|
+
hasCeo: boolean;
|
|
436
|
+
} {
|
|
437
|
+
const doc = parsed.doc;
|
|
438
|
+
const skillFiles = Object.keys(parsed.entries).filter((k) => k.startsWith("skills/") && !k.endsWith("/")).length;
|
|
439
|
+
const knowledgeFiles = Object.keys(parsed.entries).filter(
|
|
440
|
+
(k) => k.startsWith("knowledge/") && !k.endsWith("/")
|
|
441
|
+
).length;
|
|
442
|
+
const hasCeo = Object.values(doc.agents).some((a) => (a.roleKey ?? "").trim().toLowerCase() === "ceo");
|
|
443
|
+
return {
|
|
444
|
+
companyName: doc.company.name,
|
|
445
|
+
counts: {
|
|
446
|
+
projects: Object.keys(doc.projects).length,
|
|
447
|
+
agents: Object.keys(doc.agents).length,
|
|
448
|
+
goals: Object.keys(doc.goals ?? {}).length,
|
|
449
|
+
routines: Object.keys(doc.routines ?? {}).length,
|
|
450
|
+
skillFiles,
|
|
451
|
+
knowledgeFiles
|
|
452
|
+
},
|
|
453
|
+
hasCeo
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export async function importCompanyFromZipBuffer(db: BopoDb, buffer: Buffer): Promise<{ companyId: string; name: string }> {
|
|
458
|
+
const parsed = parseCompanyZipBuffer(buffer);
|
|
459
|
+
const doc = parsed.doc;
|
|
277
460
|
|
|
461
|
+
const created = await createCompany(db, {
|
|
462
|
+
name: doc.company.name,
|
|
463
|
+
mission: doc.company.mission ?? null
|
|
464
|
+
});
|
|
465
|
+
const companyId = created.id;
|
|
466
|
+
await ensureCompanyBuiltinTemplateDefaults(db, companyId);
|
|
467
|
+
await seedOperationalDataFromPackage(db, companyId, parsed);
|
|
278
468
|
return { companyId, name: doc.company.name };
|
|
279
469
|
}
|