@lasso-ai/cli 1.0.0
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 +342 -0
- package/CHANGELOG.md +102 -0
- package/CODE_OF_CONDUCT.md +129 -0
- package/CONTRIBUTING.md +122 -0
- package/LICENSE +7 -0
- package/README.md +548 -0
- package/SECURITY.md +65 -0
- package/SUPPORT.md +31 -0
- package/dist/cli/agent.d.ts +61 -0
- package/dist/cli/agent.js +418 -0
- package/dist/cli/auth.d.ts +38 -0
- package/dist/cli/auth.js +162 -0
- package/dist/cli/bridge.d.ts +160 -0
- package/dist/cli/bridge.js +450 -0
- package/dist/cli/host/certificates.d.ts +12 -0
- package/dist/cli/host/certificates.js +52 -0
- package/dist/cli/host/client.d.ts +49 -0
- package/dist/cli/host/client.js +141 -0
- package/dist/cli/host/daemon.d.ts +50 -0
- package/dist/cli/host/daemon.js +368 -0
- package/dist/cli/host/dns.d.ts +80 -0
- package/dist/cli/host/dns.js +255 -0
- package/dist/cli/host/install.d.ts +32 -0
- package/dist/cli/host/install.js +177 -0
- package/dist/cli/host/next-host-entry.d.ts +1 -0
- package/dist/cli/host/next-host-entry.js +82 -0
- package/dist/cli/host/paths.d.ts +15 -0
- package/dist/cli/host/paths.js +55 -0
- package/dist/cli/host/registry.d.ts +31 -0
- package/dist/cli/host/registry.js +103 -0
- package/dist/cli/host/runtime.d.ts +23 -0
- package/dist/cli/host/runtime.js +134 -0
- package/dist/cli/host/vite-host-entry.d.ts +1 -0
- package/dist/cli/host/vite-host-entry.js +59 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +322 -0
- package/dist/cli/project.d.ts +56 -0
- package/dist/cli/project.js +298 -0
- package/dist/cli/server/next.d.ts +6 -0
- package/dist/cli/server/next.js +76 -0
- package/dist/cli/server/vite.d.ts +1 -0
- package/dist/cli/server/vite.js +51 -0
- package/dist/cli/utils/framework.d.ts +2 -0
- package/dist/cli/utils/framework.js +26 -0
- package/dist/overlay.js +20365 -0
- package/docs/PUBLISHING.md +63 -0
- package/package.json +106 -0
package/dist/cli/auth.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.serverUrlFrom = serverUrlFrom;
|
|
7
|
+
exports.fetchWithTimeout = fetchWithTimeout;
|
|
8
|
+
exports.loadCredentials = loadCredentials;
|
|
9
|
+
exports.saveCredentials = saveCredentials;
|
|
10
|
+
exports.clearCredentials = clearCredentials;
|
|
11
|
+
exports.authLogin = authLogin;
|
|
12
|
+
exports.authStatus = authStatus;
|
|
13
|
+
exports.credentialSummary = credentialSummary;
|
|
14
|
+
exports.authLogout = authLogout;
|
|
15
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
16
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
17
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
19
|
+
const open_1 = __importDefault(require("open"));
|
|
20
|
+
const CREDENTIALS_DIR = node_path_1.default.join(node_os_1.default.homedir(), ".lasso");
|
|
21
|
+
const CREDENTIALS_FILE = node_path_1.default.join(CREDENTIALS_DIR, "credentials.json");
|
|
22
|
+
/**
|
|
23
|
+
* Base URL of the Lasso API (the main server, not the realtime server). The
|
|
24
|
+
* `lasso auth` flow lives here because the browser confirm needs the web session.
|
|
25
|
+
* Defaults to the local development port used by `server/` and `web/`.
|
|
26
|
+
*/
|
|
27
|
+
function serverUrlFrom(fileEnv) {
|
|
28
|
+
return (process.env.LASSO_AUTH_URL ||
|
|
29
|
+
fileEnv.LASSO_AUTH_URL ||
|
|
30
|
+
process.env.LASSO_API_URL ||
|
|
31
|
+
fileEnv.LASSO_API_URL ||
|
|
32
|
+
"http://localhost:3005").replace(/\/$/, "");
|
|
33
|
+
}
|
|
34
|
+
function fetchWithTimeout(url, init, timeoutMs = 8000) {
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
37
|
+
return fetch(url, { ...init, signal: controller.signal }).finally(() => clearTimeout(timer));
|
|
38
|
+
}
|
|
39
|
+
function loadCredentials() {
|
|
40
|
+
try {
|
|
41
|
+
const raw = JSON.parse(node_fs_1.default.readFileSync(CREDENTIALS_FILE, "utf8"));
|
|
42
|
+
if (raw && typeof raw.apiKey === "string" && raw.apiKey)
|
|
43
|
+
return raw;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// no credentials yet
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
function saveCredentials(credentials) {
|
|
51
|
+
node_fs_1.default.mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
52
|
+
const temp = `${CREDENTIALS_FILE}.tmp`;
|
|
53
|
+
node_fs_1.default.writeFileSync(temp, JSON.stringify(credentials, null, 2) + "\n", { mode: 0o600 });
|
|
54
|
+
node_fs_1.default.renameSync(temp, CREDENTIALS_FILE);
|
|
55
|
+
try {
|
|
56
|
+
node_fs_1.default.chmodSync(CREDENTIALS_FILE, 0o600);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// chmod is best-effort (Windows)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function clearCredentials() {
|
|
63
|
+
try {
|
|
64
|
+
node_fs_1.default.unlinkSync(CREDENTIALS_FILE);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// nothing to clear
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function mask(key) {
|
|
71
|
+
return `${key.slice(0, 9)}••••${key.slice(-4)}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* `lasso auth login` — device-style OAuth. The CLI asks the server for a
|
|
75
|
+
* one-time code + verification URL, opens the browser, and polls the status
|
|
76
|
+
* endpoint until the logged-in dashboard user authorizes. On success the
|
|
77
|
+
* freshly minted API key is stored in ~/.lasso/credentials.json (0600).
|
|
78
|
+
*/
|
|
79
|
+
async function authLogin(cwd, fileEnv) {
|
|
80
|
+
const serverUrl = serverUrlFrom(fileEnv);
|
|
81
|
+
const apiUrl = `${serverUrl}/api/v1`;
|
|
82
|
+
let startResponse;
|
|
83
|
+
try {
|
|
84
|
+
startResponse = await fetchWithTimeout(`${apiUrl}/auth/cli/start`, { method: "POST" });
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
const reason = error instanceof Error ? error.message : "request failed";
|
|
88
|
+
return { ok: false, error: `Could not reach the Lasso API at ${serverUrl} (${reason}). Is the Lasso server running?` };
|
|
89
|
+
}
|
|
90
|
+
if (!startResponse.ok) {
|
|
91
|
+
const body = (await startResponse.json().catch(() => ({})));
|
|
92
|
+
return { ok: false, error: body.message || `Could not start the login flow (${startResponse.status}).` };
|
|
93
|
+
}
|
|
94
|
+
const started = (await startResponse.json());
|
|
95
|
+
if (!started.cliId || !started.verificationUrl) {
|
|
96
|
+
return { ok: false, error: "The server did not return an authorization URL." };
|
|
97
|
+
}
|
|
98
|
+
console.log(`\n${chalk_1.default.bold("Lasso CLI authorization")}`);
|
|
99
|
+
console.log(` Open this URL to authorize the Lasso CLI:\n`);
|
|
100
|
+
console.log(` ${chalk_1.default.cyan(started.verificationUrl)}\n`);
|
|
101
|
+
let opened = false;
|
|
102
|
+
if (!process.env.LASSO_NO_BROWSER) {
|
|
103
|
+
opened = Boolean(await (0, open_1.default)(started.verificationUrl).catch(() => null));
|
|
104
|
+
}
|
|
105
|
+
if (opened) {
|
|
106
|
+
console.log(chalk_1.default.dim(" Waiting for you to authorize in the browser…\n"));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.log(chalk_1.default.dim(" Tip: paste the URL into your browser if it didn't open automatically.\n"));
|
|
110
|
+
}
|
|
111
|
+
const pollMs = 2000;
|
|
112
|
+
const timeoutMs = Math.max((started.expiresInSeconds || 600) * 1000 - 5000, 10000);
|
|
113
|
+
const deadline = Date.now() + timeoutMs;
|
|
114
|
+
while (Date.now() < deadline) {
|
|
115
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
116
|
+
let response;
|
|
117
|
+
try {
|
|
118
|
+
response = await fetchWithTimeout(`${apiUrl}/auth/cli/status?code=${encodeURIComponent(started.cliId)}`);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (!response.ok)
|
|
124
|
+
continue;
|
|
125
|
+
const status = (await response.json().catch(() => ({})));
|
|
126
|
+
if (status.status === "authorized" && status.apiKey) {
|
|
127
|
+
const credentials = {
|
|
128
|
+
apiKey: status.apiKey,
|
|
129
|
+
keyId: status.keyId || "",
|
|
130
|
+
workspaceId: status.workspaceId || "",
|
|
131
|
+
workspaceName: status.workspaceName || "",
|
|
132
|
+
userName: status.user?.name || "",
|
|
133
|
+
userEmail: status.user?.email || "",
|
|
134
|
+
createdAt: new Date().toISOString(),
|
|
135
|
+
};
|
|
136
|
+
saveCredentials(credentials);
|
|
137
|
+
const who = credentials.userEmail || "your account";
|
|
138
|
+
console.log(chalk_1.default.green("✓") + ` Authenticated as ${chalk_1.default.bold(who)}` + (credentials.workspaceName ? ` (workspace ${chalk_1.default.cyan(credentials.workspaceName)})` : "") + ".");
|
|
139
|
+
console.log(chalk_1.default.dim(` Credential saved to ${CREDENTIALS_FILE}.`));
|
|
140
|
+
return { ok: true };
|
|
141
|
+
}
|
|
142
|
+
if (status.status === "expired" || status.status === "error") {
|
|
143
|
+
return { ok: false, error: status.message || "The authorization link expired. Run `lasso auth login` again." };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { ok: false, error: "Timed out waiting for authorization. Run `lasso auth login` again when ready." };
|
|
147
|
+
}
|
|
148
|
+
function authStatus() {
|
|
149
|
+
return loadCredentials();
|
|
150
|
+
}
|
|
151
|
+
function credentialSummary(credentials) {
|
|
152
|
+
return {
|
|
153
|
+
userEmail: credentials.userEmail,
|
|
154
|
+
userName: credentials.userName,
|
|
155
|
+
workspaceName: credentials.workspaceName,
|
|
156
|
+
workspaceId: credentials.workspaceId,
|
|
157
|
+
masked: mask(credentials.apiKey),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function authLogout() {
|
|
161
|
+
clearCredentials();
|
|
162
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { type SourceChange } from "./agent";
|
|
2
|
+
import type { CollabConfig } from "./project";
|
|
3
|
+
type GitState = {
|
|
4
|
+
isRepo: boolean;
|
|
5
|
+
branch?: string;
|
|
6
|
+
status?: string[];
|
|
7
|
+
hasChanges?: boolean;
|
|
8
|
+
hasRemote?: boolean;
|
|
9
|
+
remote?: string;
|
|
10
|
+
};
|
|
11
|
+
export type BridgeMessage = {
|
|
12
|
+
type: "hello";
|
|
13
|
+
from: "overlay" | "cli";
|
|
14
|
+
} | {
|
|
15
|
+
type: "edit";
|
|
16
|
+
instruction: string;
|
|
17
|
+
model: string;
|
|
18
|
+
provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
19
|
+
messages?: Array<{
|
|
20
|
+
role: string;
|
|
21
|
+
content: string;
|
|
22
|
+
createdAt?: string;
|
|
23
|
+
}>;
|
|
24
|
+
changesHistory?: Array<{
|
|
25
|
+
summary: string;
|
|
26
|
+
changes: SourceChange[];
|
|
27
|
+
createdAt?: string;
|
|
28
|
+
}>;
|
|
29
|
+
context?: {
|
|
30
|
+
selectionId?: string;
|
|
31
|
+
position?: Record<string, number>;
|
|
32
|
+
viewport?: Record<string, unknown>;
|
|
33
|
+
styles?: Record<string, string>;
|
|
34
|
+
attributes?: Record<string, string>;
|
|
35
|
+
runtimeErrors?: string[];
|
|
36
|
+
screenshots?: {
|
|
37
|
+
full?: string;
|
|
38
|
+
element?: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
element: {
|
|
42
|
+
tag: string;
|
|
43
|
+
group: string;
|
|
44
|
+
label: string;
|
|
45
|
+
html?: string;
|
|
46
|
+
sourceHint?: string;
|
|
47
|
+
};
|
|
48
|
+
} | {
|
|
49
|
+
type: "ask";
|
|
50
|
+
question: string;
|
|
51
|
+
model: string;
|
|
52
|
+
provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
53
|
+
messages?: Array<{
|
|
54
|
+
role: string;
|
|
55
|
+
content: string;
|
|
56
|
+
createdAt?: string;
|
|
57
|
+
}>;
|
|
58
|
+
context?: {
|
|
59
|
+
selectionId?: string;
|
|
60
|
+
position?: Record<string, number>;
|
|
61
|
+
viewport?: Record<string, unknown>;
|
|
62
|
+
styles?: Record<string, string>;
|
|
63
|
+
attributes?: Record<string, string>;
|
|
64
|
+
runtimeErrors?: string[];
|
|
65
|
+
screenshots?: {
|
|
66
|
+
full?: string;
|
|
67
|
+
element?: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
element: {
|
|
71
|
+
tag: string;
|
|
72
|
+
group: string;
|
|
73
|
+
label: string;
|
|
74
|
+
html?: string;
|
|
75
|
+
sourceHint?: string;
|
|
76
|
+
};
|
|
77
|
+
} | {
|
|
78
|
+
type: "runtime_error";
|
|
79
|
+
selectionId?: string;
|
|
80
|
+
details: string;
|
|
81
|
+
} | {
|
|
82
|
+
type: "apply";
|
|
83
|
+
changes: SourceChange[];
|
|
84
|
+
} | {
|
|
85
|
+
type: "undo";
|
|
86
|
+
} | {
|
|
87
|
+
type: "stop";
|
|
88
|
+
} | {
|
|
89
|
+
type: "git_status";
|
|
90
|
+
} | {
|
|
91
|
+
type: "git_generate_message";
|
|
92
|
+
model: string;
|
|
93
|
+
provider?: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
94
|
+
} | {
|
|
95
|
+
type: "git_init";
|
|
96
|
+
} | {
|
|
97
|
+
type: "git_commit";
|
|
98
|
+
message: string;
|
|
99
|
+
} | {
|
|
100
|
+
type: "git_push";
|
|
101
|
+
} | {
|
|
102
|
+
type: "agent_status";
|
|
103
|
+
status: "thinking" | "working" | "review" | "error" | "stopped";
|
|
104
|
+
message: string;
|
|
105
|
+
} | {
|
|
106
|
+
type: "transcribe";
|
|
107
|
+
requestId: string;
|
|
108
|
+
audio: string;
|
|
109
|
+
mimeType?: string;
|
|
110
|
+
language?: string;
|
|
111
|
+
};
|
|
112
|
+
type ModelOption = {
|
|
113
|
+
id: string;
|
|
114
|
+
label: string;
|
|
115
|
+
provider: "anthropic" | "openai" | "google" | "ollama" | "cli";
|
|
116
|
+
};
|
|
117
|
+
export type ServerBridgeMessage = {
|
|
118
|
+
type: "config";
|
|
119
|
+
apiKeyConfigured: boolean;
|
|
120
|
+
agentConfigured: boolean;
|
|
121
|
+
models: ModelOption[];
|
|
122
|
+
collab?: CollabConfig | null;
|
|
123
|
+
} | {
|
|
124
|
+
type: "git_state";
|
|
125
|
+
git: GitState;
|
|
126
|
+
} | {
|
|
127
|
+
type: "git_result";
|
|
128
|
+
message?: string;
|
|
129
|
+
error?: string;
|
|
130
|
+
} | {
|
|
131
|
+
type: "git_commit_message";
|
|
132
|
+
message?: string;
|
|
133
|
+
error?: string;
|
|
134
|
+
} | {
|
|
135
|
+
type: "agent_status";
|
|
136
|
+
status: "thinking" | "working" | "review" | "error" | "stopped";
|
|
137
|
+
message: string;
|
|
138
|
+
changes?: SourceChange[];
|
|
139
|
+
} | {
|
|
140
|
+
type: "assistant_message";
|
|
141
|
+
message: string;
|
|
142
|
+
} | {
|
|
143
|
+
type: "applied";
|
|
144
|
+
message: string;
|
|
145
|
+
} | {
|
|
146
|
+
type: "undone";
|
|
147
|
+
message: string;
|
|
148
|
+
} | {
|
|
149
|
+
type: "transcribe_result";
|
|
150
|
+
requestId: string;
|
|
151
|
+
success: boolean;
|
|
152
|
+
text?: string;
|
|
153
|
+
provider?: string;
|
|
154
|
+
error?: string;
|
|
155
|
+
};
|
|
156
|
+
export declare function startBridge(cwd?: string, collabConfig?: CollabConfig | null, bridgePort?: number): {
|
|
157
|
+
send: (msg: ServerBridgeMessage) => void;
|
|
158
|
+
close(): void;
|
|
159
|
+
};
|
|
160
|
+
export {};
|