beercan 0.6.12 → 0.6.14
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/README.md +60 -10
- package/dist/chat/index.d.ts +3 -3
- package/dist/chat/index.d.ts.map +1 -1
- package/dist/chat/index.js +4 -4
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/intent.d.ts +2 -2
- package/dist/chat/intent.d.ts.map +1 -1
- package/dist/chat/intent.js +7 -7
- package/dist/chat/intent.js.map +1 -1
- package/dist/cli.js +269 -23
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/core/gatekeeper.d.ts +3 -3
- package/dist/core/gatekeeper.d.ts.map +1 -1
- package/dist/core/gatekeeper.js +12 -12
- package/dist/core/gatekeeper.js.map +1 -1
- package/dist/core/reflection.d.ts +3 -3
- package/dist/core/reflection.d.ts.map +1 -1
- package/dist/core/reflection.js +10 -10
- package/dist/core/reflection.js.map +1 -1
- package/dist/core/roles.js +1 -1
- package/dist/core/roles.js.map +1 -1
- package/dist/core/runner.d.ts +3 -3
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +12 -14
- package/dist/core/runner.js.map +1 -1
- package/dist/events/daemon.js +3 -3
- package/dist/events/daemon.js.map +1 -1
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -5
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.d.ts +8 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +70 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/factory.d.ts +3 -0
- package/dist/providers/factory.d.ts.map +1 -0
- package/dist/providers/factory.js +58 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +16 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +176 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +54 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +4 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +44 -0
- package/dist/skills/index.js.map +1 -1
- package/dist/tools/builtin/email.d.ts +5 -0
- package/dist/tools/builtin/email.d.ts.map +1 -0
- package/dist/tools/builtin/email.js +171 -0
- package/dist/tools/builtin/email.js.map +1 -0
- package/dist/tools/registry.d.ts +3 -12
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +3 -3
- package/dist/tools/registry.js.map +1 -1
- package/dist/training/curriculum.d.ts +4 -0
- package/dist/training/curriculum.d.ts.map +1 -0
- package/dist/training/curriculum.js +512 -0
- package/dist/training/curriculum.js.map +1 -0
- package/dist/training/evaluator.d.ts +21 -0
- package/dist/training/evaluator.d.ts.map +1 -0
- package/dist/training/evaluator.js +163 -0
- package/dist/training/evaluator.js.map +1 -0
- package/dist/training/exporter.d.ts +35 -0
- package/dist/training/exporter.d.ts.map +1 -0
- package/dist/training/exporter.js +377 -0
- package/dist/training/exporter.js.map +1 -0
- package/dist/training/index.d.ts +5 -0
- package/dist/training/index.d.ts.map +1 -0
- package/dist/training/index.js +5 -0
- package/dist/training/index.js.map +1 -0
- package/dist/training/sandbox-manager.d.ts +58 -0
- package/dist/training/sandbox-manager.d.ts.map +1 -0
- package/dist/training/sandbox-manager.js +416 -0
- package/dist/training/sandbox-manager.js.map +1 -0
- package/dist/training/types.d.ts +790 -0
- package/dist/training/types.d.ts.map +1 -0
- package/dist/training/types.js +154 -0
- package/dist/training/types.js.map +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -77,34 +77,72 @@ async function runSetup() {
|
|
|
77
77
|
}
|
|
78
78
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
79
79
|
try {
|
|
80
|
-
// Required
|
|
81
80
|
const mask = (v) => v ? v.slice(0, 12) + "..." : "";
|
|
82
|
-
|
|
83
|
-
console.log(chalk.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
// Provider selection
|
|
82
|
+
console.log(chalk.bold("1. LLM Provider"));
|
|
83
|
+
console.log(chalk.dim(" Options: anthropic, openai, openai-compatible (LM Studio, Ollama, OpenRouter)\n"));
|
|
84
|
+
const llmProvider = (await prompt(rl, chalk.cyan(` BEERCAN_LLM_PROVIDER [${existing.BEERCAN_LLM_PROVIDER || "anthropic"}]: `))).trim()
|
|
85
|
+
|| existing.BEERCAN_LLM_PROVIDER || "anthropic";
|
|
86
|
+
// API key(s) based on provider
|
|
87
|
+
let apiKey = "";
|
|
88
|
+
let openaiApiKey = "";
|
|
89
|
+
let llmApiKey = "";
|
|
90
|
+
let llmBaseUrl = "";
|
|
91
|
+
if (llmProvider === "anthropic") {
|
|
92
|
+
console.log(chalk.bold("\n2. Anthropic API Key") + chalk.red(" (required)"));
|
|
93
|
+
console.log(chalk.dim(" Get one at: https://console.anthropic.com/\n"));
|
|
94
|
+
apiKey = (await prompt(rl, chalk.cyan(` ANTHROPIC_API_KEY [${mask(existing.ANTHROPIC_API_KEY)}]: `))).trim()
|
|
95
|
+
|| existing.ANTHROPIC_API_KEY || "";
|
|
96
|
+
if (!apiKey) {
|
|
97
|
+
console.log(chalk.red("\n API key is required. Aborting setup."));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (llmProvider === "openai") {
|
|
102
|
+
console.log(chalk.bold("\n2. OpenAI API Key") + chalk.red(" (required)"));
|
|
103
|
+
console.log(chalk.dim(" Get one at: https://platform.openai.com/\n"));
|
|
104
|
+
openaiApiKey = (await prompt(rl, chalk.cyan(` OPENAI_API_KEY [${mask(existing.OPENAI_API_KEY)}]: `))).trim()
|
|
105
|
+
|| existing.OPENAI_API_KEY || "";
|
|
106
|
+
if (!openaiApiKey) {
|
|
107
|
+
console.log(chalk.red("\n API key is required. Aborting setup."));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
89
110
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
else {
|
|
112
|
+
console.log(chalk.bold("\n2. OpenAI-Compatible Endpoint") + chalk.red(" (required)"));
|
|
113
|
+
console.log(chalk.dim(" e.g., http://localhost:1234/v1 (LM Studio), http://localhost:11434/v1 (Ollama)\n"));
|
|
114
|
+
llmBaseUrl = (await prompt(rl, chalk.cyan(` BEERCAN_LLM_BASE_URL [${existing.BEERCAN_LLM_BASE_URL || ""}]: `))).trim()
|
|
115
|
+
|| existing.BEERCAN_LLM_BASE_URL || "";
|
|
116
|
+
if (!llmBaseUrl) {
|
|
117
|
+
console.log(chalk.red("\n Base URL is required for openai-compatible. Aborting setup."));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
llmApiKey = (await prompt(rl, chalk.cyan(` BEERCAN_LLM_API_KEY [${mask(existing.BEERCAN_LLM_API_KEY) || "not-needed"}]: `))).trim()
|
|
121
|
+
|| existing.BEERCAN_LLM_API_KEY || "";
|
|
122
|
+
}
|
|
123
|
+
// Models — show provider-appropriate defaults
|
|
124
|
+
const modelDefaults = llmProvider === "anthropic"
|
|
125
|
+
? { default: "claude-sonnet-4-6", heavy: "claude-opus-4-6" }
|
|
126
|
+
: llmProvider === "openai"
|
|
127
|
+
? { default: "gpt-4o", heavy: "gpt-4o" }
|
|
128
|
+
: { default: "default", heavy: "default" };
|
|
129
|
+
console.log(chalk.bold("\n3. Models") + chalk.dim(" (press Enter for defaults)"));
|
|
130
|
+
const defaultModel = (await prompt(rl, chalk.cyan(` Default model [${existing.BEERCAN_DEFAULT_MODEL || modelDefaults.default}]: `))).trim()
|
|
93
131
|
|| existing.BEERCAN_DEFAULT_MODEL || "";
|
|
94
|
-
const heavyModel = (await prompt(rl, chalk.cyan(` Heavy model [${existing.BEERCAN_HEAVY_MODEL ||
|
|
132
|
+
const heavyModel = (await prompt(rl, chalk.cyan(` Heavy model [${existing.BEERCAN_HEAVY_MODEL || modelDefaults.heavy}]: `))).trim()
|
|
95
133
|
|| existing.BEERCAN_HEAVY_MODEL || "";
|
|
96
134
|
// Optional — Cloudflare
|
|
97
|
-
console.log(chalk.bold("\
|
|
135
|
+
console.log(chalk.bold("\n4. Cloudflare Browser Rendering") + chalk.dim(" (optional, for web_fetch)"));
|
|
98
136
|
const cfToken = (await prompt(rl, chalk.cyan(` CLOUDFLARE_API_TOKEN [${mask(existing.CLOUDFLARE_API_TOKEN)}]: `))).trim()
|
|
99
137
|
|| existing.CLOUDFLARE_API_TOKEN || "";
|
|
100
138
|
const cfAccount = (await prompt(rl, chalk.cyan(` CLOUDFLARE_ACCOUNT_ID [${mask(existing.CLOUDFLARE_ACCOUNT_ID)}]: `))).trim()
|
|
101
139
|
|| existing.CLOUDFLARE_ACCOUNT_ID || "";
|
|
102
140
|
// Optional — Security
|
|
103
|
-
console.log(chalk.bold("\
|
|
141
|
+
console.log(chalk.bold("\n5. API Security") + chalk.dim(" (optional, for REST API auth)"));
|
|
104
142
|
const beercanApiKey = (await prompt(rl, chalk.cyan(` BEERCAN_API_KEY [${mask(existing.BEERCAN_API_KEY)}]: `))).trim()
|
|
105
143
|
|| existing.BEERCAN_API_KEY || "";
|
|
106
144
|
// Optional — Chat
|
|
107
|
-
console.log(chalk.bold("\
|
|
145
|
+
console.log(chalk.bold("\n6. Chat Providers") + chalk.dim(" (optional, for Telegram/Slack bots)"));
|
|
108
146
|
const telegramToken = (await prompt(rl, chalk.cyan(` BEERCAN_TELEGRAM_TOKEN [${mask(existing.BEERCAN_TELEGRAM_TOKEN)}]: `))).trim()
|
|
109
147
|
|| existing.BEERCAN_TELEGRAM_TOKEN || "";
|
|
110
148
|
const slackToken = (await prompt(rl, chalk.cyan(` BEERCAN_SLACK_TOKEN [${mask(existing.BEERCAN_SLACK_TOKEN)}]: `))).trim()
|
|
@@ -112,7 +150,7 @@ async function runSetup() {
|
|
|
112
150
|
const slackSecret = (await prompt(rl, chalk.cyan(` BEERCAN_SLACK_SIGNING_SECRET [${mask(existing.BEERCAN_SLACK_SIGNING_SECRET)}]: `))).trim()
|
|
113
151
|
|| existing.BEERCAN_SLACK_SIGNING_SECRET || "";
|
|
114
152
|
// Optional — Notifications
|
|
115
|
-
console.log(chalk.bold("\
|
|
153
|
+
console.log(chalk.bold("\n7. Notifications") + chalk.dim(" (optional)"));
|
|
116
154
|
const webhookUrl = (await prompt(rl, chalk.cyan(` BEERCAN_NOTIFY_WEBHOOK_URL [${existing.BEERCAN_NOTIFY_WEBHOOK_URL || ""}]: `))).trim()
|
|
117
155
|
|| existing.BEERCAN_NOTIFY_WEBHOOK_URL || "";
|
|
118
156
|
// Build .env content
|
|
@@ -120,10 +158,19 @@ async function runSetup() {
|
|
|
120
158
|
`# BeerCan Configuration`,
|
|
121
159
|
`# Generated by: beercan setup`,
|
|
122
160
|
``,
|
|
123
|
-
|
|
161
|
+
`# LLM Provider: anthropic, openai, openai-compatible`,
|
|
162
|
+
`BEERCAN_LLM_PROVIDER=${llmProvider}`,
|
|
124
163
|
];
|
|
164
|
+
if (apiKey)
|
|
165
|
+
lines.push(`ANTHROPIC_API_KEY=${apiKey}`);
|
|
166
|
+
if (openaiApiKey)
|
|
167
|
+
lines.push(`OPENAI_API_KEY=${openaiApiKey}`);
|
|
168
|
+
if (llmApiKey)
|
|
169
|
+
lines.push(`BEERCAN_LLM_API_KEY=${llmApiKey}`);
|
|
170
|
+
if (llmBaseUrl)
|
|
171
|
+
lines.push(`BEERCAN_LLM_BASE_URL=${llmBaseUrl}`);
|
|
125
172
|
if (defaultModel)
|
|
126
|
-
lines.push(`BEERCAN_DEFAULT_MODEL=${defaultModel}`);
|
|
173
|
+
lines.push(``, `# Models`, `BEERCAN_DEFAULT_MODEL=${defaultModel}`);
|
|
127
174
|
if (heavyModel)
|
|
128
175
|
lines.push(`BEERCAN_HEAVY_MODEL=${heavyModel}`);
|
|
129
176
|
if (cfToken)
|
|
@@ -921,11 +968,11 @@ Do NOT rewrite everything — make focused, incremental changes.`,
|
|
|
921
968
|
// ── Chat Mode ───────────────────────────────────────────
|
|
922
969
|
case "chat": {
|
|
923
970
|
const chatProject = args[1];
|
|
924
|
-
const {
|
|
971
|
+
const { createLLMProvider } = await import("./providers/factory.js");
|
|
925
972
|
const { ChatBridge } = await import("./chat/index.js");
|
|
926
973
|
const { TerminalProvider } = await import("./chat/providers/terminal.js");
|
|
927
|
-
const
|
|
928
|
-
const bridge = new ChatBridge(engine,
|
|
974
|
+
const provider = await createLLMProvider();
|
|
975
|
+
const bridge = new ChatBridge(engine, provider);
|
|
929
976
|
const terminal = new TerminalProvider();
|
|
930
977
|
bridge.addProvider(terminal);
|
|
931
978
|
if (chatProject) {
|
|
@@ -1255,6 +1302,197 @@ Do NOT rewrite everything — make focused, incremental changes.`,
|
|
|
1255
1302
|
rl.close();
|
|
1256
1303
|
break;
|
|
1257
1304
|
}
|
|
1305
|
+
// ── Training Commands ───────────────────────────────────
|
|
1306
|
+
case "training:create": {
|
|
1307
|
+
const traineeName = args[1];
|
|
1308
|
+
if (!traineeName) {
|
|
1309
|
+
console.log(chalk.red("Usage: beercan training:create <name> [--work-dir <path>]"));
|
|
1310
|
+
console.log(chalk.dim("Creates a new training sandbox project for an agent."));
|
|
1311
|
+
break;
|
|
1312
|
+
}
|
|
1313
|
+
const wdIdx = args.indexOf("--work-dir");
|
|
1314
|
+
const workDir = wdIdx >= 0 ? args[wdIdx + 1] : undefined;
|
|
1315
|
+
try {
|
|
1316
|
+
const project = await engine.createTrainee(traineeName, workDir);
|
|
1317
|
+
console.log(chalk.green(`✓ Created training project: ${project.name}`));
|
|
1318
|
+
console.log(chalk.dim(` Slug: ${project.slug}`));
|
|
1319
|
+
console.log(chalk.dim(` ID: ${project.id}`));
|
|
1320
|
+
if (workDir)
|
|
1321
|
+
console.log(chalk.dim(` Work dir: ${workDir}`));
|
|
1322
|
+
console.log(chalk.dim(`\nNext: beercan training:run ${project.slug}`));
|
|
1323
|
+
}
|
|
1324
|
+
catch (err) {
|
|
1325
|
+
console.log(chalk.red(`Failed to create trainee: ${err.message}`));
|
|
1326
|
+
}
|
|
1327
|
+
break;
|
|
1328
|
+
}
|
|
1329
|
+
case "training:run": {
|
|
1330
|
+
const trainingProject = args[1];
|
|
1331
|
+
if (!trainingProject) {
|
|
1332
|
+
console.log(chalk.red("Usage: beercan training:run <project> [--scenario <id>]"));
|
|
1333
|
+
console.log(chalk.dim("Runs the next (or a specific) training scenario."));
|
|
1334
|
+
break;
|
|
1335
|
+
}
|
|
1336
|
+
const scenarioIdx = args.indexOf("--scenario");
|
|
1337
|
+
const scenarioId = scenarioIdx >= 0 ? args[scenarioIdx + 1] : undefined;
|
|
1338
|
+
try {
|
|
1339
|
+
const status = await engine.getTrainingStatus(trainingProject);
|
|
1340
|
+
if (scenarioId) {
|
|
1341
|
+
console.log(chalk.bold(`\nRunning scenario: ${scenarioId}`));
|
|
1342
|
+
}
|
|
1343
|
+
else if (status.nextScenario) {
|
|
1344
|
+
console.log(chalk.bold(`\nRunning next scenario: ${status.nextScenario.name}`));
|
|
1345
|
+
console.log(chalk.dim(` Difficulty: ${status.nextScenario.difficulty}`));
|
|
1346
|
+
console.log(chalk.dim(` Category: ${status.nextScenario.category}`));
|
|
1347
|
+
console.log(chalk.dim(` Goal: ${status.nextScenario.goal.slice(0, 80)}...`));
|
|
1348
|
+
}
|
|
1349
|
+
else {
|
|
1350
|
+
console.log(chalk.yellow("No scenarios available. Check status with: beercan training:status " + trainingProject));
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
console.log();
|
|
1354
|
+
const attempt = await engine.runTrainingScenario(trainingProject, scenarioId);
|
|
1355
|
+
const statusColor = attempt.status === "pass"
|
|
1356
|
+
? chalk.green
|
|
1357
|
+
: attempt.status === "fail"
|
|
1358
|
+
? chalk.yellow
|
|
1359
|
+
: chalk.red;
|
|
1360
|
+
console.log(statusColor(`\n${attempt.status === "pass" ? "✓ PASSED" : attempt.status === "fail" ? "✗ FAILED" : "! ERROR"}`));
|
|
1361
|
+
console.log(` Score: ${(attempt.score * 100).toFixed(0)}%`);
|
|
1362
|
+
console.log(` Feedback: ${attempt.feedback}`);
|
|
1363
|
+
console.log(chalk.dim(` Tokens: ${attempt.tokensUsed.toLocaleString()}`));
|
|
1364
|
+
console.log(chalk.dim(` Duration: ${(attempt.durationMs / 1000).toFixed(1)}s`));
|
|
1365
|
+
// Show updated status
|
|
1366
|
+
const newStatus = await engine.getTrainingStatus(trainingProject);
|
|
1367
|
+
if (newStatus.progress.graduationStatus === "graduated") {
|
|
1368
|
+
console.log(chalk.bold.green("\nCongratulations! Agent has graduated!"));
|
|
1369
|
+
}
|
|
1370
|
+
else if (newStatus.nextScenario) {
|
|
1371
|
+
console.log(chalk.dim(`\nNext scenario: ${newStatus.nextScenario.name} — run again to continue`));
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
catch (err) {
|
|
1375
|
+
console.log(chalk.red(`Training run failed: ${err.message}`));
|
|
1376
|
+
}
|
|
1377
|
+
break;
|
|
1378
|
+
}
|
|
1379
|
+
case "training:status": {
|
|
1380
|
+
const statusProject = args[1];
|
|
1381
|
+
if (!statusProject) {
|
|
1382
|
+
console.log(chalk.red("Usage: beercan training:status <project>"));
|
|
1383
|
+
break;
|
|
1384
|
+
}
|
|
1385
|
+
try {
|
|
1386
|
+
const { progress, nextScenario, summary } = await engine.getTrainingStatus(statusProject);
|
|
1387
|
+
const gradColor = progress.graduationStatus === "graduated"
|
|
1388
|
+
? chalk.bold.green
|
|
1389
|
+
: progress.graduationStatus === "failed"
|
|
1390
|
+
? chalk.red
|
|
1391
|
+
: chalk.yellow;
|
|
1392
|
+
console.log(chalk.bold(`\nTraining Status: ${statusProject}\n`));
|
|
1393
|
+
console.log(` Graduation: ${gradColor(progress.graduationStatus)}`);
|
|
1394
|
+
console.log(` Level: ${progress.currentLevel}`);
|
|
1395
|
+
console.log(` Passed: ${progress.passedScenarios.length} scenarios`);
|
|
1396
|
+
console.log(` Bloops: ${progress.totalBloops}`);
|
|
1397
|
+
console.log(` Tokens: ${progress.totalTokensUsed.toLocaleString()}`);
|
|
1398
|
+
if (progress.startedAt) {
|
|
1399
|
+
console.log(chalk.dim(` Started: ${progress.startedAt.slice(0, 19)}`));
|
|
1400
|
+
}
|
|
1401
|
+
if (progress.graduatedAt) {
|
|
1402
|
+
console.log(chalk.dim(` Graduated: ${progress.graduatedAt.slice(0, 19)}`));
|
|
1403
|
+
}
|
|
1404
|
+
if (nextScenario) {
|
|
1405
|
+
console.log(chalk.bold(`\nNext Scenario:`));
|
|
1406
|
+
console.log(` ${chalk.cyan(nextScenario.name)} (${nextScenario.difficulty})`);
|
|
1407
|
+
console.log(chalk.dim(` Category: ${nextScenario.category}`));
|
|
1408
|
+
console.log(chalk.dim(` Tests: ${nextScenario.teaches.slice(0, 3).join(", ")}`));
|
|
1409
|
+
}
|
|
1410
|
+
if (progress.passedScenarios.length > 0) {
|
|
1411
|
+
console.log(chalk.bold(`\nPassed Scenarios:`));
|
|
1412
|
+
for (const id of progress.passedScenarios) {
|
|
1413
|
+
console.log(chalk.green(` ✓ ${id}`));
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
if (progress.failedScenarios.length > 0) {
|
|
1417
|
+
console.log(chalk.bold(`\nFailed Scenarios:`));
|
|
1418
|
+
for (const f of progress.failedScenarios) {
|
|
1419
|
+
console.log(chalk.red(` ✗ ${f.id} (${f.attempts} attempt${f.attempts !== 1 ? "s" : ""})`));
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
catch (err) {
|
|
1424
|
+
console.log(chalk.red(`Failed to get training status: ${err.message}`));
|
|
1425
|
+
}
|
|
1426
|
+
break;
|
|
1427
|
+
}
|
|
1428
|
+
case "training:export": {
|
|
1429
|
+
const exportProject = args[1];
|
|
1430
|
+
if (!exportProject) {
|
|
1431
|
+
console.log(chalk.red("Usage: beercan training:export <project> [--output <path>]"));
|
|
1432
|
+
break;
|
|
1433
|
+
}
|
|
1434
|
+
const outIdx = args.indexOf("--output");
|
|
1435
|
+
const outputPath = outIdx >= 0
|
|
1436
|
+
? args[outIdx + 1]
|
|
1437
|
+
: `./${exportProject}-agent-package.json`;
|
|
1438
|
+
try {
|
|
1439
|
+
console.log(chalk.dim(`Exporting agent: ${exportProject} → ${outputPath}`));
|
|
1440
|
+
await engine.exportAgent(exportProject, outputPath);
|
|
1441
|
+
console.log(chalk.green(`✓ Agent exported: ${outputPath}`));
|
|
1442
|
+
console.log(chalk.dim(` Import with: beercan training:import ${outputPath}`));
|
|
1443
|
+
}
|
|
1444
|
+
catch (err) {
|
|
1445
|
+
console.log(chalk.red(`Export failed: ${err.message}`));
|
|
1446
|
+
}
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1449
|
+
case "training:import": {
|
|
1450
|
+
const importPath = args[1];
|
|
1451
|
+
if (!importPath) {
|
|
1452
|
+
console.log(chalk.red("Usage: beercan training:import <package-path> [--name <slug>] [--global] [--project <slug>]"));
|
|
1453
|
+
console.log(chalk.dim(" --global Import only skills & tools globally (no project created)"));
|
|
1454
|
+
console.log(chalk.dim(" --project <slug> Import into an existing project"));
|
|
1455
|
+
console.log(chalk.dim(" --name <slug> Create a new project with this slug (default: from package)"));
|
|
1456
|
+
break;
|
|
1457
|
+
}
|
|
1458
|
+
const isGlobal = args.includes("--global");
|
|
1459
|
+
const projectIdx = args.indexOf("--project");
|
|
1460
|
+
const targetProject = projectIdx >= 0 ? args[projectIdx + 1] : undefined;
|
|
1461
|
+
const nameIdx = args.indexOf("--name");
|
|
1462
|
+
const targetSlug = nameIdx >= 0 ? args[nameIdx + 1] : undefined;
|
|
1463
|
+
try {
|
|
1464
|
+
if (isGlobal) {
|
|
1465
|
+
// Global import: skills + tools only, no project
|
|
1466
|
+
console.log(chalk.dim(`Importing skills & tools globally from: ${importPath}`));
|
|
1467
|
+
const { skills, tools } = await engine.importAgentGlobal(importPath);
|
|
1468
|
+
console.log(chalk.green(`✓ Global import complete`));
|
|
1469
|
+
console.log(chalk.dim(` Skills imported: ${skills}`));
|
|
1470
|
+
console.log(chalk.dim(` Tools imported: ${tools}`));
|
|
1471
|
+
}
|
|
1472
|
+
else if (targetProject) {
|
|
1473
|
+
// Import into existing project
|
|
1474
|
+
console.log(chalk.dim(`Importing agent into project: ${targetProject}`));
|
|
1475
|
+
const project = await engine.importAgentIntoProject(importPath, targetProject);
|
|
1476
|
+
console.log(chalk.green(`✓ Agent imported into: ${project.name}`));
|
|
1477
|
+
console.log(chalk.dim(` Slug: ${project.slug}`));
|
|
1478
|
+
}
|
|
1479
|
+
else {
|
|
1480
|
+
// Default: create new project from package
|
|
1481
|
+
console.log(chalk.dim(`Importing agent package: ${importPath}`));
|
|
1482
|
+
const project = await engine.importAgent(importPath, targetSlug);
|
|
1483
|
+
console.log(chalk.green(`✓ Agent imported: ${project.name}`));
|
|
1484
|
+
console.log(chalk.dim(` Slug: ${project.slug}`));
|
|
1485
|
+
console.log(chalk.dim(` ID: ${project.id}`));
|
|
1486
|
+
if (targetSlug && targetSlug !== project.slug) {
|
|
1487
|
+
console.log(chalk.dim(` Note: imported as ${project.slug}`));
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
catch (err) {
|
|
1492
|
+
console.log(chalk.red(`Import failed: ${err.message}`));
|
|
1493
|
+
}
|
|
1494
|
+
break;
|
|
1495
|
+
}
|
|
1258
1496
|
// ── Help ────────────────────────────────────────────────
|
|
1259
1497
|
default:
|
|
1260
1498
|
console.log(chalk.bold("BeerCan") + chalk.dim(" — Autonomous agent system\n"));
|
|
@@ -1286,6 +1524,14 @@ Do NOT rewrite everything — make focused, incremental changes.`,
|
|
|
1286
1524
|
console.log(chalk.cyan(" mcp:add <project> <name> <cmd> [args]") + chalk.dim(" Add MCP server"));
|
|
1287
1525
|
console.log(chalk.cyan(" mcp:list <project>") + chalk.dim(" List MCP servers"));
|
|
1288
1526
|
console.log();
|
|
1527
|
+
console.log(chalk.bold("Training:"));
|
|
1528
|
+
console.log(chalk.cyan(" training:create <name> [--work-dir <path>]") + chalk.dim(" Create a training sandbox project"));
|
|
1529
|
+
console.log(chalk.cyan(" training:run <project> [--scenario <id>]") + chalk.dim(" Run next (or specific) scenario"));
|
|
1530
|
+
console.log(chalk.cyan(" training:status <project>") + chalk.dim(" Show training progress"));
|
|
1531
|
+
console.log(chalk.cyan(" training:export <project> [--output <path>]") + chalk.dim(" Export agent package"));
|
|
1532
|
+
console.log(chalk.cyan(" training:import <package> [options]") + chalk.dim(" Import agent package"));
|
|
1533
|
+
console.log(chalk.dim(" --name <slug> Create new project with slug | --global Skills & tools only | --project <slug> Into existing"));
|
|
1534
|
+
console.log();
|
|
1289
1535
|
console.log(chalk.bold("Encryption:"));
|
|
1290
1536
|
console.log(chalk.cyan(" crypto:setup") + chalk.dim(" Configure encryption (passphrase or keyfile)"));
|
|
1291
1537
|
console.log(chalk.cyan(" crypto:status") + chalk.dim(" Show encryption status"));
|
|
@@ -1311,8 +1557,8 @@ Do NOT rewrite everything — make focused, incremental changes.`,
|
|
|
1311
1557
|
}
|
|
1312
1558
|
main().catch((err) => {
|
|
1313
1559
|
const msg = String(err?.message ?? err);
|
|
1314
|
-
if (msg.includes("
|
|
1315
|
-
console.error(chalk.red("\nMissing API key.") + " Run " + chalk.cyan("beercan setup") + " to configure BeerCan.\n");
|
|
1560
|
+
if (msg.includes("API_KEY") || msg.includes("api key") || msg.includes("Api key") || msg.includes("not set") || msg.includes("Required")) {
|
|
1561
|
+
console.error(chalk.red("\nMissing API key or provider config.") + " Run " + chalk.cyan("beercan setup") + " to configure BeerCan.\n");
|
|
1316
1562
|
}
|
|
1317
1563
|
else {
|
|
1318
1564
|
console.error(chalk.red("Fatal:"), msg);
|