@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.
@@ -5,6 +5,7 @@ export interface CollabConfig {
5
5
  version?: string;
6
6
  realtimeUrl: string;
7
7
  apiUrl: string;
8
+ serverUrl: string;
8
9
  registered: boolean;
9
10
  registeredAt?: string;
10
11
  claimed: boolean;
@@ -48,7 +49,7 @@ export declare function readProjectConfig(cwd: string): {
48
49
  * running). Only `{ "id": "proj_…", "domain": "….lasso" }` is stored — the API key
49
50
  * never touches this file.
50
51
  */
51
- export declare function initProject(cwd: string, fileEnv: Record<string, string>): Promise<InitResult>;
52
+ export declare function initProject(cwd: string, fileEnv: Record<string, string>, forcedDomain?: string): Promise<InitResult>;
52
53
  /**
53
54
  * `lasso dev` — consumes `lasso.config.json`: authenticates with the API key, lets the
54
55
  * server resolve the key's workspace, validates the project belongs to it, and
@@ -187,11 +187,21 @@ function promptForApiKey() {
187
187
  * running). Only `{ "id": "proj_…", "domain": "….lasso" }` is stored — the API key
188
188
  * never touches this file.
189
189
  */
190
- async function initProject(cwd, fileEnv) {
190
+ async function initProject(cwd, fileEnv, forcedDomain) {
191
191
  const realtimeUrl = realtimeUrlFrom(fileEnv).replace(/\/$/, "");
192
- const apiUrl = `${realtimeUrl}/api/v1`;
192
+ const serverUrl = (0, auth_1.serverUrlFrom)(fileEnv).replace(/\/$/, "");
193
+ const collabApi = `${realtimeUrl}/api/v1`;
193
194
  const framework = (0, framework_1.detectFramework)(cwd);
194
195
  const { name, payload } = await buildProjectMetadata(cwd, framework);
196
+ const existingConfig = readProjectConfig(cwd);
197
+ if (existingConfig?.id) {
198
+ payload.id = existingConfig.id;
199
+ payload.projectId = existingConfig.id;
200
+ }
201
+ const registry = (0, registry_1.loadRegistry)();
202
+ const existingHost = (0, registry_1.findByDirectory)(registry, cwd);
203
+ const domain = forcedDomain || (existingConfig?.domain ? existingConfig.domain : (existingHost ? existingHost.domain : (0, registry_1.generateUniqueDomain)(cwd, registry)));
204
+ payload.domain = domain;
195
205
  let apiKey = resolveLassoApiKey(fileEnv);
196
206
  if (!apiKey)
197
207
  apiKey = (await promptForApiKey()) || "";
@@ -199,14 +209,30 @@ async function initProject(cwd, fileEnv) {
199
209
  return { ok: false, error: "No Lasso API key found. Set LASSO_API_KEY in your environment or paste it when prompted." };
200
210
  }
201
211
  try {
202
- const response = await (0, auth_1.fetchWithTimeout)(`${apiUrl}/collab/projects/register`, {
203
- method: "POST",
204
- headers: {
205
- "content-type": "application/json",
206
- authorization: `Bearer ${apiKey}`,
207
- },
208
- body: JSON.stringify(payload),
209
- });
212
+ let response;
213
+ try {
214
+ // Registration lives on the Lasso API (api.lasso.byorello.space): it owns
215
+ // the workspace's project records and stores the local `.lasso` domain.
216
+ response = await (0, auth_1.fetchWithTimeout)(`${serverUrl}/api/v1/collab/projects/register`, {
217
+ method: "POST",
218
+ headers: {
219
+ "content-type": "application/json",
220
+ authorization: `Bearer ${apiKey}`,
221
+ },
222
+ body: JSON.stringify(payload),
223
+ });
224
+ }
225
+ catch {
226
+ // Fall back to the realtime/collab server if the API is unreachable
227
+ response = await (0, auth_1.fetchWithTimeout)(`${collabApi}/collab/projects/register`, {
228
+ method: "POST",
229
+ headers: {
230
+ "content-type": "application/json",
231
+ authorization: `Bearer ${apiKey}`,
232
+ },
233
+ body: JSON.stringify(payload),
234
+ });
235
+ }
210
236
  const body = (await response.json().catch(() => ({})));
211
237
  if (!response.ok) {
212
238
  if (response.status === 401) {
@@ -215,15 +241,13 @@ async function initProject(cwd, fileEnv) {
215
241
  if (response.status === 403) {
216
242
  return { ok: false, error: "Your API key does not have access to a Lasso workspace." };
217
243
  }
218
- return { ok: false, error: body.message || `Project registration failed (${response.status}).` };
244
+ const detail = body.message || body.error;
245
+ return { ok: false, error: detail ? `${detail} (HTTP ${response.status})` : `Project registration failed (${response.status}).` };
219
246
  }
220
- const projectId = body.project?.id;
247
+ const projectId = body.project?.id || existingConfig?.id;
221
248
  if (!projectId) {
222
249
  return { ok: false, error: "The server did not return a project id." };
223
250
  }
224
- const registry = (0, registry_1.loadRegistry)();
225
- const existing = (0, registry_1.findByDirectory)(registry, cwd);
226
- const domain = existing ? existing.domain : (0, registry_1.generateUniqueDomain)(cwd, registry);
227
251
  await (0, client_1.registerWithHost)(domain, cwd, projectId, (0, paths_1.hostProxyPort)(fileEnv));
228
252
  if (framework === "next") {
229
253
  const injected = (0, next_config_1.ensureNextAllowedDevOrigin)(cwd, domain);
@@ -243,7 +267,7 @@ async function initProject(cwd, fileEnv) {
243
267
  }
244
268
  catch (error) {
245
269
  const reason = error instanceof Error ? error.message : "request failed";
246
- return { ok: false, error: `Could not reach the Lasso realtime server at ${realtimeUrl} (${reason}). Is it running?` };
270
+ return { ok: false, error: `Could not reach the Lasso API at ${serverUrl} (${reason}). Is it running?` };
247
271
  }
248
272
  }
249
273
  /**
@@ -254,12 +278,15 @@ async function initProject(cwd, fileEnv) {
254
278
  */
255
279
  async function resolveCollabSession(cwd, fileEnv, projectId) {
256
280
  const realtimeUrl = realtimeUrlFrom(fileEnv).replace(/\/$/, "");
257
- const apiUrl = `${realtimeUrl}/api/v1`;
281
+ const serverUrl = (0, auth_1.serverUrlFrom)(fileEnv).replace(/\/$/, "");
282
+ const collabApi = `${realtimeUrl}/api/v1`;
283
+ const apiUrl = `${serverUrl}/api/v1`;
258
284
  const config = {
259
285
  projectId,
260
286
  name: "",
261
287
  version: "",
262
288
  realtimeUrl,
289
+ serverUrl,
263
290
  apiUrl,
264
291
  registered: false,
265
292
  claimed: false,
@@ -270,16 +297,34 @@ async function resolveCollabSession(cwd, fileEnv, projectId) {
270
297
  return config;
271
298
  }
272
299
  try {
273
- const response = await (0, auth_1.fetchWithTimeout)(`${apiUrl}/collab/projects/${encodeURIComponent(projectId)}/session`, {
274
- method: "POST",
275
- headers: { authorization: `Bearer ${apiKey}` },
276
- });
277
- if (!response.ok) {
278
- const reason = response.status === 401
300
+ let response = null;
301
+ try {
302
+ response = await (0, auth_1.fetchWithTimeout)(`${collabApi}/collab/projects/${encodeURIComponent(projectId)}/session`, {
303
+ method: "POST",
304
+ headers: { authorization: `Bearer ${apiKey}` },
305
+ });
306
+ }
307
+ catch {
308
+ // Collab server unreachable, will fall back below
309
+ }
310
+ if (!response || (!response.ok && (response.status === 502 || response.status === 503 || response.status === 504 || response.status === 404))) {
311
+ try {
312
+ response = await (0, auth_1.fetchWithTimeout)(`${serverUrl}/api/v1/collab/projects/${encodeURIComponent(projectId)}/session`, {
313
+ method: "POST",
314
+ headers: { authorization: `Bearer ${apiKey}` },
315
+ });
316
+ }
317
+ catch {
318
+ // Both unreachable
319
+ }
320
+ }
321
+ if (!response || !response.ok) {
322
+ const status = response?.status;
323
+ const reason = status === 401
279
324
  ? "Your Lasso API key is invalid or inactive."
280
- : response.status === 403
325
+ : status === 403
281
326
  ? "This project belongs to another workspace."
282
- : `The server rejected the project session (${response.status}).`;
327
+ : `The server rejected the project session (${status || "unreachable"}).`;
283
328
  console.warn(chalk_1.default.yellow("!") + ` Realtime collaboration unavailable: ${reason}`);
284
329
  return config;
285
330
  }
@@ -302,7 +347,7 @@ async function resolveCollabSession(cwd, fileEnv, projectId) {
302
347
  }
303
348
  catch (error) {
304
349
  const reason = error instanceof Error ? error.message : "request failed";
305
- console.warn(chalk_1.default.yellow("!") + ` Realtime collaboration unavailable: cannot reach ${realtimeUrl} (${reason}).`);
350
+ console.warn(chalk_1.default.yellow("!") + ` Realtime collaboration unavailable: cannot reach server (${reason}).`);
306
351
  return config;
307
352
  }
308
353
  }