@lasso-ai/cli 1.2.2 → 1.2.4

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,28 @@ 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
+ response = await (0, auth_1.fetchWithTimeout)(`${collabApi}/collab/projects/register`, {
215
+ method: "POST",
216
+ headers: {
217
+ "content-type": "application/json",
218
+ authorization: `Bearer ${apiKey}`,
219
+ },
220
+ body: JSON.stringify(payload),
221
+ });
222
+ }
223
+ catch {
224
+ // Fall back to main server if collab server unreachable
225
+ response = await (0, auth_1.fetchWithTimeout)(`${serverUrl}/api/v1/collab/projects/register`, {
226
+ method: "POST",
227
+ headers: {
228
+ "content-type": "application/json",
229
+ authorization: `Bearer ${apiKey}`,
230
+ },
231
+ body: JSON.stringify(payload),
232
+ });
233
+ }
210
234
  const body = (await response.json().catch(() => ({})));
211
235
  if (!response.ok) {
212
236
  if (response.status === 401) {
@@ -217,13 +241,10 @@ async function initProject(cwd, fileEnv) {
217
241
  }
218
242
  return { ok: false, error: body.message || `Project registration failed (${response.status}).` };
219
243
  }
220
- const projectId = body.project?.id;
244
+ const projectId = body.project?.id || existingConfig?.id;
221
245
  if (!projectId) {
222
246
  return { ok: false, error: "The server did not return a project id." };
223
247
  }
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
248
  await (0, client_1.registerWithHost)(domain, cwd, projectId, (0, paths_1.hostProxyPort)(fileEnv));
228
249
  if (framework === "next") {
229
250
  const injected = (0, next_config_1.ensureNextAllowedDevOrigin)(cwd, domain);
@@ -254,12 +275,15 @@ async function initProject(cwd, fileEnv) {
254
275
  */
255
276
  async function resolveCollabSession(cwd, fileEnv, projectId) {
256
277
  const realtimeUrl = realtimeUrlFrom(fileEnv).replace(/\/$/, "");
257
- const apiUrl = `${realtimeUrl}/api/v1`;
278
+ const serverUrl = (0, auth_1.serverUrlFrom)(fileEnv).replace(/\/$/, "");
279
+ const collabApi = `${realtimeUrl}/api/v1`;
280
+ const apiUrl = `${serverUrl}/api/v1`;
258
281
  const config = {
259
282
  projectId,
260
283
  name: "",
261
284
  version: "",
262
285
  realtimeUrl,
286
+ serverUrl,
263
287
  apiUrl,
264
288
  registered: false,
265
289
  claimed: false,
@@ -270,16 +294,34 @@ async function resolveCollabSession(cwd, fileEnv, projectId) {
270
294
  return config;
271
295
  }
272
296
  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
297
+ let response = null;
298
+ try {
299
+ response = await (0, auth_1.fetchWithTimeout)(`${collabApi}/collab/projects/${encodeURIComponent(projectId)}/session`, {
300
+ method: "POST",
301
+ headers: { authorization: `Bearer ${apiKey}` },
302
+ });
303
+ }
304
+ catch {
305
+ // Collab server unreachable, will fall back below
306
+ }
307
+ if (!response || (!response.ok && (response.status === 502 || response.status === 503 || response.status === 504 || response.status === 404))) {
308
+ try {
309
+ response = await (0, auth_1.fetchWithTimeout)(`${serverUrl}/api/v1/collab/projects/${encodeURIComponent(projectId)}/session`, {
310
+ method: "POST",
311
+ headers: { authorization: `Bearer ${apiKey}` },
312
+ });
313
+ }
314
+ catch {
315
+ // Both unreachable
316
+ }
317
+ }
318
+ if (!response || !response.ok) {
319
+ const status = response?.status;
320
+ const reason = status === 401
279
321
  ? "Your Lasso API key is invalid or inactive."
280
- : response.status === 403
322
+ : status === 403
281
323
  ? "This project belongs to another workspace."
282
- : `The server rejected the project session (${response.status}).`;
324
+ : `The server rejected the project session (${status || "unreachable"}).`;
283
325
  console.warn(chalk_1.default.yellow("!") + ` Realtime collaboration unavailable: ${reason}`);
284
326
  return config;
285
327
  }
@@ -302,7 +344,7 @@ async function resolveCollabSession(cwd, fileEnv, projectId) {
302
344
  }
303
345
  catch (error) {
304
346
  const reason = error instanceof Error ? error.message : "request failed";
305
- console.warn(chalk_1.default.yellow("!") + ` Realtime collaboration unavailable: cannot reach ${realtimeUrl} (${reason}).`);
347
+ console.warn(chalk_1.default.yellow("!") + ` Realtime collaboration unavailable: cannot reach server (${reason}).`);
306
348
  return config;
307
349
  }
308
350
  }