@lasso-ai/cli 1.2.3 → 1.2.5
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/ARCHITECTURE.md +10 -3
- package/CHANGELOG.md +38 -0
- package/README.md +23 -3
- package/dist/cli/agent.d.ts +38 -2
- package/dist/cli/agent.js +521 -72
- package/dist/cli/auth.js +3 -1
- package/dist/cli/bridge.d.ts +22 -6
- package/dist/cli/bridge.js +182 -49
- package/dist/cli/index.js +10 -0
- package/dist/cli/oneshot.d.ts +19 -1
- package/dist/cli/oneshot.js +611 -106
- package/dist/cli/project.d.ts +2 -1
- package/dist/cli/project.js +71 -26
- package/dist/overlay.js +8668 -6431
- package/package.json +1 -1
package/dist/cli/auth.js
CHANGED
|
@@ -30,7 +30,9 @@ function serverUrlFrom(fileEnv) {
|
|
|
30
30
|
fileEnv.LASSO_AUTH_URL ||
|
|
31
31
|
process.env.LASSO_API_URL ||
|
|
32
32
|
fileEnv.LASSO_API_URL ||
|
|
33
|
-
|
|
33
|
+
process.env.LASSO_SERVER_URL ||
|
|
34
|
+
fileEnv.LASSO_SERVER_URL ||
|
|
35
|
+
(process.env.NODE_ENV === "development" ? "http://localhost:3005" : "https://api.lasso.byorello.space")).replace(/\/$/, "");
|
|
34
36
|
}
|
|
35
37
|
function fetchWithTimeout(url, init, timeoutMs = 8000) {
|
|
36
38
|
const controller = new AbortController();
|
package/dist/cli/bridge.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type BridgeMessage = {
|
|
|
17
17
|
taskId: string;
|
|
18
18
|
instruction: string;
|
|
19
19
|
model: string;
|
|
20
|
-
provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
20
|
+
provider?: "anthropic" | "openai" | "google" | "ollama" | "nvidia" | "cli";
|
|
21
21
|
messages?: Array<{
|
|
22
22
|
role: string;
|
|
23
23
|
content: string;
|
|
@@ -52,7 +52,7 @@ export type BridgeMessage = {
|
|
|
52
52
|
taskId: string;
|
|
53
53
|
question: string;
|
|
54
54
|
model: string;
|
|
55
|
-
provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
55
|
+
provider?: "anthropic" | "openai" | "google" | "ollama" | "nvidia" | "cli";
|
|
56
56
|
messages?: Array<{
|
|
57
57
|
role: string;
|
|
58
58
|
content: string;
|
|
@@ -112,7 +112,7 @@ export type BridgeMessage = {
|
|
|
112
112
|
} | {
|
|
113
113
|
type: "git_generate_message";
|
|
114
114
|
model: string;
|
|
115
|
-
provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
115
|
+
provider?: "anthropic" | "openai" | "google" | "ollama" | "nvidia" | "cli";
|
|
116
116
|
} | {
|
|
117
117
|
type: "git_init";
|
|
118
118
|
} | {
|
|
@@ -134,14 +134,20 @@ export type BridgeMessage = {
|
|
|
134
134
|
} | {
|
|
135
135
|
type: "oneshot";
|
|
136
136
|
prompt: string;
|
|
137
|
-
scope
|
|
137
|
+
scope?: "project" | "component";
|
|
138
138
|
model?: string;
|
|
139
|
-
provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
139
|
+
provider?: "anthropic" | "openai" | "google" | "ollama" | "nvidia" | "cli";
|
|
140
|
+
messages?: Array<{
|
|
141
|
+
role: "user" | "assistant";
|
|
142
|
+
content: string;
|
|
143
|
+
}>;
|
|
144
|
+
taskId?: string;
|
|
145
|
+
pageContext?: any;
|
|
140
146
|
};
|
|
141
147
|
type ModelOption = {
|
|
142
148
|
id: string;
|
|
143
149
|
label: string;
|
|
144
|
-
provider: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
150
|
+
provider: "anthropic" | "openai" | "google" | "ollama" | "nvidia" | "cli";
|
|
145
151
|
};
|
|
146
152
|
type PageEntry = {
|
|
147
153
|
name: string;
|
|
@@ -154,6 +160,10 @@ export type ServerBridgeMessage = {
|
|
|
154
160
|
agentConfigured: boolean;
|
|
155
161
|
models: ModelOption[];
|
|
156
162
|
collab?: CollabConfig | null;
|
|
163
|
+
user?: {
|
|
164
|
+
name?: string;
|
|
165
|
+
email?: string;
|
|
166
|
+
} | null;
|
|
157
167
|
} | {
|
|
158
168
|
type: "git_state";
|
|
159
169
|
git: GitState;
|
|
@@ -175,6 +185,12 @@ export type ServerBridgeMessage = {
|
|
|
175
185
|
message: string;
|
|
176
186
|
detail?: string;
|
|
177
187
|
changes?: SourceChange[];
|
|
188
|
+
thinking?: Array<{
|
|
189
|
+
title: string;
|
|
190
|
+
detail?: string;
|
|
191
|
+
durationMs?: number;
|
|
192
|
+
}>;
|
|
193
|
+
totalThinkingTimeMs?: number;
|
|
178
194
|
} | {
|
|
179
195
|
type: "agent_prompt";
|
|
180
196
|
taskId: string;
|
package/dist/cli/bridge.js
CHANGED
|
@@ -15,6 +15,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
15
15
|
const node_util_1 = require("node:util");
|
|
16
16
|
const chalk_1 = __importDefault(require("chalk"));
|
|
17
17
|
const agent_1 = require("./agent");
|
|
18
|
+
const project_1 = require("./project");
|
|
18
19
|
const auth_1 = require("./auth");
|
|
19
20
|
exports.DEFAULT_BRIDGE_PORT = 3056;
|
|
20
21
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
@@ -52,7 +53,7 @@ function fileLineEnding(content) {
|
|
|
52
53
|
function withLineEnding(value, lineEnding) {
|
|
53
54
|
return value.replace(/\r\n?|\n/g, lineEnding);
|
|
54
55
|
}
|
|
55
|
-
function resolveProposedFile(cwd, proposedPath) {
|
|
56
|
+
function resolveProposedFile(cwd, proposedPath, allowNew = false) {
|
|
56
57
|
const root = node_path_1.default.resolve(cwd);
|
|
57
58
|
const candidate = node_path_1.default.resolve(root, proposedPath);
|
|
58
59
|
if (candidate.startsWith(`${root}${node_path_1.default.sep}`) && node_fs_1.default.existsSync(candidate))
|
|
@@ -65,10 +66,15 @@ function resolveProposedFile(cwd, proposedPath) {
|
|
|
65
66
|
const rebased = node_path_1.default.join(root, normalized.slice(srcIndex + 1));
|
|
66
67
|
if (rebased.startsWith(`${root}${node_path_1.default.sep}`) && node_fs_1.default.existsSync(rebased))
|
|
67
68
|
return rebased;
|
|
69
|
+
if (allowNew && (rebased.startsWith(`${root}${node_path_1.default.sep}`) || rebased === root))
|
|
70
|
+
return rebased;
|
|
71
|
+
}
|
|
72
|
+
if (candidate.startsWith(`${root}${node_path_1.default.sep}`) || candidate === root) {
|
|
73
|
+
if (allowNew)
|
|
74
|
+
return candidate;
|
|
75
|
+
throw new Error(`Could not find the proposed file in the current project: ${proposedPath}`);
|
|
68
76
|
}
|
|
69
|
-
|
|
70
|
-
throw new Error(`The proposed file is outside the current project: ${proposedPath}`);
|
|
71
|
-
throw new Error(`Could not find the proposed file in the current project: ${proposedPath}`);
|
|
77
|
+
throw new Error(`The proposed file is outside the current project: ${proposedPath}`);
|
|
72
78
|
}
|
|
73
79
|
function occurrenceCount(content, needle) {
|
|
74
80
|
if (!needle)
|
|
@@ -117,6 +123,8 @@ function prepareChange(content, change) {
|
|
|
117
123
|
function proposalMatchesCurrentSource(cwd, changes) {
|
|
118
124
|
try {
|
|
119
125
|
for (const change of changes) {
|
|
126
|
+
if (!change.oldString)
|
|
127
|
+
continue;
|
|
120
128
|
const filePath = resolveProposedFile(cwd, change.filePath);
|
|
121
129
|
const content = node_fs_1.default.readFileSync(filePath, "utf8");
|
|
122
130
|
// Try exact match first
|
|
@@ -171,24 +179,33 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
171
179
|
...readEnvFile(root, ".env.local"),
|
|
172
180
|
...readEnvFile(root, ".env.production"),
|
|
173
181
|
}), {});
|
|
174
|
-
const
|
|
182
|
+
const resolvedLassoKey = (0, project_1.resolveLassoApiKey)(fileEnv) ||
|
|
183
|
+
process.env.LASSO_API_KEY ||
|
|
184
|
+
fileEnv.LASSO_API_KEY ||
|
|
185
|
+
process.env.VITE_LASSO_API_KEY ||
|
|
186
|
+
process.env.NEXT_LASSO_API_KEY ||
|
|
187
|
+
fileEnv.VITE_LASSO_API_KEY ||
|
|
188
|
+
fileEnv.NEXT_LASSO_API_KEY ||
|
|
189
|
+
"";
|
|
190
|
+
const lassoKeyConfigured = Boolean(resolvedLassoKey);
|
|
191
|
+
const localProviderKeys = {
|
|
192
|
+
google: process.env.GOOGLE_GENERATIVE_AI_API_KEY || fileEnv.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GEMINI_API_KEY || fileEnv.GEMINI_API_KEY,
|
|
193
|
+
openai: process.env.OPENAI_API_KEY || fileEnv.OPENAI_API_KEY,
|
|
194
|
+
anthropic: process.env.ANTHROPIC_API_KEY || fileEnv.ANTHROPIC_API_KEY,
|
|
195
|
+
nvidia: process.env.NVIDIA_API_KEY || fileEnv.NVIDIA_API_KEY,
|
|
196
|
+
ollama: process.env.OLLAMA_BASE_URL || fileEnv.OLLAMA_BASE_URL || process.env.OLLAMA_MODEL || fileEnv.OLLAMA_MODEL ? "ollama" : undefined,
|
|
197
|
+
};
|
|
175
198
|
let agentConfig = (() => {
|
|
176
199
|
const provider = (process.env.LASSO_AGENT_PROVIDER || fileEnv.LASSO_AGENT_PROVIDER || process.env.AI_PROVIDER || fileEnv.AI_PROVIDER || "").toLowerCase();
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
anthropic: process.env.ANTHROPIC_API_KEY || fileEnv.ANTHROPIC_API_KEY,
|
|
181
|
-
ollama: process.env.OLLAMA_BASE_URL || fileEnv.OLLAMA_BASE_URL || process.env.OLLAMA_MODEL || fileEnv.OLLAMA_MODEL,
|
|
182
|
-
};
|
|
183
|
-
const selectedProvider = provider === "google" || provider === "gemini" ? "google" : provider === "openai" ? "openai" : provider === "ollama" ? "ollama" : provider === "anthropic" ? "anthropic" : keys.ollama ? "ollama" : keys.google ? "google" : keys.openai ? "openai" : "anthropic";
|
|
184
|
-
const apiKey = selectedProvider === "ollama" ? "ollama" : keys[selectedProvider];
|
|
185
|
-
if (!apiKey)
|
|
200
|
+
const selectedProvider = provider === "google" || provider === "gemini" ? "google" : provider === "openai" ? "openai" : provider === "ollama" ? "ollama" : provider === "anthropic" ? "anthropic" : provider === "nvidia" ? "nvidia" : localProviderKeys.ollama ? "ollama" : localProviderKeys.nvidia ? "nvidia" : localProviderKeys.google ? "google" : localProviderKeys.openai ? "openai" : localProviderKeys.anthropic ? "anthropic" : "google";
|
|
201
|
+
const apiKey = selectedProvider === "ollama" ? "ollama" : localProviderKeys[selectedProvider];
|
|
202
|
+
if (!apiKey && !lassoKeyConfigured)
|
|
186
203
|
return null;
|
|
187
204
|
return {
|
|
188
205
|
provider: selectedProvider,
|
|
189
|
-
apiKey,
|
|
206
|
+
apiKey: apiKey || resolvedLassoKey,
|
|
190
207
|
model: process.env.LASSO_AGENT_MODEL || fileEnv.LASSO_AGENT_MODEL || process.env.AI_MODEL || fileEnv.AI_MODEL,
|
|
191
|
-
baseUrl: process.env.OLLAMA_BASE_URL || fileEnv.OLLAMA_BASE_URL ? `${(process.env.OLLAMA_BASE_URL || fileEnv.OLLAMA_BASE_URL || "http://localhost:11434/api").replace(/\/api\/?$/, "")}/v1` : undefined,
|
|
208
|
+
baseUrl: selectedProvider === "nvidia" ? "https://integrate.api.nvidia.com/v1" : process.env.OLLAMA_BASE_URL || fileEnv.OLLAMA_BASE_URL ? `${(process.env.OLLAMA_BASE_URL || fileEnv.OLLAMA_BASE_URL || "http://localhost:11434/api").replace(/\/api\/?$/, "")}/v1` : undefined,
|
|
192
209
|
};
|
|
193
210
|
})();
|
|
194
211
|
let activeAgentController = null;
|
|
@@ -293,9 +310,26 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
293
310
|
const base = process.env.OLLAMA_BASE_URL || fileEnv.OLLAMA_BASE_URL || "http://localhost:11434/api";
|
|
294
311
|
agentConfig = { provider: "ollama", apiKey: "ollama", model: process.env.OLLAMA_MODEL || fileEnv.OLLAMA_MODEL, baseUrl: `${base.replace(/\/api\/?$/, "")}/v1` };
|
|
295
312
|
}
|
|
296
|
-
const isPaidPlan = collabConfig?.plan && collabConfig.plan !== "free";
|
|
313
|
+
const isPaidPlan = Boolean(collabConfig?.plan && collabConfig.plan.toLowerCase() !== "free");
|
|
297
314
|
const configuredProviders = collabConfig?.configuredProviders || [];
|
|
298
315
|
const allModels = [
|
|
316
|
+
// NVIDIA NIM models (confirmed working with server API key)
|
|
317
|
+
{ id: "meta/llama-3.2-11b-vision-instruct", label: "Llama 3.2 11B Vision", provider: "nvidia" },
|
|
318
|
+
{ id: "openai/gpt-oss-20b", label: "GPT-OSS 20B (NVIDIA)", provider: "nvidia" },
|
|
319
|
+
{ id: "deepseek-ai/deepseek-coder-6.7b-instruct", label: "DeepSeek Coder 6.7B", provider: "nvidia" },
|
|
320
|
+
{ id: "deepseek-ai/deepseek-v4.1-flash", label: "DeepSeek V4.1 Flash", provider: "nvidia" },
|
|
321
|
+
{ id: "ibm/granite-34b-code-instruct", label: "Granite 34B Code", provider: "nvidia" },
|
|
322
|
+
{ id: "ibm/granite-3.0-8b-instruct", label: "Granite 3.0 8B", provider: "nvidia" },
|
|
323
|
+
{ id: "mistralai/mistral-large-2-instruct", label: "Mistral Large 2", provider: "nvidia" },
|
|
324
|
+
{ id: "mistralai/codestral-22b-instruct-v0.1", label: "Codestral 22B", provider: "nvidia" },
|
|
325
|
+
{ id: "mistralai/mistral-large", label: "Mistral Large", provider: "nvidia" },
|
|
326
|
+
{ id: "nv-mistralai/mistral-nemo-12b-instruct", label: "Mistral Nemo 12B", provider: "nvidia" },
|
|
327
|
+
{ id: "nvidia/nemotron-3.5-lightning-30b-a3b", label: "Nemotron Lightning 30B", provider: "nvidia" },
|
|
328
|
+
{ id: "microsoft/phi-3.5-moe-instruct", label: "Phi-3.5 MoE", provider: "nvidia" },
|
|
329
|
+
{ id: "google/gemma-4-31b-it", label: "Gemma 4 31B", provider: "nvidia" },
|
|
330
|
+
{ id: "moonshotai/kimi-k2.6", label: "Kimi K2.6", provider: "nvidia" },
|
|
331
|
+
{ id: "poolside/laguna-xs-2.1", label: "Laguna XS 2.1", provider: "nvidia" },
|
|
332
|
+
// Existing models
|
|
299
333
|
{ id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", provider: "anthropic" },
|
|
300
334
|
{ id: "claude-opus-4-1-20250805", label: "Claude Opus 4.1", provider: "anthropic" },
|
|
301
335
|
{ id: "gpt-4.1", label: "GPT-4.1", provider: "openai" },
|
|
@@ -340,6 +374,7 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
340
374
|
anthropic: "ANTHROPIC_API_KEY",
|
|
341
375
|
openai: "OPENAI_API_KEY",
|
|
342
376
|
google: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
377
|
+
nvidia: "NVIDIA_API_KEY",
|
|
343
378
|
ollama: "OLLAMA_BASE_URL",
|
|
344
379
|
};
|
|
345
380
|
return allModels.map((model) => {
|
|
@@ -355,7 +390,8 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
355
390
|
const envKey = providerKeyMapping[model.provider];
|
|
356
391
|
const isConfigured = configuredProviders.includes(model.provider) ||
|
|
357
392
|
Boolean(process.env[envKey]) ||
|
|
358
|
-
Boolean(fileEnv[envKey])
|
|
393
|
+
Boolean(fileEnv[envKey]) ||
|
|
394
|
+
Boolean(localProviderKeys[model.provider]);
|
|
359
395
|
if (!isConfigured) {
|
|
360
396
|
return {
|
|
361
397
|
...model,
|
|
@@ -369,10 +405,14 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
369
405
|
wss.on("connection", (socket) => {
|
|
370
406
|
overlaySocket = socket;
|
|
371
407
|
console.log(chalk_1.default.green("✓") + " Overlay connected");
|
|
372
|
-
|
|
408
|
+
const creds = (0, auth_1.loadCredentials)();
|
|
409
|
+
const userInfo = creds?.userName
|
|
410
|
+
? { name: creds.userName, email: creds.userEmail }
|
|
411
|
+
: (process.env.USER ? { name: process.env.USER } : null);
|
|
412
|
+
socket.send(JSON.stringify({ type: "config", apiKeyConfigured: lassoKeyConfigured, agentConfigured: lassoKeyConfigured || Boolean(agentConfig) || localAgents.size > 0, models: [], collab: collabConfig?.registered ? { ...collabConfig, plan: collabConfig.plan, configuredProviders: collabConfig.configuredProviders } : null, user: userInfo }));
|
|
373
413
|
void availableModels(collabConfig).then((models) => {
|
|
374
414
|
if (socket.readyState === socket.OPEN)
|
|
375
|
-
socket.send(JSON.stringify({ type: "config", apiKeyConfigured: lassoKeyConfigured, agentConfigured: Boolean(agentConfig) || localAgents.size > 0, models, collab: collabConfig?.registered ? { ...collabConfig, plan: collabConfig.plan, configuredProviders: collabConfig.configuredProviders } : null }));
|
|
415
|
+
socket.send(JSON.stringify({ type: "config", apiKeyConfigured: lassoKeyConfigured, agentConfigured: lassoKeyConfigured || Boolean(agentConfig) || localAgents.size > 0, models, collab: collabConfig?.registered ? { ...collabConfig, plan: collabConfig.plan, configuredProviders: collabConfig.configuredProviders } : null, user: userInfo }));
|
|
376
416
|
});
|
|
377
417
|
void getGitState(cwd).then((git) => {
|
|
378
418
|
if (socket.readyState === socket.OPEN)
|
|
@@ -514,9 +554,29 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
514
554
|
taskControllers.get(msg.taskId)?.abort();
|
|
515
555
|
const controller = new AbortController();
|
|
516
556
|
taskControllers.set(msg.taskId, controller);
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
557
|
+
let selectedConfig;
|
|
558
|
+
if (localProvider) {
|
|
559
|
+
selectedConfig = { provider: cliProvider, model: msg.model };
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
const requestedProvider = (msg.provider || agentConfig.provider);
|
|
563
|
+
const providerLocalKey = localProviderKeys[requestedProvider];
|
|
564
|
+
const apiKey = providerLocalKey || resolvedLassoKey;
|
|
565
|
+
const baseUrl = requestedProvider === "nvidia"
|
|
566
|
+
? "https://integrate.api.nvidia.com/v1"
|
|
567
|
+
: requestedProvider === "ollama"
|
|
568
|
+
? (agentConfig.baseUrl || "http://localhost:11434/v1")
|
|
569
|
+
: agentConfig.baseUrl;
|
|
570
|
+
selectedConfig = {
|
|
571
|
+
...agentConfig,
|
|
572
|
+
provider: requestedProvider,
|
|
573
|
+
model: msg.model,
|
|
574
|
+
apiKey,
|
|
575
|
+
baseUrl,
|
|
576
|
+
lassoKey: resolvedLassoKey,
|
|
577
|
+
serverUrl: collabConfig?.serverUrl || (0, auth_1.serverUrlFrom)(fileEnv),
|
|
578
|
+
};
|
|
579
|
+
}
|
|
520
580
|
socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "working", message: "Working…" }));
|
|
521
581
|
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, level) => {
|
|
522
582
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN)
|
|
@@ -546,9 +606,29 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
546
606
|
socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "error", message: "Add a supported agent key: GOOGLE_GENERATIVE_AI_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY." }));
|
|
547
607
|
return;
|
|
548
608
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
609
|
+
let selectedConfig;
|
|
610
|
+
if (localProvider) {
|
|
611
|
+
selectedConfig = { provider: cliProvider, model: msg.model };
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
const requestedProvider = (msg.provider || agentConfig.provider);
|
|
615
|
+
const providerLocalKey = localProviderKeys[requestedProvider];
|
|
616
|
+
const apiKey = providerLocalKey || resolvedLassoKey;
|
|
617
|
+
const baseUrl = requestedProvider === "nvidia"
|
|
618
|
+
? "https://integrate.api.nvidia.com/v1"
|
|
619
|
+
: requestedProvider === "ollama"
|
|
620
|
+
? (agentConfig.baseUrl || "http://localhost:11434/v1")
|
|
621
|
+
: agentConfig.baseUrl;
|
|
622
|
+
selectedConfig = {
|
|
623
|
+
...agentConfig,
|
|
624
|
+
provider: requestedProvider,
|
|
625
|
+
model: msg.model,
|
|
626
|
+
apiKey,
|
|
627
|
+
baseUrl,
|
|
628
|
+
lassoKey: resolvedLassoKey,
|
|
629
|
+
serverUrl: collabConfig?.serverUrl || (0, auth_1.serverUrlFrom)(fileEnv),
|
|
630
|
+
};
|
|
631
|
+
}
|
|
552
632
|
editRequests.set(msg.taskId, msg);
|
|
553
633
|
editConfigs.set(msg.taskId, selectedConfig);
|
|
554
634
|
reviewRefreshAttempts.set(msg.taskId, 0);
|
|
@@ -633,10 +713,15 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
633
713
|
}
|
|
634
714
|
else if (msg.type === "transcribe") {
|
|
635
715
|
const serverUrl = (0, auth_1.serverUrlFrom)(fileEnv);
|
|
716
|
+
const creds = (0, auth_1.loadCredentials)();
|
|
717
|
+
const apiKey = resolvedLassoKey || agentConfig?.apiKey || collabConfig?.apiKey || creds?.apiKey;
|
|
636
718
|
try {
|
|
637
719
|
const resp = await fetch(`${serverUrl}/api/v1/transcribe`, {
|
|
638
720
|
method: "POST",
|
|
639
|
-
headers: {
|
|
721
|
+
headers: {
|
|
722
|
+
"Content-Type": "application/json",
|
|
723
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
724
|
+
},
|
|
640
725
|
body: JSON.stringify({
|
|
641
726
|
audio: msg.audio,
|
|
642
727
|
mimeType: msg.mimeType,
|
|
@@ -664,22 +749,38 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
664
749
|
}
|
|
665
750
|
else if (msg.type === "oneshot") {
|
|
666
751
|
console.log("[lasso] one-shot request:", msg.prompt, msg.scope);
|
|
667
|
-
|
|
668
|
-
|
|
752
|
+
const taskId = msg.taskId || `oneshot-${Date.now()}`;
|
|
753
|
+
const localProvider = msg.provider === "cli" || msg.model?.startsWith("claude-code:") || msg.model?.startsWith("opencode:") || msg.model?.startsWith("cursor:") || msg.model?.startsWith("codex:");
|
|
754
|
+
const cliProvider = msg.model?.startsWith("claude-code:") ? "claude-code" : msg.model?.startsWith("opencode:") ? "opencode" : msg.model?.startsWith("cursor:") ? "cursor" : "codex";
|
|
755
|
+
const apiKey = resolvedLassoKey || agentConfig?.apiKey || collabConfig?.apiKey;
|
|
756
|
+
if (!localProvider && !lassoKeyConfigured && !agentConfig && !apiKey) {
|
|
757
|
+
socket.send(JSON.stringify({
|
|
758
|
+
type: "agent_status",
|
|
759
|
+
taskId,
|
|
760
|
+
status: "error",
|
|
761
|
+
message: "No AI provider or Lasso key configured. Sign in with 'lasso auth login' or configure a provider key in .env."
|
|
762
|
+
}));
|
|
669
763
|
return;
|
|
670
764
|
}
|
|
671
|
-
const
|
|
672
|
-
const
|
|
765
|
+
const provider = (msg.provider || agentConfig?.provider || "google");
|
|
766
|
+
const model = msg.model || agentConfig?.model || (provider === "nvidia" ? "nvidia/llama-3.1-nemotron-70b-instruct" : provider === "google" ? "gemini-2.5-flash" : "claude-3-7-sonnet");
|
|
767
|
+
const localKey = localProviderKeys[provider];
|
|
673
768
|
const selectedConfig = localProvider
|
|
674
769
|
? { provider: cliProvider, model: msg.model || "claude-code:sonnet" }
|
|
675
|
-
: {
|
|
676
|
-
|
|
770
|
+
: {
|
|
771
|
+
...(agentConfig || {}),
|
|
772
|
+
provider,
|
|
773
|
+
model,
|
|
774
|
+
apiKey: localKey || apiKey,
|
|
775
|
+
baseUrl: provider === "nvidia" ? "https://integrate.api.nvidia.com/v1" : provider === "ollama" ? `${(process.env.OLLAMA_BASE_URL || fileEnv.OLLAMA_BASE_URL || "http://localhost:11434/api").replace(/\/api\/?$/, "")}/v1` : undefined,
|
|
776
|
+
};
|
|
677
777
|
const controller = new AbortController();
|
|
678
778
|
taskControllers.set(taskId, controller);
|
|
679
779
|
socket.send(JSON.stringify({ type: "agent_status", taskId, status: "thinking", message: "Planning your request..." }));
|
|
680
|
-
// Import the one-shot agent function
|
|
681
780
|
const { runOneShotAgent } = await import("./oneshot.js");
|
|
682
|
-
|
|
781
|
+
const serverUrl = collabConfig?.serverUrl || (0, auth_1.serverUrlFrom)(fileEnv);
|
|
782
|
+
const targetApiUrl = (serverUrl.includes("collab.lasso.byorello.space") ? "https://api.lasso.byorello.space" : serverUrl).replace(/\/api\/v1\/?$/, "");
|
|
783
|
+
runOneShotAgent(cwd, msg.prompt, msg.scope || "project", selectedConfig, controller.signal, (status, message, detail) => {
|
|
683
784
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
|
|
684
785
|
socket.send(JSON.stringify({ type: "agent_status", taskId, status, message, detail }));
|
|
685
786
|
}
|
|
@@ -687,10 +788,20 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
687
788
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
|
|
688
789
|
socket.send(JSON.stringify({ type: "agent_prompt", taskId, prompt }));
|
|
689
790
|
}
|
|
690
|
-
}).then((result) => {
|
|
791
|
+
}, msg.messages, targetApiUrl, apiKey, taskId, msg.pageContext).then((result) => {
|
|
691
792
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
|
|
692
793
|
if (result.ok) {
|
|
693
|
-
|
|
794
|
+
const hasChanges = Array.isArray(result.changes) && result.changes.length > 0;
|
|
795
|
+
socket.send(JSON.stringify({
|
|
796
|
+
type: "agent_status",
|
|
797
|
+
taskId,
|
|
798
|
+
status: hasChanges ? "review" : "complete",
|
|
799
|
+
message: result.summary,
|
|
800
|
+
todo: result.todo,
|
|
801
|
+
changes: result.changes,
|
|
802
|
+
thinking: result.thinking,
|
|
803
|
+
totalThinkingTimeMs: result.totalThinkingTimeMs,
|
|
804
|
+
}));
|
|
694
805
|
}
|
|
695
806
|
else {
|
|
696
807
|
socket.send(JSON.stringify({ type: "agent_status", taskId, status: "error", message: result.error || "One-shot agent failed" }));
|
|
@@ -710,25 +821,33 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
710
821
|
const snapshots = [];
|
|
711
822
|
const planned = new Map();
|
|
712
823
|
for (const change of msg.changes) {
|
|
713
|
-
const
|
|
714
|
-
const
|
|
715
|
-
const
|
|
824
|
+
const isNew = !change.oldString;
|
|
825
|
+
const filePath = resolveProposedFile(cwd, change.filePath, true);
|
|
826
|
+
const exists = node_fs_1.default.existsSync(filePath);
|
|
827
|
+
const content = exists ? node_fs_1.default.readFileSync(filePath, "utf8") : "";
|
|
828
|
+
const prepared = (!exists || isNew)
|
|
829
|
+
? { start: 0, end: content.length, oldString: content, newString: change.newString }
|
|
830
|
+
: prepareChange(content, change);
|
|
716
831
|
const fileChanges = planned.get(filePath) || [];
|
|
717
832
|
if (fileChanges.some((item) => prepared.start < item.end && item.start < prepared.end)) {
|
|
718
833
|
throw new Error(`Could not safely apply ${change.filePath}. Proposed changes overlap.`);
|
|
719
834
|
}
|
|
720
|
-
fileChanges.push({ filePath, content, ...prepared });
|
|
835
|
+
fileChanges.push({ filePath, content, exists, ...prepared });
|
|
721
836
|
planned.set(filePath, fileChanges);
|
|
722
837
|
}
|
|
723
838
|
// Validate every change before writing any file, then apply each
|
|
724
839
|
// file's replacements from the end toward the beginning.
|
|
725
840
|
taskSnapshots.set(msg.taskId, snapshots);
|
|
726
841
|
for (const [filePath, changes] of planned) {
|
|
842
|
+
const exists = changes[0].exists;
|
|
727
843
|
const content = changes[0].content;
|
|
728
|
-
snapshots.push({ filePath, content });
|
|
729
|
-
const nextContent =
|
|
730
|
-
|
|
731
|
-
|
|
844
|
+
snapshots.push({ filePath, content: exists ? content : null });
|
|
845
|
+
const nextContent = exists
|
|
846
|
+
? [...changes]
|
|
847
|
+
.sort((a, b) => b.start - a.start)
|
|
848
|
+
.reduce((value, change) => value.slice(0, change.start) + change.newString + value.slice(change.end), content)
|
|
849
|
+
: changes[0].newString;
|
|
850
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(filePath), { recursive: true });
|
|
732
851
|
node_fs_1.default.writeFileSync(filePath, nextContent);
|
|
733
852
|
}
|
|
734
853
|
socket.send(JSON.stringify({ type: "applied", taskId: msg.taskId, message: `${msg.changes.length} file${msg.changes.length === 1 ? "" : "s"} updated. Your dev server will reload.` }));
|
|
@@ -736,8 +855,15 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
736
855
|
catch (error) {
|
|
737
856
|
// Validation happens before writes, but restore this task's snapshot
|
|
738
857
|
// if a filesystem error occurs during the write phase.
|
|
739
|
-
for (const snapshot of taskSnapshots.get(msg.taskId) || [])
|
|
740
|
-
|
|
858
|
+
for (const snapshot of taskSnapshots.get(msg.taskId) || []) {
|
|
859
|
+
if (snapshot.content === null) {
|
|
860
|
+
if (node_fs_1.default.existsSync(snapshot.filePath))
|
|
861
|
+
node_fs_1.default.unlinkSync(snapshot.filePath);
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
node_fs_1.default.writeFileSync(snapshot.filePath, snapshot.content);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
741
867
|
taskSnapshots.delete(msg.taskId);
|
|
742
868
|
const message = error instanceof Error ? error.message : "The change could not be applied.";
|
|
743
869
|
const sourceChanged = message.includes("The source changed after the suggestion was generated");
|
|
@@ -755,8 +881,15 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
755
881
|
}
|
|
756
882
|
else if (msg.type === "undo") {
|
|
757
883
|
const snapshots = taskSnapshots.get(msg.taskId || "") || [];
|
|
758
|
-
for (const snapshot of snapshots)
|
|
759
|
-
|
|
884
|
+
for (const snapshot of snapshots) {
|
|
885
|
+
if (snapshot.content === null) {
|
|
886
|
+
if (node_fs_1.default.existsSync(snapshot.filePath))
|
|
887
|
+
node_fs_1.default.unlinkSync(snapshot.filePath);
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
node_fs_1.default.writeFileSync(snapshot.filePath, snapshot.content);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
760
893
|
taskSnapshots.delete(msg.taskId || "");
|
|
761
894
|
socket.send(JSON.stringify({ type: "undone", taskId: msg.taskId, message: snapshots.length ? "The accepted change was reverted." : "There is no accepted change to undo." }));
|
|
762
895
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -264,6 +264,16 @@ program.command("register [domain]")
|
|
|
264
264
|
console.error(chalk_1.default.red("✗") + ` "${domain}" is not a valid .lasso domain (lowercase letters, numbers, and hyphens).`);
|
|
265
265
|
process.exit(1);
|
|
266
266
|
}
|
|
267
|
+
// Register with Lasso workspace & Host
|
|
268
|
+
console.log(chalk_1.default.dim("Registering project with Lasso workspace…"));
|
|
269
|
+
const initResult = await (0, project_1.initProject)(cwd, env, domain || undefined);
|
|
270
|
+
if (initResult.ok) {
|
|
271
|
+
console.log(chalk_1.default.green("✓") + ` Registered project: ${chalk_1.default.cyan(initResult.projectId)}`);
|
|
272
|
+
console.log(chalk_1.default.green("✓") + ` Local domain: ${chalk_1.default.cyan(initResult.domain || domain)}`);
|
|
273
|
+
console.log(chalk_1.default.green("✓") + ` Points to ${chalk_1.default.dim(cwd)}`);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
// If server registration was not possible (e.g. offline/no key), fall back to local host registration
|
|
267
277
|
if (!domain) {
|
|
268
278
|
if (config?.domain) {
|
|
269
279
|
domain = config.domain;
|
package/dist/cli/oneshot.d.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import type { AgentConfig, SourceChange } from "./agent";
|
|
2
|
+
export interface OneShotThinkingStep {
|
|
3
|
+
title: string;
|
|
4
|
+
detail?: string;
|
|
5
|
+
durationMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface OneShotTodoItem {
|
|
8
|
+
file: string;
|
|
9
|
+
action: "modify" | "create" | "delete";
|
|
10
|
+
reason: string;
|
|
11
|
+
}
|
|
2
12
|
export interface OneShotResult {
|
|
3
13
|
ok: boolean;
|
|
4
14
|
summary?: string;
|
|
15
|
+
todo?: OneShotTodoItem[];
|
|
5
16
|
changes?: SourceChange[];
|
|
17
|
+
thinking?: OneShotThinkingStep[];
|
|
18
|
+
totalThinkingTimeMs?: number;
|
|
6
19
|
error?: string;
|
|
7
20
|
}
|
|
8
21
|
export interface ProgressCallback {
|
|
@@ -14,4 +27,9 @@ export interface PromptCallback {
|
|
|
14
27
|
options?: string[];
|
|
15
28
|
}): void;
|
|
16
29
|
}
|
|
17
|
-
export
|
|
30
|
+
export interface ConversationMessage {
|
|
31
|
+
role: "user" | "assistant";
|
|
32
|
+
content: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function isConversationalPrompt(prompt: string): boolean;
|
|
35
|
+
export declare function runOneShotAgent(cwd: string, prompt: string, scope: "project" | "component", config: AgentConfig, signal: AbortSignal, onProgress: ProgressCallback, onPrompt: PromptCallback, messages?: ConversationMessage[], serverUrl?: string, apiKey?: string, taskId?: string, pageContext?: any): Promise<OneShotResult>;
|