lunel-cli 0.1.23 → 0.1.27
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/dist/index.js +100 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { WebSocket } from "ws";
|
|
3
3
|
import qrcode from "qrcode-terminal";
|
|
4
|
-
import {
|
|
4
|
+
import { createOpencodeServer, createOpencodeClient } from "@opencode-ai/sdk";
|
|
5
|
+
import * as crypto from "crypto";
|
|
5
6
|
import Ignore from "ignore";
|
|
6
7
|
const ignore = Ignore.default;
|
|
7
8
|
import * as fs from "fs/promises";
|
|
@@ -1176,18 +1177,36 @@ function requireData(response, label) {
|
|
|
1176
1177
|
const errMsg = response.error
|
|
1177
1178
|
? (typeof response.error === "string" ? response.error : JSON.stringify(response.error))
|
|
1178
1179
|
: `${label} returned no data`;
|
|
1180
|
+
console.error(`[ai] ${label} failed:`, errMsg, "raw response:", JSON.stringify(response).substring(0, 500));
|
|
1179
1181
|
throw new Error(errMsg);
|
|
1180
1182
|
}
|
|
1181
1183
|
return response.data;
|
|
1182
1184
|
}
|
|
1183
1185
|
async function handleAiCreateSession(payload) {
|
|
1184
1186
|
const title = payload.title || undefined;
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
+
console.log("[ai] createSession called, title:", title);
|
|
1188
|
+
try {
|
|
1189
|
+
const response = await opencodeClient.session.create({ body: { title } });
|
|
1190
|
+
console.log("[ai] createSession response ok:", !!response.data, "error:", response.error ? JSON.stringify(response.error).substring(0, 200) : "none");
|
|
1191
|
+
return { session: requireData(response, "session.create") };
|
|
1192
|
+
}
|
|
1193
|
+
catch (err) {
|
|
1194
|
+
console.error("[ai] createSession exception:", err.message, err.stack);
|
|
1195
|
+
throw err;
|
|
1196
|
+
}
|
|
1187
1197
|
}
|
|
1188
1198
|
async function handleAiListSessions() {
|
|
1189
|
-
|
|
1190
|
-
|
|
1199
|
+
console.log("[ai] listSessions called");
|
|
1200
|
+
try {
|
|
1201
|
+
const response = await opencodeClient.session.list();
|
|
1202
|
+
const data = requireData(response, "session.list");
|
|
1203
|
+
console.log("[ai] listSessions returned", Array.isArray(data) ? data.length : typeof data, "sessions");
|
|
1204
|
+
return { sessions: data };
|
|
1205
|
+
}
|
|
1206
|
+
catch (err) {
|
|
1207
|
+
console.error("[ai] listSessions exception:", err.message);
|
|
1208
|
+
throw err;
|
|
1209
|
+
}
|
|
1191
1210
|
}
|
|
1192
1211
|
async function handleAiGetSession(payload) {
|
|
1193
1212
|
const id = payload.id;
|
|
@@ -1203,14 +1222,22 @@ async function handleAiDeleteSession(payload) {
|
|
|
1203
1222
|
}
|
|
1204
1223
|
async function handleAiGetMessages(payload) {
|
|
1205
1224
|
const id = payload.id;
|
|
1206
|
-
|
|
1207
|
-
|
|
1225
|
+
console.log("[ai] getMessages called, sessionId:", id);
|
|
1226
|
+
try {
|
|
1227
|
+
const response = await opencodeClient.session.messages({ path: { id } });
|
|
1228
|
+
return { messages: requireData(response, "session.messages") };
|
|
1229
|
+
}
|
|
1230
|
+
catch (err) {
|
|
1231
|
+
console.error("[ai] getMessages exception:", err.message);
|
|
1232
|
+
throw err;
|
|
1233
|
+
}
|
|
1208
1234
|
}
|
|
1209
1235
|
async function handleAiPrompt(payload) {
|
|
1210
1236
|
const sessionId = payload.sessionId;
|
|
1211
1237
|
const text = payload.text;
|
|
1212
1238
|
const model = payload.model;
|
|
1213
1239
|
const agent = payload.agent;
|
|
1240
|
+
console.log("[ai] prompt called, sessionId:", sessionId, "model:", JSON.stringify(model), "agent:", agent, "text:", text.substring(0, 100));
|
|
1214
1241
|
// Fire and forget — results stream via SSE events forwarded on data channel
|
|
1215
1242
|
opencodeClient.session.prompt({
|
|
1216
1243
|
path: { id: sessionId },
|
|
@@ -1220,6 +1247,7 @@ async function handleAiPrompt(payload) {
|
|
|
1220
1247
|
...(agent ? { agent } : {}),
|
|
1221
1248
|
},
|
|
1222
1249
|
}).catch((err) => {
|
|
1250
|
+
console.error("[ai] prompt error:", err.message);
|
|
1223
1251
|
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
1224
1252
|
dataChannel.send(JSON.stringify({
|
|
1225
1253
|
v: 1,
|
|
@@ -1241,13 +1269,30 @@ async function handleAiAbort(payload) {
|
|
|
1241
1269
|
return {};
|
|
1242
1270
|
}
|
|
1243
1271
|
async function handleAiAgents() {
|
|
1244
|
-
|
|
1245
|
-
|
|
1272
|
+
console.log("[ai] getAgents called");
|
|
1273
|
+
try {
|
|
1274
|
+
const response = await opencodeClient.app.agents();
|
|
1275
|
+
const data = requireData(response, "app.agents");
|
|
1276
|
+
console.log("[ai] getAgents returned:", JSON.stringify(data).substring(0, 300));
|
|
1277
|
+
return { agents: data };
|
|
1278
|
+
}
|
|
1279
|
+
catch (err) {
|
|
1280
|
+
console.error("[ai] getAgents exception:", err.message);
|
|
1281
|
+
throw err;
|
|
1282
|
+
}
|
|
1246
1283
|
}
|
|
1247
1284
|
async function handleAiProviders() {
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1285
|
+
console.log("[ai] getProviders called");
|
|
1286
|
+
try {
|
|
1287
|
+
const response = await opencodeClient.config.providers();
|
|
1288
|
+
const data = requireData(response, "config.providers");
|
|
1289
|
+
console.log("[ai] getProviders returned", data.providers?.length, "providers, defaults:", JSON.stringify(data.default));
|
|
1290
|
+
return { providers: data.providers, default: data.default };
|
|
1291
|
+
}
|
|
1292
|
+
catch (err) {
|
|
1293
|
+
console.error("[ai] getProviders exception:", err.message);
|
|
1294
|
+
throw err;
|
|
1295
|
+
}
|
|
1251
1296
|
}
|
|
1252
1297
|
async function handleAiSetAuth(payload) {
|
|
1253
1298
|
const providerId = payload.providerId;
|
|
@@ -1310,7 +1355,19 @@ async function handleAiPermissionReply(payload) {
|
|
|
1310
1355
|
async function subscribeToOpenCodeEvents(client) {
|
|
1311
1356
|
try {
|
|
1312
1357
|
const events = await client.event.subscribe();
|
|
1313
|
-
for await (const
|
|
1358
|
+
for await (const raw of events.stream) {
|
|
1359
|
+
// OpenCode SSE payload shapes vary across versions:
|
|
1360
|
+
// { type, properties, ... }
|
|
1361
|
+
// { payload: { type, properties, ... }, directory: "..." }
|
|
1362
|
+
const parsed = raw;
|
|
1363
|
+
const base = parsed?.payload && typeof parsed.payload === "object"
|
|
1364
|
+
? parsed.payload
|
|
1365
|
+
: parsed;
|
|
1366
|
+
if (!base || typeof base.type !== "string") {
|
|
1367
|
+
console.warn("[sse] Dropped malformed event:", JSON.stringify(parsed).substring(0, 200));
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
console.log("[sse]", base.type);
|
|
1314
1371
|
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
1315
1372
|
const msg = {
|
|
1316
1373
|
v: 1,
|
|
@@ -1318,16 +1375,19 @@ async function subscribeToOpenCodeEvents(client) {
|
|
|
1318
1375
|
ns: "ai",
|
|
1319
1376
|
action: "event",
|
|
1320
1377
|
payload: {
|
|
1321
|
-
type:
|
|
1322
|
-
properties:
|
|
1378
|
+
type: base.type,
|
|
1379
|
+
properties: base.properties || {},
|
|
1323
1380
|
},
|
|
1324
1381
|
};
|
|
1325
1382
|
dataChannel.send(JSON.stringify(msg));
|
|
1326
1383
|
}
|
|
1327
1384
|
}
|
|
1385
|
+
// Stream ended normally — reconnect
|
|
1386
|
+
console.log("[sse] Event stream ended, reconnecting...");
|
|
1387
|
+
setTimeout(() => subscribeToOpenCodeEvents(client), 1000);
|
|
1328
1388
|
}
|
|
1329
1389
|
catch (err) {
|
|
1330
|
-
console.error("
|
|
1390
|
+
console.error("[sse] Event stream error:", err.message);
|
|
1331
1391
|
setTimeout(() => subscribeToOpenCodeEvents(client), 3000);
|
|
1332
1392
|
}
|
|
1333
1393
|
}
|
|
@@ -1710,6 +1770,7 @@ async function processMessage(message) {
|
|
|
1710
1770
|
}
|
|
1711
1771
|
catch (error) {
|
|
1712
1772
|
const err = error;
|
|
1773
|
+
console.error(`[router] ${ns}.${action} error:`, err.code || "ERROR", err.message);
|
|
1713
1774
|
return {
|
|
1714
1775
|
v: 1,
|
|
1715
1776
|
id,
|
|
@@ -1879,9 +1940,28 @@ async function main() {
|
|
|
1879
1940
|
console.log("Lunel CLI v" + VERSION);
|
|
1880
1941
|
console.log("=".repeat(20) + "\n");
|
|
1881
1942
|
try {
|
|
1882
|
-
//
|
|
1943
|
+
// Generate auth credentials (like CodeNomad does)
|
|
1944
|
+
const opencodeUsername = "lunel";
|
|
1945
|
+
const opencodePassword = crypto.randomBytes(32).toString("base64url");
|
|
1946
|
+
const authHeader = `Basic ${Buffer.from(`${opencodeUsername}:${opencodePassword}`).toString("base64")}`;
|
|
1947
|
+
// Set auth env vars BEFORE spawning opencode
|
|
1948
|
+
process.env.OPENCODE_SERVER_USERNAME = opencodeUsername;
|
|
1949
|
+
process.env.OPENCODE_SERVER_PASSWORD = opencodePassword;
|
|
1950
|
+
// Start OpenCode server with random port (like CodeNomad: --port 0)
|
|
1883
1951
|
console.log("Starting OpenCode...");
|
|
1884
|
-
const
|
|
1952
|
+
const server = await createOpencodeServer({
|
|
1953
|
+
hostname: "127.0.0.1",
|
|
1954
|
+
port: 0,
|
|
1955
|
+
timeout: 15000,
|
|
1956
|
+
});
|
|
1957
|
+
console.log(`OpenCode server listening on ${server.url}`);
|
|
1958
|
+
// Create client with auth headers
|
|
1959
|
+
const client = createOpencodeClient({
|
|
1960
|
+
baseUrl: server.url,
|
|
1961
|
+
headers: {
|
|
1962
|
+
"Authorization": authHeader,
|
|
1963
|
+
},
|
|
1964
|
+
});
|
|
1885
1965
|
opencodeClient = client;
|
|
1886
1966
|
console.log("OpenCode ready.\n");
|
|
1887
1967
|
// Subscribe to OpenCode events
|
|
@@ -1893,6 +1973,8 @@ async function main() {
|
|
|
1893
1973
|
catch (error) {
|
|
1894
1974
|
if (error instanceof Error) {
|
|
1895
1975
|
console.error(`Error: ${error.message}`);
|
|
1976
|
+
if (error.stack)
|
|
1977
|
+
console.error(error.stack);
|
|
1896
1978
|
}
|
|
1897
1979
|
else {
|
|
1898
1980
|
console.error("An unexpected error occurred");
|