@lobu/cli 6.0.1 → 7.0.0
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 +20 -27
- package/dist/bundled-skills/lobu/SKILL.md +11 -11
- package/dist/commands/_lib/apply/apply-cmd.d.ts +38 -0
- package/dist/commands/_lib/apply/apply-cmd.d.ts.map +1 -1
- package/dist/commands/_lib/apply/apply-cmd.js +574 -40
- package/dist/commands/_lib/apply/apply-cmd.js.map +1 -1
- package/dist/commands/_lib/apply/client.d.ts +180 -1
- package/dist/commands/_lib/apply/client.d.ts.map +1 -1
- package/dist/commands/_lib/apply/client.js +308 -28
- package/dist/commands/_lib/apply/client.js.map +1 -1
- package/dist/commands/_lib/apply/desired-state.d.ts +134 -3
- package/dist/commands/_lib/apply/desired-state.d.ts.map +1 -1
- package/dist/commands/_lib/apply/desired-state.js +703 -89
- package/dist/commands/_lib/apply/desired-state.js.map +1 -1
- package/dist/commands/_lib/apply/diff.d.ts +61 -3
- package/dist/commands/_lib/apply/diff.d.ts.map +1 -1
- package/dist/commands/_lib/apply/diff.js +382 -92
- package/dist/commands/_lib/apply/diff.js.map +1 -1
- package/dist/commands/_lib/apply/prompt.d.ts +6 -0
- package/dist/commands/_lib/apply/prompt.d.ts.map +1 -1
- package/dist/commands/_lib/apply/prompt.js +16 -0
- package/dist/commands/_lib/apply/prompt.js.map +1 -1
- package/dist/commands/_lib/apply/render.d.ts +9 -0
- package/dist/commands/_lib/apply/render.d.ts.map +1 -1
- package/dist/commands/_lib/apply/render.js +80 -3
- package/dist/commands/_lib/apply/render.js.map +1 -1
- package/dist/commands/agent.d.ts +7 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +65 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/chat.d.ts +12 -9
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +125 -57
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/dev.d.ts +23 -7
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +197 -49
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +136 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/eval.d.ts +8 -0
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +72 -6
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/init.d.ts +22 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +355 -182
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/link.d.ts +11 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +28 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +14 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/memory/_lib/browser-auth-cmd.d.ts.map +1 -1
- package/dist/commands/memory/_lib/browser-auth-cmd.js +3 -3
- package/dist/commands/memory/_lib/browser-auth-cmd.js.map +1 -1
- package/dist/commands/memory/_lib/mcp.d.ts +2 -2
- package/dist/commands/memory/_lib/mcp.d.ts.map +1 -1
- package/dist/commands/memory/_lib/mcp.js +24 -12
- package/dist/commands/memory/_lib/mcp.js.map +1 -1
- package/dist/commands/memory/_lib/openclaw-auth.d.ts +1 -0
- package/dist/commands/memory/_lib/openclaw-auth.d.ts.map +1 -1
- package/dist/commands/memory/_lib/openclaw-auth.js +14 -3
- package/dist/commands/memory/_lib/openclaw-auth.js.map +1 -1
- package/dist/commands/memory/_lib/openclaw-cmd.js +1 -1
- package/dist/commands/memory/_lib/openclaw-cmd.js.map +1 -1
- package/dist/commands/memory/_lib/schema.d.ts +29 -2
- package/dist/commands/memory/_lib/schema.d.ts.map +1 -1
- package/dist/commands/memory/_lib/schema.js +121 -5
- package/dist/commands/memory/_lib/schema.js.map +1 -1
- package/dist/commands/memory/_lib/seed-cmd.d.ts.map +1 -1
- package/dist/commands/memory/_lib/seed-cmd.js +46 -24
- package/dist/commands/memory/_lib/seed-cmd.js.map +1 -1
- package/dist/commands/memory/run.d.ts.map +1 -1
- package/dist/commands/memory/run.js +2 -2
- package/dist/commands/memory/run.js.map +1 -1
- package/dist/commands/org.d.ts +4 -0
- package/dist/commands/org.d.ts.map +1 -1
- package/dist/commands/org.js +10 -0
- package/dist/commands/org.js.map +1 -1
- package/dist/commands/platforms/platform-prompts.d.ts +0 -1
- package/dist/commands/platforms/platform-prompts.d.ts.map +1 -1
- package/dist/commands/platforms/platform-prompts.js +54 -8
- package/dist/commands/platforms/platform-prompts.js.map +1 -1
- package/dist/commands/telemetry.d.ts +10 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/commands/telemetry.js +68 -0
- package/dist/commands/telemetry.js.map +1 -0
- package/dist/commands/token.d.ts +9 -0
- package/dist/commands/token.d.ts.map +1 -1
- package/dist/commands/token.js +54 -0
- package/dist/commands/token.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +1 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/connectors/README.md +534 -0
- package/dist/connectors/__tests__/browser-scraper-utils.test.ts +186 -0
- package/dist/connectors/apple_health.ts +138 -0
- package/dist/connectors/apple_screen_time.ts +82 -0
- package/dist/connectors/browser-scraper-utils.ts +246 -0
- package/dist/connectors/capterra.ts +277 -0
- package/dist/connectors/g2.ts +290 -0
- package/dist/connectors/github.ts +1530 -0
- package/dist/connectors/glassdoor.ts +295 -0
- package/dist/connectors/gmaps.ts +197 -0
- package/dist/connectors/google_calendar.ts +641 -0
- package/dist/connectors/google_gmail.ts +754 -0
- package/dist/connectors/google_photos.ts +776 -0
- package/dist/connectors/google_play.ts +349 -0
- package/dist/connectors/hackernews.ts +471 -0
- package/dist/connectors/index.ts +28 -0
- package/dist/connectors/ios_appstore.ts +226 -0
- package/dist/connectors/linkedin.ts +494 -0
- package/dist/connectors/local_directory.ts +91 -0
- package/dist/connectors/microsoft_outlook.ts +410 -0
- package/dist/connectors/producthunt.ts +471 -0
- package/dist/connectors/reddit.ts +600 -0
- package/dist/connectors/revolut.ts +572 -0
- package/dist/connectors/rss.ts +448 -0
- package/dist/connectors/spotify.ts +590 -0
- package/dist/connectors/trustpilot.ts +203 -0
- package/dist/connectors/website.ts +629 -0
- package/dist/connectors/whatsapp.ts +1081 -0
- package/dist/connectors/whatsapp_local.ts +125 -0
- package/dist/connectors/x.ts +536 -0
- package/dist/connectors/youtube.ts +666 -0
- package/dist/db/migrations/00000000000000_baseline.sql +4867 -0
- package/dist/db/migrations/20260405193000_add_mcp_sessions.sql +33 -0
- package/dist/db/migrations/20260408120000_remove_system_connectors.sql +48 -0
- package/dist/db/migrations/20260408120001_optional_compiled_code.sql +6 -0
- package/dist/db/migrations/20260409110000_add_active_watcher_run_index.sql +9 -0
- package/dist/db/migrations/20260409130000_connector_default_config.sql +5 -0
- package/dist/db/migrations/20260410120000_add_agent_secrets.sql +25 -0
- package/dist/db/migrations/20260413170000_add_watcher_group_id.sql +67 -0
- package/dist/db/migrations/20260416120000_add_entity_wa_jid_index.sql +14 -0
- package/dist/db/migrations/20260417100000_add_entity_identities.sql +77 -0
- package/dist/db/migrations/20260418100000_add_auth_runs.sql +83 -0
- package/dist/db/migrations/20260418110000_add_runs_created_by_user.sql +18 -0
- package/dist/db/migrations/20260419120000_add_event_identity_indexes.sql +56 -0
- package/dist/db/migrations/20260420120000_extend_reserved_org_slugs.sql +56 -0
- package/dist/db/migrations/20260424030000_add_watcher_run_correlation.sql +52 -0
- package/dist/db/migrations/20260424130000_relax_events_client_id_fk.sql +47 -0
- package/dist/db/migrations/20260425100000_normalize_watcher_feedback.sql +91 -0
- package/dist/db/migrations/20260425120000_add_run_diagnostics.sql +20 -0
- package/dist/db/migrations/20260425130000_add_repair_agent_plumbing.sql +46 -0
- package/dist/db/migrations/20260426120000_entities_entity_type_fk.sql +101 -0
- package/dist/db/migrations/20260426130000_db_integrity_cleanup.sql +104 -0
- package/dist/db/migrations/20260426130001_db_integrity_cleanup_concurrent.sql +187 -0
- package/dist/db/migrations/20260427133000_events_created_by_nullable.sql +74 -0
- package/dist/db/migrations/20260427140000_identity_engine_indexes.sql +140 -0
- package/dist/db/migrations/20260427150000_drop_events_source_id.sql +177 -0
- package/dist/db/migrations/20260427160000_drop_dead_schema.sql +76 -0
- package/dist/db/migrations/20260427170000_market_founder_to_member.sql +364 -0
- package/dist/db/migrations/20260428040000_cascade_events_watchers_org_fk.sql +66 -0
- package/dist/db/migrations/20260428050000_add_runs_approved_input.sql +9 -0
- package/dist/db/migrations/20260429010000_auth_profile_tenant_scoped_fk.sql +79 -0
- package/dist/db/migrations/20260429060000_extend_runs_for_lobu_queue.sql +108 -0
- package/dist/db/migrations/20260429120000_agent_changed_notify.sql +97 -0
- package/dist/db/migrations/20260429120100_user_auth_profiles_and_model_prefs.sql +36 -0
- package/dist/db/migrations/20260429120200_fix_notify_old_keys.sql +130 -0
- package/dist/db/migrations/20260429130000_oauth_states_cli_sessions_rate_limits.sql +83 -0
- package/dist/db/migrations/20260429140000_phase8_grants_chat_connections_mcp_sessions.sql +84 -0
- package/dist/db/migrations/20260429140100_runs_priority_expires_at_retry_delay.sql +44 -0
- package/dist/db/migrations/20260429180000_drop_invalidatable_cache_triggers.sql +25 -0
- package/dist/db/migrations/20260430005614_agents_apply_fields.sql +21 -0
- package/dist/db/migrations/20260430022231_fix_connection_config_encryption.sql +69 -0
- package/dist/db/migrations/20260430151215_add_task_run_type.sql +77 -0
- package/dist/db/migrations/20260501000000_drop_cli_sessions.sql +27 -0
- package/dist/db/migrations/20260501133000_lobu_memory_mcp_id.sql +117 -0
- package/dist/db/migrations/20260502000000_drop_chat_connections.sql +60 -0
- package/dist/db/migrations/20260503000000_agent_secrets_org_scope.sql +56 -0
- package/dist/db/migrations/20260504000000_flatten_agents_drop_sandbox_model.sql +48 -0
- package/dist/db/migrations/20260510220000_connector_required_capability.sql +47 -0
- package/dist/db/migrations/20260512000000_device_worker_connection_binding.sql +113 -0
- package/dist/db/migrations/20260512131703_connections_slug.sql +131 -0
- package/dist/db/migrations/20260513000000_chat_user_identities.sql +24 -0
- package/dist/db/migrations/20260513120000_auth_profiles_device_binding.sql +50 -0
- package/dist/db/migrations/20260513150000_auth_profiles_cdp_url.sql +43 -0
- package/dist/db/migrations/20260513200000_notifications_as_events.sql +86 -0
- package/dist/db/migrations/20260514000000_scheduled_jobs.sql +97 -0
- package/dist/db/migrations/20260514120000_auth_profiles_connector_key_nullable.sql +42 -0
- package/dist/eval/types.d.ts +2 -0
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +210 -132
- package/dist/index.js.map +1 -1
- package/dist/internal/api-client.d.ts +4 -8
- package/dist/internal/api-client.d.ts.map +1 -1
- package/dist/internal/api-client.js +1 -1
- package/dist/internal/api-client.js.map +1 -1
- package/dist/internal/context.js +2 -2
- package/dist/internal/context.js.map +1 -1
- package/dist/internal/credentials.d.ts.map +1 -1
- package/dist/internal/credentials.js +6 -1
- package/dist/internal/credentials.js.map +1 -1
- package/dist/internal/gateway-url.d.ts +14 -0
- package/dist/internal/gateway-url.d.ts.map +1 -1
- package/dist/internal/gateway-url.js +19 -0
- package/dist/internal/gateway-url.js.map +1 -1
- package/dist/internal/index.d.ts +3 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +3 -3
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/oauth.d.ts +6 -5
- package/dist/internal/oauth.d.ts.map +1 -1
- package/dist/internal/oauth.js +2 -2
- package/dist/internal/project-link.d.ts +10 -0
- package/dist/internal/project-link.d.ts.map +1 -0
- package/dist/internal/project-link.js +48 -0
- package/dist/internal/project-link.js.map +1 -0
- package/dist/providers.json +2 -2
- package/dist/server.bundle.mjs +31654 -30866
- package/dist/start-local.bundle.mjs +74409 -0
- package/dist/templates/README.md.tmpl +10 -11
- package/dist/templates/TESTING.md.tmpl +9 -9
- package/package.json +15 -13
- package/dist/__tests__/chat.integration.test.d.ts +0 -2
- package/dist/__tests__/chat.integration.test.d.ts.map +0 -1
- package/dist/__tests__/chat.integration.test.js +0 -337
- package/dist/__tests__/chat.integration.test.js.map +0 -1
- package/dist/__tests__/dev.test.d.ts +0 -2
- package/dist/__tests__/dev.test.d.ts.map +0 -1
- package/dist/__tests__/dev.test.js +0 -25
- package/dist/__tests__/dev.test.js.map +0 -1
- package/dist/__tests__/init-memory.test.d.ts +0 -2
- package/dist/__tests__/init-memory.test.d.ts.map +0 -1
- package/dist/__tests__/init-memory.test.js +0 -45
- package/dist/__tests__/init-memory.test.js.map +0 -1
- package/dist/__tests__/token.test.d.ts +0 -2
- package/dist/__tests__/token.test.d.ts.map +0 -1
- package/dist/__tests__/token.test.js +0 -52
- package/dist/__tests__/token.test.js.map +0 -1
- package/dist/commands/_lib/apply/__tests__/client.test.d.ts +0 -2
- package/dist/commands/_lib/apply/__tests__/client.test.d.ts.map +0 -1
- package/dist/commands/_lib/apply/__tests__/client.test.js +0 -23
- package/dist/commands/_lib/apply/__tests__/client.test.js.map +0 -1
- package/dist/commands/_lib/apply/__tests__/desired-state.test.d.ts +0 -2
- package/dist/commands/_lib/apply/__tests__/desired-state.test.d.ts.map +0 -1
- package/dist/commands/_lib/apply/__tests__/desired-state.test.js +0 -140
- package/dist/commands/_lib/apply/__tests__/desired-state.test.js.map +0 -1
- package/dist/commands/_lib/apply/__tests__/diff.test.d.ts +0 -2
- package/dist/commands/_lib/apply/__tests__/diff.test.d.ts.map +0 -1
- package/dist/commands/_lib/apply/__tests__/diff.test.js +0 -378
- package/dist/commands/_lib/apply/__tests__/diff.test.js.map +0 -1
- package/dist/commands/apply.d.ts +0 -3
- package/dist/commands/apply.d.ts.map +0 -1
- package/dist/commands/apply.js +0 -5
- package/dist/commands/apply.js.map +0 -1
- package/dist/commands/memory/_lib/openclaw-auth.test.d.ts +0 -2
- package/dist/commands/memory/_lib/openclaw-auth.test.d.ts.map +0 -1
- package/dist/commands/memory/_lib/openclaw-auth.test.js +0 -9
- package/dist/commands/memory/_lib/openclaw-auth.test.js.map +0 -1
- package/dist/internal/__tests__/api-client.test.d.ts +0 -2
- package/dist/internal/__tests__/api-client.test.d.ts.map +0 -1
- package/dist/internal/__tests__/api-client.test.js +0 -95
- package/dist/internal/__tests__/api-client.test.js.map +0 -1
- package/dist/internal/__tests__/context.test.d.ts +0 -2
- package/dist/internal/__tests__/context.test.d.ts.map +0 -1
- package/dist/internal/__tests__/context.test.js +0 -77
- package/dist/internal/__tests__/context.test.js.map +0 -1
package/dist/commands/init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
|
-
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
-
import { join } from "node:path";
|
|
3
|
+
import { access, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { basename, join, resolve } from "node:path";
|
|
5
5
|
import { confirm, input, password, select } from "@inquirer/prompts";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import ora from "ora";
|
|
@@ -9,76 +9,138 @@ import { promptPlatformConfig } from "../commands/platforms/platform-prompts.js"
|
|
|
9
9
|
import { setLocalEnvValue } from "../internal/local-env.js";
|
|
10
10
|
import { getProviderById, loadProviderRegistry, } from "../commands/providers/registry.js";
|
|
11
11
|
import { renderTemplate } from "../utils/template.js";
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const DEFAULT_LOBU_MCP_URL = "https://lobu.ai/mcp";
|
|
13
|
+
const PROJECT_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
14
|
+
const PLATFORM_CHOICES = [
|
|
15
|
+
"telegram",
|
|
16
|
+
"slack",
|
|
17
|
+
"discord",
|
|
18
|
+
"whatsapp",
|
|
19
|
+
"teams",
|
|
20
|
+
"gchat",
|
|
21
|
+
];
|
|
22
|
+
const NETWORK_CHOICES = ["restricted", "open", "isolated"];
|
|
23
|
+
const MEMORY_CHOICES = ["none", "lobu-cloud", "lobu-custom"];
|
|
24
|
+
export async function initCommand(cwd = process.cwd(), projectNameArg, options = {}) {
|
|
16
25
|
const cliVersion = await getCliVersion();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
const useDefaults = options.yes === true;
|
|
27
|
+
// Catch flag combos that can't satisfy a prompt before we mkdir anything.
|
|
28
|
+
if (useDefaults && options.memory === "lobu-custom" && !options.memoryUrl) {
|
|
29
|
+
console.error(chalk.red("\n✗ --memory lobu-custom requires --memory-url <url>.\n"));
|
|
20
30
|
process.exit(1);
|
|
21
31
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
const here = options.here || projectNameArg === ".";
|
|
33
|
+
let projectName;
|
|
34
|
+
let projectDir;
|
|
35
|
+
if (here) {
|
|
36
|
+
projectDir = cwd;
|
|
37
|
+
projectName = basename(resolve(cwd));
|
|
38
|
+
if (!PROJECT_NAME_PATTERN.test(projectName)) {
|
|
39
|
+
// Common when cwd is e.g. "My Project". Force user to pick a slug.
|
|
40
|
+
projectName =
|
|
41
|
+
projectNameArg && projectNameArg !== "."
|
|
42
|
+
? projectNameArg
|
|
43
|
+
: await promptOrDefault({
|
|
44
|
+
flag: undefined,
|
|
45
|
+
useDefaults,
|
|
46
|
+
defaultValue: slugify(basename(resolve(cwd))) || "my-lobu",
|
|
47
|
+
prompt: () => input({
|
|
48
|
+
message: "Project slug?",
|
|
49
|
+
default: slugify(basename(resolve(cwd))) || "my-lobu",
|
|
50
|
+
validate: validateProjectName,
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const entries = await readdir(projectDir).catch(() => []);
|
|
55
|
+
const conflict = entries.some((n) => n === "lobu.toml" || n === "agents" || n === ".env");
|
|
56
|
+
if (conflict) {
|
|
57
|
+
console.log(chalk.red(`\n✗ ${projectDir} already contains a Lobu project (lobu.toml / agents/ / .env).\n Remove them or pick another directory.\n`));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
console.log(chalk.dim(`\nScaffolding into current directory: ${chalk.cyan(projectDir)}\n`));
|
|
39
61
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
else {
|
|
63
|
+
if (projectNameArg && !PROJECT_NAME_PATTERN.test(projectNameArg)) {
|
|
64
|
+
console.log(chalk.red(`\n✗ Project name must be lowercase alphanumeric with hyphens only (got: ${projectNameArg}).\n`));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
projectName =
|
|
68
|
+
projectNameArg ??
|
|
69
|
+
(await promptOrDefault({
|
|
70
|
+
flag: undefined,
|
|
71
|
+
useDefaults,
|
|
72
|
+
defaultValue: "my-lobu",
|
|
73
|
+
prompt: () => input({
|
|
74
|
+
message: "Project name?",
|
|
75
|
+
default: "my-lobu",
|
|
76
|
+
validate: validateProjectName,
|
|
77
|
+
}),
|
|
78
|
+
}));
|
|
79
|
+
projectDir = join(cwd, projectName);
|
|
80
|
+
try {
|
|
81
|
+
await access(projectDir, constants.F_OK);
|
|
82
|
+
console.log(chalk.red(`\n✗ Directory "${projectName}" already exists. Pick a different name, remove it, or pass \`--here\` to scaffold into the current directory.\n`));
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
await mkdir(projectDir, { recursive: true });
|
|
87
|
+
console.log(chalk.dim(`\nCreating project in: ${chalk.cyan(projectDir)}\n`));
|
|
88
|
+
}
|
|
44
89
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
90
|
+
const gatewayPort = await promptOrDefault({
|
|
91
|
+
flag: options.port,
|
|
92
|
+
useDefaults,
|
|
93
|
+
defaultValue: "8787",
|
|
49
94
|
validate: (value) => {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return true;
|
|
95
|
+
const p = Number(value);
|
|
96
|
+
return Number.isInteger(p) && p >= 1 && p <= 65535
|
|
97
|
+
? true
|
|
98
|
+
: "Please enter a valid port (1-65535)";
|
|
55
99
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
choices: [
|
|
66
|
-
{
|
|
67
|
-
name: "Restricted (recommended) — common registries only (npm, GitHub, PyPI)",
|
|
68
|
-
value: "restricted",
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: "Open — workers can access any domain",
|
|
72
|
-
value: "open",
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
name: "Isolated — workers have no internet access",
|
|
76
|
-
value: "isolated",
|
|
100
|
+
prompt: () => input({
|
|
101
|
+
message: "Gateway port?",
|
|
102
|
+
default: "8787",
|
|
103
|
+
validate: (value) => {
|
|
104
|
+
const p = Number(value);
|
|
105
|
+
if (!Number.isInteger(p) || p < 1 || p > 65535) {
|
|
106
|
+
return "Please enter a valid port number (1-65535)";
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
77
109
|
},
|
|
78
|
-
|
|
79
|
-
|
|
110
|
+
}),
|
|
111
|
+
});
|
|
112
|
+
const publicGatewayUrl = await promptOrDefault({
|
|
113
|
+
flag: options.publicUrl,
|
|
114
|
+
useDefaults,
|
|
115
|
+
defaultValue: "",
|
|
116
|
+
prompt: () => input({
|
|
117
|
+
message: "Public gateway URL? (leave empty for local dev, set for OAuth/webhooks)",
|
|
118
|
+
default: "",
|
|
119
|
+
}),
|
|
80
120
|
});
|
|
81
|
-
|
|
121
|
+
const networkPolicy = (await promptOrDefault({
|
|
122
|
+
flag: options.network,
|
|
123
|
+
useDefaults,
|
|
124
|
+
defaultValue: "restricted",
|
|
125
|
+
validate: (v) => NETWORK_CHOICES.includes(v)
|
|
126
|
+
? true
|
|
127
|
+
: `Must be one of: ${NETWORK_CHOICES.join(", ")}`,
|
|
128
|
+
prompt: () => select({
|
|
129
|
+
message: "Worker network access?",
|
|
130
|
+
choices: [
|
|
131
|
+
{
|
|
132
|
+
name: "Restricted (recommended) — common registries only (npm, GitHub, PyPI)",
|
|
133
|
+
value: "restricted",
|
|
134
|
+
},
|
|
135
|
+
{ name: "Open — workers can access any domain", value: "open" },
|
|
136
|
+
{
|
|
137
|
+
name: "Isolated — workers have no internet access",
|
|
138
|
+
value: "isolated",
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
default: "restricted",
|
|
142
|
+
}),
|
|
143
|
+
}));
|
|
82
144
|
const providerSkills = loadProviderRegistry();
|
|
83
145
|
const providerChoices = [
|
|
84
146
|
{ name: "Skip — I'll add a provider later", value: "" },
|
|
@@ -87,10 +149,21 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
87
149
|
value: s.id,
|
|
88
150
|
})),
|
|
89
151
|
];
|
|
90
|
-
const providerId = await
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
152
|
+
const providerId = await promptOrDefault({
|
|
153
|
+
flag: options.provider,
|
|
154
|
+
useDefaults,
|
|
155
|
+
defaultValue: "",
|
|
156
|
+
validate: (v) => v === "" || providerChoices.some((c) => c.value === v)
|
|
157
|
+
? true
|
|
158
|
+
: `Unknown provider "${v}". Available: ${providerChoices
|
|
159
|
+
.filter((c) => c.value)
|
|
160
|
+
.map((c) => c.value)
|
|
161
|
+
.join(", ")}`,
|
|
162
|
+
prompt: () => select({
|
|
163
|
+
message: "AI provider?",
|
|
164
|
+
choices: providerChoices,
|
|
165
|
+
default: "",
|
|
166
|
+
}),
|
|
94
167
|
});
|
|
95
168
|
let providerApiKey = "";
|
|
96
169
|
let selectedProvider;
|
|
@@ -98,15 +171,21 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
98
171
|
selectedProvider = getProviderById(providerId);
|
|
99
172
|
const p = selectedProvider?.providers?.[0];
|
|
100
173
|
if (p) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
174
|
+
if (options.providerKey) {
|
|
175
|
+
providerApiKey = options.providerKey;
|
|
176
|
+
}
|
|
177
|
+
else if (process.env[p.envVarName]) {
|
|
178
|
+
// Inherit from env so `--yes` can pick up keys set in the shell.
|
|
179
|
+
providerApiKey = process.env[p.envVarName] ?? "";
|
|
180
|
+
}
|
|
181
|
+
else if (!useDefaults) {
|
|
182
|
+
providerApiKey = await password({
|
|
183
|
+
message: `${p.displayName} API key:`,
|
|
184
|
+
mask: true,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
105
187
|
}
|
|
106
188
|
}
|
|
107
|
-
// Define skills locally via skills/<name>/SKILL.md or
|
|
108
|
-
// agents/<id>/skills/<name>/SKILL.md.
|
|
109
|
-
// Chat platform selection
|
|
110
189
|
const platformChoices = [
|
|
111
190
|
{ name: "Skip — I'll connect a platform later", value: "" },
|
|
112
191
|
{ name: "Telegram", value: "telegram" },
|
|
@@ -116,42 +195,81 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
116
195
|
{ name: "Microsoft Teams", value: "teams" },
|
|
117
196
|
{ name: "Google Chat", value: "gchat" },
|
|
118
197
|
];
|
|
119
|
-
const platformType = await
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
198
|
+
const platformType = await promptOrDefault({
|
|
199
|
+
flag: options.platform,
|
|
200
|
+
useDefaults,
|
|
201
|
+
defaultValue: "",
|
|
202
|
+
validate: (v) => v === "" || PLATFORM_CHOICES.includes(v)
|
|
203
|
+
? true
|
|
204
|
+
: `Unknown platform "${v}". Available: ${PLATFORM_CHOICES.join(", ")}`,
|
|
205
|
+
prompt: () => select({
|
|
206
|
+
message: "Connect a chat platform?",
|
|
207
|
+
choices: platformChoices,
|
|
208
|
+
default: "",
|
|
209
|
+
}),
|
|
123
210
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
{
|
|
134
|
-
|
|
135
|
-
|
|
211
|
+
// Interactive: prompt for real secrets. --yes: write placeholder env-var
|
|
212
|
+
// refs into lobu.toml; the user fills .env afterwards.
|
|
213
|
+
let platformConfig = {};
|
|
214
|
+
let platformSecrets = [];
|
|
215
|
+
if (platformType) {
|
|
216
|
+
if (useDefaults) {
|
|
217
|
+
platformConfig = PLATFORM_PLACEHOLDERS[platformType];
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
({ platformConfig, platformSecrets } =
|
|
221
|
+
await promptPlatformConfig(platformType));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const enableSlackPreview = await promptBooleanOrDefault({
|
|
225
|
+
flag: options.slackPreview,
|
|
226
|
+
useDefaults,
|
|
227
|
+
defaultValue: false,
|
|
228
|
+
prompt: () => confirm({
|
|
229
|
+
message: "Enable Slack Preview with the public Lobu Developer Slack bot?",
|
|
230
|
+
default: false,
|
|
231
|
+
}),
|
|
136
232
|
});
|
|
233
|
+
const memoryChoice = (await promptOrDefault({
|
|
234
|
+
flag: options.memory,
|
|
235
|
+
useDefaults,
|
|
236
|
+
defaultValue: "none",
|
|
237
|
+
validate: (v) => MEMORY_CHOICES.includes(v)
|
|
238
|
+
? true
|
|
239
|
+
: `Must be one of: ${MEMORY_CHOICES.join(", ")}`,
|
|
240
|
+
prompt: () => select({
|
|
241
|
+
message: "Memory:",
|
|
242
|
+
choices: [
|
|
243
|
+
{ name: "None (filesystem memory)", value: "none" },
|
|
244
|
+
{ name: "Lobu Cloud (app.lobu.ai)", value: "lobu-cloud" },
|
|
245
|
+
{ name: "Custom Lobu memory URL", value: "lobu-custom" },
|
|
246
|
+
],
|
|
247
|
+
default: "none",
|
|
248
|
+
}),
|
|
249
|
+
}));
|
|
137
250
|
const envSecrets = [];
|
|
138
|
-
const
|
|
139
|
-
let
|
|
140
|
-
if (memoryChoice === "
|
|
141
|
-
|
|
251
|
+
const includeLobuMemory = memoryChoice !== "none";
|
|
252
|
+
let lobuUrl = "";
|
|
253
|
+
if (memoryChoice === "lobu-cloud") {
|
|
254
|
+
lobuUrl = DEFAULT_LOBU_MCP_URL;
|
|
142
255
|
}
|
|
143
|
-
else if (memoryChoice === "
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
256
|
+
else if (memoryChoice === "lobu-custom") {
|
|
257
|
+
lobuUrl =
|
|
258
|
+
options.memoryUrl ??
|
|
259
|
+
(await input({
|
|
260
|
+
message: "Lobu memory MCP URL:",
|
|
261
|
+
validate: (v) => (v ? true : "URL is required"),
|
|
262
|
+
}));
|
|
263
|
+
envSecrets.push({ envVar: "MEMORY_URL", value: lobuUrl });
|
|
149
264
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
265
|
+
const otelEndpoint = await promptOrDefault({
|
|
266
|
+
flag: options.otelEndpoint,
|
|
267
|
+
useDefaults,
|
|
268
|
+
defaultValue: "",
|
|
269
|
+
prompt: () => input({
|
|
270
|
+
message: "OpenTelemetry collector endpoint? (leave empty to disable tracing)",
|
|
271
|
+
default: "",
|
|
272
|
+
}),
|
|
155
273
|
});
|
|
156
274
|
if (otelEndpoint) {
|
|
157
275
|
envSecrets.push({
|
|
@@ -159,18 +277,25 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
159
277
|
value: otelEndpoint,
|
|
160
278
|
});
|
|
161
279
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
280
|
+
let enableSentry = false;
|
|
281
|
+
if (options.sentry === true) {
|
|
282
|
+
enableSentry = true;
|
|
283
|
+
}
|
|
284
|
+
else if (options.noSentry === true) {
|
|
285
|
+
enableSentry = false;
|
|
286
|
+
}
|
|
287
|
+
else if (!useDefaults) {
|
|
288
|
+
enableSentry = await confirm({
|
|
289
|
+
message: "Share anonymous error reports with Sentry to help improve Lobu?",
|
|
290
|
+
default: false,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
167
293
|
if (enableSentry) {
|
|
168
294
|
envSecrets.push({
|
|
169
295
|
envVar: "SENTRY_DSN",
|
|
170
296
|
value: "https://c5910e58d1a134d64ff93a95a9c535bb@o4507291398897664.ingest.us.sentry.io/4511097466781696",
|
|
171
297
|
});
|
|
172
298
|
}
|
|
173
|
-
// Compute network domains from selected policy
|
|
174
299
|
let allowedDomains;
|
|
175
300
|
let disallowedDomains;
|
|
176
301
|
if (networkPolicy === "open") {
|
|
@@ -182,7 +307,6 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
182
307
|
disallowedDomains = "";
|
|
183
308
|
}
|
|
184
309
|
else {
|
|
185
|
-
// restricted (default)
|
|
186
310
|
allowedDomains = [
|
|
187
311
|
"registry.npmjs.org",
|
|
188
312
|
".npmjs.org",
|
|
@@ -204,9 +328,8 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
204
328
|
};
|
|
205
329
|
const spinner = ora("Creating Lobu project...").start();
|
|
206
330
|
try {
|
|
207
|
-
// Create data directory in project directory
|
|
208
331
|
await mkdir(join(projectDir, "data"), { recursive: true });
|
|
209
|
-
if (
|
|
332
|
+
if (includeLobuMemory) {
|
|
210
333
|
await mkdir(join(projectDir, "models"), { recursive: true });
|
|
211
334
|
await mkdir(join(projectDir, "data", "entities"), { recursive: true });
|
|
212
335
|
await mkdir(join(projectDir, "data", "relationships"), {
|
|
@@ -216,7 +339,6 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
216
339
|
await writeFile(join(projectDir, "data", "entities", ".gitkeep"), "");
|
|
217
340
|
await writeFile(join(projectDir, "data", "relationships", ".gitkeep"), "");
|
|
218
341
|
}
|
|
219
|
-
// Generate lobu.toml
|
|
220
342
|
await generateLobuToml(projectDir, {
|
|
221
343
|
agentName: projectName,
|
|
222
344
|
allowedDomains: answers.allowedDomains,
|
|
@@ -225,9 +347,10 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
225
347
|
providerModel: selectedProvider?.providers?.[0]?.defaultModel,
|
|
226
348
|
platformType: platformType || undefined,
|
|
227
349
|
platformConfig: Object.keys(platformConfig).length > 0 ? platformConfig : undefined,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
350
|
+
includeLobuMemory,
|
|
351
|
+
enableSlackPreview,
|
|
352
|
+
lobuOrg: includeLobuMemory ? projectName : undefined,
|
|
353
|
+
lobuName: includeLobuMemory ? humanizeSlug(projectName) : undefined,
|
|
231
354
|
});
|
|
232
355
|
const variables = {
|
|
233
356
|
PROJECT_NAME: projectName,
|
|
@@ -237,40 +360,72 @@ export async function initCommand(cwd = process.cwd(), projectNameArg) {
|
|
|
237
360
|
WORKER_ALLOWED_DOMAINS: answers.allowedDomains,
|
|
238
361
|
WORKER_DISALLOWED_DOMAINS: answers.disallowedDomains,
|
|
239
362
|
};
|
|
240
|
-
// Create .env file
|
|
241
363
|
await renderTemplate(".env.tmpl", variables, join(projectDir, ".env"));
|
|
242
|
-
|
|
364
|
+
const envVarsToFill = new Set();
|
|
365
|
+
if (selectedProvider?.providers?.[0]?.envVarName) {
|
|
366
|
+
envVarsToFill.add(selectedProvider.providers[0].envVarName);
|
|
367
|
+
}
|
|
368
|
+
for (const value of Object.values(platformConfig)) {
|
|
369
|
+
const envVar = extractEnvVarRef(value);
|
|
370
|
+
if (envVar)
|
|
371
|
+
envVarsToFill.add(envVar);
|
|
372
|
+
}
|
|
373
|
+
for (const envVar of envVarsToFill) {
|
|
374
|
+
await setLocalEnvValue(projectDir, envVar, "");
|
|
375
|
+
}
|
|
243
376
|
if (publicGatewayUrl) {
|
|
244
377
|
await setLocalEnvValue(projectDir, "PUBLIC_GATEWAY_URL", publicGatewayUrl);
|
|
245
378
|
}
|
|
246
|
-
// Save provider API key to .env
|
|
247
379
|
if (providerApiKey && selectedProvider?.providers?.[0]?.envVarName) {
|
|
248
380
|
await setLocalEnvValue(projectDir, selectedProvider.providers[0].envVarName, providerApiKey);
|
|
249
381
|
}
|
|
250
|
-
// Save platform secrets to .env
|
|
251
382
|
for (const secret of platformSecrets) {
|
|
252
383
|
await setLocalEnvValue(projectDir, secret.envVar, secret.value);
|
|
253
384
|
}
|
|
254
|
-
// Save OAuth secrets to .env
|
|
255
385
|
for (const secret of envSecrets) {
|
|
256
386
|
await setLocalEnvValue(projectDir, secret.envVar, secret.value);
|
|
257
387
|
}
|
|
258
|
-
// Create .gitignore
|
|
259
388
|
await renderTemplate(".gitignore.tmpl", {}, join(projectDir, ".gitignore"));
|
|
260
|
-
// Create README
|
|
261
389
|
await renderTemplate("README.md.tmpl", variables, join(projectDir, "README.md"));
|
|
262
|
-
// Create agent directory with instruction files
|
|
263
390
|
const agentDir = join(projectDir, "agents", projectName);
|
|
264
391
|
await mkdir(agentDir, { recursive: true });
|
|
265
392
|
await writeFile(join(agentDir, "IDENTITY.md"), `# Identity\n\nYou are ${projectName}, a helpful AI assistant.\n`);
|
|
266
393
|
await writeFile(join(agentDir, "SOUL.md"), `# Instructions\n\nBe concise and helpful. Ask clarifying questions when the request is ambiguous.\n`);
|
|
267
394
|
await writeFile(join(agentDir, "USER.md"), `# User Context\n\n<!-- Add user-specific preferences, timezone, environment details here -->\n`);
|
|
268
|
-
// Create agent-specific skills directory
|
|
269
395
|
await mkdir(join(agentDir, "skills"), { recursive: true });
|
|
270
396
|
await writeFile(join(agentDir, "skills", ".gitkeep"), "");
|
|
271
|
-
// Create evals directory with sample eval
|
|
272
397
|
await mkdir(join(agentDir, "evals"), { recursive: true });
|
|
273
|
-
await writeFile(join(agentDir, "evals", "ping.yaml"),
|
|
398
|
+
await writeFile(join(agentDir, "evals", "ping.yaml"), DEFAULT_EVAL_YAML);
|
|
399
|
+
await mkdir(join(projectDir, "skills"), { recursive: true });
|
|
400
|
+
await writeFile(join(projectDir, "skills", ".gitkeep"), "");
|
|
401
|
+
await renderTemplate("AGENTS.md.tmpl", variables, join(projectDir, "AGENTS.md"));
|
|
402
|
+
await renderTemplate("TESTING.md.tmpl", variables, join(projectDir, "TESTING.md"));
|
|
403
|
+
spinner.succeed("Project created successfully!");
|
|
404
|
+
const gatewayUrl = `http://localhost:${gatewayPort}`;
|
|
405
|
+
console.log(chalk.green("\n✓ Lobu initialized!\n"));
|
|
406
|
+
console.log(chalk.bold("Next steps:\n"));
|
|
407
|
+
let n = 1;
|
|
408
|
+
if (!here) {
|
|
409
|
+
console.log(chalk.cyan(` ${n++}. cd ${projectName}`));
|
|
410
|
+
}
|
|
411
|
+
console.log(chalk.cyan(` ${n++}. Start the local stack: lobu run (uses PGlite by default)`));
|
|
412
|
+
console.log(chalk.dim(" Optional: set DATABASE_URL in .env to use external Postgres instead."));
|
|
413
|
+
if (lobuUrl) {
|
|
414
|
+
console.log(chalk.cyan(` ${n++}. Wire memory clients: lobu memory init`));
|
|
415
|
+
}
|
|
416
|
+
if (enableSlackPreview) {
|
|
417
|
+
console.log(chalk.cyan(` ${n++}. Link the project to Lobu Cloud and register it: lobu login && lobu org set <slug> && lobu apply`));
|
|
418
|
+
console.log(chalk.dim(" Then `lobu run` will print a short-lived Slack Preview link code."));
|
|
419
|
+
}
|
|
420
|
+
console.log(chalk.cyan(` ${n++}. API docs: ${gatewayUrl}/api/docs`));
|
|
421
|
+
console.log(chalk.dim("\n See README.md for layout, AGENTS.md for the agent contract.\n"));
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
spinner.fail("Failed to create project");
|
|
425
|
+
throw error;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
const DEFAULT_EVAL_YAML = `version: 1
|
|
274
429
|
name: ping
|
|
275
430
|
description: Agent responds to a simple greeting
|
|
276
431
|
trials: 3
|
|
@@ -287,53 +442,69 @@ turns:
|
|
|
287
442
|
- type: llm-rubric
|
|
288
443
|
value: "Response is friendly and acknowledges the greeting"
|
|
289
444
|
weight: 0.7
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
// Create AGENTS.md
|
|
295
|
-
await renderTemplate("AGENTS.md.tmpl", variables, join(projectDir, "AGENTS.md"));
|
|
296
|
-
// Create TESTING.md
|
|
297
|
-
await renderTemplate("TESTING.md.tmpl", variables, join(projectDir, "TESTING.md"));
|
|
298
|
-
spinner.succeed("Project created successfully!");
|
|
299
|
-
// Print next steps
|
|
300
|
-
console.log(chalk.green("\n✓ Lobu initialized!\n"));
|
|
301
|
-
console.log(chalk.bold("Next steps:\n"));
|
|
302
|
-
console.log(chalk.cyan(" 1. Navigate to your project:"));
|
|
303
|
-
console.log(chalk.dim(` cd ${projectName}\n`));
|
|
304
|
-
console.log(chalk.cyan(" 2. Review your configuration:"));
|
|
305
|
-
console.log(chalk.dim(" - lobu.toml (agents, providers, skills, network)"));
|
|
306
|
-
console.log(chalk.dim(` - agents/${projectName}/ (IDENTITY.md, SOUL.md, USER.md, skills/)`));
|
|
307
|
-
console.log(chalk.dim(" - skills/ (shared skills — all agents)"));
|
|
308
|
-
if (includeOwlettoMemory) {
|
|
309
|
-
console.log(chalk.dim(" - models/ (memory model files)"));
|
|
310
|
-
console.log(chalk.dim(" - data/ (memory seed data)"));
|
|
311
|
-
}
|
|
312
|
-
console.log(chalk.dim(" - .env (secrets)"));
|
|
313
|
-
console.log();
|
|
314
|
-
const gatewayUrl = `http://localhost:${gatewayPort}`;
|
|
315
|
-
console.log(chalk.cyan(" 3. Set DATABASE_URL in .env:"));
|
|
316
|
-
console.log(chalk.dim(" Lobu connects to a user-provided Postgres. Run one yourself"));
|
|
317
|
-
console.log(chalk.dim(" (managed instance or local: e.g. `brew services start postgresql`)\n"));
|
|
318
|
-
if (owlettoUrl) {
|
|
319
|
-
console.log(chalk.cyan(" Lobu memory:"));
|
|
320
|
-
console.log(chalk.dim(` ${owlettoUrl}`));
|
|
321
|
-
console.log(chalk.dim(" Run `lobu memory init` to configure local MCP clients.\n"));
|
|
322
|
-
}
|
|
323
|
-
console.log(chalk.cyan(" 4. Start the services:"));
|
|
324
|
-
console.log(chalk.dim(" npx @lobu/cli@latest run\n"));
|
|
325
|
-
console.log(chalk.cyan(" 5. Open the API docs:"));
|
|
326
|
-
console.log(chalk.dim(` ${gatewayUrl}/api/docs\n`));
|
|
327
|
-
console.log(chalk.cyan(" 6. Build with a coding agent:"));
|
|
328
|
-
console.log(chalk.dim(" Ask Codex or Claude Code to read AGENTS.md, lobu.toml, and agents/*/{IDENTITY,SOUL,USER}.md"));
|
|
329
|
-
console.log(chalk.dim(" Optional external skill: lobu-builder\n"));
|
|
330
|
-
console.log(chalk.cyan(" 7. Stop the services:"));
|
|
331
|
-
console.log(chalk.dim(" Ctrl+C in the terminal running `lobu run`\n"));
|
|
445
|
+
`;
|
|
446
|
+
function validateProjectName(value) {
|
|
447
|
+
if (!PROJECT_NAME_PATTERN.test(value)) {
|
|
448
|
+
return "Project name must be lowercase alphanumeric with hyphens only";
|
|
332
449
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
function slugify(s) {
|
|
453
|
+
return s
|
|
454
|
+
.toLowerCase()
|
|
455
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
456
|
+
.replace(/-+/g, "-")
|
|
457
|
+
.replace(/^-|-$/g, "");
|
|
458
|
+
}
|
|
459
|
+
async function promptOrDefault(opts) {
|
|
460
|
+
if (opts.flag !== undefined) {
|
|
461
|
+
if (opts.validate) {
|
|
462
|
+
const result = opts.validate(opts.flag);
|
|
463
|
+
if (result !== true) {
|
|
464
|
+
throw new Error(result);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return opts.flag;
|
|
336
468
|
}
|
|
469
|
+
if (opts.useDefaults)
|
|
470
|
+
return opts.defaultValue;
|
|
471
|
+
return opts.prompt();
|
|
472
|
+
}
|
|
473
|
+
async function promptBooleanOrDefault(opts) {
|
|
474
|
+
if (opts.flag !== undefined)
|
|
475
|
+
return opts.flag;
|
|
476
|
+
if (opts.useDefaults)
|
|
477
|
+
return opts.defaultValue;
|
|
478
|
+
return opts.prompt();
|
|
479
|
+
}
|
|
480
|
+
// Placeholder env-var refs for `--yes` mode; the user fills the values into .env.
|
|
481
|
+
const PLATFORM_PLACEHOLDERS = {
|
|
482
|
+
telegram: { botToken: "$TELEGRAM_BOT_TOKEN" },
|
|
483
|
+
slack: {
|
|
484
|
+
botToken: "$SLACK_BOT_TOKEN",
|
|
485
|
+
signingSecret: "$SLACK_SIGNING_SECRET",
|
|
486
|
+
},
|
|
487
|
+
discord: {
|
|
488
|
+
botToken: "$DISCORD_BOT_TOKEN",
|
|
489
|
+
applicationId: "$DISCORD_APPLICATION_ID",
|
|
490
|
+
publicKey: "$DISCORD_PUBLIC_KEY",
|
|
491
|
+
},
|
|
492
|
+
whatsapp: {
|
|
493
|
+
accessToken: "$WHATSAPP_ACCESS_TOKEN",
|
|
494
|
+
phoneNumberId: "$WHATSAPP_PHONE_NUMBER_ID",
|
|
495
|
+
verifyToken: "$WHATSAPP_WEBHOOK_VERIFY_TOKEN",
|
|
496
|
+
appSecret: "$WHATSAPP_APP_SECRET",
|
|
497
|
+
},
|
|
498
|
+
teams: {
|
|
499
|
+
appId: "$TEAMS_APP_ID",
|
|
500
|
+
appPassword: "$TEAMS_APP_PASSWORD",
|
|
501
|
+
appType: "MultiTenant",
|
|
502
|
+
},
|
|
503
|
+
gchat: { credentials: "$GOOGLE_CHAT_CREDENTIALS" },
|
|
504
|
+
};
|
|
505
|
+
function extractEnvVarRef(value) {
|
|
506
|
+
const match = value.match(/^\$([A-Z_][A-Z0-9_]*)$/);
|
|
507
|
+
return match?.[1] ?? null;
|
|
337
508
|
}
|
|
338
509
|
function humanizeSlug(slug) {
|
|
339
510
|
return slug
|
|
@@ -363,7 +534,7 @@ export async function generateLobuToml(projectDir, options) {
|
|
|
363
534
|
lines.push(`[[agents.${id}.providers]]`, `id = "${options.providerId}"`, ...(options.providerModel ? [`model = "${options.providerModel}"`] : []), `key = "$${options.providerEnvVar}"`);
|
|
364
535
|
}
|
|
365
536
|
else {
|
|
366
|
-
lines.push("# Add providers via the gateway configuration APIs or uncomment below:", `# [[agents.${id}.providers]]`, '# id = "
|
|
537
|
+
lines.push("# Add providers via the gateway configuration APIs or uncomment below:", `# [[agents.${id}.providers]]`, '# id = "openrouter"', '# key = "$OPENROUTER_API_KEY"');
|
|
367
538
|
}
|
|
368
539
|
lines.push("");
|
|
369
540
|
if (options.platformType && options.platformConfig) {
|
|
@@ -377,7 +548,9 @@ export async function generateLobuToml(projectDir, options) {
|
|
|
377
548
|
lines.push("# Chat platform (add via the gateway configuration APIs or uncomment below):", `# [[agents.${id}.platforms]]`, '# type = "telegram"', `# [agents.${id}.platforms.config]`, '# botToken = "$TELEGRAM_BOT_TOKEN"');
|
|
378
549
|
}
|
|
379
550
|
lines.push("", "# Local skills live in skills/<name>/SKILL.md or agents/<id>/skills/<name>/SKILL.md", `[agents.${id}.skills]`, "", "# MCP servers (add custom tool servers with optional OAuth):", `# [agents.${id}.skills.mcp.my-mcp]`, '# url = "https://my-mcp.example.com"', `# [agents.${id}.skills.mcp.my-mcp.oauth]`, '# auth_url = "https://auth.example.com/authorize"', '# token_url = "https://auth.example.com/token"', '# client_id = "$MY_MCP_CLIENT_ID"');
|
|
380
|
-
|
|
551
|
+
if (options.enableSlackPreview) {
|
|
552
|
+
lines.push("", "# Hosted preview — `lobu run` prints a `/lobu link <code>` you redeem by", "# DMing the hosted Lobu Slack bot. The block key is the chat platform;", "# `[agents.<id>.preview.telegram]` works the same way (redeem with `/link <code>`).", `[agents.${id}.preview.slack]`, "enabled = true", 'surfaces = ["dm"]', "code_ttl_minutes = 15");
|
|
553
|
+
}
|
|
381
554
|
lines.push("", `[agents.${id}.network]`);
|
|
382
555
|
if (options.allowedDomains) {
|
|
383
556
|
const domains = options.allowedDomains
|
|
@@ -389,14 +562,14 @@ export async function generateLobuToml(projectDir, options) {
|
|
|
389
562
|
else {
|
|
390
563
|
lines.push("allowed = []");
|
|
391
564
|
}
|
|
392
|
-
if (options.
|
|
393
|
-
const org = options.
|
|
394
|
-
const name = options.
|
|
395
|
-
lines.push("", "# Project-scoped Lobu memory", `[memory
|
|
396
|
-
? [`description = ${JSON.stringify(options.
|
|
565
|
+
if (options.includeLobuMemory) {
|
|
566
|
+
const org = options.lobuOrg ?? options.agentName;
|
|
567
|
+
const name = options.lobuName ?? humanizeSlug(options.agentName);
|
|
568
|
+
lines.push("", "# Project-scoped Lobu memory", `[memory]`, "enabled = true", `org = ${JSON.stringify(org)}`, `name = ${JSON.stringify(name)}`, ...(options.lobuDescription
|
|
569
|
+
? [`description = ${JSON.stringify(options.lobuDescription)}`]
|
|
397
570
|
: []), 'models = "./models"', 'data = "./data"');
|
|
398
571
|
}
|
|
399
|
-
lines.push("");
|
|
572
|
+
lines.push("");
|
|
400
573
|
await writeFile(join(projectDir, "lobu.toml"), lines.join("\n"));
|
|
401
574
|
}
|
|
402
575
|
async function getCliVersion() {
|