@lasso-ai/cli 1.0.29 → 1.1.1

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.
@@ -181,6 +181,34 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
181
181
  let activeAgentController = null;
182
182
  const taskControllers = new Map();
183
183
  let localAgents = new Set();
184
+ const pageIgnoredDirs = new Set(["node_modules", ".git", ".next", "dist", "build", ".turbo", "out", "coverage", ".vercel", ".cache"]);
185
+ const resolveInsideProject = (root, relative) => {
186
+ const target = node_path_1.default.resolve(root, relative && relative !== "." ? relative : "");
187
+ return target === root || target.startsWith(`${root}${node_path_1.default.sep}`) ? target : null;
188
+ };
189
+ // Directories are returned first so the overlay can render an explorer-style
190
+ // tree that mirrors how VS Code orders folders above files.
191
+ const pageEntries = (root, relative) => {
192
+ const directory = resolveInsideProject(root, relative);
193
+ if (!directory)
194
+ return [];
195
+ let entries = [];
196
+ try {
197
+ for (const entry of node_fs_1.default.readdirSync(directory, { withFileTypes: true })) {
198
+ if (entry.name.startsWith(".") || (entry.isDirectory() && pageIgnoredDirs.has(entry.name)))
199
+ continue;
200
+ entries.push({
201
+ name: entry.name,
202
+ path: relative && relative !== "." ? node_path_1.default.join(relative, entry.name) : entry.name,
203
+ type: entry.isDirectory() ? "directory" : "file",
204
+ });
205
+ }
206
+ }
207
+ catch {
208
+ // Folders that cannot be read are treated as empty.
209
+ }
210
+ return entries.sort((a, b) => a.type === b.type ? a.name.localeCompare(b.name) : a.type === "directory" ? -1 : 1);
211
+ };
184
212
  bridgeServer.on("request", (req, res) => {
185
213
  if (req.method !== "POST" || req.url?.split("?", 1)[0] !== "/__lasso/bridge/restart")
186
214
  return;
@@ -284,6 +312,10 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
284
312
  if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
285
313
  socket.send(JSON.stringify({ type: "agent_status", taskId: request.taskId, status: "working", message, detail }));
286
314
  }
315
+ }, (prompt) => {
316
+ if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
317
+ socket.send(JSON.stringify({ type: "agent_prompt", taskId: request.taskId, prompt }));
318
+ }
287
319
  })
288
320
  .then((proposal) => {
289
321
  if (controller.signal.aborted || socket.readyState !== socket.OPEN)
@@ -319,6 +351,69 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
319
351
  else {
320
352
  console.log("[lasso] received from overlay:", msg);
321
353
  }
354
+ if (msg.type === "list_page_folders") {
355
+ const requested = msg.path || ".";
356
+ const root = node_path_1.default.resolve(cwd);
357
+ const directory = resolveInsideProject(root, requested);
358
+ if (!directory) {
359
+ socket.send(JSON.stringify({ type: "page_folders", path: requested, entries: [], error: "That folder is outside the current project." }));
360
+ return;
361
+ }
362
+ socket.send(JSON.stringify({ type: "page_folders", path: requested, entries: pageEntries(root, requested), project: node_path_1.default.basename(root) }));
363
+ return;
364
+ }
365
+ if (msg.type === "create_page_folder") {
366
+ const root = node_path_1.default.resolve(cwd);
367
+ const name = node_path_1.default.basename(msg.name || "");
368
+ const parent = resolveInsideProject(root, msg.parent || ".");
369
+ if (!parent || !name || name !== msg.name || name === "." || name === "..") {
370
+ socket.send(JSON.stringify({ type: "page_folder_created", path: msg.name || "", error: "Enter a folder name without slashes or dots." }));
371
+ return;
372
+ }
373
+ const target = node_path_1.default.resolve(parent, name);
374
+ if (!target.startsWith(`${root}${node_path_1.default.sep}`)) {
375
+ socket.send(JSON.stringify({ type: "page_folder_created", path: name, error: "Choose a folder inside the current project." }));
376
+ return;
377
+ }
378
+ try {
379
+ node_fs_1.default.mkdirSync(target);
380
+ socket.send(JSON.stringify({ type: "page_folder_created", path: node_path_1.default.relative(root, target) }));
381
+ }
382
+ catch (error) {
383
+ socket.send(JSON.stringify({
384
+ type: "page_folder_created",
385
+ path: node_path_1.default.relative(root, target),
386
+ error: error instanceof Error && error.message.includes("EEXIST") ? "A folder with that name already exists." : "The folder could not be created.",
387
+ }));
388
+ }
389
+ return;
390
+ }
391
+ if (msg.type === "create_page") {
392
+ const root = node_path_1.default.resolve(cwd);
393
+ const folder = resolveInsideProject(root, msg.folder || ".");
394
+ // A page name may contain "/" so the overlay can create nested routes in
395
+ // one step, but every segment is validated and resolved inside the project.
396
+ const segments = (msg.fileName || "").split("/").map((segment) => segment.trim()).filter(Boolean);
397
+ const fileName = segments.pop() || "";
398
+ if (!folder || !fileName || segments.some((segment) => segment === "." || segment === "..") || fileName === "." || fileName === "..") {
399
+ socket.send(JSON.stringify({ type: "page_created", path: msg.fileName || "", error: "Choose a valid project folder and file name." }));
400
+ return;
401
+ }
402
+ const target = node_path_1.default.resolve(folder, ...segments, fileName);
403
+ if (!target.startsWith(`${root}${node_path_1.default.sep}`)) {
404
+ socket.send(JSON.stringify({ type: "page_created", path: msg.fileName, error: "Choose a valid project folder and file name." }));
405
+ return;
406
+ }
407
+ try {
408
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(target), { recursive: true });
409
+ node_fs_1.default.writeFileSync(target, msg.content.slice(0, 200000), { encoding: "utf8", flag: "wx" });
410
+ socket.send(JSON.stringify({ type: "page_created", path: node_path_1.default.relative(root, target) }));
411
+ }
412
+ catch (error) {
413
+ socket.send(JSON.stringify({ type: "page_created", path: node_path_1.default.relative(root, target), error: error instanceof Error && error.message.includes("EEXIST") ? "A file with that name already exists." : "The page could not be created." }));
414
+ }
415
+ return;
416
+ }
322
417
  if (msg.type === "ask") {
323
418
  const localProvider = msg.provider === "cli";
324
419
  const cliProvider = msg.model.startsWith("claude-code:") ? "claude-code" : msg.model.startsWith("opencode:") ? "opencode" : "codex";
@@ -337,9 +432,12 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
337
432
  ? { provider: cliProvider, model: msg.model }
338
433
  : { ...agentConfig, provider: (msg.provider || agentConfig.provider), model: msg.model };
339
434
  socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "working", message: "Working…" }));
340
- void (0, agent_1.answerQuestion)(cwd, { question: msg.question, context: msg.context, element: msg.element, messages: msg.messages }, selectedConfig, controller.signal, (message, detail) => {
435
+ void (0, agent_1.answerQuestion)(cwd, { taskId: msg.taskId, question: msg.question, context: msg.context, element: msg.element, messages: msg.messages }, selectedConfig, controller.signal, (message, detail) => {
341
436
  if (!controller.signal.aborted && socket.readyState === socket.OPEN)
342
437
  socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "working", message, detail }));
438
+ }, (prompt) => {
439
+ if (!controller.signal.aborted && socket.readyState === socket.OPEN)
440
+ socket.send(JSON.stringify({ type: "agent_prompt", taskId: msg.taskId, prompt }));
343
441
  }).then((answer) => {
344
442
  if (!controller.signal.aborted)
345
443
  socket.send(JSON.stringify({ type: "assistant_message", taskId: msg.taskId, message: answer }));
@@ -370,6 +468,9 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
370
468
  reviewRefreshAttempts.set(msg.taskId, 0);
371
469
  runEditReview(msg, selectedConfig);
372
470
  }
471
+ else if (msg.type === "agent_prompt_response") {
472
+ (0, agent_1.respondToAgentPrompt)(msg.taskId, msg.response);
473
+ }
373
474
  else if (msg.type === "stop") {
374
475
  if (msg.taskId)
375
476
  taskControllers.get(msg.taskId)?.abort();