@lobu/cli 3.0.3 → 3.0.4
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 +8 -8
- package/dist/__tests__/login.test.d.ts +2 -0
- package/dist/__tests__/login.test.d.ts.map +1 -0
- package/dist/__tests__/login.test.js +173 -0
- package/dist/__tests__/login.test.js.map +1 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +3 -5
- package/dist/api/client.js.map +1 -1
- package/dist/api/context.d.ts +22 -0
- package/dist/api/context.d.ts.map +1 -0
- package/dist/api/context.js +113 -0
- package/dist/api/context.js.map +1 -0
- package/dist/api/credentials.d.ts +9 -4
- package/dist/api/credentials.d.ts.map +1 -1
- package/dist/api/credentials.js +127 -15
- package/dist/api/credentials.js.map +1 -1
- package/dist/commands/chat.d.ts +11 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +195 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/context.d.ts +8 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +46 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/dev.d.ts +1 -8
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +236 -57
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +351 -676
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +2 -8
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +283 -14
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +3 -1
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +5 -3
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/providers/add.d.ts.map +1 -1
- package/dist/commands/providers/add.js +38 -14
- package/dist/commands/providers/add.js.map +1 -1
- package/dist/commands/providers/list.d.ts.map +1 -1
- package/dist/commands/providers/list.js +4 -2
- package/dist/commands/providers/list.js.map +1 -1
- package/dist/commands/skills/add.d.ts.map +1 -1
- package/dist/commands/skills/add.js +25 -7
- package/dist/commands/skills/add.js.map +1 -1
- package/dist/commands/skills/info.d.ts.map +1 -1
- package/dist/commands/skills/info.js.map +1 -1
- package/dist/commands/skills/list.d.ts.map +1 -1
- package/dist/commands/skills/list.js.map +1 -1
- package/dist/commands/skills/registry.d.ts +5 -0
- package/dist/commands/skills/registry.d.ts.map +1 -1
- package/dist/commands/skills/registry.js.map +1 -1
- package/dist/commands/skills/search.d.ts.map +1 -1
- package/dist/commands/skills/search.js +3 -1
- package/dist/commands/skills/search.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +107 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +9 -20
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/whoami.d.ts +3 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +17 -3
- package/dist/commands/whoami.js.map +1 -1
- package/dist/config/agents-manifest.d.ts +92 -0
- package/dist/config/agents-manifest.d.ts.map +1 -0
- package/dist/config/agents-manifest.js +7 -0
- package/dist/config/agents-manifest.js.map +1 -0
- package/dist/config/loader.d.ts +18 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +62 -2
- package/dist/config/loader.js.map +1 -1
- package/dist/config/platform-schemas.d.ts +120 -0
- package/dist/config/platform-schemas.d.ts.map +1 -0
- package/dist/config/platform-schemas.js +97 -0
- package/dist/config/platform-schemas.js.map +1 -0
- package/dist/config/schema.d.ts +546 -111
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +26 -19
- package/dist/config/schema.js.map +1 -1
- package/dist/config/transformer.d.ts +3 -0
- package/dist/config/transformer.d.ts.map +1 -1
- package/dist/config/transformer.js +7 -10
- package/dist/config/transformer.js.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -10
- package/dist/index.js.map +1 -1
- package/dist/system-skills.json +89 -29
- package/dist/templates/.env.tmpl +3 -14
- package/dist/templates/.gitignore.tmpl +1 -0
- package/dist/templates/README.md.tmpl +7 -8
- package/dist/utils/markdown.d.ts +3 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/utils/markdown.js +10 -0
- package/dist/utils/markdown.js.map +1 -0
- package/package.json +4 -2
- package/dist/templates/lobu.toml.tmpl +0 -44
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lobu chat "prompt"` — send a prompt to an agent and stream the response.
|
|
3
|
+
*
|
|
4
|
+
* Requires `lobu dev` running. Connects to the local gateway,
|
|
5
|
+
* creates a session, sends the message, streams output, then exits.
|
|
6
|
+
*/
|
|
7
|
+
export declare function chatCommand(cwd: string, prompt: string, options: {
|
|
8
|
+
agent?: string;
|
|
9
|
+
gateway?: string;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=chat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,IAAI,CAAC,CAgFf"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { getToken } from "../api/credentials.js";
|
|
5
|
+
import { isLoadError, loadConfig } from "../config/loader.js";
|
|
6
|
+
import { renderMarkdown } from "../utils/markdown.js";
|
|
7
|
+
/**
|
|
8
|
+
* `lobu chat "prompt"` — send a prompt to an agent and stream the response.
|
|
9
|
+
*
|
|
10
|
+
* Requires `lobu dev` running. Connects to the local gateway,
|
|
11
|
+
* creates a session, sends the message, streams output, then exits.
|
|
12
|
+
*/
|
|
13
|
+
export async function chatCommand(cwd, prompt, options) {
|
|
14
|
+
const gatewayUrl = (options.gateway ?? (await resolveGatewayUrl(cwd))).replace(/\/$/, "");
|
|
15
|
+
// Resolve auth token: CLI JWT (from `lobu login`) → ADMIN_PASSWORD env var
|
|
16
|
+
const authToken = (await getToken()) ?? process.env.ADMIN_PASSWORD;
|
|
17
|
+
if (!authToken) {
|
|
18
|
+
console.error(chalk.red("\n Authentication required. Run `lobu login` or set ADMIN_PASSWORD.\n"));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
// Resolve agent ID from flag or first agent in lobu.toml
|
|
22
|
+
const agentId = options.agent ?? (await resolveAgentId(cwd));
|
|
23
|
+
// 1. Create agent session
|
|
24
|
+
const createRes = await fetch(`${gatewayUrl}/api/v1/agents`, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
Authorization: `Bearer ${authToken}`,
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify({ agentId }),
|
|
31
|
+
});
|
|
32
|
+
if (!createRes.ok) {
|
|
33
|
+
const body = await createRes.text().catch(() => "");
|
|
34
|
+
if (createRes.status === 401) {
|
|
35
|
+
console.error(chalk.red("\n Authentication required. Run `lobu login` or set ADMIN_PASSWORD.\n"));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
console.error(chalk.red(`\n Failed to create session (${createRes.status}): ${body}\n`));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const session = (await createRes.json());
|
|
42
|
+
// Build URLs from gateway flag (returned URLs may point to a public domain)
|
|
43
|
+
const base = `${gatewayUrl}/api/v1/agents/${session.agentId}`;
|
|
44
|
+
const sseUrl = `${base}/events`;
|
|
45
|
+
const messagesUrl = `${base}/messages`;
|
|
46
|
+
// 2. Open SSE connection before sending message so we don't miss events
|
|
47
|
+
const sseController = new AbortController();
|
|
48
|
+
const streaming = streamResponse(sseUrl, session.token, sseController);
|
|
49
|
+
// 3. Send the prompt
|
|
50
|
+
const msgRes = await fetch(messagesUrl, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: {
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
Authorization: `Bearer ${session.token}`,
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify({ content: prompt }),
|
|
57
|
+
});
|
|
58
|
+
if (!msgRes.ok) {
|
|
59
|
+
sseController.abort();
|
|
60
|
+
const body = await msgRes.text().catch(() => "");
|
|
61
|
+
console.error(chalk.red(`\n Failed to send message (${msgRes.status}): ${body}\n`));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
// 4. Wait for streaming to complete
|
|
65
|
+
await streaming;
|
|
66
|
+
}
|
|
67
|
+
async function resolveAgentId(cwd) {
|
|
68
|
+
const result = await loadConfig(cwd);
|
|
69
|
+
if (isLoadError(result)) {
|
|
70
|
+
console.error(chalk.red(`\n ${result.error}\n`));
|
|
71
|
+
return process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const ids = Object.keys(result.config.agents);
|
|
74
|
+
if (ids.length === 0) {
|
|
75
|
+
console.error(chalk.red("\n No agents found in lobu.toml\n"));
|
|
76
|
+
return process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
return ids[0];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Connect to SSE, print deltas to stdout, resolve on complete/error.
|
|
82
|
+
*/
|
|
83
|
+
async function streamResponse(sseUrl, token, controller) {
|
|
84
|
+
const OVERALL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
85
|
+
const IDLE_TIMEOUT_MS = 60 * 1000;
|
|
86
|
+
const overallTimer = setTimeout(() => controller.abort(), OVERALL_TIMEOUT_MS);
|
|
87
|
+
let idleTimer = setTimeout(() => controller.abort(), IDLE_TIMEOUT_MS);
|
|
88
|
+
const resetIdleTimer = () => {
|
|
89
|
+
clearTimeout(idleTimer);
|
|
90
|
+
idleTimer = setTimeout(() => controller.abort(), IDLE_TIMEOUT_MS);
|
|
91
|
+
};
|
|
92
|
+
try {
|
|
93
|
+
const res = await fetch(sseUrl, {
|
|
94
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
95
|
+
signal: controller.signal,
|
|
96
|
+
});
|
|
97
|
+
if (!res.ok || !res.body) {
|
|
98
|
+
console.error(chalk.red(`\n SSE connection failed (${res.status})\n`));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
const reader = res.body.getReader();
|
|
102
|
+
const decoder = new TextDecoder();
|
|
103
|
+
let buffer = "";
|
|
104
|
+
let currentEvent = "";
|
|
105
|
+
while (true) {
|
|
106
|
+
const { done, value } = await reader.read();
|
|
107
|
+
if (done)
|
|
108
|
+
break;
|
|
109
|
+
resetIdleTimer();
|
|
110
|
+
buffer += decoder.decode(value, { stream: true });
|
|
111
|
+
const lines = buffer.split("\n");
|
|
112
|
+
buffer = lines.pop() ?? "";
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
if (line.startsWith("event: ")) {
|
|
115
|
+
currentEvent = line.slice(7).trim();
|
|
116
|
+
}
|
|
117
|
+
else if (line.startsWith("data: ") && currentEvent) {
|
|
118
|
+
const data = parseJSON(line.slice(6));
|
|
119
|
+
if (!data)
|
|
120
|
+
continue;
|
|
121
|
+
switch (currentEvent) {
|
|
122
|
+
case "output":
|
|
123
|
+
if (typeof data.content === "string")
|
|
124
|
+
process.stdout.write(renderMarkdown(data.content));
|
|
125
|
+
break;
|
|
126
|
+
case "ephemeral":
|
|
127
|
+
if (typeof data.content === "string") {
|
|
128
|
+
console.error(`\n${renderMarkdown(data.content)}\n`);
|
|
129
|
+
}
|
|
130
|
+
controller.abort();
|
|
131
|
+
process.exit(1);
|
|
132
|
+
break;
|
|
133
|
+
case "complete":
|
|
134
|
+
process.stdout.write("\n");
|
|
135
|
+
controller.abort();
|
|
136
|
+
return;
|
|
137
|
+
case "error":
|
|
138
|
+
process.stdout.write("\n");
|
|
139
|
+
console.error(chalk.red(`\n Agent error: ${String(data.error)}\n`));
|
|
140
|
+
controller.abort();
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
currentEvent = "";
|
|
144
|
+
}
|
|
145
|
+
else if (line === "") {
|
|
146
|
+
currentEvent = "";
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
// AbortError is expected when we call controller.abort() on complete
|
|
153
|
+
if (err instanceof Error && err.name === "AbortError")
|
|
154
|
+
return;
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
clearTimeout(overallTimer);
|
|
159
|
+
clearTimeout(idleTimer);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function parseJSON(str) {
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(str);
|
|
165
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
166
|
+
return parsed;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function resolveGatewayUrl(cwd) {
|
|
175
|
+
try {
|
|
176
|
+
const envContent = await readFile(join(cwd, ".env"), "utf-8");
|
|
177
|
+
for (const line of envContent.split("\n")) {
|
|
178
|
+
const trimmed = line.trim();
|
|
179
|
+
if (trimmed.startsWith("GATEWAY_PORT=")) {
|
|
180
|
+
let port = trimmed.slice("GATEWAY_PORT=".length);
|
|
181
|
+
if ((port.startsWith('"') && port.endsWith('"')) ||
|
|
182
|
+
(port.startsWith("'") && port.endsWith("'"))) {
|
|
183
|
+
port = port.slice(1, -1);
|
|
184
|
+
}
|
|
185
|
+
if (port)
|
|
186
|
+
return `http://localhost:${port}`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// No .env file
|
|
192
|
+
}
|
|
193
|
+
return "http://localhost:8080";
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAW,EACX,MAAc,EACd,OAA6C;IAE7C,MAAM,UAAU,GAAG,CACjB,OAAO,CAAC,OAAO,IAAI,CAAC,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAClD,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAErB,2EAA2E;IAC3E,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,EAAE,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACnE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CACP,wEAAwE,CACzE,CACF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,yDAAyD;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IAE7D,0BAA0B;IAC1B,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,gBAAgB,EAAE;QAC3D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,SAAS,EAAE;SACrC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;KAClC,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,SAAS,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CACP,wEAAwE,CACzE,CACF,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,iCAAiC,SAAS,CAAC,MAAM,MAAM,IAAI,IAAI,CAAC,CAC3E,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,MAAM,SAAS,CAAC,IAAI,EAAE,CAGtC,CAAC;IAEF,4EAA4E;IAC5E,MAAM,IAAI,GAAG,GAAG,UAAU,kBAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAC9D,MAAM,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC;IAChC,MAAM,WAAW,GAAG,GAAG,IAAI,WAAW,CAAC;IAEvC,wEAAwE;IACxE,MAAM,aAAa,GAAG,IAAI,eAAe,EAAE,CAAC;IAC5C,MAAM,SAAS,GAAG,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAEvE,qBAAqB;IACrB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE;QACtC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,OAAO,CAAC,KAAK,EAAE;SACzC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;KAC1C,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,aAAa,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,+BAA+B,MAAM,CAAC,MAAM,MAAM,IAAI,IAAI,CAAC,CACtE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,oCAAoC;IACpC,MAAM,SAAS,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,GAAW;IACvC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;QAClD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC,CAAC;QAC/D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,GAAG,CAAC,CAAC,CAAE,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,cAAc,CAC3B,MAAc,EACd,KAAa,EACb,UAA2B;IAE3B,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IACzC,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,CAAC;IAElC,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,kBAAkB,CAAC,CAAC;IAC9E,IAAI,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,eAAe,CAAC,CAAC;IAEtE,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,YAAY,CAAC,SAAS,CAAC,CAAC;QACxB,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,eAAe,CAAC,CAAC;IACpE,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;YAC9B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;YAC7C,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,8BAA8B,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;YACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,YAAY,GAAG,EAAE,CAAC;QAEtB,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI;gBAAE,MAAM;YAEhB,cAAc,EAAE,CAAC;YACjB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YAE3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC/B,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBACtC,CAAC;qBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,YAAY,EAAE,CAAC;oBACrD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;oBACtC,IAAI,CAAC,IAAI;wBAAE,SAAS;oBAEpB,QAAQ,YAAY,EAAE,CAAC;wBACrB,KAAK,QAAQ;4BACX,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;gCAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;4BACrD,MAAM;wBACR,KAAK,WAAW;4BACd,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;gCACrC,OAAO,CAAC,KAAK,CAAC,KAAK,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;4BACvD,CAAC;4BACD,UAAU,CAAC,KAAK,EAAE,CAAC;4BACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;4BAChB,MAAM;wBACR,KAAK,UAAU;4BACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;4BAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;4BACnB,OAAO;wBACT,KAAK,OAAO;4BACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;4BAC3B,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CACtD,CAAC;4BACF,UAAU,CAAC,KAAK,EAAE,CAAC;4BACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBACpB,CAAC;oBAED,YAAY,GAAG,EAAE,CAAC;gBACpB,CAAC;qBAAM,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;oBACvB,YAAY,GAAG,EAAE,CAAC;gBACpB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,qEAAqE;QACrE,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO;QAC9D,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,YAAY,CAAC,CAAC;QAC3B,YAAY,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,OAAO,MAAiC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9D,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;gBACxC,IAAI,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;gBACjD,IACE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBAC5C,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC5C,CAAC;oBACD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC3B,CAAC;gBACD,IAAI,IAAI;oBAAE,OAAO,oBAAoB,IAAI,EAAE,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IACD,OAAO,uBAAuB,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function contextListCommand(): Promise<void>;
|
|
2
|
+
export declare function contextCurrentCommand(): Promise<void>;
|
|
3
|
+
export declare function contextAddCommand(options: {
|
|
4
|
+
name: string;
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
export declare function contextUseCommand(name: string): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AASA,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBxD;AAED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAU3D;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWnE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { addContext, getCurrentContextName, loadContextConfig, resolveContext, setCurrentContext, } from "../api/context.js";
|
|
3
|
+
export async function contextListCommand() {
|
|
4
|
+
const config = await loadContextConfig();
|
|
5
|
+
const currentContext = await getCurrentContextName();
|
|
6
|
+
console.log(chalk.bold("\n Lobu contexts"));
|
|
7
|
+
for (const [name, context] of Object.entries(config.contexts)) {
|
|
8
|
+
const marker = name === currentContext ? chalk.green(" *") : " ";
|
|
9
|
+
console.log(`${marker} ${name} ${chalk.dim(context.apiUrl)}`);
|
|
10
|
+
}
|
|
11
|
+
if (process.env.LOBU_CONTEXT || process.env.LOBU_API_URL) {
|
|
12
|
+
console.log(chalk.dim("\n Environment override is active."));
|
|
13
|
+
if (process.env.LOBU_CONTEXT) {
|
|
14
|
+
console.log(chalk.dim(` LOBU_CONTEXT=${process.env.LOBU_CONTEXT}`));
|
|
15
|
+
}
|
|
16
|
+
if (process.env.LOBU_API_URL) {
|
|
17
|
+
console.log(chalk.dim(` LOBU_API_URL=${process.env.LOBU_API_URL}`));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
console.log();
|
|
21
|
+
}
|
|
22
|
+
export async function contextCurrentCommand() {
|
|
23
|
+
const context = await resolveContext();
|
|
24
|
+
console.log(chalk.bold("\n Current context"));
|
|
25
|
+
console.log(chalk.dim(` Name: ${context.name}`));
|
|
26
|
+
console.log(chalk.dim(` API URL: ${context.apiUrl}`));
|
|
27
|
+
if (context.source === "env") {
|
|
28
|
+
console.log(chalk.dim(" Source: environment override"));
|
|
29
|
+
}
|
|
30
|
+
console.log();
|
|
31
|
+
}
|
|
32
|
+
export async function contextAddCommand(options) {
|
|
33
|
+
await addContext(options.name, options.apiUrl);
|
|
34
|
+
console.log(chalk.green(`\n Saved context ${options.name} -> ${options.apiUrl}\n`));
|
|
35
|
+
}
|
|
36
|
+
export async function contextUseCommand(name) {
|
|
37
|
+
const trimmedName = name.trim();
|
|
38
|
+
const config = await setCurrentContext(trimmedName);
|
|
39
|
+
const context = config.contexts[trimmedName];
|
|
40
|
+
if (!context) {
|
|
41
|
+
throw new Error(`Context ${trimmedName} was not found after update.`);
|
|
42
|
+
}
|
|
43
|
+
console.log(chalk.green(`\n Switched to context ${trimmedName}`));
|
|
44
|
+
console.log(chalk.dim(` API URL: ${context.apiUrl}\n`));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;IACzC,MAAM,cAAc,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAErD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,MAAM,MAAM,GAAG,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC,CAAC;QAC9D,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvD,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAGvC;IACC,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,qBAAqB,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,MAAM,IAAI,CAAC,CACxE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAE7C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,WAAW,WAAW,8BAA8B,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,2BAA2B,WAAW,EAAE,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
package/dist/commands/dev.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `lobu dev` — smart wrapper around `docker compose up`.
|
|
3
|
-
* Reads lobu.toml, seeds .env +
|
|
4
|
-
*
|
|
5
|
-
* Examples:
|
|
6
|
-
* lobu dev → docker compose up
|
|
7
|
-
* lobu dev -d → docker compose up -d
|
|
8
|
-
* lobu dev --build → docker compose up --build
|
|
9
|
-
* lobu dev -d --build → docker compose up -d --build
|
|
10
|
-
* lobu dev --force-recreate → docker compose up --force-recreate
|
|
3
|
+
* Reads lobu.toml, seeds .env + agents manifest, then passes all args through.
|
|
11
4
|
*/
|
|
12
5
|
export declare function devCommand(cwd: string, passthroughArgs: string[]): Promise<void>;
|
|
13
6
|
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC,CA0If"}
|
package/dist/commands/dev.js
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, join, resolve } from "node:path";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import ora from "ora";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { loadSkillsRegistry } from "../commands/skills/registry.js";
|
|
7
|
+
import { isLoadError, loadAgentMarkdown, loadConfig, loadSkillFiles, } from "../config/loader.js";
|
|
8
8
|
/**
|
|
9
9
|
* `lobu dev` — smart wrapper around `docker compose up`.
|
|
10
|
-
* Reads lobu.toml, seeds .env +
|
|
11
|
-
*
|
|
12
|
-
* Examples:
|
|
13
|
-
* lobu dev → docker compose up
|
|
14
|
-
* lobu dev -d → docker compose up -d
|
|
15
|
-
* lobu dev --build → docker compose up --build
|
|
16
|
-
* lobu dev -d --build → docker compose up -d --build
|
|
17
|
-
* lobu dev --force-recreate → docker compose up --force-recreate
|
|
10
|
+
* Reads lobu.toml, seeds .env + agents manifest, then passes all args through.
|
|
18
11
|
*/
|
|
19
12
|
export async function devCommand(cwd, passthroughArgs) {
|
|
20
13
|
const result = await loadConfig(cwd);
|
|
@@ -31,8 +24,9 @@ export async function devCommand(cwd, passthroughArgs) {
|
|
|
31
24
|
const { config } = result;
|
|
32
25
|
const spinner = ora("Preparing local dev environment...").start();
|
|
33
26
|
try {
|
|
34
|
-
const
|
|
35
|
-
|
|
27
|
+
const lobuDir = join(cwd, ".lobu");
|
|
28
|
+
await mkdir(lobuDir, { recursive: true });
|
|
29
|
+
// Parse .env first so we can resolve $VAR references in lobu.toml
|
|
36
30
|
const envPath = join(cwd, ".env");
|
|
37
31
|
let existingEnv = "";
|
|
38
32
|
try {
|
|
@@ -41,45 +35,64 @@ export async function devCommand(cwd, passthroughArgs) {
|
|
|
41
35
|
catch {
|
|
42
36
|
// No existing .env, start fresh
|
|
43
37
|
}
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.join("\n");
|
|
50
|
-
await writeFile(envPath, envContent + "\n");
|
|
51
|
-
// Write MCP config if needed
|
|
52
|
-
if (mcpConfig) {
|
|
53
|
-
const lobuDir = join(cwd, ".lobu");
|
|
54
|
-
await mkdir(lobuDir, { recursive: true });
|
|
55
|
-
await writeFile(join(lobuDir, "mcp.config.json"), JSON.stringify({ mcpServers: mcpConfig }, null, 2));
|
|
56
|
-
}
|
|
57
|
-
// Load IDENTITY.md, SOUL.md, USER.md if they exist
|
|
58
|
-
await seedMarkdownFiles(cwd, mergedVars);
|
|
38
|
+
const dotenvVars = parseEnvFile(existingEnv);
|
|
39
|
+
const { manifest, envVars } = await buildManifest(cwd, config.agents, dotenvVars);
|
|
40
|
+
await writeFile(join(lobuDir, "agents.json"), JSON.stringify(manifest, null, 2));
|
|
41
|
+
// Merge derived vars into existing .env (preserves comments and formatting)
|
|
42
|
+
await mergeEnvFile(envPath, existingEnv, envVars);
|
|
59
43
|
spinner.succeed("Environment prepared from lobu.toml");
|
|
60
44
|
// Check for docker-compose.yml
|
|
61
|
-
const composePath = join(cwd, "docker-compose.yml");
|
|
62
45
|
try {
|
|
63
|
-
await readFile(
|
|
46
|
+
await readFile(join(cwd, "docker-compose.yml"), "utf-8");
|
|
64
47
|
}
|
|
65
48
|
catch {
|
|
66
49
|
console.log(chalk.yellow("\n No docker-compose.yml found. Run `lobu init` to generate one.\n"));
|
|
67
50
|
process.exit(1);
|
|
68
51
|
}
|
|
69
|
-
|
|
70
|
-
console.log(chalk.cyan(`\n Starting ${
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
52
|
+
const fallbackPort = dotenvVars.GATEWAY_PORT || "8080";
|
|
53
|
+
console.log(chalk.cyan(`\n Starting ${manifest.agents.length} agent(s)...\n`));
|
|
54
|
+
const explicitDetach = passthroughArgs.includes("-d") || passthroughArgs.includes("--detach");
|
|
55
|
+
// Always start detached so we can print the banner before logs
|
|
56
|
+
const upArgs = explicitDetach
|
|
57
|
+
? ["compose", "up", ...passthroughArgs]
|
|
58
|
+
: ["compose", "up", "-d", ...passthroughArgs];
|
|
59
|
+
const up = spawn("docker", upArgs, { cwd, stdio: "inherit" });
|
|
60
|
+
up.on("error", (err) => {
|
|
77
61
|
console.error(chalk.red(`\n Failed to start docker compose: ${err.message}`));
|
|
78
62
|
console.log(chalk.dim(" Make sure Docker Desktop is running.\n"));
|
|
79
63
|
process.exit(1);
|
|
80
64
|
});
|
|
81
|
-
|
|
82
|
-
|
|
65
|
+
up.on("exit", (code) => {
|
|
66
|
+
if (code !== 0) {
|
|
67
|
+
process.exit(code ?? 1);
|
|
68
|
+
}
|
|
69
|
+
// Detect actual host port from the running container
|
|
70
|
+
const portProbe = spawn("docker", ["compose", "port", "gateway", "8080"], { cwd, stdio: ["ignore", "pipe", "ignore"] });
|
|
71
|
+
let portOutput = "";
|
|
72
|
+
portProbe.stdout.on("data", (data) => {
|
|
73
|
+
portOutput += data.toString();
|
|
74
|
+
});
|
|
75
|
+
portProbe.on("exit", () => {
|
|
76
|
+
const match = portOutput.trim().match(/:(\d+)$/);
|
|
77
|
+
const port = match ? match[1] : fallbackPort;
|
|
78
|
+
const gatewayUrl = `http://localhost:${port}`;
|
|
79
|
+
console.log(chalk.green("\n Lobu is running!\n"));
|
|
80
|
+
console.log(chalk.cyan(` Admin page: ${gatewayUrl}/agents`));
|
|
81
|
+
console.log(chalk.dim(`\n Stop: docker compose down`));
|
|
82
|
+
if (explicitDetach) {
|
|
83
|
+
console.log(chalk.dim(` View logs: docker compose logs -f gateway\n`));
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
console.log(chalk.dim(` Ctrl+C stops log tail, containers keep running.\n`));
|
|
87
|
+
// Tail only gateway logs (skip redis noise)
|
|
88
|
+
const logs = spawn("docker", ["compose", "logs", "-f", "gateway"], {
|
|
89
|
+
cwd,
|
|
90
|
+
stdio: "inherit",
|
|
91
|
+
});
|
|
92
|
+
logs.on("exit", (logCode) => {
|
|
93
|
+
process.exit(logCode ?? 0);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
83
96
|
});
|
|
84
97
|
}
|
|
85
98
|
catch (err) {
|
|
@@ -89,26 +102,156 @@ export async function devCommand(cwd, passthroughArgs) {
|
|
|
89
102
|
}
|
|
90
103
|
}
|
|
91
104
|
/**
|
|
92
|
-
*
|
|
93
|
-
* so the gateway can seed agent settings on startup.
|
|
105
|
+
* Build agents manifest and merged env vars from [agents.*] config.
|
|
94
106
|
*/
|
|
95
|
-
async function
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
async function buildManifest(cwd, agents, dotenvVars) {
|
|
108
|
+
const entries = [];
|
|
109
|
+
const rootSkillsDir = join(cwd, "skills");
|
|
110
|
+
const registrySkills = new Map(loadSkillsRegistry().map((skill) => [skill.id, skill]));
|
|
111
|
+
for (const [agentId, agentConfig] of Object.entries(agents)) {
|
|
112
|
+
const agentDir = resolve(cwd, agentConfig.dir);
|
|
113
|
+
const markdown = await loadAgentMarkdown(agentDir);
|
|
114
|
+
const skillFiles = await loadSkillFiles([
|
|
115
|
+
rootSkillsDir,
|
|
116
|
+
join(agentDir, "skills"),
|
|
117
|
+
]);
|
|
118
|
+
const systemSkills = agentConfig.skills.enabled
|
|
119
|
+
.map((skillId) => registrySkills.get(skillId))
|
|
120
|
+
.filter((skill) => !!skill)
|
|
121
|
+
.map((skill) => ({
|
|
122
|
+
repo: `system/${skill.id}`,
|
|
123
|
+
name: skill.name,
|
|
124
|
+
description: skill.description,
|
|
125
|
+
enabled: true,
|
|
126
|
+
system: true,
|
|
127
|
+
content: "",
|
|
128
|
+
integrations: skill.integrations?.map((integration) => ({
|
|
129
|
+
id: integration.id,
|
|
130
|
+
label: integration.label,
|
|
131
|
+
authType: integration.authType,
|
|
132
|
+
scopesConfig: integration.scopesConfig,
|
|
133
|
+
apiDomains: integration.apiDomains,
|
|
134
|
+
})),
|
|
135
|
+
mcpServers: skill.mcpServers?.map((mcp) => ({
|
|
136
|
+
id: mcp.id,
|
|
137
|
+
name: mcp.name,
|
|
138
|
+
url: mcp.url,
|
|
139
|
+
type: mcp.type,
|
|
140
|
+
command: mcp.command,
|
|
141
|
+
args: mcp.args,
|
|
142
|
+
})),
|
|
143
|
+
nixPackages: skill.nixPackages,
|
|
144
|
+
permissions: skill.permissions,
|
|
145
|
+
providers: skill.providers?.length ? [skill.id] : undefined,
|
|
146
|
+
}));
|
|
147
|
+
const localSkills = skillFiles.map((skillFile) => ({
|
|
148
|
+
repo: `local/${skillFile.name}`,
|
|
149
|
+
name: skillFile.name,
|
|
150
|
+
content: skillFile.content,
|
|
151
|
+
enabled: true,
|
|
152
|
+
}));
|
|
153
|
+
const entry = {
|
|
154
|
+
agentId,
|
|
155
|
+
name: agentConfig.name,
|
|
156
|
+
description: agentConfig.description,
|
|
157
|
+
settings: { ...markdown },
|
|
158
|
+
};
|
|
159
|
+
if (agentConfig.providers.length > 0) {
|
|
160
|
+
entry.settings.installedProviders = agentConfig.providers.map((p) => ({
|
|
161
|
+
providerId: p.id,
|
|
162
|
+
}));
|
|
163
|
+
entry.settings.modelSelection = { mode: "auto" };
|
|
164
|
+
const providerModelPreferences = Object.fromEntries(agentConfig.providers
|
|
165
|
+
.filter((provider) => !!provider.model?.trim())
|
|
166
|
+
.map((provider) => [provider.id, provider.model.trim()]));
|
|
167
|
+
if (Object.keys(providerModelPreferences).length > 0) {
|
|
168
|
+
entry.settings.providerModelPreferences = providerModelPreferences;
|
|
106
169
|
}
|
|
107
170
|
}
|
|
108
|
-
|
|
109
|
-
|
|
171
|
+
if (systemSkills.length > 0 || localSkills.length > 0) {
|
|
172
|
+
entry.settings.skillsConfig = {
|
|
173
|
+
skills: [...systemSkills, ...localSkills],
|
|
174
|
+
};
|
|
110
175
|
}
|
|
176
|
+
if (agentConfig.network) {
|
|
177
|
+
entry.settings.networkConfig = {
|
|
178
|
+
allowedDomains: agentConfig.network.allowed,
|
|
179
|
+
deniedDomains: agentConfig.network.denied,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (agentConfig.worker?.nix_packages?.length) {
|
|
183
|
+
entry.settings.nixConfig = {
|
|
184
|
+
packages: agentConfig.worker.nix_packages,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (agentConfig.skills.mcp) {
|
|
188
|
+
const mcpServers = {};
|
|
189
|
+
for (const [id, mcp] of Object.entries(agentConfig.skills.mcp)) {
|
|
190
|
+
const mapped = { ...mcp };
|
|
191
|
+
if (mcp.oauth) {
|
|
192
|
+
mapped.oauth = {
|
|
193
|
+
authUrl: mcp.oauth.auth_url,
|
|
194
|
+
tokenUrl: mcp.oauth.token_url,
|
|
195
|
+
clientId: resolveEnvVar(mcp.oauth.client_id || "", dotenvVars),
|
|
196
|
+
clientSecret: resolveEnvVar(mcp.oauth.client_secret || "", dotenvVars),
|
|
197
|
+
scopes: mcp.oauth.scopes,
|
|
198
|
+
tokenEndpointAuthMethod: mcp.oauth.token_endpoint_auth_method,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Resolve env vars in MCP env block
|
|
202
|
+
if (mcp.env) {
|
|
203
|
+
mapped.env = Object.fromEntries(Object.entries(mcp.env).map(([k, v]) => [
|
|
204
|
+
k,
|
|
205
|
+
resolveEnvVar(v, dotenvVars),
|
|
206
|
+
]));
|
|
207
|
+
}
|
|
208
|
+
mcpServers[id] = mapped;
|
|
209
|
+
}
|
|
210
|
+
entry.settings.mcpServers = mcpServers;
|
|
211
|
+
}
|
|
212
|
+
// Resolve provider credentials from $ENV_VAR references
|
|
213
|
+
const credentials = agentConfig.providers
|
|
214
|
+
.filter((p) => p.key)
|
|
215
|
+
.map((p) => ({
|
|
216
|
+
providerId: p.id,
|
|
217
|
+
key: resolveEnvVar(p.key, dotenvVars),
|
|
218
|
+
}))
|
|
219
|
+
.filter((c) => c.key); // skip if env var not found
|
|
220
|
+
if (credentials.length > 0) {
|
|
221
|
+
entry.credentials = credentials;
|
|
222
|
+
}
|
|
223
|
+
// Resolve connection configs from $ENV_VAR references
|
|
224
|
+
const connections = agentConfig.connections
|
|
225
|
+
.map((conn) => ({
|
|
226
|
+
type: conn.type,
|
|
227
|
+
config: Object.fromEntries(Object.entries(conn.config).map(([k, v]) => [
|
|
228
|
+
k,
|
|
229
|
+
resolveEnvVar(v, dotenvVars),
|
|
230
|
+
])),
|
|
231
|
+
}))
|
|
232
|
+
.filter((conn) => Object.values(conn.config).every((v) => v !== "")); // skip if any env var missing
|
|
233
|
+
if (connections.length > 0) {
|
|
234
|
+
entry.connections = connections;
|
|
235
|
+
}
|
|
236
|
+
entries.push(entry);
|
|
237
|
+
}
|
|
238
|
+
const envVars = {
|
|
239
|
+
COMPOSE_PROJECT_NAME: entries[0]?.name
|
|
240
|
+
? entries[0].name.toLowerCase().replace(/\s+/g, "-")
|
|
241
|
+
: basename(cwd),
|
|
242
|
+
};
|
|
243
|
+
return { manifest: { version: 1, agents: entries }, envVars };
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Resolve a value that may be a $ENV_VAR reference.
|
|
247
|
+
* Returns the resolved value, or empty string if the env var is not set.
|
|
248
|
+
*/
|
|
249
|
+
function resolveEnvVar(value, envVars) {
|
|
250
|
+
if (value.startsWith("$")) {
|
|
251
|
+
const varName = value.slice(1);
|
|
252
|
+
return envVars[varName] || process.env[varName] || "";
|
|
111
253
|
}
|
|
254
|
+
return value;
|
|
112
255
|
}
|
|
113
256
|
function parseEnvFile(content) {
|
|
114
257
|
const vars = {};
|
|
@@ -120,9 +263,45 @@ function parseEnvFile(content) {
|
|
|
120
263
|
if (eqIdx === -1)
|
|
121
264
|
continue;
|
|
122
265
|
const key = trimmed.slice(0, eqIdx);
|
|
123
|
-
|
|
266
|
+
let value = trimmed.slice(eqIdx + 1);
|
|
267
|
+
// Strip surrounding quotes (double or single)
|
|
268
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
269
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
270
|
+
value = value.slice(1, -1);
|
|
271
|
+
}
|
|
124
272
|
vars[key] = value;
|
|
125
273
|
}
|
|
126
274
|
return vars;
|
|
127
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Merge derived env vars into an existing .env file.
|
|
278
|
+
* Updates existing keys in-place and appends new ones at the end.
|
|
279
|
+
* Preserves comments, blank lines, and formatting.
|
|
280
|
+
*/
|
|
281
|
+
async function mergeEnvFile(envPath, existingContent, newVars) {
|
|
282
|
+
const remaining = { ...newVars };
|
|
283
|
+
const lines = existingContent.split("\n");
|
|
284
|
+
const updated = lines.map((line) => {
|
|
285
|
+
const trimmed = line.trim();
|
|
286
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
287
|
+
return line;
|
|
288
|
+
const eqIdx = trimmed.indexOf("=");
|
|
289
|
+
if (eqIdx === -1)
|
|
290
|
+
return line;
|
|
291
|
+
const key = trimmed.slice(0, eqIdx);
|
|
292
|
+
if (key in remaining) {
|
|
293
|
+
const val = remaining[key];
|
|
294
|
+
delete remaining[key];
|
|
295
|
+
return `${key}=${val}`;
|
|
296
|
+
}
|
|
297
|
+
return line;
|
|
298
|
+
});
|
|
299
|
+
// Append any new vars that weren't already in the file
|
|
300
|
+
for (const [key, value] of Object.entries(remaining)) {
|
|
301
|
+
updated.push(`${key}=${value}`);
|
|
302
|
+
}
|
|
303
|
+
// Ensure trailing newline
|
|
304
|
+
const content = `${updated.join("\n").trimEnd()}\n`;
|
|
305
|
+
await writeFile(envPath, content);
|
|
306
|
+
}
|
|
128
307
|
//# sourceMappingURL=dev.js.map
|