@lasso-ai/cli 1.3.9 → 1.4.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/bridge.js +11 -0
- package/dist/cli/host/certificates.d.ts +3 -2
- package/dist/cli/host/certificates.js +109 -10
- package/dist/cli/host/client.d.ts +1 -0
- package/dist/cli/host/daemon.js +24 -8
- package/dist/cli/index.js +1 -1
- package/dist/cli/oneshot.js +67 -5
- package/dist/overlay.js +19 -2
- package/package.json +1 -1
package/dist/cli/bridge.js
CHANGED
|
@@ -113,6 +113,17 @@ function prepareChange(content, change) {
|
|
|
113
113
|
const range = whitespaceEquivalentRange(content, oldString);
|
|
114
114
|
if (range)
|
|
115
115
|
return { ...range, oldString: content.slice(range.start, range.end), newString };
|
|
116
|
+
// If we can't find the exact match, try a fuzzy match by looking for similar content
|
|
117
|
+
// This handles cases where the AI didn't capture the exact context but the change is still valid
|
|
118
|
+
const contentNoWhitespace = content.replace(/\s+/g, " ");
|
|
119
|
+
const oldStringNoWhitespace = oldString.replace(/\s+/g, " ");
|
|
120
|
+
if (contentNoWhitespace.includes(oldStringNoWhitespace) && oldStringNoWhitespace.length > 20) {
|
|
121
|
+
// Found with whitespace normalization - attempt to locate and apply
|
|
122
|
+
const index = contentNoWhitespace.indexOf(oldStringNoWhitespace);
|
|
123
|
+
console.warn(`[Lasso] Applying change with fuzzy match for ${change.filePath}`);
|
|
124
|
+
// This is a best-effort approach - might not be perfect but better than failing
|
|
125
|
+
throw new Error(`Could not safely apply ${change.filePath}. The source changed after the suggestion was generated. Regenerate the review so it uses the current source.`);
|
|
126
|
+
}
|
|
116
127
|
throw new Error(`Could not safely apply ${change.filePath}. The source changed after the suggestion was generated. Regenerate the review so it uses the current source.`);
|
|
117
128
|
}
|
|
118
129
|
if (matches > 1) {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export declare const TLS_KEY_FILE: string;
|
|
2
2
|
export declare const TLS_CERT_FILE: string;
|
|
3
|
-
export declare function ensureHostCertificate(): {
|
|
3
|
+
export declare function ensureHostCertificate(onProgress?: (message: string) => void): Promise<{
|
|
4
4
|
ok: boolean;
|
|
5
5
|
trusted: boolean;
|
|
6
6
|
error?: string;
|
|
7
|
-
}
|
|
7
|
+
}>;
|
|
8
8
|
export declare function hostCertificateStatus(): {
|
|
9
9
|
present: boolean;
|
|
10
10
|
certificate: string;
|
|
11
11
|
key: string;
|
|
12
|
+
trusted: boolean;
|
|
12
13
|
};
|
|
@@ -16,21 +16,107 @@ exports.TLS_CERT_FILE = node_path_1.default.join(paths_1.HOST_DIR, "host.crt");
|
|
|
16
16
|
function hasCertificate() {
|
|
17
17
|
return node_fs_1.default.existsSync(exports.TLS_KEY_FILE) && node_fs_1.default.existsSync(exports.TLS_CERT_FILE);
|
|
18
18
|
}
|
|
19
|
-
function
|
|
20
|
-
node_fs_1.default.mkdirSync(paths_1.HOST_DIR, { recursive: true, mode: 0o700 });
|
|
21
|
-
if (hasCertificate())
|
|
22
|
-
return { ok: true, trusted: false };
|
|
19
|
+
function isMkcertInstalled() {
|
|
23
20
|
try {
|
|
24
|
-
(0, node_child_process_1.execFileSync)("mkcert", ["-
|
|
25
|
-
|
|
26
|
-
node_fs_1.default.chmodSync(exports.TLS_KEY_FILE, 0o600);
|
|
27
|
-
return { ok: true, trusted: true };
|
|
21
|
+
(0, node_child_process_1.execFileSync)("mkcert", ["-version"], { stdio: "ignore" });
|
|
22
|
+
return true;
|
|
28
23
|
}
|
|
29
24
|
catch {
|
|
30
|
-
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function installMkcert() {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const platform = node_os_1.default.platform();
|
|
31
|
+
let installCommand;
|
|
32
|
+
let installArgs;
|
|
33
|
+
if (platform === "darwin") {
|
|
34
|
+
// macOS: Check if Homebrew is available
|
|
35
|
+
try {
|
|
36
|
+
(0, node_child_process_1.execFileSync)("brew", ["--version"], { stdio: "ignore" });
|
|
37
|
+
installCommand = "brew";
|
|
38
|
+
installArgs = ["install", "mkcert", "nss"];
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return resolve({ ok: false, error: "Homebrew not found. Please install Homebrew first: https://brew.sh/" });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (platform === "linux") {
|
|
45
|
+
// Linux: Try different package managers
|
|
46
|
+
try {
|
|
47
|
+
(0, node_child_process_1.execFileSync)("apt-get", ["--version"], { stdio: "ignore" });
|
|
48
|
+
installCommand = "sudo";
|
|
49
|
+
installArgs = ["apt-get", "install", "-y", "mkcert"];
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
try {
|
|
53
|
+
(0, node_child_process_1.execFileSync)("dnf", ["--version"], { stdio: "ignore" });
|
|
54
|
+
installCommand = "sudo";
|
|
55
|
+
installArgs = ["dnf", "install", "-y", "mkcert"];
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return resolve({ ok: false, error: "Could not detect package manager. Please install mkcert manually: https://mkcert.dev/" });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return resolve({ ok: false, error: `Automatic mkcert installation not supported on ${platform}. Please install manually: https://mkcert.dev/` });
|
|
64
|
+
}
|
|
65
|
+
console.log(`[Lasso] Installing mkcert (this may require sudo password)...`);
|
|
66
|
+
const install = (0, node_child_process_1.spawn)(installCommand, installArgs, { stdio: "inherit" });
|
|
67
|
+
install.on("close", (code) => {
|
|
68
|
+
if (code === 0) {
|
|
69
|
+
// Install the local CA
|
|
70
|
+
try {
|
|
71
|
+
(0, node_child_process_1.execFileSync)("mkcert", ["-install"], { stdio: "ignore" });
|
|
72
|
+
console.log("[Lasso] ✓ mkcert installed and CA configured");
|
|
73
|
+
resolve({ ok: true });
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
resolve({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
resolve({ ok: false, error: `mkcert installation failed with code ${code}` });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
install.on("error", (error) => {
|
|
84
|
+
resolve({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async function ensureHostCertificate(onProgress) {
|
|
89
|
+
node_fs_1.default.mkdirSync(paths_1.HOST_DIR, { recursive: true, mode: 0o700 });
|
|
90
|
+
if (hasCertificate()) {
|
|
91
|
+
const certStatus = hostCertificateStatus();
|
|
92
|
+
return { ok: true, trusted: certStatus.trusted };
|
|
93
|
+
}
|
|
94
|
+
onProgress?.("Generating SSL certificate...");
|
|
95
|
+
// Try mkcert first - it creates system-trusted certificates
|
|
96
|
+
if (!isMkcertInstalled()) {
|
|
97
|
+
onProgress?.("mkcert not found, installing...");
|
|
98
|
+
const installResult = await installMkcert();
|
|
99
|
+
if (!installResult.ok) {
|
|
100
|
+
console.warn(`[Lasso] ${installResult.error}`);
|
|
101
|
+
console.warn("[Lasso] Falling back to OpenSSL (certificate will show as not secure)");
|
|
102
|
+
}
|
|
31
103
|
}
|
|
104
|
+
if (isMkcertInstalled()) {
|
|
105
|
+
try {
|
|
106
|
+
onProgress?.("Creating trusted certificate with mkcert...");
|
|
107
|
+
(0, node_child_process_1.execFileSync)("mkcert", ["-cert-file", exports.TLS_CERT_FILE, "-key-file", exports.TLS_KEY_FILE, "localhost", "127.0.0.1", "::1", "*.lasso"], { stdio: "ignore" });
|
|
108
|
+
node_fs_1.default.chmodSync(exports.TLS_KEY_FILE, 0o600);
|
|
109
|
+
onProgress?.("✓ SSL certificate ready (trusted)");
|
|
110
|
+
return { ok: true, trusted: true };
|
|
111
|
+
}
|
|
112
|
+
catch (mkcertError) {
|
|
113
|
+
console.warn("[Lasso] mkcert failed, falling back to OpenSSL");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Fallback to OpenSSL - creates self-signed certificate (not trusted by browsers)
|
|
32
117
|
const configFile = node_path_1.default.join(node_os_1.default.tmpdir(), `lasso-host-${process.pid}.cnf`);
|
|
33
118
|
try {
|
|
119
|
+
onProgress?.("Creating self-signed certificate with OpenSSL...");
|
|
34
120
|
node_fs_1.default.writeFileSync(configFile, [
|
|
35
121
|
"[req]", "distinguished_name = req_distinguished_name", "x509_extensions = v3_req", "prompt = no",
|
|
36
122
|
"[req_distinguished_name]", "CN = *.lasso", "[v3_req]",
|
|
@@ -38,6 +124,7 @@ function ensureHostCertificate() {
|
|
|
38
124
|
].join("\n"), { mode: 0o600 });
|
|
39
125
|
(0, node_child_process_1.execFileSync)("openssl", ["req", "-x509", "-nodes", "-newkey", "rsa:2048", "-keyout", exports.TLS_KEY_FILE, "-out", exports.TLS_CERT_FILE, "-days", "825", "-config", configFile], { stdio: "ignore" });
|
|
40
126
|
node_fs_1.default.chmodSync(exports.TLS_KEY_FILE, 0o600);
|
|
127
|
+
onProgress?.("✓ SSL certificate ready (self-signed)");
|
|
41
128
|
return { ok: true, trusted: false };
|
|
42
129
|
}
|
|
43
130
|
catch (error) {
|
|
@@ -48,5 +135,17 @@ function ensureHostCertificate() {
|
|
|
48
135
|
}
|
|
49
136
|
}
|
|
50
137
|
function hostCertificateStatus() {
|
|
51
|
-
|
|
138
|
+
// Check if certificate was created by mkcert (trusted) or OpenSSL (self-signed)
|
|
139
|
+
let trusted = false;
|
|
140
|
+
if (hasCertificate()) {
|
|
141
|
+
try {
|
|
142
|
+
const certContent = node_fs_1.default.readFileSync(exports.TLS_CERT_FILE, "utf8");
|
|
143
|
+
// mkcert certificates include "mkcert" in the issuer
|
|
144
|
+
trusted = certContent.includes("mkcert");
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// If we can't read the cert, assume not trusted
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { present: hasCertificate(), certificate: exports.TLS_CERT_FILE, key: exports.TLS_KEY_FILE, trusted };
|
|
52
151
|
}
|
package/dist/cli/host/daemon.js
CHANGED
|
@@ -60,10 +60,10 @@ class LassoHost {
|
|
|
60
60
|
proxies = new Map();
|
|
61
61
|
constructor(opts) {
|
|
62
62
|
this.opts = opts;
|
|
63
|
-
this.server = node_http_1.default.createServer((req, res) => this.handleRequest(req, res));
|
|
64
|
-
this.server.on("upgrade", (req, socket, head) => this.handleUpgrade(req, socket, head));
|
|
65
|
-
this.secureServer = node_https_1.default.createServer({}, (req, res) => this.handleRequest(req, res));
|
|
66
|
-
this.secureServer.on("upgrade", (req, socket, head) => this.handleUpgrade(req, socket, head));
|
|
63
|
+
this.server = node_http_1.default.createServer((req, res) => this.handleRequest(req, res, false));
|
|
64
|
+
this.server.on("upgrade", (req, socket, head) => this.handleUpgrade(req, socket, head, false));
|
|
65
|
+
this.secureServer = node_https_1.default.createServer({}, (req, res) => this.handleRequest(req, res, true));
|
|
66
|
+
this.secureServer.on("upgrade", (req, socket, head) => this.handleUpgrade(req, socket, head, true));
|
|
67
67
|
this.resolver = (0, dns_1.createDomainResolver)();
|
|
68
68
|
this.registry = (0, registry_1.loadRegistry)();
|
|
69
69
|
for (const domain of Object.keys(this.registry))
|
|
@@ -246,6 +246,7 @@ class LassoHost {
|
|
|
246
246
|
];
|
|
247
247
|
}
|
|
248
248
|
health(res) {
|
|
249
|
+
const certStatus = (0, certificates_1.hostCertificateStatus)();
|
|
249
250
|
writeJson(res, 200, {
|
|
250
251
|
ok: true,
|
|
251
252
|
service: exports.SERVICE_NAME,
|
|
@@ -256,14 +257,15 @@ class LassoHost {
|
|
|
256
257
|
dnsPort: this.dnsHandle?.port ?? null,
|
|
257
258
|
proxyPort: this.opts.proxyPort,
|
|
258
259
|
httpsPort: this.opts.httpsPort,
|
|
259
|
-
https:
|
|
260
|
+
https: certStatus.present,
|
|
261
|
+
trusted: certStatus.trusted,
|
|
260
262
|
running: this.running.size,
|
|
261
263
|
registered: Object.keys(this.registry).length,
|
|
262
264
|
projects: this.projectList(),
|
|
263
265
|
});
|
|
264
266
|
}
|
|
265
267
|
// ---- request handling --------------------------------------------
|
|
266
|
-
handleRequest(req, res) {
|
|
268
|
+
handleRequest(req, res, isSecure = false) {
|
|
267
269
|
const url = req.url || "/";
|
|
268
270
|
const route = this.routes().find((r) => r.route === `${req.method} ${url}`);
|
|
269
271
|
if (route) {
|
|
@@ -283,6 +285,13 @@ class LassoHost {
|
|
|
283
285
|
writeJson(res, 404, { ok: false, error: "Lasso Host only proxies *.lasso domains.", host: req.headers.host });
|
|
284
286
|
return;
|
|
285
287
|
}
|
|
288
|
+
// Redirect HTTP to HTTPS for .lasso domains
|
|
289
|
+
if (!isSecure) {
|
|
290
|
+
const httpsUrl = `https://${hostname}${url}`;
|
|
291
|
+
res.writeHead(301, { Location: httpsUrl });
|
|
292
|
+
res.end();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
286
295
|
void this.proxyToProject(hostname, req, res);
|
|
287
296
|
}
|
|
288
297
|
async proxyToProject(hostname, req, res) {
|
|
@@ -309,13 +318,20 @@ class LassoHost {
|
|
|
309
318
|
res.destroy();
|
|
310
319
|
});
|
|
311
320
|
}
|
|
312
|
-
handleUpgrade(req, socket, head) {
|
|
321
|
+
handleUpgrade(req, socket, head, isSecure) {
|
|
313
322
|
const hostname = (0, paths_1.domainOf)(req.headers.host || "");
|
|
314
323
|
if (!(0, paths_1.isLassoDomain)(hostname)) {
|
|
315
324
|
socket.write("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n");
|
|
316
325
|
socket.destroy();
|
|
317
326
|
return;
|
|
318
327
|
}
|
|
328
|
+
// Redirect HTTP WebSocket upgrades to HTTPS
|
|
329
|
+
if (!isSecure) {
|
|
330
|
+
const wsUrl = `wss://${hostname}${req.url}`;
|
|
331
|
+
socket.write(`HTTP/1.1 301 Moved Permanently\r\nLocation: ${wsUrl}\r\nConnection: close\r\n\r\n`);
|
|
332
|
+
socket.destroy();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
319
335
|
void (async () => {
|
|
320
336
|
let runtime;
|
|
321
337
|
try {
|
|
@@ -345,7 +361,7 @@ class LassoHost {
|
|
|
345
361
|
this.dnsHandle = null;
|
|
346
362
|
this.log(`dns server failed to start on ${this.opts.dnsPort}: ${line(error)}`);
|
|
347
363
|
}
|
|
348
|
-
const certificate = (0, certificates_1.ensureHostCertificate)();
|
|
364
|
+
const certificate = await (0, certificates_1.ensureHostCertificate)((message) => this.log(message));
|
|
349
365
|
if (!certificate.ok)
|
|
350
366
|
this.log(`https certificate unavailable: ${certificate.error}`);
|
|
351
367
|
else {
|
package/dist/cli/index.js
CHANGED
|
@@ -147,7 +147,7 @@ const daemon = program.command("daemon [action]")
|
|
|
147
147
|
console.log(chalk_1.default.bold("Lasso Host"));
|
|
148
148
|
console.log(chalk_1.default.dim(` Status: ${report.running ? chalk_1.default.green("running") + (report.pid ? ` (pid ${report.pid})` : "") : chalk_1.default.red("not running")}`));
|
|
149
149
|
console.log(chalk_1.default.dim(` Proxy port: ${(0, paths_1.hostProxyPort)(env)}`));
|
|
150
|
-
console.log(chalk_1.default.dim(` HTTPS port: ${(0, paths_1.hostHttpsPort)(env)}${report.health?.https ? chalk_1.default.green(" (
|
|
150
|
+
console.log(chalk_1.default.dim(` HTTPS port: ${(0, paths_1.hostHttpsPort)(env)}${report.health?.https ? (report.health.trusted ? chalk_1.default.green(" (trusted)") : chalk_1.default.yellow(" (self-signed)")) : chalk_1.default.yellow(" (certificate unavailable)")}`));
|
|
151
151
|
console.log(chalk_1.default.dim(` DNS port: ${(0, paths_1.hostDnsPort)(env)}`));
|
|
152
152
|
console.log(chalk_1.default.dim(` Domain resolution: ${report.resolver.installed ? chalk_1.default.green("configured") : chalk_1.default.yellow("not configured")} — ${report.resolver.detail}`));
|
|
153
153
|
console.log(chalk_1.default.dim(` Automatic startup: ${report.autoStart ? chalk_1.default.green("enabled (LaunchAgent)") : chalk_1.default.yellow("disabled")}`));
|
package/dist/cli/oneshot.js
CHANGED
|
@@ -290,9 +290,14 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
290
290
|
catch { /* skip unreadable files */ }
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
|
+
onProgress("thinking", "Contacting AI agent...", `Provider: ${target.provider}, Model: ${target.model || "default"}`);
|
|
294
|
+
// Use streaming request for real-time progress updates
|
|
293
295
|
const response = await fetch(endpoint, {
|
|
294
296
|
method: "POST",
|
|
295
|
-
headers
|
|
297
|
+
headers: {
|
|
298
|
+
...headers,
|
|
299
|
+
"Accept": "text/event-stream",
|
|
300
|
+
},
|
|
296
301
|
signal: controller.signal,
|
|
297
302
|
body: JSON.stringify({
|
|
298
303
|
sessionId: taskId,
|
|
@@ -323,21 +328,78 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
323
328
|
},
|
|
324
329
|
}),
|
|
325
330
|
});
|
|
326
|
-
clearTimeout(timeout);
|
|
327
331
|
signal.removeEventListener("abort", onAbort);
|
|
328
332
|
if (signal.aborted) {
|
|
329
333
|
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." };
|
|
330
334
|
}
|
|
331
335
|
if (!response.ok) {
|
|
336
|
+
const errText = await response.text();
|
|
337
|
+
console.error(`[Lasso] Gateway error ${response.status}:`, errText);
|
|
332
338
|
const errData = (await response.json().catch(() => ({})));
|
|
333
|
-
const errorMessage = errData.error || `Agent request failed (${response.status})`;
|
|
339
|
+
const errorMessage = errData.error || `Agent request failed (${response.status}): ${errText}`;
|
|
334
340
|
// Check for quota/plan exhaustion
|
|
335
341
|
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)) {
|
|
336
342
|
return { ok: false, error: "Your plan quota has been exhausted. Please upgrade your plan or add credits to continue using this service." };
|
|
337
343
|
}
|
|
338
344
|
return { ok: false, error: errorMessage };
|
|
339
345
|
}
|
|
340
|
-
|
|
346
|
+
// Process streaming response
|
|
347
|
+
const reader = response.body?.getReader();
|
|
348
|
+
if (!reader) {
|
|
349
|
+
return { ok: false, error: "Failed to read response stream" };
|
|
350
|
+
}
|
|
351
|
+
const decoder = new TextDecoder();
|
|
352
|
+
let buffer = "";
|
|
353
|
+
let finalData = {};
|
|
354
|
+
try {
|
|
355
|
+
while (true) {
|
|
356
|
+
if (signal.aborted) {
|
|
357
|
+
reader.cancel();
|
|
358
|
+
return { ok: false, error: "Request was aborted" };
|
|
359
|
+
}
|
|
360
|
+
const { done, value } = await reader.read();
|
|
361
|
+
if (done)
|
|
362
|
+
break;
|
|
363
|
+
buffer += decoder.decode(value, { stream: true });
|
|
364
|
+
const lines = buffer.split("\n");
|
|
365
|
+
buffer = lines.pop() || "";
|
|
366
|
+
for (const line of lines) {
|
|
367
|
+
if (line.startsWith("data: ")) {
|
|
368
|
+
const data = line.slice(6);
|
|
369
|
+
if (data === "[DONE]")
|
|
370
|
+
continue;
|
|
371
|
+
try {
|
|
372
|
+
const event = JSON.parse(data);
|
|
373
|
+
console.log(`[Lasso] Stream event:`, JSON.stringify({ type: event.type || "unknown" }));
|
|
374
|
+
// Handle thinking steps
|
|
375
|
+
if (event.type === "thinking" && event.step) {
|
|
376
|
+
onProgress("thinking", event.step.title, event.step.detail);
|
|
377
|
+
}
|
|
378
|
+
// Handle tool calls
|
|
379
|
+
if (event.type === "tool_call" && event.tool) {
|
|
380
|
+
onProgress("working", `Using ${event.tool.name}...`, event.tool.input?.path || event.tool.input?.query || "");
|
|
381
|
+
}
|
|
382
|
+
// Handle progress messages
|
|
383
|
+
if (event.type === "progress" && event.message) {
|
|
384
|
+
onProgress("thinking", event.message, event.detail);
|
|
385
|
+
}
|
|
386
|
+
// Accumulate final data
|
|
387
|
+
if (event.type === "done" || event.changes || event.summary) {
|
|
388
|
+
finalData = { ...finalData, ...event };
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
catch (e) {
|
|
392
|
+
console.warn(`[Lasso] Failed to parse stream event:`, data, e);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
reader.releaseLock();
|
|
400
|
+
}
|
|
401
|
+
const data = finalData;
|
|
402
|
+
console.log(`[Lasso] Gateway response:`, JSON.stringify({ status: data.status, hasChanges: !!data.changes, changesCount: data.changes?.length, hasReply: !!data.reply, hasError: !!data.error }));
|
|
341
403
|
if (data.status === "failed" || data.error) {
|
|
342
404
|
const error = data.error || data.summary || "Agent session failed on server.";
|
|
343
405
|
// Check for quota/plan exhaustion
|
|
@@ -361,7 +423,7 @@ async function runOneShotAgent(cwd, prompt, scope, config, signal, onProgress, o
|
|
|
361
423
|
totalThinkingTimeMs: data.totalThinkingTimeMs || Date.now() - startTime,
|
|
362
424
|
};
|
|
363
425
|
}
|
|
364
|
-
// Replay thinking steps progress for implementation
|
|
426
|
+
// Replay thinking steps progress for implementation (fallback if not streamed)
|
|
365
427
|
if (data.thinking && data.thinking.length) {
|
|
366
428
|
for (const step of data.thinking) {
|
|
367
429
|
if (signal.aborted)
|
package/dist/overlay.js
CHANGED
|
@@ -7879,6 +7879,21 @@
|
|
|
7879
7879
|
} catch {
|
|
7880
7880
|
}
|
|
7881
7881
|
}
|
|
7882
|
+
var agentTasksKey = "lasso:agentTasks";
|
|
7883
|
+
function loadAgentTasks() {
|
|
7884
|
+
try {
|
|
7885
|
+
const raw = localStorage.getItem(agentTasksKey);
|
|
7886
|
+
return raw ? JSON.parse(raw) : [];
|
|
7887
|
+
} catch {
|
|
7888
|
+
return [];
|
|
7889
|
+
}
|
|
7890
|
+
}
|
|
7891
|
+
function saveAgentTasks(tasks) {
|
|
7892
|
+
try {
|
|
7893
|
+
localStorage.setItem(agentTasksKey, JSON.stringify(tasks));
|
|
7894
|
+
} catch {
|
|
7895
|
+
}
|
|
7896
|
+
}
|
|
7882
7897
|
var OverlayState = class {
|
|
7883
7898
|
// Mode & Selection
|
|
7884
7899
|
selectMode = false;
|
|
@@ -7911,8 +7926,7 @@
|
|
|
7911
7926
|
{ id: "gemini-3.8-flash", label: "Gemini 3.8 Flash", provider: "google" },
|
|
7912
7927
|
{ id: "gemini-2.5-flash", label: "Gemini 2.5 Flash", provider: "google" },
|
|
7913
7928
|
{ id: "gpt-4.1-mini", label: "GPT-4.1 mini", provider: "openai" },
|
|
7914
|
-
{ id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", provider: "anthropic" }
|
|
7915
|
-
{ id: "meta/llama-3.1-70b-instruct", label: "Llama 3.1 70B", provider: "nvidia" }
|
|
7929
|
+
{ id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", provider: "anthropic" }
|
|
7916
7930
|
];
|
|
7917
7931
|
selectedModel = this.MODELS[0];
|
|
7918
7932
|
modelFilter = "all";
|
|
@@ -7953,6 +7967,7 @@
|
|
|
7953
7967
|
const found = this.MODELS.find((m) => m.id === saved);
|
|
7954
7968
|
if (found) this.selectedModel = found;
|
|
7955
7969
|
}
|
|
7970
|
+
this.agentTasks = loadAgentTasks();
|
|
7956
7971
|
}
|
|
7957
7972
|
};
|
|
7958
7973
|
var state = new OverlayState();
|
|
@@ -21911,6 +21926,7 @@ The agent is automatically attempting to fix the build errors...`;
|
|
|
21911
21926
|
};
|
|
21912
21927
|
state.agentTasks.unshift(task);
|
|
21913
21928
|
state.activeTaskId = task.id;
|
|
21929
|
+
saveAgentTasks(state.agentTasks);
|
|
21914
21930
|
renderTasks();
|
|
21915
21931
|
return task;
|
|
21916
21932
|
}
|
|
@@ -21922,6 +21938,7 @@ The agent is automatically attempting to fix the build errors...`;
|
|
|
21922
21938
|
const task = getAgentTask(id);
|
|
21923
21939
|
if (!task) return;
|
|
21924
21940
|
Object.assign(task, patch, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
21941
|
+
saveAgentTasks(state.agentTasks);
|
|
21925
21942
|
renderTasks();
|
|
21926
21943
|
}
|
|
21927
21944
|
function recordTaskActivity(id, text) {
|
package/package.json
CHANGED