@alook/cli 0.0.59 → 0.0.61

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/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import { Command as Command11 } from "commander";
20
20
  // commands/register.ts
21
21
  import { Command } from "commander";
22
22
  import { execSync } from "child_process";
23
- import { hostname as hostname2 } from "os";
23
+ import { hostname as hostname4 } from "os";
24
24
 
25
25
  // lib/client.ts
26
26
  class APIClient {
@@ -110,485 +110,46 @@ class APIClient {
110
110
  import { readFileSync, writeFileSync, mkdirSync } from "fs";
111
111
  import { join } from "path";
112
112
  import { homedir } from "os";
113
-
114
- // lib/env.ts
115
- function isDev() {
116
- return !!process.env.ALOOK_SERVER_URL;
117
- }
118
- function cmdPrefix() {
119
- return isDev() ? "pnpm dev:cli" : "npx @alook/cli";
120
- }
121
-
122
- // lib/config.ts
123
- function configDir() {
124
- if (isDev() && process.env.ALOOK_PROJECT_ROOT) {
125
- return join(process.env.ALOOK_PROJECT_ROOT, ".alook");
126
- }
127
- return join(homedir(), ".alook");
128
- }
129
- function configPath() {
130
- return join(configDir(), "config.json");
131
- }
132
- function loadCLIConfig() {
133
- try {
134
- return JSON.parse(readFileSync(configPath(), "utf-8"));
135
- } catch {
136
- return {};
137
- }
138
- }
139
- function loadCLIConfigForProfile(profile) {
140
- const cfg = loadCLIConfig();
141
- const profileName = profile || cfg.default_profile;
142
- if (profileName && cfg.profiles?.[profileName]) {
143
- return cfg.profiles[profileName];
144
- }
145
- return {
146
- server_url: cfg.server_url || "",
147
- watched_workspaces: cfg.watched_workspaces || []
148
- };
149
- }
150
- function saveCLIConfig(cfg) {
151
- mkdirSync(configDir(), { recursive: true, mode: 448 });
152
- writeFileSync(configPath(), JSON.stringify(cfg, null, 2), { mode: 384 });
153
- }
154
- function saveCLIConfigForProfile(profile, profileConfig) {
155
- const cfg = loadCLIConfig();
156
- if (profile) {
157
- if (!cfg.profiles)
158
- cfg.profiles = {};
159
- cfg.profiles[profile] = profileConfig;
160
- } else {
161
- cfg.server_url = profileConfig.server_url;
162
- cfg.watched_workspaces = profileConfig.watched_workspaces;
163
- }
164
- saveCLIConfig(cfg);
165
- }
166
-
167
- // daemon/pidfile.ts
168
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync2 } from "fs";
169
- import { dirname as dirname2 } from "path";
170
-
171
- // daemon/config.ts
172
- import { hostname } from "os";
173
- import { join as join3 } from "path";
174
-
175
- // lib/version.ts
176
- import { readFileSync as readFileSync2 } from "fs";
177
- import { join as join2, dirname } from "path";
178
- import { fileURLToPath } from "url";
179
- function getCurrentVersion() {
180
- const __dirname2 = dirname(fileURLToPath(import.meta.url));
181
- const candidates = [
182
- join2(__dirname2, "..", "package.json"),
183
- join2(__dirname2, "..", "..", "package.json")
184
- ];
185
- for (const candidate of candidates) {
186
- try {
187
- const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
188
- if (typeof pkg.version === "string")
189
- return pkg.version;
190
- } catch {}
191
- }
192
- return "unknown";
193
- }
194
-
195
- // daemon/config.ts
196
- function pidFilePath(profile) {
197
- const name = profile ? `daemon_${profile}.pid` : "daemon.pid";
198
- return join3(configDir(), name);
199
- }
200
- function lastUpdateMarkerPath(profile) {
201
- const name = profile ? `last_update_${profile}` : "last_update";
202
- return join3(configDir(), name);
203
- }
204
- function daemonLogDir() {
205
- return join3(configDir(), "daemon", "logs");
206
- }
207
- function sessionRunnerLogDir() {
208
- return join3(configDir(), "daemon", "session-runners");
209
- }
210
- function daemonLogFilePath(date = new Date) {
211
- const y = date.getFullYear();
212
- const m = String(date.getMonth() + 1).padStart(2, "0");
213
- const d = String(date.getDate()).padStart(2, "0");
214
- return join3(daemonLogDir(), `${y}-${m}-${d}.log`);
215
- }
216
- function parseDuration(s) {
217
- if (!s)
218
- return 0;
219
- let total = 0;
220
- const regex = /(\d+(?:\.\d+)?)(ns|us|µs|ms|s|m|h)/g;
221
- let match;
222
- while ((match = regex.exec(s)) !== null) {
223
- const val = parseFloat(match[1]);
224
- switch (match[2]) {
225
- case "ns":
226
- total += val / 1e6;
227
- break;
228
- case "us":
229
- case "µs":
230
- total += val / 1000;
231
- break;
232
- case "ms":
233
- total += val;
234
- break;
235
- case "s":
236
- total += val * 1000;
237
- break;
238
- case "m":
239
- total += val * 60000;
240
- break;
241
- case "h":
242
- total += val * 3600000;
243
- break;
244
- }
245
- }
246
- return total;
247
- }
248
- function loadDaemonConfig(profile) {
249
- const h = hostname();
250
- let daemonId = process.env.ALOOK_DAEMON_ID || h;
251
- if (profile && !daemonId.endsWith(`-${profile}`)) {
252
- daemonId = `${daemonId}-${profile}`;
253
- }
254
- const defaultRoot = join3(configDir(), profile ? `workspaces_${profile}` : "workspaces");
255
- const workspacesRoot = process.env.ALOOK_WORKSPACES_ROOT || defaultRoot;
256
- return {
257
- serverURL: normalizeServerBaseURL(process.env.ALOOK_SERVER_URL || "https://alook.ai"),
258
- claudePath: process.env.ALOOK_CLAUDE_PATH || "claude",
259
- codexPath: process.env.ALOOK_CODEX_PATH || "codex",
260
- opencodePath: process.env.ALOOK_OPENCODE_PATH || "opencode",
261
- claudeModel: process.env.ALOOK_CLAUDE_MODEL || "",
262
- codexModel: process.env.ALOOK_CODEX_MODEL || "",
263
- opencodeModel: process.env.ALOOK_OPENCODE_MODEL || "",
264
- pollInterval: parseDuration(process.env.ALOOK_DAEMON_POLL_INTERVAL || "3s"),
265
- agentTimeout: parseDuration(process.env.ALOOK_AGENT_TIMEOUT || "12h"),
266
- messageInactivityTimeout: parseDuration(process.env.ALOOK_MESSAGE_INACTIVITY_TIMEOUT || "20m"),
267
- maxConcurrentTasks: parseInt(process.env.ALOOK_DAEMON_MAX_CONCURRENT_TASKS || "20"),
268
- daemonId,
269
- deviceName: process.env.ALOOK_DAEMON_DEVICE_NAME || h,
270
- workspacesRoot,
271
- cliVersion: getCurrentVersion()
272
- };
273
- }
274
- function normalizeServerBaseURL(url) {
275
- return url.replace(/^ws:\/\//, "http://").replace(/^wss:\/\//, "https://").replace(/\/ws$/, "");
276
- }
277
-
278
- // lib/logger.ts
279
- var LEVELS = {
280
- debug: 0,
281
- info: 1,
282
- warn: 2,
283
- error: 3,
284
- silent: 4
285
- };
286
- var LABELS = {
287
- debug: "DEBUG",
288
- info: "INFO ",
289
- warn: "WARN ",
290
- error: "ERROR"
291
- };
292
- var COLORS = {
293
- debug: "\x1B[90m",
294
- info: "\x1B[36m",
295
- warn: "\x1B[33m",
296
- error: "\x1B[31m"
297
- };
298
- var RESET = "\x1B[0m";
299
- var DIM = "\x1B[2m";
300
- var BOLD = "\x1B[1m";
301
- function useColor() {
302
- if (process.env.NO_COLOR !== undefined)
303
- return false;
304
- if (process.env.FORCE_COLOR !== undefined)
305
- return true;
306
- return process.stdout.isTTY === true;
307
- }
308
- function timestamp() {
309
- const d = new Date;
310
- const Y = d.getFullYear();
311
- const M = String(d.getMonth() + 1).padStart(2, "0");
312
- const D = String(d.getDate()).padStart(2, "0");
313
- const h = String(d.getHours()).padStart(2, "0");
314
- const m = String(d.getMinutes()).padStart(2, "0");
315
- const s = String(d.getSeconds()).padStart(2, "0");
316
- return `${Y}-${M}-${D} ${h}:${m}:${s}`;
317
- }
318
-
319
- class Logger {
320
- level;
321
- color;
322
- module;
323
- constructor(opts = {}) {
324
- const envLevel = process.env.ALOOK_LOG_LEVEL;
325
- this.level = LEVELS[opts.level ?? envLevel ?? "info"];
326
- this.color = useColor();
327
- this.module = opts.module;
328
- }
329
- setLevel(level) {
330
- this.level = LEVELS[level];
331
- }
332
- child(module) {
333
- const child = new Logger({ level: this.levelName(), module });
334
- return child;
335
- }
336
- debug(msg, ...args) {
337
- this.write("debug", msg, args);
338
- }
339
- info(msg, ...args) {
340
- this.write("info", msg, args);
341
- }
342
- warn(msg, ...args) {
343
- this.write("warn", msg, args);
344
- }
345
- error(msg, ...args) {
346
- this.write("error", msg, args);
347
- }
348
- levelName() {
349
- for (const [name, num] of Object.entries(LEVELS)) {
350
- if (num === this.level)
351
- return name;
352
- }
353
- return "info";
354
- }
355
- write(level, msg, args) {
356
- if (LEVELS[level] < this.level)
357
- return;
358
- const ts = timestamp();
359
- const label = LABELS[level];
360
- const mod = this.module ? `[${this.module}]` : "";
361
- let line;
362
- if (this.color) {
363
- const c = COLORS[level];
364
- const modStr = mod ? ` ${BOLD}${mod}${RESET}` : "";
365
- line = `${DIM}${ts}${RESET} ${c}${label}${RESET}${modStr} ${msg}`;
366
- } else {
367
- const modStr = mod ? ` ${mod}` : "";
368
- line = `${ts} ${label}${modStr} ${msg}`;
369
- }
370
- const dest = level === "error" ? process.stderr : process.stdout;
371
- dest.write(line + `
372
- `);
373
- for (const a of args) {
374
- if (a instanceof Error) {
375
- dest.write(` ${a.message}
376
- `);
377
- if (a.stack && this.level <= LEVELS.debug) {
378
- dest.write(` ${a.stack}
379
- `);
380
- }
381
- } else if (a !== null && typeof a === "object") {
382
- const pairs = Object.entries(a).map(([k, v]) => `${k}=${typeof v === "object" ? JSON.stringify(v) : v}`).join(" ");
383
- if (pairs)
384
- dest.write(` ${pairs}
385
- `);
386
- } else if (a !== undefined) {
387
- dest.write(` ${String(a)}
388
- `);
389
- }
390
- }
391
- }
392
- }
393
- function createLogger(opts) {
394
- return new Logger(opts);
395
- }
396
- var log = createLogger();
397
-
398
- // daemon/pidfile.ts
399
- var log2 = createLogger({ module: "pidfile" });
400
- function isProcessAlive(pid) {
401
- try {
402
- process.kill(pid, 0);
403
- return true;
404
- } catch {
405
- return false;
406
- }
407
- }
408
- function readDaemonPid(profile) {
409
- try {
410
- const content = readFileSync3(pidFilePath(profile), "utf-8").trim();
411
- const pid = parseInt(content, 10);
412
- return Number.isNaN(pid) ? null : pid;
413
- } catch {
414
- return null;
415
- }
416
- }
417
- function acquireDaemonPid(profile) {
418
- const pidPath = pidFilePath(profile);
419
- try {
420
- const content = readFileSync3(pidPath, "utf-8").trim();
421
- const existingPid = parseInt(content, 10);
422
- if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
423
- log2.error(`Another daemon is already running (PID ${existingPid}). ` + `Remove ${pidPath} if this is stale.`);
424
- return false;
425
- }
426
- } catch {}
427
- mkdirSync2(dirname2(pidPath), { recursive: true, mode: 448 });
428
- writeFileSync2(pidPath, String(process.pid), { mode: 384 });
429
- return true;
430
- }
431
- function removePidFileIfMatches(pid, profile) {
432
- const pidPath = pidFilePath(profile);
433
- const onDisk = readDaemonPid(profile);
434
- if (onDisk !== pid)
435
- return;
436
- try {
437
- unlinkSync(pidPath);
438
- } catch {}
439
- }
440
- function releaseDaemonPid(profile) {
441
- removePidFileIfMatches(process.pid, profile);
442
- }
443
-
444
- // commands/register.ts
445
- function isCommandAvailable(cmd) {
446
- try {
447
- const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
448
- execSync(check, { stdio: "ignore" });
449
- return true;
450
- } catch {
451
- return false;
452
- }
453
- }
454
- function detectRuntimes() {
455
- const found = [];
456
- for (const type of ["claude", "codex", "opencode"]) {
457
- if (isCommandAvailable(type)) {
458
- let version = "";
459
- try {
460
- version = execSync(`${type} --version`, { encoding: "utf-8" }).trim();
461
- } catch {}
462
- found.push({ type, version });
463
- }
464
- }
465
- return found;
466
- }
467
- function registerCommand() {
468
- const cmd = new Command("register").description("Register CLI with your Alook account").requiredOption("--token <token>", "API token (starts with al_)").option("--server <url>", "Server URL").option("--profile <name>", "Profile name").action(async (opts, command) => {
469
- const token = opts.token;
470
- const profile = opts.profile || command.parent?.opts().profile;
471
- const serverUrl = opts.server || command.parent?.opts().server || process.env.ALOOK_SERVER_URL || "https://alook.ai";
472
- if (!token) {
473
- console.error(`Error: --token is required
474
- Usage: ${cmdPrefix()} register --token <token>`);
475
- process.exit(1);
476
- }
477
- if (!token.startsWith("al_")) {
478
- console.error("Error: invalid token format: must start with 'al_'");
479
- process.exit(1);
480
- }
481
- const client = new APIClient(serverUrl, token);
482
- let me;
483
- try {
484
- me = await client.getJSON("/api/me");
485
- } catch (err) {
486
- console.error(`Error: failed to verify token: ${err instanceof Error ? err.message : err}`);
487
- process.exit(1);
488
- }
489
- let workspaces;
490
- try {
491
- workspaces = await client.getJSON("/api/workspaces");
492
- } catch (err) {
493
- console.error(`Error: failed to fetch workspaces: ${err instanceof Error ? err.message : err}`);
494
- process.exit(1);
495
- }
496
- if (!workspaces.length) {
497
- console.error("Error: no workspaces found for this user");
498
- process.exit(1);
499
- }
500
- console.log("Scanning for AI runtimes...");
501
- const runtimes = detectRuntimes();
502
- if (runtimes.length === 0) {
503
- console.error("Error: no runtimes found. Install claude, codex, or opencode first.");
504
- process.exit(1);
505
- }
506
- console.log(`Found: ${runtimes.map((r) => r.type).join(", ")}`);
507
- const host = hostname2();
508
- console.log("Registering runtime...");
509
- let activateResp;
510
- try {
511
- const res = await fetch(`${serverUrl}/api/machine-tokens/activate`, {
512
- method: "POST",
513
- headers: { "Content-Type": "application/json" },
514
- body: JSON.stringify({ token, hostname: host, runtimes })
515
- });
516
- if (!res.ok) {
517
- const text = await res.text();
518
- console.error(`Error: registration failed (${res.status}): ${text}`);
519
- process.exit(1);
520
- }
521
- activateResp = await res.json();
522
- } catch (err) {
523
- console.error(`Error: failed to activate: ${err instanceof Error ? err.message : err}`);
524
- process.exit(1);
525
- }
526
- const ws = workspaces.find((w) => w.id === activateResp.workspace_id) || workspaces[0];
527
- const wsClient = new APIClient(serverUrl, token, ws.id);
528
- let agentIds = [];
529
- try {
530
- const agents = await wsClient.getJSON(`/api/agents?workspace_id=${ws.id}`);
531
- agentIds = agents.map((a) => a.id);
532
- } catch {}
533
- const existing = loadCLIConfigForProfile(profile);
534
- const watched = existing.watched_workspaces || [];
535
- const idx = watched.findIndex((w) => w.id === ws.id);
536
- if (idx >= 0) {
537
- watched[idx] = { id: ws.id, name: ws.name, token, agent_ids: agentIds };
538
- } else {
539
- watched.push({ id: ws.id, name: ws.name, token, agent_ids: agentIds });
540
- }
541
- saveCLIConfigForProfile(profile, {
542
- server_url: serverUrl,
543
- watched_workspaces: watched
544
- });
545
- console.log(`
546
- Registered as ${me.email}`);
547
- console.log(`Workspace: ${ws.name} (${ws.id})`);
548
- console.log(`Runtimes: ${activateResp.runtimes.map((r) => r.provider).join(", ")}`);
549
- const daemonPid = readDaemonPid(profile);
550
- if (daemonPid && isProcessAlive(daemonPid)) {
551
- try {
552
- process.kill(daemonPid, "SIGHUP");
553
- console.log(`
554
- Daemon (pid ${daemonPid}) notified — workspace will be active shortly.`);
555
- } catch {
556
- console.log(`
557
- Daemon is running but could not be notified. Restart it to pick up the new workspace.`);
558
- }
559
- } else {
560
- const startCmd = isDev() ? `${cmdPrefix()} daemon start --foreground` : `${cmdPrefix()} daemon start`;
561
- console.log();
562
- console.log(`Run '${startCmd}' to start the daemon.`);
563
- }
564
- });
565
- return cmd;
113
+ function configDir() {
114
+ return process.env.ALOOK_PROJECT_ROOT || join(homedir(), ".alook");
566
115
  }
567
-
568
- // commands/status.ts
569
- import { Command as Command2 } from "commander";
570
- function statusCommand() {
571
- const cmd = new Command2("status").description("Show registration status").action((_opts, command) => {
572
- const profile = command.parent?.opts().profile;
573
- const cfg = loadCLIConfigForProfile(profile);
574
- const ws = cfg.watched_workspaces?.[0];
575
- if (!ws?.token) {
576
- console.log("Not registered");
577
- console.log(`Run '${cmdPrefix()} register --token <token>' to register.`);
578
- return;
579
- }
580
- console.log("Status: Registered");
581
- console.log(`Server: ${cfg.server_url}`);
582
- console.log(`Workspace: ${ws.name} (${ws.id})`);
583
- });
584
- return cmd;
116
+ function configPath() {
117
+ return join(configDir(), "config.json");
118
+ }
119
+ function loadCLIConfig() {
120
+ try {
121
+ return JSON.parse(readFileSync(configPath(), "utf-8"));
122
+ } catch {
123
+ return {};
124
+ }
125
+ }
126
+ function loadCLIConfigForProfile(profile) {
127
+ const cfg = loadCLIConfig();
128
+ const profileName = profile || cfg.default_profile;
129
+ if (profileName && cfg.profiles?.[profileName]) {
130
+ return cfg.profiles[profileName];
131
+ }
132
+ return {
133
+ server_url: cfg.server_url || "",
134
+ watched_workspaces: cfg.watched_workspaces || []
135
+ };
136
+ }
137
+ function saveCLIConfig(cfg) {
138
+ mkdirSync(configDir(), { recursive: true, mode: 448 });
139
+ writeFileSync(configPath(), JSON.stringify(cfg, null, 2), { mode: 384 });
140
+ }
141
+ function saveCLIConfigForProfile(profile, profileConfig) {
142
+ const cfg = loadCLIConfig();
143
+ if (profile) {
144
+ if (!cfg.profiles)
145
+ cfg.profiles = {};
146
+ cfg.profiles[profile] = profileConfig;
147
+ } else {
148
+ cfg.server_url = profileConfig.server_url;
149
+ cfg.watched_workspaces = profileConfig.watched_workspaces;
150
+ }
151
+ saveCLIConfig(cfg);
585
152
  }
586
-
587
- // commands/daemon.ts
588
- import { Command as Command3 } from "commander";
589
- import { spawn as spawn3 } from "child_process";
590
- import { openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync6 } from "fs";
591
- import { dirname as dirname4 } from "path";
592
153
 
593
154
  // ../shared/src/constants.ts
594
155
  var TaskStatus = {
@@ -761,7 +322,7 @@ __export(exports_external, {
761
322
  instanceof: () => _instanceof,
762
323
  includes: () => _includes,
763
324
  httpUrl: () => httpUrl,
764
- hostname: () => hostname4,
325
+ hostname: () => hostname2,
765
326
  hex: () => hex2,
766
327
  hash: () => hash,
767
328
  guid: () => guid2,
@@ -2212,7 +1773,7 @@ __export(exports_regexes, {
2212
1773
  idnEmail: () => idnEmail,
2213
1774
  httpProtocol: () => httpProtocol,
2214
1775
  html5Email: () => html5Email,
2215
- hostname: () => hostname3,
1776
+ hostname: () => hostname,
2216
1777
  hex: () => hex,
2217
1778
  guid: () => guid,
2218
1779
  extendedDuration: () => extendedDuration,
@@ -2270,7 +1831,7 @@ var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]
2270
1831
  var cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
2271
1832
  var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
2272
1833
  var base64url = /^[A-Za-z0-9_-]*$/;
2273
- var hostname3 = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
1834
+ var hostname = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
2274
1835
  var domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
2275
1836
  var httpProtocol = /^https?$/;
2276
1837
  var e164 = /^\+[1-9]\d{6,14}$/;
@@ -12115,7 +11676,7 @@ function finalize(ctx, schema) {
12115
11676
  result.$schema = "http://json-schema.org/draft-07/schema#";
12116
11677
  } else if (ctx.target === "draft-04") {
12117
11678
  result.$schema = "http://json-schema.org/draft-04/schema#";
12118
- } else if (ctx.target === "openapi-3.0") {} else {}
11679
+ } else if (ctx.target === "openapi-3.0") {}
12119
11680
  if (ctx.external?.uri) {
12120
11681
  const id = ctx.external.registry.get(schema)?.id;
12121
11682
  if (!id)
@@ -12359,7 +11920,7 @@ var literalProcessor = (schema, ctx, json, _params) => {
12359
11920
  if (val === undefined) {
12360
11921
  if (ctx.unrepresentable === "throw") {
12361
11922
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
12362
- } else {}
11923
+ }
12363
11924
  } else if (typeof val === "bigint") {
12364
11925
  if (ctx.unrepresentable === "throw") {
12365
11926
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -12894,7 +12455,7 @@ __export(exports_schemas2, {
12894
12455
  int: () => int,
12895
12456
  instanceof: () => _instanceof,
12896
12457
  httpUrl: () => httpUrl,
12897
- hostname: () => hostname4,
12458
+ hostname: () => hostname2,
12898
12459
  hex: () => hex2,
12899
12460
  hash: () => hash,
12900
12461
  guid: () => guid2,
@@ -13546,7 +13107,7 @@ var ZodCustomStringFormat = /* @__PURE__ */ $constructor("ZodCustomStringFormat"
13546
13107
  function stringFormat(format, fnOrRegex, _params = {}) {
13547
13108
  return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
13548
13109
  }
13549
- function hostname4(_params) {
13110
+ function hostname2(_params) {
13550
13111
  return _stringFormat(ZodCustomStringFormat, "hostname", exports_regexes.hostname, _params);
13551
13112
  }
13552
13113
  function hex2(_params) {
@@ -15022,7 +14583,9 @@ var PollMeetingItemSchema = exports_external.object({
15022
14583
  meeting_url: exports_external.string(),
15023
14584
  participants: exports_external.array(exports_external.string()),
15024
14585
  workspace_id: exports_external.string(),
15025
- agent_name: exports_external.string()
14586
+ agent_id: exports_external.string(),
14587
+ agent_name: exports_external.string(),
14588
+ title: exports_external.string().optional()
15026
14589
  });
15027
14590
  var PollResponseSchema = exports_external.object({
15028
14591
  tasks: exports_external.array(TaskApiSchema),
@@ -15053,10 +14616,11 @@ var ActivateTokenRequestSchema = exports_external.object({
15053
14616
  runtimes: exports_external.array(ActivateTokenRuntimeSchema).min(1)
15054
14617
  });
15055
14618
  var RegisterDaemonRequestSchema = exports_external.object({
15056
- workspace_id: exports_external.string().min(1),
14619
+ workspace_id: exports_external.string().min(1).optional(),
15057
14620
  daemon_id: exports_external.string().min(1),
15058
14621
  device_name: exports_external.string().optional().default(""),
15059
14622
  cli_version: exports_external.string().optional().default(""),
14623
+ workspaces_root: exports_external.string().optional().default(""),
15060
14624
  runtimes: exports_external.array(DaemonRuntimeItemSchema).min(1)
15061
14625
  });
15062
14626
  var DeregisterRequestSchema = exports_external.object({
@@ -15265,7 +14829,8 @@ var EmailNotifyRequestSchema = exports_external.object({
15265
14829
  meetingInfo: MeetingInfoSchema.nullable().optional(),
15266
14830
  attachments: exports_external.string().optional(),
15267
14831
  traceId: exports_external.string().optional(),
15268
- sourceTaskId: exports_external.string().optional()
14832
+ sourceTaskId: exports_external.string().optional(),
14833
+ isInternal: exports_external.boolean().optional().default(false)
15269
14834
  });
15270
14835
  var CreateEmailAccountSchema = exports_external.object({
15271
14836
  emailAddress: exports_external.string().email("valid email required"),
@@ -15946,7 +15511,7 @@ function sql(strings, ...params) {
15946
15511
  return new SQL([new StringChunk(str)]);
15947
15512
  }
15948
15513
  sql2.raw = raw;
15949
- function join4(chunks, separator) {
15514
+ function join2(chunks, separator) {
15950
15515
  const result = [];
15951
15516
  for (const [i, chunk] of chunks.entries()) {
15952
15517
  if (i > 0 && separator !== undefined) {
@@ -15956,7 +15521,7 @@ function sql(strings, ...params) {
15956
15521
  }
15957
15522
  return new SQL(result);
15958
15523
  }
15959
- sql2.join = join4;
15524
+ sql2.join = join2;
15960
15525
  function identifier(value) {
15961
15526
  return new Name(value);
15962
15527
  }
@@ -17157,13 +16722,24 @@ var meetingSession = sqliteTable("meeting_session", {
17157
16722
  var machineToken = sqliteTable("machine_token", {
17158
16723
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
17159
16724
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
17160
- workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
16725
+ workspaceId: text("workspace_id").references(() => workspace.id, { onDelete: "cascade" }),
17161
16726
  token: text("token").unique().notNull(),
17162
16727
  name: text("name").notNull().default(""),
17163
16728
  status: text("status").notNull().default("active"),
17164
16729
  lastUsedAt: text("last_used_at"),
17165
16730
  createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
17166
16731
  }, (t) => [index("idx_machine_token").on(t.token)]);
16732
+ var messageFlag = sqliteTable("message_flag", {
16733
+ id: text("id").primaryKey().$defaultFn(() => nanoid3()),
16734
+ messageId: text("message_id").notNull().references(() => message.id, { onDelete: "cascade" }),
16735
+ userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
16736
+ workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
16737
+ createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
16738
+ }, (t) => [
16739
+ unique("message_flag_message_user").on(t.messageId, t.userId),
16740
+ index("idx_message_flag_ws_user_created").on(t.workspaceId, t.userId, t.createdAt),
16741
+ index("idx_message_flag_message_user").on(t.messageId, t.userId)
16742
+ ]);
17167
16743
  var conversationMap = sqliteTable("conversation_map", {
17168
16744
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
17169
16745
  key: text("key").notNull(),
@@ -17255,6 +16831,468 @@ function semverGte(a, b) {
17255
16831
  }
17256
16832
  return true;
17257
16833
  }
16834
+ // ../shared/src/mode.ts
16835
+ function resolveMode(signals) {
16836
+ if (signals.nodeEnv === "development" && !signals.cmdPrefix)
16837
+ return "dev";
16838
+ if (signals.serverUrl && !signals.cmdPrefix)
16839
+ return "dev";
16840
+ if (signals.cmdPrefix)
16841
+ return "app";
16842
+ if (signals.hostname && ["localhost", "127.0.0.1"].includes(signals.hostname))
16843
+ return "app";
16844
+ return "production";
16845
+ }
16846
+ function cliCommand(mode) {
16847
+ switch (mode) {
16848
+ case "dev":
16849
+ return "pnpm dev:cli";
16850
+ case "app":
16851
+ return "npx @alook/app cli";
16852
+ case "production":
16853
+ return "npx @alook/cli";
16854
+ }
16855
+ }
16856
+ // lib/env.ts
16857
+ function isDev() {
16858
+ return resolveMode({
16859
+ serverUrl: process.env.ALOOK_SERVER_URL,
16860
+ cmdPrefix: process.env.ALOOK_CMD_PREFIX
16861
+ }) === "dev";
16862
+ }
16863
+ function cmdPrefix() {
16864
+ return process.env.ALOOK_CMD_PREFIX || cliCommand(resolveMode({
16865
+ serverUrl: process.env.ALOOK_SERVER_URL,
16866
+ cmdPrefix: process.env.ALOOK_CMD_PREFIX
16867
+ }));
16868
+ }
16869
+
16870
+ // daemon/pidfile.ts
16871
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync2 } from "fs";
16872
+ import { dirname as dirname2 } from "path";
16873
+
16874
+ // daemon/config.ts
16875
+ import { hostname as hostname3 } from "os";
16876
+ import { join as join3 } from "path";
16877
+
16878
+ // lib/version.ts
16879
+ import { readFileSync as readFileSync2 } from "fs";
16880
+ import { join as join2, dirname } from "path";
16881
+ import { fileURLToPath } from "url";
16882
+ function getCurrentVersion() {
16883
+ const __dirname2 = dirname(fileURLToPath(import.meta.url));
16884
+ const candidates = [
16885
+ join2(__dirname2, "..", "package.json"),
16886
+ join2(__dirname2, "..", "..", "package.json")
16887
+ ];
16888
+ for (const candidate of candidates) {
16889
+ try {
16890
+ const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
16891
+ if (typeof pkg.version === "string")
16892
+ return pkg.version;
16893
+ } catch {}
16894
+ }
16895
+ return "unknown";
16896
+ }
16897
+
16898
+ // daemon/config.ts
16899
+ function pidFilePath(profile) {
16900
+ const name = profile ? `daemon_${profile}.pid` : "daemon.pid";
16901
+ return join3(configDir(), name);
16902
+ }
16903
+ function lastUpdateMarkerPath(profile) {
16904
+ const name = profile ? `last_update_${profile}` : "last_update";
16905
+ return join3(configDir(), name);
16906
+ }
16907
+ function daemonLogDir() {
16908
+ return join3(configDir(), "daemon", "logs");
16909
+ }
16910
+ function sessionRunnerLogDir() {
16911
+ return join3(configDir(), "daemon", "session-runners");
16912
+ }
16913
+ function daemonLogFilePath(date5 = new Date) {
16914
+ const y = date5.getFullYear();
16915
+ const m = String(date5.getMonth() + 1).padStart(2, "0");
16916
+ const d = String(date5.getDate()).padStart(2, "0");
16917
+ return join3(daemonLogDir(), `${y}-${m}-${d}.log`);
16918
+ }
16919
+ function parseDuration(s) {
16920
+ if (!s)
16921
+ return 0;
16922
+ let total = 0;
16923
+ const regex = /(\d+(?:\.\d+)?)(ns|us|µs|ms|s|m|h)/g;
16924
+ let match;
16925
+ while ((match = regex.exec(s)) !== null) {
16926
+ const val = parseFloat(match[1]);
16927
+ switch (match[2]) {
16928
+ case "ns":
16929
+ total += val / 1e6;
16930
+ break;
16931
+ case "us":
16932
+ case "µs":
16933
+ total += val / 1000;
16934
+ break;
16935
+ case "ms":
16936
+ total += val;
16937
+ break;
16938
+ case "s":
16939
+ total += val * 1000;
16940
+ break;
16941
+ case "m":
16942
+ total += val * 60000;
16943
+ break;
16944
+ case "h":
16945
+ total += val * 3600000;
16946
+ break;
16947
+ }
16948
+ }
16949
+ return total;
16950
+ }
16951
+ function loadDaemonConfig(profile) {
16952
+ const h = hostname3();
16953
+ let daemonId = process.env.ALOOK_DAEMON_ID || h;
16954
+ if (profile && !daemonId.endsWith(`-${profile}`)) {
16955
+ daemonId = `${daemonId}-${profile}`;
16956
+ }
16957
+ const defaultRoot = join3(configDir(), profile ? `workspaces_${profile}` : "workspaces");
16958
+ const workspacesRoot = process.env.ALOOK_WORKSPACES_ROOT || defaultRoot;
16959
+ return {
16960
+ serverURL: normalizeServerBaseURL(process.env.ALOOK_SERVER_URL || "https://alook.ai"),
16961
+ claudePath: process.env.ALOOK_CLAUDE_PATH || "claude",
16962
+ codexPath: process.env.ALOOK_CODEX_PATH || "codex",
16963
+ opencodePath: process.env.ALOOK_OPENCODE_PATH || "opencode",
16964
+ claudeModel: process.env.ALOOK_CLAUDE_MODEL || "",
16965
+ codexModel: process.env.ALOOK_CODEX_MODEL || "",
16966
+ opencodeModel: process.env.ALOOK_OPENCODE_MODEL || "",
16967
+ pollInterval: parseDuration(process.env.ALOOK_DAEMON_POLL_INTERVAL || "3s"),
16968
+ agentTimeout: parseDuration(process.env.ALOOK_AGENT_TIMEOUT || "12h"),
16969
+ messageInactivityTimeout: parseDuration(process.env.ALOOK_MESSAGE_INACTIVITY_TIMEOUT || "20m"),
16970
+ maxConcurrentTasks: parseInt(process.env.ALOOK_DAEMON_MAX_CONCURRENT_TASKS || "20"),
16971
+ daemonId,
16972
+ deviceName: process.env.ALOOK_DAEMON_DEVICE_NAME || h,
16973
+ workspacesRoot,
16974
+ cliVersion: getCurrentVersion()
16975
+ };
16976
+ }
16977
+ function normalizeServerBaseURL(url2) {
16978
+ return url2.replace(/^ws:\/\//, "http://").replace(/^wss:\/\//, "https://").replace(/\/ws$/, "");
16979
+ }
16980
+
16981
+ // lib/logger.ts
16982
+ var LEVELS = {
16983
+ debug: 0,
16984
+ info: 1,
16985
+ warn: 2,
16986
+ error: 3,
16987
+ silent: 4
16988
+ };
16989
+ var LABELS = {
16990
+ debug: "DEBUG",
16991
+ info: "INFO ",
16992
+ warn: "WARN ",
16993
+ error: "ERROR"
16994
+ };
16995
+ var COLORS = {
16996
+ debug: "\x1B[90m",
16997
+ info: "\x1B[36m",
16998
+ warn: "\x1B[33m",
16999
+ error: "\x1B[31m"
17000
+ };
17001
+ var RESET = "\x1B[0m";
17002
+ var DIM = "\x1B[2m";
17003
+ var BOLD = "\x1B[1m";
17004
+ function useColor() {
17005
+ if (process.env.NO_COLOR !== undefined)
17006
+ return false;
17007
+ if (process.env.FORCE_COLOR !== undefined)
17008
+ return true;
17009
+ return process.stdout.isTTY === true;
17010
+ }
17011
+ function timestamp() {
17012
+ const d = new Date;
17013
+ const Y = d.getFullYear();
17014
+ const M = String(d.getMonth() + 1).padStart(2, "0");
17015
+ const D = String(d.getDate()).padStart(2, "0");
17016
+ const h = String(d.getHours()).padStart(2, "0");
17017
+ const m = String(d.getMinutes()).padStart(2, "0");
17018
+ const s = String(d.getSeconds()).padStart(2, "0");
17019
+ return `${Y}-${M}-${D} ${h}:${m}:${s}`;
17020
+ }
17021
+
17022
+ class Logger2 {
17023
+ level;
17024
+ color;
17025
+ module;
17026
+ constructor(opts = {}) {
17027
+ const envLevel = process.env.ALOOK_LOG_LEVEL;
17028
+ this.level = LEVELS[opts.level ?? envLevel ?? "info"];
17029
+ this.color = useColor();
17030
+ this.module = opts.module;
17031
+ }
17032
+ setLevel(level) {
17033
+ this.level = LEVELS[level];
17034
+ }
17035
+ child(module) {
17036
+ const child = new Logger2({ level: this.levelName(), module });
17037
+ return child;
17038
+ }
17039
+ debug(msg, ...args) {
17040
+ this.write("debug", msg, args);
17041
+ }
17042
+ info(msg, ...args) {
17043
+ this.write("info", msg, args);
17044
+ }
17045
+ warn(msg, ...args) {
17046
+ this.write("warn", msg, args);
17047
+ }
17048
+ error(msg, ...args) {
17049
+ this.write("error", msg, args);
17050
+ }
17051
+ levelName() {
17052
+ for (const [name, num] of Object.entries(LEVELS)) {
17053
+ if (num === this.level)
17054
+ return name;
17055
+ }
17056
+ return "info";
17057
+ }
17058
+ write(level, msg, args) {
17059
+ if (LEVELS[level] < this.level)
17060
+ return;
17061
+ const ts = timestamp();
17062
+ const label = LABELS[level];
17063
+ const mod = this.module ? `[${this.module}]` : "";
17064
+ let line;
17065
+ if (this.color) {
17066
+ const c = COLORS[level];
17067
+ const modStr = mod ? ` ${BOLD}${mod}${RESET}` : "";
17068
+ line = `${DIM}${ts}${RESET} ${c}${label}${RESET}${modStr} ${msg}`;
17069
+ } else {
17070
+ const modStr = mod ? ` ${mod}` : "";
17071
+ line = `${ts} ${label}${modStr} ${msg}`;
17072
+ }
17073
+ const dest = level === "error" ? process.stderr : process.stdout;
17074
+ dest.write(line + `
17075
+ `);
17076
+ for (const a of args) {
17077
+ if (a instanceof Error) {
17078
+ dest.write(` ${a.message}
17079
+ `);
17080
+ if (a.stack && this.level <= LEVELS.debug) {
17081
+ dest.write(` ${a.stack}
17082
+ `);
17083
+ }
17084
+ } else if (a !== null && typeof a === "object") {
17085
+ const pairs = Object.entries(a).map(([k, v]) => `${k}=${typeof v === "object" ? JSON.stringify(v) : v}`).join(" ");
17086
+ if (pairs)
17087
+ dest.write(` ${pairs}
17088
+ `);
17089
+ } else if (a !== undefined) {
17090
+ dest.write(` ${String(a)}
17091
+ `);
17092
+ }
17093
+ }
17094
+ }
17095
+ }
17096
+ function createLogger2(opts) {
17097
+ return new Logger2(opts);
17098
+ }
17099
+ var log = createLogger2();
17100
+
17101
+ // daemon/pidfile.ts
17102
+ var log2 = createLogger2({ module: "pidfile" });
17103
+ function isProcessAlive(pid) {
17104
+ try {
17105
+ process.kill(pid, 0);
17106
+ return true;
17107
+ } catch {
17108
+ return false;
17109
+ }
17110
+ }
17111
+ function readDaemonPid(profile) {
17112
+ try {
17113
+ const content = readFileSync3(pidFilePath(profile), "utf-8").trim();
17114
+ const pid = parseInt(content, 10);
17115
+ return Number.isNaN(pid) ? null : pid;
17116
+ } catch {
17117
+ return null;
17118
+ }
17119
+ }
17120
+ function acquireDaemonPid(profile) {
17121
+ const pidPath = pidFilePath(profile);
17122
+ try {
17123
+ const content = readFileSync3(pidPath, "utf-8").trim();
17124
+ const existingPid = parseInt(content, 10);
17125
+ if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
17126
+ log2.error(`Another daemon is already running (PID ${existingPid}). ` + `Remove ${pidPath} if this is stale.`);
17127
+ return false;
17128
+ }
17129
+ } catch {}
17130
+ mkdirSync2(dirname2(pidPath), { recursive: true, mode: 448 });
17131
+ writeFileSync2(pidPath, String(process.pid), { mode: 384 });
17132
+ return true;
17133
+ }
17134
+ function removePidFileIfMatches(pid, profile) {
17135
+ const pidPath = pidFilePath(profile);
17136
+ const onDisk = readDaemonPid(profile);
17137
+ if (onDisk !== pid)
17138
+ return;
17139
+ try {
17140
+ unlinkSync(pidPath);
17141
+ } catch {}
17142
+ }
17143
+ function releaseDaemonPid(profile) {
17144
+ removePidFileIfMatches(process.pid, profile);
17145
+ }
17146
+
17147
+ // commands/register.ts
17148
+ function isCommandAvailable(cmd) {
17149
+ try {
17150
+ const check2 = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
17151
+ execSync(check2, { stdio: "ignore" });
17152
+ return true;
17153
+ } catch {
17154
+ return false;
17155
+ }
17156
+ }
17157
+ function detectRuntimes() {
17158
+ const found = [];
17159
+ for (const type of ["claude", "codex", "opencode"]) {
17160
+ if (isCommandAvailable(type)) {
17161
+ let version3 = "";
17162
+ try {
17163
+ version3 = execSync(`${type} --version`, { encoding: "utf-8" }).trim();
17164
+ } catch {}
17165
+ found.push({ type, version: version3 });
17166
+ }
17167
+ }
17168
+ return found;
17169
+ }
17170
+ function registerCommand() {
17171
+ const cmd = new Command("register").description("Register CLI with your Alook account").requiredOption("--token <token>", "API token (starts with al_)").option("--server <url>", "Server URL").option("--profile <name>", "Profile name").action(async (opts, command) => {
17172
+ const token = opts.token;
17173
+ const profile = opts.profile || command.parent?.opts().profile;
17174
+ const serverUrl = opts.server || command.parent?.opts().server || process.env.ALOOK_SERVER_URL || "https://alook.ai";
17175
+ if (!token) {
17176
+ console.error(`Error: --token is required
17177
+ Usage: ${cmdPrefix()} register --token <token>`);
17178
+ process.exit(1);
17179
+ }
17180
+ if (!token.startsWith("al_")) {
17181
+ console.error("Error: invalid token format: must start with 'al_'");
17182
+ process.exit(1);
17183
+ }
17184
+ const client = new APIClient(serverUrl, token);
17185
+ let me;
17186
+ try {
17187
+ me = await client.getJSON("/api/me");
17188
+ } catch (err) {
17189
+ console.error(`Error: failed to verify token: ${err instanceof Error ? err.message : err}`);
17190
+ process.exit(1);
17191
+ }
17192
+ console.log("Scanning for AI runtimes...");
17193
+ const runtimes = detectRuntimes();
17194
+ if (runtimes.length === 0) {
17195
+ console.error("Error: no runtimes found. Install claude, codex, or opencode first.");
17196
+ process.exit(1);
17197
+ }
17198
+ console.log(`Found: ${runtimes.map((r) => r.type).join(", ")}`);
17199
+ const host = hostname4();
17200
+ console.log("Registering runtime...");
17201
+ let activateResp;
17202
+ try {
17203
+ const res = await fetch(`${serverUrl}/api/machine-tokens/activate`, {
17204
+ method: "POST",
17205
+ headers: { "Content-Type": "application/json" },
17206
+ body: JSON.stringify({ token, hostname: host, runtimes })
17207
+ });
17208
+ if (!res.ok) {
17209
+ const text2 = await res.text();
17210
+ console.error(`Error: registration failed (${res.status}): ${text2}`);
17211
+ process.exit(1);
17212
+ }
17213
+ activateResp = await res.json();
17214
+ } catch (err) {
17215
+ console.error(`Error: failed to activate: ${err instanceof Error ? err.message : err}`);
17216
+ process.exit(1);
17217
+ }
17218
+ let workspaces;
17219
+ try {
17220
+ workspaces = await client.getJSON("/api/workspaces");
17221
+ } catch (err) {
17222
+ console.error(`Error: failed to fetch workspaces: ${err instanceof Error ? err.message : err}`);
17223
+ process.exit(1);
17224
+ }
17225
+ if (!workspaces.length) {
17226
+ console.error("Error: no workspaces found for this user");
17227
+ process.exit(1);
17228
+ }
17229
+ const ws = workspaces.find((w) => w.id === activateResp.workspace_id) || workspaces[0];
17230
+ const wsClient = new APIClient(serverUrl, token, ws.id);
17231
+ let agentIds = [];
17232
+ try {
17233
+ const agents = await wsClient.getJSON(`/api/agents?workspace_id=${ws.id}`);
17234
+ agentIds = agents.map((a) => a.id);
17235
+ } catch {}
17236
+ const existing = loadCLIConfigForProfile(profile);
17237
+ const watched = existing.watched_workspaces || [];
17238
+ const idx = watched.findIndex((w) => w.id === ws.id);
17239
+ if (idx >= 0) {
17240
+ watched[idx] = { id: ws.id, name: ws.name, token, agent_ids: agentIds };
17241
+ } else {
17242
+ watched.push({ id: ws.id, name: ws.name, token, agent_ids: agentIds });
17243
+ }
17244
+ saveCLIConfigForProfile(profile, {
17245
+ server_url: serverUrl,
17246
+ watched_workspaces: watched
17247
+ });
17248
+ console.log(`
17249
+ Registered as ${me.email}`);
17250
+ console.log(`Workspace: ${ws.name} (${ws.id})`);
17251
+ console.log(`Runtimes: ${activateResp.runtimes.map((r) => r.provider).join(", ")}`);
17252
+ const daemonPid = readDaemonPid(profile);
17253
+ if (daemonPid && isProcessAlive(daemonPid)) {
17254
+ try {
17255
+ process.kill(daemonPid, "SIGHUP");
17256
+ console.log(`
17257
+ Daemon (pid ${daemonPid}) notified — workspace will be active shortly.`);
17258
+ } catch {
17259
+ console.log(`
17260
+ Daemon is running but could not be notified. Restart it to pick up the new workspace.`);
17261
+ }
17262
+ } else {
17263
+ const startCmd = isDev() ? `${cmdPrefix()} daemon start --foreground` : `${cmdPrefix()} daemon start`;
17264
+ console.log();
17265
+ console.log(`Run '${startCmd}' to start the daemon.`);
17266
+ }
17267
+ });
17268
+ return cmd;
17269
+ }
17270
+
17271
+ // commands/status.ts
17272
+ import { Command as Command2 } from "commander";
17273
+ function statusCommand() {
17274
+ const cmd = new Command2("status").description("Show registration status").action((_opts, command) => {
17275
+ const profile = command.parent?.opts().profile;
17276
+ const cfg = loadCLIConfigForProfile(profile);
17277
+ const ws = cfg.watched_workspaces?.[0];
17278
+ if (!ws?.token) {
17279
+ console.log("Not registered");
17280
+ console.log(`Run '${cmdPrefix()} register --token <token>' to register.`);
17281
+ return;
17282
+ }
17283
+ console.log("Status: Registered");
17284
+ console.log(`Server: ${cfg.server_url}`);
17285
+ console.log(`Workspace: ${ws.name} (${ws.id})`);
17286
+ });
17287
+ return cmd;
17288
+ }
17289
+
17290
+ // commands/daemon.ts
17291
+ import { Command as Command3 } from "commander";
17292
+ import { spawn as spawn3 } from "child_process";
17293
+ import { openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync6 } from "fs";
17294
+ import { dirname as dirname4 } from "path";
17295
+
17258
17296
  // daemon/client.ts
17259
17297
  class DaemonClient {
17260
17298
  baseURL;
@@ -17482,7 +17520,7 @@ function runNpmUpdate(targetVersion) {
17482
17520
  }
17483
17521
 
17484
17522
  // daemon/update-handler.ts
17485
- var log3 = createLogger({ module: "updater" });
17523
+ var log3 = createLogger2({ module: "updater" });
17486
17524
  var updating = false;
17487
17525
  var retryCount = 0;
17488
17526
  var MAX_RETRIES = 3;
@@ -17511,6 +17549,10 @@ async function handleCliUpdate(version3, onSuccess, profile) {
17511
17549
  return;
17512
17550
  if (retryCount >= MAX_RETRIES)
17513
17551
  return;
17552
+ if (process.env.ALOOK_CMD_PREFIX) {
17553
+ log3.info(`Skipping auto-update in app mode — user should run: npx @alook/app@latest update`);
17554
+ return;
17555
+ }
17514
17556
  const marker = readUpdateMarker(profile);
17515
17557
  if (marker === version3) {
17516
17558
  log3.info(`Skipping update to v${version3} — already attempted (marker exists)`);
@@ -17577,7 +17619,7 @@ function releaseLock(lockPath) {
17577
17619
  }
17578
17620
 
17579
17621
  // daemon/execenv/timeline.ts
17580
- var log4 = createLogger({ module: "timeline" });
17622
+ var log4 = createLogger2({ module: "timeline" });
17581
17623
  function readJsonl(filePath) {
17582
17624
  let content;
17583
17625
  try {
@@ -17640,7 +17682,7 @@ function findRunningEntryByContextKey(timelineDir, contextKey, provider) {
17640
17682
  // daemon/execenv/steering.ts
17641
17683
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync3, readdirSync, statSync as statSync2 } from "fs";
17642
17684
  import { join as join5 } from "path";
17643
- var log5 = createLogger({ module: "steering" });
17685
+ var log5 = createLogger2({ module: "steering" });
17644
17686
  var INTENT_DIR_NAME = ".kill_intents";
17645
17687
  var STEERING_LOCK_DIR = ".steering_locks";
17646
17688
  var INTENT_STALE_MS = 10 * 60 * 1000;
@@ -17806,6 +17848,7 @@ function tempDir(subdir) {
17806
17848
  }
17807
17849
 
17808
17850
  // lib/shell-env.ts
17851
+ var PASSTHROUGH_VARS = ["ALOOK_PROJECT_ROOT", "ALOOK_SERVER_URL", "ALOOK_CMD_PREFIX", "ALOOK_HEALTH_PORT"];
17809
17852
  function resolveLoginShellEnv() {
17810
17853
  if (isWindows) {
17811
17854
  return { ...process.env };
@@ -17825,8 +17868,13 @@ function resolveLoginShellEnv() {
17825
17868
  env[line.slice(0, idx)] = line.slice(idx + 1);
17826
17869
  }
17827
17870
  }
17828
- if (env.PATH)
17871
+ if (env.PATH) {
17872
+ for (const key of PASSTHROUGH_VARS) {
17873
+ if (process.env[key])
17874
+ env[key] = process.env[key];
17875
+ }
17829
17876
  return env;
17877
+ }
17830
17878
  } catch {}
17831
17879
  return { ...process.env };
17832
17880
  }
@@ -17837,7 +17885,7 @@ import { readdir as readdir2, readFile as readFile2, unlink, stat as fsStat } fr
17837
17885
  import { execSync as execSync4, spawn as spawn2 } from "child_process";
17838
17886
  import { fileURLToPath as fileURLToPath2 } from "url";
17839
17887
  import { dirname as dirname3, join as join8 } from "path";
17840
- var log6 = createLogger({ module: "daemon" });
17888
+ var log6 = createLogger2({ module: "daemon" });
17841
17889
  var _dir = dirname3(fileURLToPath2(import.meta.url));
17842
17890
  var sessionRunnerPath = existsSync(join8(_dir, "session-runner.js")) ? join8(_dir, "session-runner.js") : join8(_dir, "session-runner.ts");
17843
17891
  var meetingRunnerPath = existsSync(join8(_dir, "meeting-runner.js")) ? join8(_dir, "meeting-runner.js") : join8(_dir, "meeting-runner.ts");
@@ -18066,6 +18114,7 @@ async function startDaemon(profile, serverUrl) {
18066
18114
  daemon_id: config2.daemonId,
18067
18115
  device_name: config2.deviceName,
18068
18116
  cli_version: config2.cliVersion,
18117
+ workspaces_root: config2.workspacesRoot,
18069
18118
  runtimes
18070
18119
  });
18071
18120
  } catch (e) {
@@ -18179,6 +18228,8 @@ async function startDaemon(profile, serverUrl) {
18179
18228
  }
18180
18229
  if (meetings) {
18181
18230
  for (const m of meetings) {
18231
+ const agentBaseDir = join8(config2.workspacesRoot, m.workspace_id, m.agent_id, "workdir");
18232
+ const timelineDir = join8(agentBaseDir, ".context_timeline");
18182
18233
  spawnMeetingRunner({
18183
18234
  meetingId: m.id,
18184
18235
  meetingUrl: m.meeting_url,
@@ -18186,7 +18237,10 @@ async function startDaemon(profile, serverUrl) {
18186
18237
  workspaceId: m.workspace_id,
18187
18238
  callbackUrl: config2.serverURL,
18188
18239
  authToken: ws.token,
18189
- agentName: m.agent_name
18240
+ agentName: m.agent_name,
18241
+ agentId: m.agent_id,
18242
+ timelineDir,
18243
+ title: m.title
18190
18244
  });
18191
18245
  }
18192
18246
  }
@@ -18282,6 +18336,7 @@ async function startDaemon(profile, serverUrl) {
18282
18336
  daemon_id: config2.daemonId,
18283
18337
  device_name: config2.deviceName,
18284
18338
  cli_version: config2.cliVersion,
18339
+ workspaces_root: config2.workspacesRoot,
18285
18340
  runtimes
18286
18341
  });
18287
18342
  const runtimeIds = resp.runtimes.map((r) => r.id);
@@ -18611,7 +18666,7 @@ async function stopCommand(profile) {
18611
18666
  removePidFileIfMatches(pid, profile);
18612
18667
  console.log("Daemon stopped.");
18613
18668
  }
18614
- function daemonCommand() {
18669
+ function daemonCommand2() {
18615
18670
  const cmd = new Command3("daemon").description("Manage the Alook daemon");
18616
18671
  cmd.command("start").description("Start the daemon").option("--foreground", "Run in foreground").option("--server <url>", "Server URL override").action(async (opts, command) => {
18617
18672
  const parentOpts = command.parent?.parent?.opts() || {};
@@ -18668,7 +18723,7 @@ import { Command as Command5 } from "commander";
18668
18723
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
18669
18724
  import { basename, join as join9 } from "path";
18670
18725
  import PostalMime from "postal-mime";
18671
- var log7 = createLogger({ module: "email" });
18726
+ var log7 = createLogger2({ module: "email" });
18672
18727
  var VALID_STATUSES = ["unread", "read", "archived", "sent"];
18673
18728
  var VALID_FOLDERS = ["inbox", "sent", "untrust"];
18674
18729
  var EMAIL_BASE = tempDir("alook-emails");
@@ -19567,7 +19622,7 @@ ${result.output}`);
19567
19622
 
19568
19623
  // commands/sync.ts
19569
19624
  import { Command as Command10 } from "commander";
19570
- import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
19625
+ import { readFileSync as readFileSync9 } from "fs";
19571
19626
  import { basename as basename2 } from "path";
19572
19627
  var MIME_BY_EXT2 = {
19573
19628
  ".pdf": "application/pdf",
@@ -19614,10 +19669,7 @@ function syncCommand() {
19614
19669
  const { serverUrl, token, workspaceId } = resolveClientOpts4(command, opts.agent_id);
19615
19670
  const client = new APIClient(serverUrl, token, workspaceId);
19616
19671
  let bytes;
19617
- let size;
19618
19672
  try {
19619
- const stat2 = statSync5(opts.file);
19620
- size = stat2.size;
19621
19673
  bytes = readFileSync9(opts.file);
19622
19674
  } catch (err) {
19623
19675
  console.error(`Error: cannot read file "${opts.file}": ${err.message}`);
@@ -19645,7 +19697,7 @@ var program = new Command11;
19645
19697
  program.name("alook").description("Alook CLI").option("--server <url>", "Server URL").option("--profile <name>", "Profile name");
19646
19698
  program.addCommand(registerCommand());
19647
19699
  program.addCommand(statusCommand());
19648
- program.addCommand(daemonCommand());
19700
+ program.addCommand(daemonCommand2());
19649
19701
  program.addCommand(emailCommand());
19650
19702
  program.addCommand(calendarCommand());
19651
19703
  program.addCommand(issueCommand());