@lasso-ai/cli 1.2.6 → 1.3.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.
- package/dist/cli/agent.js +175 -31
- package/dist/cli/bridge.d.ts +9 -2
- package/dist/cli/bridge.js +199 -50
- package/dist/cli/host/next-host-entry.js +1 -1
- package/dist/cli/host/vite-host-entry.js +1 -1
- package/dist/cli/index.js +18 -1
- package/dist/cli/oneshot.d.ts +0 -1
- package/dist/cli/oneshot.js +137 -75
- package/dist/cli/project.d.ts +28 -0
- package/dist/cli/project.js +57 -0
- package/dist/overlay.js +49 -1
- package/package.json +1 -1
package/dist/cli/oneshot.js
CHANGED
|
@@ -3,13 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isConversationalPrompt = isConversationalPrompt;
|
|
7
6
|
exports.runOneShotAgent = runOneShotAgent;
|
|
8
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
9
|
const node_child_process_1 = require("node:child_process");
|
|
11
10
|
const node_util_1 = require("node:util");
|
|
12
11
|
const agent_1 = require("./agent");
|
|
12
|
+
const auth_1 = require("./auth");
|
|
13
|
+
const framework_1 = require("./utils/framework");
|
|
13
14
|
const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
|
|
14
15
|
const CLI_PROVIDERS = new Set(["claude-code", "codex", "opencode", "cursor"]);
|
|
15
16
|
function isCliProvider(provider) {
|
|
@@ -40,28 +41,46 @@ function gatewayTargetForCli(provider, model) {
|
|
|
40
41
|
return { provider: "google", model: GATEWAY_MODELS.google };
|
|
41
42
|
return { provider: "anthropic", model: GATEWAY_MODELS.anthropic };
|
|
42
43
|
}
|
|
43
|
-
function isConversationalPrompt(prompt) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
44
|
+
async function isConversationalPrompt(prompt, config, apiKey) {
|
|
45
|
+
// Use the user's selected model to intelligently detect if prompt is conversational vs. action-oriented
|
|
46
|
+
try {
|
|
47
|
+
// Use the gateway to classify - this respects the user's selected model
|
|
48
|
+
const response = await fetch(`${config.serverUrl || "https://api.lasso.byorello.space"}/api/v1/agent/generate`, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
"Authorization": `Bearer ${apiKey || ""}`,
|
|
53
|
+
},
|
|
54
|
+
body: JSON.stringify({
|
|
55
|
+
model: {
|
|
56
|
+
id: config.model,
|
|
57
|
+
provider: config.provider,
|
|
58
|
+
},
|
|
59
|
+
prompt: `Classify this user prompt as either "conversational" or "action". Return ONLY the word "conversational" or "action" - nothing else.
|
|
60
|
+
|
|
61
|
+
Prompt: "${prompt}"`,
|
|
62
|
+
maxTokens: 10,
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
if (response.ok) {
|
|
66
|
+
const data = await response.json();
|
|
67
|
+
const classification = data.reply?.toLowerCase().trim();
|
|
68
|
+
console.log('[Lasso] Classification result:', classification);
|
|
69
|
+
return classification === "conversational";
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log('[Lasso] Classification failed:', response.status);
|
|
73
|
+
}
|
|
58
74
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (questionOnly && !actionVerbs.test(p)) {
|
|
62
|
-
return true;
|
|
75
|
+
catch (err) {
|
|
76
|
+
console.log('[Lasso] Classification error:', err);
|
|
63
77
|
}
|
|
64
|
-
|
|
78
|
+
// Fallback: smarter heuristic
|
|
79
|
+
const p = prompt.trim().toLowerCase();
|
|
80
|
+
const actionKeywords = /(add|create|build|make|fix|update|modify|change|refactor|implement|delete|remove|style|install|wire|code|replace|insert|append|generate)/i;
|
|
81
|
+
const isAction = actionKeywords.test(p);
|
|
82
|
+
console.log('[Lasso] Fallback classification:', isAction ? 'action' : 'conversational');
|
|
83
|
+
return !isAction;
|
|
65
84
|
}
|
|
66
85
|
function runtimeErrorObservation(pageContext) {
|
|
67
86
|
const errors = Array.isArray(pageContext?.runtimeErrors) ? pageContext.runtimeErrors : [];
|
|
@@ -113,7 +132,10 @@ Do not output code changes, file patches, or JSON schemas — just talk to the d
|
|
|
113
132
|
return "Hello! I'm Lasso, your AI pair programmer. You can ask me questions about your project or tell me what to build, modify, or fix. Run `lasso auth login` (or add a provider API key in settings) to unlock AI replies.";
|
|
114
133
|
}
|
|
115
134
|
try {
|
|
116
|
-
const model = config.model
|
|
135
|
+
const model = config.model;
|
|
136
|
+
if (!model) {
|
|
137
|
+
return "No model selected. Please select a model in settings.";
|
|
138
|
+
}
|
|
117
139
|
if (config.provider === "openai" || config.provider === "ollama" || config.provider === "nvidia") {
|
|
118
140
|
const baseUrl = config.baseUrl || (config.provider === "nvidia" ? "https://integrate.api.nvidia.com/v1" : "https://api.openai.com/v1");
|
|
119
141
|
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
@@ -184,9 +206,10 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
184
206
|
if (signal.aborted) {
|
|
185
207
|
return { ok: false, error: "Operation was cancelled" };
|
|
186
208
|
}
|
|
187
|
-
const isConversational = isConversationalPrompt(prompt);
|
|
188
|
-
|
|
189
|
-
|
|
209
|
+
const isConversational = await isConversationalPrompt(prompt, config, apiKey);
|
|
210
|
+
console.log('[Lasso] Is conversational:', isConversational);
|
|
211
|
+
// Browser inspection & snapshot steps - skip for simple conversational queries
|
|
212
|
+
if (pageContext && !isConversational) {
|
|
190
213
|
onProgress("thinking", "Inspecting browser page...", `Page: "${pageContext.title || "Landing page"}" (${pageContext.route || "/"})`);
|
|
191
214
|
if (pageContext.viewport) {
|
|
192
215
|
onProgress("thinking", "Capturing visual snapshot...", `Viewport: ${pageContext.viewport.width}x${pageContext.viewport.height} (Document height: ${pageContext.viewport.scrollHeight}px)`);
|
|
@@ -201,8 +224,9 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
201
224
|
else {
|
|
202
225
|
onProgress("thinking", isConversational ? "Thinking..." : "Analyzing project structure...", isConversational ? undefined : "Scanning files and dependencies");
|
|
203
226
|
}
|
|
204
|
-
|
|
205
|
-
const
|
|
227
|
+
// Skip project structure analysis for simple conversational queries
|
|
228
|
+
const projectStructure = isConversational ? null : await analyzeProject(cwd);
|
|
229
|
+
const framework = isConversational ? null : (0, framework_1.detectFramework)(cwd);
|
|
206
230
|
if (!isConversational && !pageContext) {
|
|
207
231
|
onProgress("thinking", "Inspecting application...", `Detected ${framework} framework`);
|
|
208
232
|
}
|
|
@@ -216,15 +240,18 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
216
240
|
}
|
|
217
241
|
const targetServerUrl = rawServerUrl.replace(/\/$/, "").replace(/\/api\/v1$/, "");
|
|
218
242
|
const useCliAgent = isCliProvider(config.provider);
|
|
219
|
-
|
|
243
|
+
// Prioritize CLI auth credentials over env vars
|
|
244
|
+
const cliCredentials = (0, auth_1.loadCredentials)();
|
|
245
|
+
const gatewayKey = cliCredentials?.apiKey || apiKey || config.lassoKey || (config.apiKey && (config.apiKey.startsWith("lss_live_") || config.apiKey.startsWith("lss_")) ? config.apiKey : "") || process.env.LASSO_API_KEY || "";
|
|
220
246
|
const hasLocalProviderKey = Boolean(config.apiKey &&
|
|
221
247
|
config.apiKey !== "ollama" &&
|
|
222
248
|
!config.apiKey.startsWith("lss_live_") &&
|
|
223
249
|
!config.apiKey.startsWith("lss_"));
|
|
250
|
+
// If the user has CLI auth, always route to server (unless using local agents)
|
|
224
251
|
// If the user does NOT have a local provider key in .env, route to Lasso Agent Gateway on server.
|
|
225
252
|
// When they DO have a local key in .env, it runs locally to save server resources!
|
|
226
253
|
// Ollama always runs locally (the server cannot reach the user's localhost).
|
|
227
|
-
const shouldRouteToServer = !useCliAgent && config.provider !== "ollama" && !hasLocalProviderKey && Boolean(targetServerUrl);
|
|
254
|
+
const shouldRouteToServer = !useCliAgent && config.provider !== "ollama" && (Boolean(cliCredentials) || !hasLocalProviderKey) && Boolean(targetServerUrl);
|
|
228
255
|
const callGateway = async (target) => {
|
|
229
256
|
try {
|
|
230
257
|
if (!isConversational && !pageContext) {
|
|
@@ -241,10 +268,15 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
241
268
|
const controller = new AbortController();
|
|
242
269
|
const onAbort = () => controller.abort();
|
|
243
270
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
271
|
+
// Add a 5-minute timeout for gateway requests
|
|
272
|
+
const timeout = setTimeout(() => {
|
|
273
|
+
controller.abort();
|
|
274
|
+
}, 5 * 60 * 1000);
|
|
275
|
+
timeout.unref();
|
|
244
276
|
// Build sourceHints: read relevant file contents to give the AI real code context.
|
|
245
277
|
// This is what enables code changes instead of "please share the file" responses.
|
|
246
278
|
const sourceHints = {};
|
|
247
|
-
if (!isConversational) {
|
|
279
|
+
if (!isConversational && projectStructure) {
|
|
248
280
|
const promptLower = prompt.toLowerCase();
|
|
249
281
|
const allCssFiles = projectStructure.files.filter((f) => /\.(css|scss)$/i.test(f));
|
|
250
282
|
const entryFiles = projectStructure.files.filter((f) => /(App|page|index|layout|main|hero|header|landing)\.(tsx|jsx|js|ts|html)$/i.test(f));
|
|
@@ -274,30 +306,48 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
274
306
|
provider: target.provider,
|
|
275
307
|
},
|
|
276
308
|
context: {
|
|
277
|
-
framework,
|
|
278
|
-
hasTypeScript: projectStructure
|
|
279
|
-
hasTailwind: projectStructure
|
|
280
|
-
hasReact: projectStructure
|
|
281
|
-
hasNext: projectStructure
|
|
282
|
-
hasVite: projectStructure
|
|
283
|
-
dependencies: projectStructure
|
|
284
|
-
devDependencies: projectStructure
|
|
285
|
-
files: projectStructure
|
|
309
|
+
framework: framework || undefined,
|
|
310
|
+
hasTypeScript: projectStructure?.hasTypeScript,
|
|
311
|
+
hasTailwind: projectStructure?.hasTailwind,
|
|
312
|
+
hasReact: projectStructure?.hasReact,
|
|
313
|
+
hasNext: projectStructure?.hasNext,
|
|
314
|
+
hasVite: projectStructure?.hasVite,
|
|
315
|
+
dependencies: projectStructure?.dependencies,
|
|
316
|
+
devDependencies: projectStructure?.devDependencies,
|
|
317
|
+
files: projectStructure?.files.slice(0, 100) || [],
|
|
318
|
+
directories: projectStructure?.directories.slice(0, 50) || [],
|
|
319
|
+
sourceRoot: projectStructure?.sourceRoot,
|
|
320
|
+
appRouter: projectStructure?.appRouter,
|
|
321
|
+
pagesRouter: projectStructure?.pagesRouter,
|
|
322
|
+
multipleAppDirs: projectStructure?.multipleAppDirs,
|
|
286
323
|
sourceHints: Object.keys(sourceHints).length > 0 ? sourceHints : undefined,
|
|
287
324
|
currentRoute: pageContext?.route || "/",
|
|
288
325
|
pageContext,
|
|
289
326
|
},
|
|
290
327
|
}),
|
|
291
328
|
});
|
|
329
|
+
clearTimeout(timeout);
|
|
292
330
|
signal.removeEventListener("abort", onAbort);
|
|
331
|
+
if (signal.aborted) {
|
|
332
|
+
return { ok: false, error: "Request timed out after 5 minutes. Your plan quota may be exhausted. Please upgrade your plan or add credits to continue using this service." };
|
|
333
|
+
}
|
|
293
334
|
if (!response.ok) {
|
|
294
335
|
const errData = (await response.json().catch(() => ({})));
|
|
295
336
|
const errorMessage = errData.error || `Agent request failed (${response.status})`;
|
|
337
|
+
// Check for quota/plan exhaustion
|
|
338
|
+
if (/quota exceeded|quota limit|out of quota|no quota|plan.*limit|plan.*exhausted|credit.*exhausted|billing.*quota|rate.*limit|429|too many requests|request.*limit|insufficient.*credits|no.*credits|credits.*exhausted|payment.*required|subscription.*required|upgrade.*required|upgrade.*plan|billing.*required/i.test(errorMessage)) {
|
|
339
|
+
return { ok: false, error: "Your plan quota has been exhausted. Please upgrade your plan or add credits to continue using this service." };
|
|
340
|
+
}
|
|
296
341
|
return { ok: false, error: errorMessage };
|
|
297
342
|
}
|
|
298
343
|
const data = (await response.json());
|
|
299
344
|
if (data.status === "failed" || data.error) {
|
|
300
|
-
|
|
345
|
+
const error = data.error || data.summary || "Agent session failed on server.";
|
|
346
|
+
// Check for quota/plan exhaustion
|
|
347
|
+
if (/quota exceeded|quota limit|out of quota|no quota|plan.*limit|plan.*exhausted|credit.*exhausted|billing.*quota|rate.*limit|429|too many requests|request.*limit|insufficient.*credits|no.*credits|credits.*exhausted|payment.*required|subscription.*required|upgrade.*required|upgrade.*plan|billing.*required/i.test(error)) {
|
|
348
|
+
return { ok: false, error: "Your plan quota has been exhausted. Please upgrade your plan or add credits to continue using this service." };
|
|
349
|
+
}
|
|
350
|
+
return { ok: false, error };
|
|
301
351
|
}
|
|
302
352
|
if (data) {
|
|
303
353
|
const changes = Array.isArray(data.changes) ? data.changes : [];
|
|
@@ -422,7 +472,7 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
422
472
|
}
|
|
423
473
|
if (useCliAgent && isConversational) {
|
|
424
474
|
try {
|
|
425
|
-
const reply = await generateConversationalReply(cwd, prompt, framework, config, signal, messages, pageContext, (message, detail) => onProgress("thinking", message, detail), taskId, (agentPrompt) => onPrompt({ question: agentPrompt.message, options: agentPrompt.options }));
|
|
475
|
+
const reply = await generateConversationalReply(cwd, prompt, framework || "", config, signal, messages, pageContext, (message, detail) => onProgress("thinking", message, detail), taskId, (agentPrompt) => onPrompt({ question: agentPrompt.message, options: agentPrompt.options }));
|
|
426
476
|
return {
|
|
427
477
|
ok: true,
|
|
428
478
|
summary: reply,
|
|
@@ -443,7 +493,7 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
443
493
|
}
|
|
444
494
|
// If it's a conversational prompt and server was not used or failed, respond directly without modifying files!
|
|
445
495
|
if (isConversational) {
|
|
446
|
-
const reply = await generateConversationalReply(cwd, prompt, framework, config, signal, messages, pageContext);
|
|
496
|
+
const reply = await generateConversationalReply(cwd, prompt, framework || "", config, signal, messages, pageContext);
|
|
447
497
|
return {
|
|
448
498
|
ok: true,
|
|
449
499
|
summary: reply,
|
|
@@ -454,7 +504,7 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
454
504
|
}
|
|
455
505
|
// Phase 2: Local agent generation fallback for code changes
|
|
456
506
|
onProgress("thinking", "Generating implementation plan...", `Planning components for ${prompt}`);
|
|
457
|
-
const plan = await generatePlan(cwd, prompt, scope, projectStructure, framework, config, signal, messages, pageContext);
|
|
507
|
+
const plan = await generatePlan(cwd, prompt, scope, projectStructure || { framework: "", hasTypeScript: false, hasTailwind: false, hasReact: false, hasNext: false, hasVite: false, dependencies: [], devDependencies: [], files: [], directories: [], sourceRoot: ".", appRouter: false, pagesRouter: false, multipleAppDirs: [] }, framework || "", config, signal, messages, pageContext);
|
|
458
508
|
if (signal.aborted) {
|
|
459
509
|
return { ok: false, error: "Operation was cancelled during planning" };
|
|
460
510
|
}
|
|
@@ -549,24 +599,33 @@ async function analyzeProject(cwd) {
|
|
|
549
599
|
const hasReact = dependencies.includes("react") || devDependencies.includes("react");
|
|
550
600
|
const hasNext = dependencies.includes("next") || devDependencies.includes("next");
|
|
551
601
|
const hasVite = dependencies.includes("vite") || devDependencies.includes("vite");
|
|
552
|
-
// Scan
|
|
602
|
+
// Scan directory structure
|
|
553
603
|
const files = [];
|
|
554
|
-
const
|
|
604
|
+
const directories = [];
|
|
605
|
+
const appDirs = [];
|
|
606
|
+
const pagesDirs = [];
|
|
607
|
+
const roots = ["src", "app", "pages", ".", "web", "lib", "components"];
|
|
555
608
|
for (const root of roots) {
|
|
556
609
|
const rootPath = node_path_1.default.join(cwd, root);
|
|
557
610
|
if (!node_fs_1.default.existsSync(rootPath))
|
|
558
611
|
continue;
|
|
559
612
|
const scanDir = (dir, base = "", depth = 0) => {
|
|
560
|
-
if (depth >
|
|
613
|
+
if (depth > 5)
|
|
561
614
|
return;
|
|
562
615
|
try {
|
|
563
616
|
const entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
564
617
|
for (const entry of entries) {
|
|
565
|
-
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build") {
|
|
618
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".next") {
|
|
566
619
|
continue;
|
|
567
620
|
}
|
|
568
621
|
const rel = base ? node_path_1.default.join(base, entry.name) : entry.name;
|
|
569
622
|
if (entry.isDirectory()) {
|
|
623
|
+
directories.push(rel);
|
|
624
|
+
// Track app and pages directories
|
|
625
|
+
if (entry.name === "app")
|
|
626
|
+
appDirs.push(rel);
|
|
627
|
+
if (entry.name === "pages")
|
|
628
|
+
pagesDirs.push(rel);
|
|
570
629
|
scanDir(node_path_1.default.join(dir, entry.name), rel, depth + 1);
|
|
571
630
|
}
|
|
572
631
|
else if (/\.(tsx?|jsx?|vue|svelte|css|html|json)$/i.test(entry.name)) {
|
|
@@ -579,44 +638,40 @@ async function analyzeProject(cwd) {
|
|
|
579
638
|
}
|
|
580
639
|
};
|
|
581
640
|
scanDir(rootPath);
|
|
582
|
-
if (files.length >
|
|
641
|
+
if (files.length > 100)
|
|
583
642
|
break;
|
|
584
643
|
}
|
|
644
|
+
// Determine source root
|
|
645
|
+
let sourceRoot = ".";
|
|
646
|
+
if (directories.includes("src/app"))
|
|
647
|
+
sourceRoot = "src";
|
|
648
|
+
else if (directories.includes("app"))
|
|
649
|
+
sourceRoot = ".";
|
|
650
|
+
else if (directories.includes("src"))
|
|
651
|
+
sourceRoot = "src";
|
|
652
|
+
// Detect App Router vs Pages Router
|
|
653
|
+
const appRouter = appDirs.length > 0 && appDirs.some(dir => {
|
|
654
|
+
const appPath = node_path_1.default.join(cwd, dir);
|
|
655
|
+
return node_fs_1.default.existsSync(node_path_1.default.join(appPath, "layout.tsx")) || node_fs_1.default.existsSync(node_path_1.default.join(appPath, "page.tsx"));
|
|
656
|
+
});
|
|
657
|
+
const pagesRouter = pagesDirs.length > 0 && node_fs_1.default.existsSync(node_path_1.default.join(cwd, "pages", "_app.tsx")) || node_fs_1.default.existsSync(node_path_1.default.join(cwd, "pages", "_document.tsx"));
|
|
585
658
|
return {
|
|
586
|
-
framework: detectFramework(cwd),
|
|
659
|
+
framework: (0, framework_1.detectFramework)(cwd),
|
|
587
660
|
hasTypeScript,
|
|
588
661
|
hasTailwind,
|
|
589
662
|
hasReact,
|
|
590
663
|
hasNext,
|
|
591
664
|
hasVite,
|
|
592
|
-
dependencies,
|
|
593
|
-
devDependencies,
|
|
665
|
+
dependencies: dependencies,
|
|
666
|
+
devDependencies: devDependencies,
|
|
594
667
|
files,
|
|
668
|
+
directories,
|
|
669
|
+
sourceRoot,
|
|
670
|
+
appRouter,
|
|
671
|
+
pagesRouter,
|
|
672
|
+
multipleAppDirs: appDirs,
|
|
595
673
|
};
|
|
596
674
|
}
|
|
597
|
-
function detectFramework(cwd) {
|
|
598
|
-
const pkgPath = node_path_1.default.join(cwd, "package.json");
|
|
599
|
-
if (node_fs_1.default.existsSync(pkgPath)) {
|
|
600
|
-
try {
|
|
601
|
-
const pkg = JSON.parse(node_fs_1.default.readFileSync(pkgPath, "utf8"));
|
|
602
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
603
|
-
if (deps.next)
|
|
604
|
-
return "next";
|
|
605
|
-
if (deps.vite)
|
|
606
|
-
return "vite";
|
|
607
|
-
if (deps.react)
|
|
608
|
-
return "react";
|
|
609
|
-
if (deps.vue)
|
|
610
|
-
return "vue";
|
|
611
|
-
if (deps.svelte)
|
|
612
|
-
return "svelte";
|
|
613
|
-
}
|
|
614
|
-
catch {
|
|
615
|
-
// fallback
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
return "unknown";
|
|
619
|
-
}
|
|
620
675
|
async function generatePlan(cwd, prompt, _scope, structure, framework, config, signal, _messages, pageContext) {
|
|
621
676
|
const steps = [];
|
|
622
677
|
// Identify relevant files to supply as context.
|
|
@@ -711,7 +766,14 @@ Analyze the existing code structure, then respond in JSON:`;
|
|
|
711
766
|
};
|
|
712
767
|
}
|
|
713
768
|
try {
|
|
714
|
-
const model = config.model
|
|
769
|
+
const model = config.model;
|
|
770
|
+
if (!model) {
|
|
771
|
+
return {
|
|
772
|
+
steps: [],
|
|
773
|
+
isConversational: true,
|
|
774
|
+
reply: "No model selected. Please select a model in settings.",
|
|
775
|
+
};
|
|
776
|
+
}
|
|
715
777
|
let text = "";
|
|
716
778
|
if (config.provider === "openai" || config.provider === "ollama" || config.provider === "nvidia") {
|
|
717
779
|
const baseUrl = config.baseUrl || (config.provider === "nvidia" ? "https://integrate.api.nvidia.com/v1" : "https://api.openai.com/v1");
|
package/dist/cli/project.d.ts
CHANGED
|
@@ -15,6 +15,24 @@ export interface CollabConfig {
|
|
|
15
15
|
plan?: string;
|
|
16
16
|
configuredProviders?: string[];
|
|
17
17
|
}
|
|
18
|
+
export interface WorkspaceConfig {
|
|
19
|
+
workspaceId?: string;
|
|
20
|
+
workspaceName?: string;
|
|
21
|
+
plan?: string;
|
|
22
|
+
configuredProviders?: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface ModelInfo {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
provider: string;
|
|
28
|
+
capabilities?: string[];
|
|
29
|
+
}
|
|
30
|
+
export interface ModelsResponse {
|
|
31
|
+
anthropic: ModelInfo[];
|
|
32
|
+
openai: ModelInfo[];
|
|
33
|
+
google: ModelInfo[];
|
|
34
|
+
nvidia: ModelInfo[];
|
|
35
|
+
}
|
|
18
36
|
export interface InitResult {
|
|
19
37
|
ok: boolean;
|
|
20
38
|
error?: string;
|
|
@@ -57,3 +75,13 @@ export declare function initProject(cwd: string, fileEnv: Record<string, string>
|
|
|
57
75
|
* safely degraded (no collaboration, local editing unaffected) on any failure.
|
|
58
76
|
*/
|
|
59
77
|
export declare function resolveCollabSession(cwd: string, fileEnv: Record<string, string>, projectId: string): Promise<CollabConfig>;
|
|
78
|
+
/**
|
|
79
|
+
* Fetch workspace configuration from the server using CLI auth credentials.
|
|
80
|
+
* This allows users to see their dashboard-configured providers even without a project config.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveWorkspaceConfig(fileEnv: Record<string, string>): Promise<WorkspaceConfig>;
|
|
83
|
+
/**
|
|
84
|
+
* Fetch available models from the server's models endpoint.
|
|
85
|
+
* This fetches from provider APIs dynamically and caches results.
|
|
86
|
+
*/
|
|
87
|
+
export declare function fetchModels(fileEnv: Record<string, string>): Promise<ModelsResponse>;
|
package/dist/cli/project.js
CHANGED
|
@@ -10,6 +10,8 @@ exports.resolveLassoApiKey = resolveLassoApiKey;
|
|
|
10
10
|
exports.readProjectConfig = readProjectConfig;
|
|
11
11
|
exports.initProject = initProject;
|
|
12
12
|
exports.resolveCollabSession = resolveCollabSession;
|
|
13
|
+
exports.resolveWorkspaceConfig = resolveWorkspaceConfig;
|
|
14
|
+
exports.fetchModels = fetchModels;
|
|
13
15
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
17
|
const node_readline_1 = __importDefault(require("node:readline"));
|
|
@@ -351,3 +353,58 @@ async function resolveCollabSession(cwd, fileEnv, projectId) {
|
|
|
351
353
|
return config;
|
|
352
354
|
}
|
|
353
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Fetch workspace configuration from the server using CLI auth credentials.
|
|
358
|
+
* This allows users to see their dashboard-configured providers even without a project config.
|
|
359
|
+
*/
|
|
360
|
+
async function resolveWorkspaceConfig(fileEnv) {
|
|
361
|
+
const serverUrl = (0, auth_1.serverUrlFrom)(fileEnv).replace(/\/$/, "");
|
|
362
|
+
const apiUrl = `${serverUrl}/api/v1`;
|
|
363
|
+
const apiKey = resolveLassoApiKey(fileEnv);
|
|
364
|
+
if (!apiKey) {
|
|
365
|
+
return {};
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
const response = await (0, auth_1.fetchWithTimeout)(`${apiUrl}/auth/workspace`, {
|
|
369
|
+
method: "GET",
|
|
370
|
+
headers: { authorization: `Bearer ${apiKey}` },
|
|
371
|
+
});
|
|
372
|
+
if (!response.ok) {
|
|
373
|
+
return {};
|
|
374
|
+
}
|
|
375
|
+
const body = (await response.json());
|
|
376
|
+
if (!body.success || !body.workspace) {
|
|
377
|
+
return {};
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
workspaceId: body.workspace.id,
|
|
381
|
+
workspaceName: body.workspace.name,
|
|
382
|
+
plan: body.workspace.plan || "free",
|
|
383
|
+
configuredProviders: body.workspace.configuredProviders || [],
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
return {};
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Fetch available models from the server's models endpoint.
|
|
392
|
+
* This fetches from provider APIs dynamically and caches results.
|
|
393
|
+
*/
|
|
394
|
+
async function fetchModels(fileEnv) {
|
|
395
|
+
const serverUrl = (0, auth_1.serverUrlFrom)(fileEnv).replace(/\/$/, "");
|
|
396
|
+
const apiUrl = `${serverUrl}/api/v1`;
|
|
397
|
+
try {
|
|
398
|
+
const response = await (0, auth_1.fetchWithTimeout)(`${apiUrl}/models`, {
|
|
399
|
+
method: "GET",
|
|
400
|
+
});
|
|
401
|
+
if (!response.ok) {
|
|
402
|
+
return { anthropic: [], openai: [], google: [], nvidia: [] };
|
|
403
|
+
}
|
|
404
|
+
const body = await response.json();
|
|
405
|
+
return body;
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
return { anthropic: [], openai: [], google: [], nvidia: [] };
|
|
409
|
+
}
|
|
410
|
+
}
|
package/dist/overlay.js
CHANGED
|
@@ -7848,6 +7848,8 @@
|
|
|
7848
7848
|
return `${Math.floor(seconds / 86400)}d`;
|
|
7849
7849
|
}
|
|
7850
7850
|
var modelSessionKey = "lasso:selected-model";
|
|
7851
|
+
var chatHistoryKey = "lasso:chat-history";
|
|
7852
|
+
var changesHistoryKey = "lasso:changes-history";
|
|
7851
7853
|
function storedModelId() {
|
|
7852
7854
|
try {
|
|
7853
7855
|
return window.sessionStorage.getItem(modelSessionKey);
|
|
@@ -7861,6 +7863,22 @@
|
|
|
7861
7863
|
} catch {
|
|
7862
7864
|
}
|
|
7863
7865
|
}
|
|
7866
|
+
function loadChatHistory() {
|
|
7867
|
+
try {
|
|
7868
|
+
const chatHistory = JSON.parse(localStorage.getItem(chatHistoryKey) || "[]");
|
|
7869
|
+
const changesHistory = JSON.parse(localStorage.getItem(changesHistoryKey) || "[]");
|
|
7870
|
+
return { chatHistory, changesHistory };
|
|
7871
|
+
} catch {
|
|
7872
|
+
return { chatHistory: [], changesHistory: [] };
|
|
7873
|
+
}
|
|
7874
|
+
}
|
|
7875
|
+
function saveChatHistory(chatHistory, changesHistory) {
|
|
7876
|
+
try {
|
|
7877
|
+
localStorage.setItem(chatHistoryKey, JSON.stringify(chatHistory));
|
|
7878
|
+
localStorage.setItem(changesHistoryKey, JSON.stringify(changesHistory));
|
|
7879
|
+
} catch {
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7864
7882
|
var OverlayState = class {
|
|
7865
7883
|
// Mode & Selection
|
|
7866
7884
|
selectMode = false;
|
|
@@ -21611,7 +21629,24 @@
|
|
|
21611
21629
|
recordTaskActivity(task.id, message.message || "Done.");
|
|
21612
21630
|
}
|
|
21613
21631
|
const isPromptTask = !taskId2 || taskId2 === state.promptTaskId;
|
|
21614
|
-
|
|
21632
|
+
let result = message.message || "Done.";
|
|
21633
|
+
if (message.type === "applied" && message.validation) {
|
|
21634
|
+
const { passed, errors, command } = message.validation;
|
|
21635
|
+
if (!passed && errors.length > 0) {
|
|
21636
|
+
result = `${message.message}
|
|
21637
|
+
|
|
21638
|
+
\u26A0\uFE0F Build validation failed after running \`${command}\`:
|
|
21639
|
+
|
|
21640
|
+
${errors.slice(0, 5).join("\n")}${errors.length > 5 ? `
|
|
21641
|
+
...and ${errors.length - 5} more errors` : ""}
|
|
21642
|
+
|
|
21643
|
+
You may need to manually fix these errors or undo the change.`;
|
|
21644
|
+
} else if (passed) {
|
|
21645
|
+
result = `${message.message}
|
|
21646
|
+
|
|
21647
|
+
\u2705 Build validation passed.`;
|
|
21648
|
+
}
|
|
21649
|
+
}
|
|
21615
21650
|
void notifyAgent(message.type === "applied" ? "Changes applied" : "Change undone", result);
|
|
21616
21651
|
appendChat("assistant", result, taskId2);
|
|
21617
21652
|
if (task?.element) releaseHeldLock(elementKey(task.element));
|
|
@@ -21743,6 +21778,8 @@
|
|
|
21743
21778
|
updateAgentTask(id, {
|
|
21744
21779
|
changesHistory: [...task.changesHistory, { summary, changes: changes.map((change) => ({ ...change })), createdAt: (/* @__PURE__ */ new Date()).toISOString() }]
|
|
21745
21780
|
});
|
|
21781
|
+
state.changesHistory = task.changesHistory;
|
|
21782
|
+
saveChatHistory(state.chatHistory, state.changesHistory);
|
|
21746
21783
|
}
|
|
21747
21784
|
function appendTaskMessage(id, role, text) {
|
|
21748
21785
|
const task = getAgentTask(id);
|
|
@@ -21757,6 +21794,8 @@
|
|
|
21757
21794
|
contextId: task.selectionId || void 0
|
|
21758
21795
|
};
|
|
21759
21796
|
updateAgentTask(task.id, { messages: [...task.messages, message] });
|
|
21797
|
+
state.chatHistory = task.messages;
|
|
21798
|
+
saveChatHistory(state.chatHistory, state.changesHistory);
|
|
21760
21799
|
return message;
|
|
21761
21800
|
}
|
|
21762
21801
|
function selectAgentTask(id) {
|
|
@@ -21916,6 +21955,7 @@
|
|
|
21916
21955
|
state.selectionId = selectionId;
|
|
21917
21956
|
state.chatHistory = conversation.messages;
|
|
21918
21957
|
state.changesHistory = conversation.changesHistory;
|
|
21958
|
+
saveChatHistory(state.chatHistory, state.changesHistory);
|
|
21919
21959
|
}
|
|
21920
21960
|
function buildPrompt() {
|
|
21921
21961
|
const dom = getDOM();
|
|
@@ -22101,6 +22141,12 @@
|
|
|
22101
22141
|
if (task) restoreTaskPrompt(task);
|
|
22102
22142
|
else resetPromptSession();
|
|
22103
22143
|
});
|
|
22144
|
+
const { chatHistory, changesHistory } = loadChatHistory();
|
|
22145
|
+
if (chatHistory.length > 0) {
|
|
22146
|
+
state.chatHistory = chatHistory;
|
|
22147
|
+
state.changesHistory = changesHistory;
|
|
22148
|
+
renderChatThread();
|
|
22149
|
+
}
|
|
22104
22150
|
onAgentTaskReviewRequested((task) => {
|
|
22105
22151
|
state.promptTaskId = task.id;
|
|
22106
22152
|
state.pendingChanges = [...task.pendingChanges.length ? task.pendingChanges : task.changes || []];
|
|
@@ -22610,6 +22656,7 @@
|
|
|
22610
22656
|
contextId: state.selectionId || void 0
|
|
22611
22657
|
});
|
|
22612
22658
|
}
|
|
22659
|
+
saveChatHistory(state.chatHistory, state.changesHistory);
|
|
22613
22660
|
renderChatThread();
|
|
22614
22661
|
}
|
|
22615
22662
|
function renderChatText(container, text) {
|
|
@@ -22696,6 +22743,7 @@
|
|
|
22696
22743
|
state.chatHistory = [];
|
|
22697
22744
|
state.changesHistory = [];
|
|
22698
22745
|
state.selectionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
22746
|
+
saveChatHistory(state.chatHistory, state.changesHistory);
|
|
22699
22747
|
}
|
|
22700
22748
|
state.screenshotPromise = Promise.resolve({});
|
|
22701
22749
|
hideReview();
|
package/package.json
CHANGED