@alook/cli 0.0.121 → 0.0.122

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.
Files changed (2) hide show
  1. package/dist/index.js +166 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16986,6 +16986,7 @@ function loadCLIConfigForProfile(profile) {
16986
16986
  }
16987
16987
  const result = {
16988
16988
  server_url: cfg.server_url || "",
16989
+ session_token: cfg.session_token,
16989
16990
  watched_workspaces: cfg.watched_workspaces || []
16990
16991
  };
16991
16992
  const legacy = cfg.machine_token;
@@ -17010,6 +17011,7 @@ function saveCLIConfigForProfile(profile, profileConfig) {
17010
17011
  cfg.profiles[profile] = profileConfig;
17011
17012
  } else {
17012
17013
  cfg.server_url = profileConfig.server_url;
17014
+ cfg.session_token = profileConfig.session_token;
17013
17015
  cfg.watched_workspaces = profileConfig.watched_workspaces;
17014
17016
  delete cfg.machine_token;
17015
17017
  }
@@ -17429,6 +17431,30 @@ function openBrowser(url2) {
17429
17431
  function sleep(ms) {
17430
17432
  return new Promise((resolve) => setTimeout(resolve, ms));
17431
17433
  }
17434
+ function syncWorkspacesToConfig(serverWorkspaces, profile, sessionToken) {
17435
+ const cfg = loadCLIConfigForProfile(profile);
17436
+ const watched = cfg.watched_workspaces || [];
17437
+ const serverIds = new Set(serverWorkspaces.map((w) => w.id));
17438
+ for (const sw of serverWorkspaces) {
17439
+ const existing = watched.find((w) => w.id === sw.id);
17440
+ if (existing) {
17441
+ existing.status = "active";
17442
+ existing.name = sw.name;
17443
+ } else {
17444
+ watched.push({ id: sw.id, name: sw.name, token: "", status: "active", agent_ids: [] });
17445
+ }
17446
+ }
17447
+ for (const w of watched) {
17448
+ if (w.id && !serverIds.has(w.id) && w.status !== "registered") {
17449
+ w.status = "deleted";
17450
+ }
17451
+ }
17452
+ saveCLIConfigForProfile(profile, {
17453
+ server_url: cfg.server_url,
17454
+ session_token: sessionToken ?? cfg.session_token,
17455
+ watched_workspaces: watched
17456
+ });
17457
+ }
17432
17458
  async function pollAndActivate(opts) {
17433
17459
  const { deviceCode, expiresIn, serverUrl, profile } = opts;
17434
17460
  let interval = opts.interval;
@@ -17479,13 +17505,12 @@ async function pollAndActivate(opts) {
17479
17505
  const me = await client.getJSON("/api/me");
17480
17506
  email3 = me.email;
17481
17507
  } catch {}
17482
- let existingWorkspaceId = "";
17508
+ let serverWorkspaces = [];
17483
17509
  try {
17484
- const workspaces = await client.getJSON("/api/workspaces");
17485
- if (workspaces.length > 0) {
17486
- existingWorkspaceId = workspaces[0].id;
17487
- }
17510
+ serverWorkspaces = await client.getJSON("/api/workspaces");
17488
17511
  } catch {}
17512
+ syncWorkspacesToConfig(serverWorkspaces, profile, sessionToken);
17513
+ const existingWorkspaceId = serverWorkspaces.length > 0 ? serverWorkspaces[0].id : "";
17489
17514
  const mtUrl = existingWorkspaceId ? `/api/machine-tokens?workspace_id=${existingWorkspaceId}` : "/api/machine-tokens";
17490
17515
  let machineToken2;
17491
17516
  try {
@@ -17515,32 +17540,37 @@ if (process.argv.includes("--__login-poll")) {
17515
17540
  }
17516
17541
  async function checkExistingAuth(serverUrl, profile) {
17517
17542
  const config2 = loadCLIConfigForProfile(profile);
17543
+ const sessionToken = config2.session_token;
17518
17544
  const workspaces = config2.watched_workspaces || [];
17519
- if (workspaces.length === 0) {
17520
- return { valid: false };
17521
- }
17522
17545
  const ws = workspaces[0];
17523
- if (!ws.token) {
17546
+ const authToken = sessionToken || ws?.token;
17547
+ if (!authToken) {
17524
17548
  return { valid: false };
17525
17549
  }
17526
17550
  try {
17527
17551
  const res = await fetch(`${serverUrl}/api/workspaces`, {
17528
- headers: { Authorization: `Bearer ${ws.token}` }
17552
+ headers: { Authorization: `Bearer ${authToken}` }
17529
17553
  });
17530
17554
  if (!res.ok) {
17531
17555
  return { valid: false };
17532
17556
  }
17557
+ const serverWorkspaces = await res.json();
17558
+ const hasValidWorkspace = workspaces.some((w) => w.id && w.status !== "deleted");
17559
+ if (!hasValidWorkspace && serverWorkspaces.length > 0) {
17560
+ syncWorkspacesToConfig(serverWorkspaces, profile);
17561
+ }
17533
17562
  let email3;
17534
17563
  try {
17535
17564
  const meRes = await fetch(`${serverUrl}/api/me`, {
17536
- headers: { Authorization: `Bearer ${ws.token}` }
17565
+ headers: { Authorization: `Bearer ${authToken}` }
17537
17566
  });
17538
17567
  if (meRes.ok) {
17539
17568
  const me = await meRes.json();
17540
17569
  email3 = me.email;
17541
17570
  }
17542
17571
  } catch {}
17543
- return { valid: true, email: email3, workspaceName: ws.name ?? undefined };
17572
+ const workspaceName = (serverWorkspaces.length > 0 ? serverWorkspaces[0].name : undefined) || ws?.name || undefined;
17573
+ return { valid: true, email: email3, workspaceName };
17544
17574
  } catch {
17545
17575
  return { valid: false };
17546
17576
  }
@@ -22175,6 +22205,14 @@ function getRootOpts(command) {
22175
22205
 
22176
22206
  // lib/resolve-client.ts
22177
22207
  function resolveClientOpts(command, opts = {}) {
22208
+ const result = resolveClientOptsPartial(command, opts);
22209
+ if (!result.workspaceId) {
22210
+ console.error("Error: cannot determine workspace. Set ALOOK_WORKSPACE_ID env var or use --workspace flag.");
22211
+ process.exit(1);
22212
+ }
22213
+ return result;
22214
+ }
22215
+ function resolveClientOptsPartial(command, opts = {}) {
22178
22216
  const parentOpts = getRootOpts(command);
22179
22217
  const cfg = loadCLIConfigForProfile(parentOpts.profile);
22180
22218
  const serverUrl = parentOpts.server || process.env.ALOOK_SERVER_URL || cfg.server_url;
@@ -22206,16 +22244,12 @@ function resolveClientOpts(command, opts = {}) {
22206
22244
  ws = workspaces[0];
22207
22245
  }
22208
22246
  const envToken = process.env.ALOOK_TOKEN;
22209
- const token = envToken || ws?.token;
22247
+ const token = envToken || ws?.token || cfg.session_token;
22210
22248
  if (!token) {
22211
22249
  console.error(`Error: not registered. Run '${cmdPrefix()} register --token <token>' first.`);
22212
22250
  process.exit(1);
22213
22251
  }
22214
- const workspaceId = ws?.id || envWorkspaceId;
22215
- if (!workspaceId) {
22216
- console.error("Error: cannot determine workspace. Set ALOOK_WORKSPACE_ID env var or use --workspace flag.");
22217
- process.exit(1);
22218
- }
22252
+ const workspaceId = ws?.id || envWorkspaceId || undefined;
22219
22253
  return { serverUrl, token, workspaceId };
22220
22254
  }
22221
22255
 
@@ -23231,11 +23265,40 @@ import { readFileSync as readFileSync14 } from "fs";
23231
23265
  function slugify2(name) {
23232
23266
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
23233
23267
  }
23268
+ function sleep4(ms) {
23269
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
23270
+ }
23271
+ async function resolveWorkspaceId(client, configName) {
23272
+ let workspaces;
23273
+ try {
23274
+ workspaces = await client.getJSON("/api/workspaces");
23275
+ } catch (err) {
23276
+ console.error(`Error: failed to fetch workspaces: ${err instanceof Error ? err.message : err}`);
23277
+ process.exit(1);
23278
+ }
23279
+ for (const ws of workspaces) {
23280
+ try {
23281
+ const agents = await client.getJSON(`/api/agents?workspace_id=${ws.id}`);
23282
+ if (agents.length === 0) {
23283
+ return { workspaceId: ws.id, created: false };
23284
+ }
23285
+ } catch {}
23286
+ }
23287
+ const wsName = configName || "Personal";
23288
+ try {
23289
+ const newWs = await client.postJSON("/api/workspaces", { name: wsName, slug: slugify2(wsName) });
23290
+ console.log(`Created workspace: ${newWs.name} (${newWs.id})`);
23291
+ return { workspaceId: newWs.id, created: true };
23292
+ } catch (err) {
23293
+ console.error(`Error: failed to create workspace: ${err instanceof Error ? err.message : err}`);
23294
+ process.exit(1);
23295
+ }
23296
+ }
23234
23297
  function workspaceCommand() {
23235
23298
  const cmd = new Command13("workspace").description("Manage workspaces");
23236
23299
  cmd.command("init").description("Initialize a workspace from a JSON configuration file").requiredOption("--json-file <path>", "Path to the JSON configuration file").option("--name <name>", "Workspace name (overrides JSON)").option("--json", "Output as JSON").action(async (opts, command) => {
23237
- const { serverUrl, token, workspaceId } = resolveClientOpts(command);
23238
- const client = new APIClient(serverUrl, token, workspaceId);
23300
+ const { serverUrl, token, workspaceId: resolvedWorkspaceId } = resolveClientOptsPartial(command);
23301
+ const client = new APIClient(serverUrl, token, resolvedWorkspaceId);
23239
23302
  let configJson;
23240
23303
  try {
23241
23304
  configJson = readFileSync14(opts.jsonFile, "utf-8");
@@ -23254,9 +23317,89 @@ function workspaceCommand() {
23254
23317
  console.error("Error: JSON must contain a 'members' array with at least one member");
23255
23318
  process.exit(1);
23256
23319
  }
23320
+ let targetWorkspaceId = resolvedWorkspaceId;
23321
+ if (!targetWorkspaceId) {
23322
+ const resolved = await resolveWorkspaceId(client, opts.name || config2.name);
23323
+ targetWorkspaceId = resolved.workspaceId;
23324
+ const parentOpts = getRootOpts(command);
23325
+ const cfg = loadCLIConfigForProfile(parentOpts.profile);
23326
+ const registeredWs = cfg.watched_workspaces?.find((w) => w.status === "registered" && !w.id);
23327
+ if (registeredWs) {
23328
+ try {
23329
+ await client.postJSON("/api/machine-tokens/bind-workspace", { workspace_id: targetWorkspaceId });
23330
+ console.log("Machine token bound to workspace. Waiting for runtime registration...");
23331
+ } catch (err) {
23332
+ console.warn(`Warning: bind-workspace failed: ${err instanceof Error ? err.message : err}`);
23333
+ }
23334
+ }
23335
+ let runtimes2 = [];
23336
+ const wsClient = new APIClient(serverUrl, token, targetWorkspaceId);
23337
+ for (let attempt = 0;attempt < 15; attempt++) {
23338
+ try {
23339
+ runtimes2 = await wsClient.getJSON("/api/runtimes");
23340
+ if (runtimes2.length > 0)
23341
+ break;
23342
+ } catch {}
23343
+ await sleep4(1000);
23344
+ }
23345
+ if (runtimes2.length === 0) {
23346
+ console.error(`Error: No daemon registered after waiting. Run '${cmdPrefix()} daemon start' first.`);
23347
+ process.exit(1);
23348
+ }
23349
+ const OFFLINE_THRESHOLD_MS3 = 5 * 60 * 1000;
23350
+ const now2 = Date.now();
23351
+ const onlineRuntime2 = runtimes2.find((r) => {
23352
+ if (!r.machineLastSeenAt)
23353
+ return false;
23354
+ const lastSeen = new Date(r.machineLastSeenAt.includes("Z") ? r.machineLastSeenAt : r.machineLastSeenAt + "Z").getTime();
23355
+ return now2 - lastSeen < OFFLINE_THRESHOLD_MS3;
23356
+ });
23357
+ const runtime2 = onlineRuntime2 || runtimes2[0];
23358
+ const members2 = config2.members.map((m) => ({
23359
+ ...m,
23360
+ runtime_id: runtime2.id
23361
+ }));
23362
+ const payload2 = {
23363
+ name: opts.name || config2.name,
23364
+ scenario: config2.scenario,
23365
+ members: members2
23366
+ };
23367
+ try {
23368
+ const res = await wsClient.postJSON("/api/studios", payload2);
23369
+ try {
23370
+ const freshCfg = loadCLIConfigForProfile(parentOpts.profile);
23371
+ const watched = freshCfg.watched_workspaces || [];
23372
+ const existing = watched.find((w) => w.id === targetWorkspaceId);
23373
+ if (existing) {
23374
+ existing.status = "active";
23375
+ existing.name = res.workspace.name;
23376
+ } else {
23377
+ watched.push({ id: targetWorkspaceId, name: res.workspace.name, token, status: "active", agent_ids: [] });
23378
+ }
23379
+ freshCfg.watched_workspaces = watched;
23380
+ saveCLIConfigForProfile(parentOpts.profile, freshCfg);
23381
+ } catch {}
23382
+ if (opts.json)
23383
+ return printJSON(res);
23384
+ console.log(`
23385
+ Workspace initialized: ${res.studio.name || res.workspace.name}`);
23386
+ console.log("Agents created:");
23387
+ for (const agent2 of res.agents) {
23388
+ const email3 = agent2.email_handle ? toAlookAddress(agent2.email_handle) : "no email";
23389
+ console.log(` - ${agent2.name} (${email3})`);
23390
+ }
23391
+ console.log(`
23392
+ Open: ${serverUrl}/w/${res.workspace.slug}`);
23393
+ } catch (err) {
23394
+ console.error(`Error: failed to create workspace: ${err instanceof Error ? err.message : err}`);
23395
+ process.exit(1);
23396
+ }
23397
+ return;
23398
+ }
23399
+ let targetClient = new APIClient(serverUrl, token, targetWorkspaceId);
23257
23400
  let runtimes;
23258
23401
  try {
23259
- runtimes = await client.getJSON("/api/runtimes");
23402
+ runtimes = await targetClient.getJSON("/api/runtimes");
23260
23403
  } catch (err) {
23261
23404
  console.error(`Error: failed to fetch runtimes: ${err instanceof Error ? err.message : err}`);
23262
23405
  process.exit(1);
@@ -23274,14 +23417,12 @@ function workspaceCommand() {
23274
23417
  return now - lastSeen < OFFLINE_THRESHOLD_MS2;
23275
23418
  });
23276
23419
  let runtime = onlineRuntime || runtimes[0];
23277
- let targetWorkspaceId = workspaceId;
23278
- let targetClient = client;
23279
23420
  try {
23280
- const agents = await client.getJSON(`/api/agents?workspace_id=${workspaceId}`);
23421
+ const agents = await targetClient.getJSON(`/api/agents?workspace_id=${targetWorkspaceId}`);
23281
23422
  if (agents.length > 0) {
23282
23423
  console.log("Current workspace has existing agents. Creating a new workspace...");
23283
23424
  const wsName = opts.name || config2.name || "New Workspace";
23284
- const newWs = await client.postJSON("/api/workspaces", { name: wsName, slug: slugify2(wsName) });
23425
+ const newWs = await targetClient.postJSON("/api/workspaces", { name: wsName, slug: slugify2(wsName) });
23285
23426
  targetWorkspaceId = newWs.id;
23286
23427
  targetClient = new APIClient(serverUrl, token, targetWorkspaceId);
23287
23428
  console.log(`Created workspace: ${newWs.name} (${newWs.id})`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alook/cli",
3
- "version": "0.0.121",
3
+ "version": "0.0.122",
4
4
  "description": "Alook CLI — Enable Your Person Colleague",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/alookai/alook#readme",