feat(04-03): add Nexus agent bootstrap to CLI onboarding
- Add bootstrapNexusAgents function with health-check poll (max 30s) - Create workspace (company) then PM agent (role:ceo) and Engineer agent - Idempotent: skips if workspace already exists - Bootstrap runs concurrently before runCommand starts server - Failures are warnings, not errors - [nexus] comments on all new lines
This commit is contained in:
parent
e9398a8777
commit
baaa847236
1 changed files with 90 additions and 0 deletions
|
|
@ -35,6 +35,89 @@ import { bootstrapCeoInvite } from "./auth-bootstrap-ceo.js";
|
|||
import { printNexusCliBanner } from "../utils/banner.js";
|
||||
import { VOCAB } from "@paperclipai/branding"; // [nexus]
|
||||
|
||||
// [nexus] Auto-create PM and Engineer agents on first run
|
||||
async function bootstrapNexusAgents(serverUrl: string, rootDir: string): Promise<void> {
|
||||
// [nexus] Health-check poll — wait for server to be ready (max 30 seconds)
|
||||
const maxRetries = 30;
|
||||
let serverReady = false;
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
const res = await fetch(`${serverUrl}/api/health`);
|
||||
if (res.ok) {
|
||||
serverReady = true;
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
// [nexus] Server not ready yet
|
||||
}
|
||||
if (i < maxRetries - 1) {
|
||||
await new Promise<void>((r) => setTimeout(r, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
if (!serverReady) {
|
||||
console.warn("[nexus] Server did not become ready in 30s, skipping agent bootstrap");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// [nexus] Check if workspace already exists (idempotent — skip if already bootstrapped)
|
||||
const companiesRes = await fetch(`${serverUrl}/api/companies`);
|
||||
if (!companiesRes.ok) {
|
||||
console.warn("[nexus] Could not fetch workspaces, skipping agent bootstrap");
|
||||
return;
|
||||
}
|
||||
const companies = (await companiesRes.json()) as unknown[];
|
||||
if (companies.length > 0) {
|
||||
return; // [nexus] Already bootstrapped — skip
|
||||
}
|
||||
|
||||
// [nexus] Create workspace
|
||||
p.log.step(`Creating your ${VOCAB.company} workspace...`);
|
||||
const companyRes = await fetch(`${serverUrl}/api/companies`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ name: VOCAB.appName }),
|
||||
});
|
||||
if (!companyRes.ok) {
|
||||
console.warn("[nexus] Could not create workspace, skipping agent bootstrap");
|
||||
return;
|
||||
}
|
||||
const company = (await companyRes.json()) as { id: string };
|
||||
|
||||
// [nexus] Create PM agent (role: "ceo" for elevated permissions — displays as Project Manager)
|
||||
p.log.step(`Adding ${VOCAB.ceo} agent...`);
|
||||
await fetch(`${serverUrl}/api/companies/${company.id}/agents`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: "Project Manager",
|
||||
role: "ceo",
|
||||
adapterType: "claude_local",
|
||||
adapterConfig: { cwd: rootDir },
|
||||
}),
|
||||
});
|
||||
|
||||
// [nexus] Create Engineer agent
|
||||
p.log.step("Adding Engineer agent...");
|
||||
await fetch(`${serverUrl}/api/companies/${company.id}/agents`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: "Engineer",
|
||||
role: "engineer",
|
||||
adapterType: "claude_local",
|
||||
adapterConfig: { cwd: rootDir },
|
||||
}),
|
||||
});
|
||||
|
||||
p.log.success("Workspace and agents created — you're ready to go!");
|
||||
} catch (err) {
|
||||
// [nexus] Bootstrap failures are warnings, not errors — user can create agents manually
|
||||
console.warn("[nexus] Agent bootstrap failed:", err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
|
||||
type SetupMode = "quickstart" | "advanced";
|
||||
|
||||
type OnboardOptions = {
|
||||
|
|
@ -474,6 +557,13 @@ export async function onboard(opts: OnboardOptions): Promise<void> {
|
|||
if (shouldRunNow && !opts.invokedByRun) {
|
||||
process.env.PAPERCLIP_OPEN_ON_LISTEN = "true";
|
||||
const { runCommand } = await import("./run.js");
|
||||
// [nexus] Start bootstrap concurrently — health-check poll waits for server readiness
|
||||
const serverUrl = `http://${server.host}:${server.port}`;
|
||||
const rootDir = process.cwd();
|
||||
bootstrapNexusAgents(serverUrl, rootDir).catch((err: unknown) => {
|
||||
// [nexus] Bootstrap failures are non-fatal
|
||||
console.warn("[nexus] Agent bootstrap error:", err instanceof Error ? err.message : String(err));
|
||||
});
|
||||
await runCommand({ config: configPath, repair: true, yes: true });
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue