@alook/cli 0.0.15 → 0.0.17

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
@@ -297,14 +297,30 @@ function statusCommand() {
297
297
  // commands/daemon.ts
298
298
  import { Command as Command3 } from "commander";
299
299
  import { spawn as spawn3 } from "child_process";
300
- import { openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync4 } from "fs";
300
+ import { openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync6 } from "fs";
301
301
  import { dirname as dirname4 } from "path";
302
302
 
303
303
  // ../shared/src/constants.ts
304
+ var TaskStatus = {
305
+ QUEUED: "queued",
306
+ DISPATCHED: "dispatched",
307
+ RUNNING: "running",
308
+ COMPLETED: "completed",
309
+ FAILED: "failed",
310
+ CANCELLED: "cancelled",
311
+ SUPERSEDED: "superseded"
312
+ };
313
+ var TERMINAL_TASK_STATUSES = [
314
+ TaskStatus.COMPLETED,
315
+ TaskStatus.FAILED,
316
+ TaskStatus.CANCELLED,
317
+ TaskStatus.SUPERSEDED
318
+ ];
304
319
  var TASK_TYPES = {
305
320
  USER_DM_MESSAGE: "user_dm_message",
306
321
  EMAIL_NOTIFICATION: "email_notification",
307
- CALENDAR_EVENT: "calendar_event"
322
+ CALENDAR_EVENT: "calendar_event",
323
+ KILL_TASK: "kill_task"
308
324
  };
309
325
  var POLL_INTERVAL_MS = Number(process.env.POLL_INTERVAL_MS) || 3000;
310
326
  var OFFLINE_THRESHOLD_MS = Number(process.env.OFFLINE_THRESHOLD_MS) || 9000;
@@ -13851,7 +13867,8 @@ var TaskStatusSchema = exports_external.enum([
13851
13867
  "running",
13852
13868
  "completed",
13853
13869
  "failed",
13854
- "cancelled"
13870
+ "cancelled",
13871
+ "superseded"
13855
13872
  ]);
13856
13873
  var ClaimedTaskRowSchema = exports_external.object({
13857
13874
  id: exports_external.string(),
@@ -14032,8 +14049,9 @@ var UpdateAgentRequestSchema = exports_external.object({
14032
14049
  description: exports_external.string().optional(),
14033
14050
  instructions: exports_external.string().optional(),
14034
14051
  runtime_id: exports_external.string().min(1).optional(),
14035
- runtime_config: RuntimeConfigSchema
14036
- }).refine((v) => v.name !== undefined || v.description !== undefined || v.instructions !== undefined || v.runtime_id !== undefined || v.runtime_config !== undefined, { message: "at least one field is required" });
14052
+ runtime_config: RuntimeConfigSchema,
14053
+ visibility: exports_external.enum(["public", "private"]).optional()
14054
+ }).refine((v) => v.name !== undefined || v.description !== undefined || v.instructions !== undefined || v.runtime_id !== undefined || v.runtime_config !== undefined || v.visibility !== undefined, { message: "at least one field is required" });
14037
14055
  var CreateConversationRequestSchema = exports_external.object({
14038
14056
  agent_id: exports_external.string().min(1, "agent_id is required")
14039
14057
  });
@@ -14125,6 +14143,16 @@ var CreateWorkspaceRequestSchema = exports_external.object({
14125
14143
  name: exports_external.string().min(1, "name is required"),
14126
14144
  slug: exports_external.string().min(1, "slug is required")
14127
14145
  });
14146
+ var UpdateWorkspaceRequestSchema = exports_external.object({
14147
+ name: exports_external.string().min(1, "name is required").max(100).trim().optional(),
14148
+ slug: exports_external.string().min(1, "slug is required").max(100).trim().toLowerCase().optional()
14149
+ });
14150
+ var DeleteWorkspaceRequestSchema = exports_external.object({
14151
+ confirm_name: exports_external.string().min(1, "confirm_name is required")
14152
+ });
14153
+ var GrantAgentAccessRequestSchema = exports_external.object({
14154
+ user_id: exports_external.string().min(1, "user_id is required")
14155
+ });
14128
14156
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@4.20260418.1_@opentelemetry+api@1.9.1_bun-types@1.3.12_kysely@0.28.16/node_modules/drizzle-orm/entity.js
14129
14157
  var entityKind = Symbol.for("drizzle:entityKind");
14130
14158
  var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
@@ -15556,6 +15584,48 @@ var member = sqliteTable("member", {
15556
15584
  globalInstruction: text("global_instruction").notNull().default(""),
15557
15585
  createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
15558
15586
  }, (t) => [unique("member_workspace_user").on(t.workspaceId, t.userId)]);
15587
+ var workspaceInvite = sqliteTable("workspace_invite", {
15588
+ id: text("id").primaryKey().$defaultFn(() => "inv_" + nanoid3()),
15589
+ workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
15590
+ token: text("token").unique().notNull().$defaultFn(() => nanoid3(32)),
15591
+ createdBy: text("created_by").notNull().references(() => user.id, { onDelete: "cascade" }),
15592
+ usedBy: text("used_by").references(() => user.id, { onDelete: "set null" }),
15593
+ usedAt: text("used_at"),
15594
+ expiresAt: text("expires_at").notNull(),
15595
+ createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
15596
+ }, (t) => [
15597
+ index("idx_workspace_invite_token").on(t.token),
15598
+ index("idx_workspace_invite_workspace").on(t.workspaceId)
15599
+ ]);
15600
+ var agentAccess = sqliteTable("agent_access", {
15601
+ id: text("id").primaryKey().$defaultFn(() => nanoid3()),
15602
+ agentId: text("agent_id").notNull(),
15603
+ workspaceId: text("workspace_id").notNull(),
15604
+ userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
15605
+ createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
15606
+ }, (t) => [
15607
+ unique("agent_access_agent_ws_user").on(t.agentId, t.workspaceId, t.userId),
15608
+ index("idx_agent_access_agent_ws").on(t.agentId, t.workspaceId),
15609
+ index("idx_agent_access_user").on(t.userId),
15610
+ foreignKey({
15611
+ columns: [t.agentId, t.workspaceId],
15612
+ foreignColumns: [agent.id, agent.workspaceId]
15613
+ }).onDelete("cascade")
15614
+ ]);
15615
+ var agentPin = sqliteTable("agent_pin", {
15616
+ id: text("id").primaryKey().$defaultFn(() => nanoid3()),
15617
+ agentId: text("agent_id").notNull(),
15618
+ workspaceId: text("workspace_id").notNull(),
15619
+ userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
15620
+ createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
15621
+ }, (t) => [
15622
+ unique("agent_pin_agent_ws_user").on(t.agentId, t.workspaceId, t.userId),
15623
+ index("idx_agent_pin_ws_user").on(t.workspaceId, t.userId),
15624
+ foreignKey({
15625
+ columns: [t.agentId, t.workspaceId],
15626
+ foreignColumns: [agent.id, agent.workspaceId]
15627
+ }).onDelete("cascade")
15628
+ ]);
15559
15629
  var machine = sqliteTable("machine", {
15560
15630
  daemonId: text("daemon_id").notNull(),
15561
15631
  workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
@@ -15693,6 +15763,7 @@ var emails = sqliteTable("emails", {
15693
15763
  htmlBody: text("html_body").notNull().default(""),
15694
15764
  attachments: text("attachments").notNull().default("[]"),
15695
15765
  status: text("status").notNull().default("unread"),
15766
+ direction: text("direction").notNull().default("inbound"),
15696
15767
  createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
15697
15768
  }, (t) => [
15698
15769
  foreignKey({
@@ -15782,6 +15853,7 @@ var machineToken = sqliteTable("machine_token", {
15782
15853
  }, (t) => [index("idx_machine_token").on(t.token)]);
15783
15854
  // ../shared/src/db/queries/task.ts
15784
15855
  var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
15856
+ var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
15785
15857
  // ../shared/src/utils/email.ts
15786
15858
  var DOMAIN = `@${process.env.ALOOK_DOMAIN || "alook.ai"}`;
15787
15859
  var RESERVED_HANDLES = new Set([
@@ -15883,6 +15955,9 @@ class DaemonClient {
15883
15955
  error: error48
15884
15956
  });
15885
15957
  }
15958
+ supersedeTask(token, taskId) {
15959
+ return this.request("POST", `/api/daemon/tasks/${taskId}/supersede`, token);
15960
+ }
15886
15961
  async getArtifactMeta(token, artifactId, workspaceId) {
15887
15962
  return this.request("GET", `/api/artifacts/${artifactId}?workspace_id=${encodeURIComponent(workspaceId)}`, token);
15888
15963
  }
@@ -16010,6 +16085,7 @@ function loadDaemonConfig(profile) {
16010
16085
  opencodeModel: process.env.ALOOK_OPENCODE_MODEL || "",
16011
16086
  pollInterval: parseDuration(process.env.ALOOK_DAEMON_POLL_INTERVAL || "3s"),
16012
16087
  agentTimeout: parseDuration(process.env.ALOOK_AGENT_TIMEOUT || "12h"),
16088
+ messageInactivityTimeout: parseDuration(process.env.ALOOK_MESSAGE_INACTIVITY_TIMEOUT || "5m"),
16013
16089
  maxConcurrentTasks: parseInt(process.env.ALOOK_DAEMON_MAX_CONCURRENT_TASKS || "20"),
16014
16090
  daemonId,
16015
16091
  deviceName: process.env.ALOOK_DAEMON_DEVICE_NAME || h,
@@ -16316,13 +16392,173 @@ async function handleCliUpdate(version3, onSuccess, profile) {
16316
16392
  }
16317
16393
  }
16318
16394
 
16395
+ // daemon/execenv/timeline.ts
16396
+ import { appendFileSync, readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync } from "fs";
16397
+ import { join as join4 } from "path";
16398
+
16399
+ // daemon/execenv/filelock.ts
16400
+ import { mkdirSync as mkdirSync3, rmdirSync, statSync } from "fs";
16401
+ var DEFAULT_STALE_MS = 3600000;
16402
+ function acquireLock(lockPath, staleMs = DEFAULT_STALE_MS) {
16403
+ try {
16404
+ mkdirSync3(lockPath);
16405
+ return true;
16406
+ } catch {
16407
+ try {
16408
+ const stat = statSync(lockPath);
16409
+ if (Date.now() - stat.mtimeMs > staleMs) {
16410
+ rmdirSync(lockPath);
16411
+ try {
16412
+ mkdirSync3(lockPath);
16413
+ return true;
16414
+ } catch {
16415
+ return false;
16416
+ }
16417
+ }
16418
+ } catch {
16419
+ try {
16420
+ mkdirSync3(lockPath);
16421
+ return true;
16422
+ } catch {
16423
+ return false;
16424
+ }
16425
+ }
16426
+ return false;
16427
+ }
16428
+ }
16429
+ function releaseLock(lockPath) {
16430
+ try {
16431
+ rmdirSync(lockPath);
16432
+ } catch {}
16433
+ }
16434
+
16435
+ // daemon/execenv/timeline.ts
16436
+ function readJsonl(filePath) {
16437
+ let content;
16438
+ try {
16439
+ content = readFileSync5(filePath, "utf-8");
16440
+ } catch {
16441
+ return [];
16442
+ }
16443
+ const entries = [];
16444
+ for (const line of content.trimEnd().split(`
16445
+ `)) {
16446
+ if (!line)
16447
+ continue;
16448
+ try {
16449
+ entries.push(JSON.parse(line));
16450
+ } catch {}
16451
+ }
16452
+ return entries;
16453
+ }
16454
+ function filenameForDate(date5) {
16455
+ const y = date5.getFullYear();
16456
+ const m = String(date5.getMonth() + 1).padStart(2, "0");
16457
+ const d = String(date5.getDate()).padStart(2, "0");
16458
+ return `${y}-${m}-${d}.jsonl`;
16459
+ }
16460
+ function recentFilenames(maxDays) {
16461
+ const filenames = [];
16462
+ const now = new Date;
16463
+ for (let i = 0;i < maxDays; i++) {
16464
+ const d = new Date(now);
16465
+ d.setDate(d.getDate() - i);
16466
+ filenames.push(filenameForDate(d));
16467
+ }
16468
+ return filenames;
16469
+ }
16470
+ var DEFAULT_RESUME_MAX_AGE_MS = 3 * 60 * 60 * 1000;
16471
+ var EMAIL_RESUME_MAX_AGE_MS = 48 * 60 * 60 * 1000;
16472
+ function findRunningPidByTaskId(timelineDir, taskId) {
16473
+ for (const filename of recentFilenames(7)) {
16474
+ const entries = readJsonl(join4(timelineDir, filename));
16475
+ for (const entry of entries) {
16476
+ if (entry.task_id === taskId && entry.status === "running" && entry.pid != null) {
16477
+ return entry.pid;
16478
+ }
16479
+ }
16480
+ }
16481
+ return null;
16482
+ }
16483
+ function findRunningEntryByContextKey(timelineDir, contextKey, provider) {
16484
+ for (const filename of recentFilenames(7)) {
16485
+ const dayEntries = readJsonl(join4(timelineDir, filename));
16486
+ for (let i = dayEntries.length - 1;i >= 0; i--) {
16487
+ const entry = dayEntries[i];
16488
+ if (entry.status === "running" && entry.context_key === contextKey && entry.provider === provider) {
16489
+ return entry;
16490
+ }
16491
+ }
16492
+ }
16493
+ return null;
16494
+ }
16495
+
16496
+ // daemon/execenv/steering.ts
16497
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync3, readdirSync, statSync as statSync2 } from "fs";
16498
+ import { join as join5 } from "path";
16499
+ var INTENT_DIR_NAME = ".kill_intents";
16500
+ var STEERING_LOCK_DIR = ".steering_locks";
16501
+ var INTENT_STALE_MS = 10 * 60 * 1000;
16502
+ function intentFilePath(baseDir, taskId) {
16503
+ return join5(baseDir, INTENT_DIR_NAME, `${taskId}.json`);
16504
+ }
16505
+ function intentDirPath(baseDir) {
16506
+ return join5(baseDir, INTENT_DIR_NAME);
16507
+ }
16508
+ function steeringLockPath(baseDir, contextKey) {
16509
+ const safeKey = contextKey.replace(/[^a-zA-Z0-9_:-]/g, "_");
16510
+ return join5(baseDir, STEERING_LOCK_DIR, safeKey);
16511
+ }
16512
+ function writeKillIntent(baseDir, intent) {
16513
+ const dir = intentDirPath(baseDir);
16514
+ try {
16515
+ mkdirSync4(dir, { recursive: true });
16516
+ } catch {}
16517
+ const filePath = intentFilePath(baseDir, intent.targetTaskId);
16518
+ writeFileSync5(filePath, JSON.stringify(intent));
16519
+ }
16520
+ function cleanupStaleIntents(baseDir) {
16521
+ const dir = intentDirPath(baseDir);
16522
+ let files;
16523
+ try {
16524
+ files = readdirSync(dir).filter((f) => f.endsWith(".json"));
16525
+ } catch {
16526
+ return;
16527
+ }
16528
+ const now = Date.now();
16529
+ for (const file2 of files) {
16530
+ const filePath = join5(dir, file2);
16531
+ try {
16532
+ const content = readFileSync6(filePath, "utf-8");
16533
+ const intent = JSON.parse(content);
16534
+ const stat = statSync2(filePath);
16535
+ if (now - stat.mtimeMs > INTENT_STALE_MS) {
16536
+ unlinkSync3(filePath);
16537
+ log.debug(`Cleaned up stale kill intent for task ${intent.targetTaskId}`);
16538
+ }
16539
+ } catch {}
16540
+ }
16541
+ }
16542
+ function acquireSteeringLock(baseDir, contextKey) {
16543
+ const lockPath = steeringLockPath(baseDir, contextKey);
16544
+ try {
16545
+ mkdirSync4(join5(baseDir, STEERING_LOCK_DIR), { recursive: true });
16546
+ } catch {}
16547
+ return acquireLock(lockPath, 60000);
16548
+ }
16549
+ function releaseSteeringLock(baseDir, contextKey) {
16550
+ const lockPath = steeringLockPath(baseDir, contextKey);
16551
+ releaseLock(lockPath);
16552
+ }
16553
+
16319
16554
  // daemon/daemon.ts
16320
- import { existsSync, mkdirSync as mkdirSync3, openSync, closeSync, readdirSync, statSync, unlinkSync as unlinkSync3 } from "fs";
16555
+ import { existsSync, mkdirSync as mkdirSync5, openSync, closeSync, readdirSync as readdirSync2, statSync as statSync3, unlinkSync as unlinkSync4 } from "fs";
16556
+ import { readdir, readFile, unlink, stat as fsStat } from "fs/promises";
16321
16557
  import { execSync as execSync3, spawn as spawn2 } from "child_process";
16322
16558
  import { fileURLToPath as fileURLToPath2 } from "url";
16323
- import { dirname as dirname3, join as join4 } from "path";
16559
+ import { dirname as dirname3, join as join6 } from "path";
16324
16560
  var _dir = dirname3(fileURLToPath2(import.meta.url));
16325
- var sessionRunnerPath = existsSync(join4(_dir, "session-runner.js")) ? join4(_dir, "session-runner.js") : join4(_dir, "session-runner.ts");
16561
+ var sessionRunnerPath = existsSync(join6(_dir, "session-runner.js")) ? join6(_dir, "session-runner.js") : join6(_dir, "session-runner.ts");
16326
16562
  function isCommandAvailable2(cmd) {
16327
16563
  try {
16328
16564
  const check2 = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
@@ -16332,21 +16568,21 @@ function isCommandAvailable2(cmd) {
16332
16568
  return false;
16333
16569
  }
16334
16570
  }
16335
- var MAX_SESSION_RUNNER_LOGS = 50;
16571
+ var MAX_SESSION_RUNNER_LOGS = 500;
16336
16572
  function pruneSessionRunnerLogs() {
16337
16573
  const logDir = sessionRunnerLogDir();
16338
16574
  let entries;
16339
16575
  try {
16340
- entries = readdirSync(logDir).filter((f) => f.endsWith(".log"));
16576
+ entries = readdirSync2(logDir).filter((f) => f.endsWith(".log"));
16341
16577
  } catch {
16342
16578
  return;
16343
16579
  }
16344
16580
  if (entries.length <= MAX_SESSION_RUNNER_LOGS)
16345
16581
  return;
16346
16582
  const withMtime = entries.map((name) => {
16347
- const full = join4(logDir, name);
16583
+ const full = join6(logDir, name);
16348
16584
  try {
16349
- return { name, mtime: statSync(full).mtimeMs };
16585
+ return { name, mtime: statSync3(full).mtimeMs };
16350
16586
  } catch {
16351
16587
  return { name, mtime: 0 };
16352
16588
  }
@@ -16354,10 +16590,126 @@ function pruneSessionRunnerLogs() {
16354
16590
  withMtime.sort((a, b) => b.mtime - a.mtime);
16355
16591
  for (const entry of withMtime.slice(MAX_SESSION_RUNNER_LOGS)) {
16356
16592
  try {
16357
- unlinkSync3(join4(logDir, entry.name));
16593
+ unlinkSync4(join6(logDir, entry.name));
16358
16594
  } catch {}
16359
16595
  }
16360
16596
  }
16597
+ function isClientError(error48) {
16598
+ if (!(error48 instanceof Error))
16599
+ return false;
16600
+ const match = error48.message.match(/^HTTP (\d+):/);
16601
+ if (!match)
16602
+ return false;
16603
+ const status = Number(match[1]);
16604
+ if (status === 408 || status === 429)
16605
+ return false;
16606
+ return status >= 400 && status < 500;
16607
+ }
16608
+ function isValidMarker(data) {
16609
+ if (!data || typeof data !== "object")
16610
+ return false;
16611
+ const d = data;
16612
+ if (typeof d.taskId !== "string")
16613
+ return false;
16614
+ if (typeof d.token !== "string")
16615
+ return false;
16616
+ if (typeof d.serverURL !== "string")
16617
+ return false;
16618
+ if (typeof d.createdAt !== "string" || isNaN(new Date(d.createdAt).getTime()))
16619
+ return false;
16620
+ if (!d.payload || typeof d.payload !== "object")
16621
+ return false;
16622
+ const payload = d.payload;
16623
+ if (d.type === "complete") {
16624
+ return typeof payload.output === "string";
16625
+ }
16626
+ if (d.type === "fail") {
16627
+ return typeof payload.error === "string";
16628
+ }
16629
+ return false;
16630
+ }
16631
+ var MARKER_STALE_MS = 24 * 60 * 60 * 1000;
16632
+ var TMP_STALE_MS = 60 * 60 * 1000;
16633
+ async function reconcilePendingCompletions(workspacesRoot) {
16634
+ const dir = join6(workspacesRoot, ".pending_completions");
16635
+ let entries;
16636
+ try {
16637
+ entries = await readdir(dir);
16638
+ } catch {
16639
+ return;
16640
+ }
16641
+ for (const name of entries) {
16642
+ if (!name.endsWith(".tmp"))
16643
+ continue;
16644
+ try {
16645
+ const s = await fsStat(join6(dir, name));
16646
+ if (Date.now() - s.mtimeMs > TMP_STALE_MS) {
16647
+ await unlink(join6(dir, name));
16648
+ }
16649
+ } catch {}
16650
+ }
16651
+ const jsonFiles = entries.filter((f) => f.endsWith(".json"));
16652
+ for (const name of jsonFiles) {
16653
+ const filePath = join6(dir, name);
16654
+ try {
16655
+ let raw;
16656
+ try {
16657
+ raw = await readFile(filePath, "utf-8");
16658
+ } catch {
16659
+ continue;
16660
+ }
16661
+ let parsed;
16662
+ try {
16663
+ parsed = JSON.parse(raw);
16664
+ } catch {
16665
+ log.warn(`reconcile: malformed marker ${name}, deleting`);
16666
+ try {
16667
+ await unlink(filePath);
16668
+ } catch {}
16669
+ continue;
16670
+ }
16671
+ if (!isValidMarker(parsed)) {
16672
+ log.warn(`reconcile: invalid marker structure ${name}, deleting`);
16673
+ try {
16674
+ await unlink(filePath);
16675
+ } catch {}
16676
+ continue;
16677
+ }
16678
+ const marker = parsed;
16679
+ const age = Date.now() - new Date(marker.createdAt).getTime();
16680
+ if (age > MARKER_STALE_MS) {
16681
+ log.warn(`reconcile: stale marker ${name} (${Math.round(age / 3600000)}h old), deleting`);
16682
+ try {
16683
+ await unlink(filePath);
16684
+ } catch {}
16685
+ continue;
16686
+ }
16687
+ const client = new DaemonClient(marker.serverURL);
16688
+ try {
16689
+ if (marker.type === "complete") {
16690
+ await client.completeTask(marker.token, marker.taskId, marker.payload);
16691
+ } else {
16692
+ await client.failTask(marker.token, marker.taskId, marker.payload.error);
16693
+ }
16694
+ try {
16695
+ await unlink(filePath);
16696
+ } catch (delErr) {
16697
+ log.warn(`reconcile: delivered marker ${name} but failed to delete: ${delErr}`);
16698
+ }
16699
+ } catch (deliverErr) {
16700
+ if (isClientError(deliverErr)) {
16701
+ try {
16702
+ await unlink(filePath);
16703
+ } catch {}
16704
+ } else {
16705
+ log.debug(`reconcile: delivery failed for ${name}, will retry next cycle`);
16706
+ }
16707
+ }
16708
+ } catch (e) {
16709
+ log.debug(`reconcile: error processing ${name}`, e);
16710
+ }
16711
+ }
16712
+ }
16361
16713
  async function startDaemon(profile, serverUrl) {
16362
16714
  pruneSessionRunnerLogs();
16363
16715
  if (!acquireDaemonPid(profile)) {
@@ -16433,12 +16785,7 @@ async function startDaemon(profile, serverUrl) {
16433
16785
  });
16434
16786
  } catch (e) {
16435
16787
  if (e instanceof Error && e.message.startsWith("HTTP 401")) {
16436
- log.warn(`Workspace ${ws.id} token invalid — removing from config`);
16437
- try {
16438
- const cfg = loadCLIConfigForProfile(profile);
16439
- cfg.watched_workspaces = (cfg.watched_workspaces || []).filter((w) => w.id !== ws.id);
16440
- saveCLIConfigForProfile(profile, cfg);
16441
- } catch {}
16788
+ log.warn(`Workspace ${ws.id} token invalid — skipping (run '${cmdPrefix()} register --token <token>' to fix)`);
16442
16789
  } else {
16443
16790
  log.error(`Failed to register workspace ${ws.id}, skipping`, e);
16444
16791
  }
@@ -16497,7 +16844,7 @@ async function startDaemon(profile, serverUrl) {
16497
16844
  cfg.watched_workspaces = (cfg.watched_workspaces || []).filter((w) => w.id !== workspaceId);
16498
16845
  saveCLIConfigForProfile(profile, cfg);
16499
16846
  } catch {}
16500
- log.info(`Workspace ${workspaceId} evictedruntimes removed server-side`);
16847
+ log.info(`Workspace ${workspaceId} deleted server-side — removed from config`);
16501
16848
  }
16502
16849
  const pollCycle = async () => {
16503
16850
  let remaining = config2.maxConcurrentTasks - activeTasks.size;
@@ -16534,7 +16881,7 @@ async function startDaemon(profile, serverUrl) {
16534
16881
  }
16535
16882
  } catch (e) {
16536
16883
  if (e instanceof Error && e.message.startsWith("HTTP 401")) {
16537
- evictedIds.push(ws.workspaceId);
16884
+ log.warn(`Workspace ${ws.workspaceId} poll returned 401 — will retry next cycle`);
16538
16885
  } else {
16539
16886
  log.debug("Poll error", e);
16540
16887
  }
@@ -16543,6 +16890,11 @@ async function startDaemon(profile, serverUrl) {
16543
16890
  for (const id of evictedIds) {
16544
16891
  evictWorkspace(id);
16545
16892
  }
16893
+ try {
16894
+ await reconcilePendingCompletions(config2.workspacesRoot);
16895
+ } catch (e) {
16896
+ log.debug("reconciliation error", e);
16897
+ }
16546
16898
  if (workspaceStates.length === 0) {
16547
16899
  log.info("All workspaces evicted — shutting down");
16548
16900
  shutdown();
@@ -16571,17 +16923,28 @@ async function startDaemon(profile, serverUrl) {
16571
16923
  releaseDaemonPid(profile);
16572
16924
  health.server.close(() => {
16573
16925
  if (restartRequested) {
16574
- const args = ["daemon", "start", "--foreground"];
16926
+ const entry = process.argv[1];
16927
+ const args = [entry, "daemon", "start", "--foreground"];
16575
16928
  if (profile)
16576
16929
  args.push("--profile", profile);
16577
16930
  if (serverUrl)
16578
16931
  args.push("--server", serverUrl);
16579
- const child = spawn2("alook", args, {
16932
+ const logPath = daemonLogFilePath();
16933
+ let logFd;
16934
+ try {
16935
+ mkdirSync5(dirname3(logPath), { recursive: true, mode: 448 });
16936
+ logFd = openSync(logPath, "a", 384);
16937
+ } catch (e) {
16938
+ log.error(`Failed to open daemon log file ${logPath}`, e);
16939
+ }
16940
+ const child = spawn2(process.execPath, args, {
16580
16941
  detached: true,
16581
- stdio: ["ignore", "ignore", "ignore"]
16942
+ stdio: logFd != null ? ["ignore", logFd, logFd] : ["ignore", "ignore", "ignore"]
16582
16943
  });
16583
16944
  child.unref();
16584
- log.info(`Spawned new daemon (pid=${child.pid})`);
16945
+ if (logFd != null)
16946
+ closeSync(logFd);
16947
+ log.info(`Spawned new daemon (pid=${child.pid}), logs: ${logPath}`);
16585
16948
  }
16586
16949
  clearTimeout(timeout);
16587
16950
  process.exit(0);
@@ -16593,21 +16956,62 @@ async function startDaemon(profile, serverUrl) {
16593
16956
  }
16594
16957
  function spawnSessionRunner(input) {
16595
16958
  const logDir = sessionRunnerLogDir();
16596
- mkdirSync3(logDir, { recursive: true });
16597
- const logFilePath = join4(logDir, `${input.task.id}.log`);
16959
+ mkdirSync5(logDir, { recursive: true });
16960
+ const logFilePath = join6(logDir, `${input.task.id}.log`);
16598
16961
  input.logFilePath = logFilePath;
16599
16962
  const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
16600
- const fd = openSync(logFilePath, "a");
16963
+ let fd;
16964
+ try {
16965
+ fd = openSync(logFilePath, "a");
16966
+ } catch (e) {
16967
+ log.error(`Failed to open log file ${logFilePath}`, e);
16968
+ }
16601
16969
  const child = spawn2(process.execPath, [sessionRunnerPath, encoded], {
16602
16970
  detached: true,
16603
- stdio: ["ignore", fd, fd]
16971
+ stdio: fd != null ? ["ignore", fd, fd] : ["ignore", "ignore", "ignore"]
16604
16972
  });
16605
16973
  child.unref();
16606
- closeSync(fd);
16974
+ if (fd != null)
16975
+ closeSync(fd);
16607
16976
  return child;
16608
16977
  }
16609
16978
  async function handleTask(client, config2, runtimeIndex, task, token, activeTasks) {
16610
16979
  log.info(`Task ${task.id} claimed agent=${task.agentId}`);
16980
+ if (task.type === TASK_TYPES.KILL_TASK) {
16981
+ const targetTaskId = task.context?.target_task_id;
16982
+ if (!targetTaskId) {
16983
+ await client.failTask(token, task.id, "missing target_task_id in context");
16984
+ activeTasks.delete(task.id);
16985
+ return;
16986
+ }
16987
+ const agentBaseDir = join6(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir");
16988
+ const timelineDir = join6(agentBaseDir, ".context_timeline");
16989
+ const pid = findRunningPidByTaskId(timelineDir, targetTaskId);
16990
+ if (pid != null) {
16991
+ writeKillIntent(agentBaseDir, {
16992
+ reason: "cancelled",
16993
+ targetTaskId,
16994
+ expectedPid: pid
16995
+ });
16996
+ try {
16997
+ process.kill(pid, "SIGTERM");
16998
+ await client.failTask(token, task.id, "killed");
16999
+ log.info(`Kill task ${task.id}: sent SIGTERM to pid=${pid} for target=${targetTaskId}`);
17000
+ } catch (e) {
17001
+ if (e?.code === "ESRCH") {
17002
+ await client.failTask(token, task.id, "target process already exited");
17003
+ log.info(`Kill task ${task.id}: target pid=${pid} already exited`);
17004
+ } else {
17005
+ await client.failTask(token, task.id, `kill failed: ${e}`);
17006
+ }
17007
+ }
17008
+ } else {
17009
+ await client.failTask(token, task.id, "target not found in timeline");
17010
+ log.info(`Kill task ${task.id}: target ${targetTaskId} not found in timeline`);
17011
+ }
17012
+ activeTasks.delete(task.id);
17013
+ return;
17014
+ }
16611
17015
  try {
16612
17016
  await client.startTask(token, task.id);
16613
17017
  } catch (e) {
@@ -16622,6 +17026,60 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
16622
17026
  return;
16623
17027
  }
16624
17028
  const provider = runtimeData.provider;
17029
+ if (task.contextKey) {
17030
+ const agentBaseDir = join6(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir");
17031
+ cleanupStaleIntents(agentBaseDir);
17032
+ const timelineDir = join6(agentBaseDir, ".context_timeline");
17033
+ const lockAcquired = acquireSteeringLock(agentBaseDir, task.contextKey);
17034
+ if (!lockAcquired) {
17035
+ log.warn(`Steering lock contention for context_key=${task.contextKey}, proceeding without steering`);
17036
+ } else {
17037
+ try {
17038
+ const predecessor = findRunningEntryByContextKey(timelineDir, task.contextKey, provider);
17039
+ if (predecessor && predecessor.task_id !== task.id) {
17040
+ log.info(`Steering: task ${task.id} supersedes predecessor ${predecessor.task_id} (context_key=${task.contextKey})`);
17041
+ if (predecessor.pid != null) {
17042
+ writeKillIntent(agentBaseDir, {
17043
+ reason: "superseded",
17044
+ targetTaskId: predecessor.task_id,
17045
+ expectedPid: predecessor.pid,
17046
+ successorTaskId: task.id
17047
+ });
17048
+ try {
17049
+ process.kill(predecessor.pid, "SIGTERM");
17050
+ log.info(`Steering: sent SIGTERM to predecessor pid=${predecessor.pid}`);
17051
+ } catch (e) {
17052
+ if (e?.code === "ESRCH") {
17053
+ log.info(`Steering: predecessor pid=${predecessor.pid} already exited`);
17054
+ } else {
17055
+ log.warn(`Steering: kill failed for pid=${predecessor.pid}`, e);
17056
+ }
17057
+ }
17058
+ const waitStart = Date.now();
17059
+ const MAX_WAIT_MS = 15000;
17060
+ const POLL_MS = 200;
17061
+ while (Date.now() - waitStart < MAX_WAIT_MS) {
17062
+ const stillRunning = findRunningPidByTaskId(timelineDir, predecessor.task_id);
17063
+ if (stillRunning == null)
17064
+ break;
17065
+ await new Promise((r) => setTimeout(r, POLL_MS));
17066
+ }
17067
+ if (findRunningPidByTaskId(timelineDir, predecessor.task_id) != null) {
17068
+ log.warn(`Steering: predecessor pid=${predecessor.pid} did not exit within ${MAX_WAIT_MS}ms, proceeding anyway`);
17069
+ }
17070
+ }
17071
+ try {
17072
+ await client.supersedeTask(token, predecessor.task_id);
17073
+ log.info(`Steering: predecessor ${predecessor.task_id} marked superseded`);
17074
+ } catch (e) {
17075
+ log.warn(`Steering: failed to mark predecessor superseded server-side`, e);
17076
+ }
17077
+ }
17078
+ } finally {
17079
+ releaseSteeringLock(agentBaseDir, task.contextKey);
17080
+ }
17081
+ }
17082
+ }
16625
17083
  const cliPath = provider === "claude" ? config2.claudePath : provider === "codex" ? config2.codexPath : config2.opencodePath;
16626
17084
  const configModel = provider === "claude" ? config2.claudeModel : provider === "codex" ? config2.codexModel : config2.opencodeModel;
16627
17085
  const agentModel = task.agent?.runtimeConfig?.model;
@@ -16634,7 +17092,8 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
16634
17092
  serverURL: config2.serverURL,
16635
17093
  token,
16636
17094
  workspacesRoot: config2.workspacesRoot,
16637
- agentTimeout: config2.agentTimeout
17095
+ agentTimeout: config2.agentTimeout,
17096
+ messageInactivityTimeout: config2.messageInactivityTimeout
16638
17097
  };
16639
17098
  const child = spawnSessionRunner(input);
16640
17099
  child.on("close", () => activeTasks.delete(task.id));
@@ -16676,7 +17135,7 @@ async function startInBackground(profile, serverUrl) {
16676
17135
  return;
16677
17136
  }
16678
17137
  const logPath = daemonLogFilePath();
16679
- mkdirSync4(dirname4(logPath), { recursive: true, mode: 448 });
17138
+ mkdirSync6(dirname4(logPath), { recursive: true, mode: 448 });
16680
17139
  const logFd = openSync2(logPath, "a", 384);
16681
17140
  const child = spawn3(process.execPath, buildChildArgs(profile, serverUrl), {
16682
17141
  detached: true,
@@ -16767,6 +17226,14 @@ function daemonCommand() {
16767
17226
  import { Command as Command4 } from "commander";
16768
17227
 
16769
17228
  // lib/output.ts
17229
+ function printTable(headers, rows) {
17230
+ const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length)));
17231
+ const header = headers.map((h, i) => h.padEnd(widths[i])).join(" ");
17232
+ const separator = widths.map((w) => "-".repeat(w)).join(" ");
17233
+ console.log(header);
17234
+ console.log(separator);
17235
+ rows.forEach((r) => console.log(r.map((c, i) => (c || "").padEnd(widths[i])).join(" ")));
17236
+ }
16770
17237
  function printJSON(data) {
16771
17238
  console.log(JSON.stringify(data, null, 2));
16772
17239
  }
@@ -16786,8 +17253,8 @@ function configCommand() {
16786
17253
 
16787
17254
  // commands/email.ts
16788
17255
  import { Command as Command5 } from "commander";
16789
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
16790
- import { basename, join as join5 } from "path";
17256
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
17257
+ import { basename, join as join7 } from "path";
16791
17258
  import PostalMime from "postal-mime";
16792
17259
  var VALID_STATUSES = ["unread", "read", "archived"];
16793
17260
  var EMAIL_BASE = "/tmp/alook-emails";
@@ -16818,7 +17285,10 @@ function collectRepeated(value, previous) {
16818
17285
  return previous.concat([value]);
16819
17286
  }
16820
17287
  function resolveClientOpts(command, opts) {
16821
- const parentOpts = command.parent?.parent?.opts() || {};
17288
+ let root = command;
17289
+ while (root.parent)
17290
+ root = root.parent;
17291
+ const parentOpts = root.opts() || {};
16822
17292
  const profile = parentOpts.profile;
16823
17293
  const cfg = loadCLIConfigForProfile(profile);
16824
17294
  const serverUrl = parentOpts.server || cfg.server_url;
@@ -16847,7 +17317,7 @@ function emailCommand() {
16847
17317
  console.error(`Error: invalid status "${opts.status}", must be one of: ${VALID_STATUSES.join(", ")}`);
16848
17318
  process.exit(1);
16849
17319
  }
16850
- const emailDir_base = join5(EMAIL_BASE, workspaceId, opts.agent_id);
17320
+ const emailDir_base = join7(EMAIL_BASE, workspaceId, opts.agent_id);
16851
17321
  try {
16852
17322
  let query = `/api/email?agentId=${opts.agent_id}`;
16853
17323
  if (opts.status)
@@ -16861,11 +17331,11 @@ function emailCommand() {
16861
17331
  printJSON(emails2);
16862
17332
  return;
16863
17333
  }
16864
- mkdirSync5(emailDir_base, { recursive: true });
17334
+ mkdirSync7(emailDir_base, { recursive: true });
16865
17335
  const downloadedPaths = [];
16866
17336
  for (const email3 of emails2) {
16867
- const emailDir = join5(emailDir_base, email3.id);
16868
- mkdirSync5(emailDir, { recursive: true });
17337
+ const emailDir = join7(emailDir_base, email3.id);
17338
+ mkdirSync7(emailDir, { recursive: true });
16869
17339
  const metadata = {
16870
17340
  id: email3.id,
16871
17341
  from: email3.from_email,
@@ -16877,8 +17347,8 @@ function emailCommand() {
16877
17347
  in_reply_to: email3.in_reply_to || "",
16878
17348
  references: email3.references || ""
16879
17349
  };
16880
- const metadataPath = join5(emailDir, "metadata.json");
16881
- writeFileSync4(metadataPath, JSON.stringify(metadata, null, 2));
17350
+ const metadataPath = join7(emailDir, "metadata.json");
17351
+ writeFileSync6(metadataPath, JSON.stringify(metadata, null, 2));
16882
17352
  downloadedPaths.push(metadataPath);
16883
17353
  let rawMime;
16884
17354
  try {
@@ -16893,18 +17363,18 @@ function emailCommand() {
16893
17363
  }
16894
17364
  const parsed = await new PostalMime().parse(rawMime);
16895
17365
  if (parsed.text) {
16896
- const bodyPath = join5(emailDir, "body.txt");
16897
- writeFileSync4(bodyPath, parsed.text);
17366
+ const bodyPath = join7(emailDir, "body.txt");
17367
+ writeFileSync6(bodyPath, parsed.text);
16898
17368
  downloadedPaths.push(bodyPath);
16899
17369
  }
16900
17370
  if (parsed.html) {
16901
- const htmlPath = join5(emailDir, "body.html");
16902
- writeFileSync4(htmlPath, parsed.html);
17371
+ const htmlPath = join7(emailDir, "body.html");
17372
+ writeFileSync6(htmlPath, parsed.html);
16903
17373
  downloadedPaths.push(htmlPath);
16904
17374
  }
16905
17375
  if (parsed.attachments && parsed.attachments.length > 0) {
16906
- const attDir = join5(emailDir, "attachments");
16907
- mkdirSync5(attDir, { recursive: true });
17376
+ const attDir = join7(emailDir, "attachments");
17377
+ mkdirSync7(attDir, { recursive: true });
16908
17378
  const usedFilenames = new Set;
16909
17379
  for (let i = 0;i < parsed.attachments.length; i++) {
16910
17380
  const att = parsed.attachments[i];
@@ -16913,7 +17383,7 @@ function emailCommand() {
16913
17383
  filename = `${i}-${filename}`;
16914
17384
  }
16915
17385
  usedFilenames.add(filename);
16916
- const attPath = join5(attDir, filename);
17386
+ const attPath = join7(attDir, filename);
16917
17387
  const content = att.content;
16918
17388
  let buf;
16919
17389
  if (typeof content === "string") {
@@ -16923,7 +17393,7 @@ function emailCommand() {
16923
17393
  } else {
16924
17394
  buf = Buffer.from(content);
16925
17395
  }
16926
- writeFileSync4(attPath, buf);
17396
+ writeFileSync6(attPath, buf);
16927
17397
  downloadedPaths.push(attPath);
16928
17398
  }
16929
17399
  }
@@ -16962,7 +17432,7 @@ function emailCommand() {
16962
17432
  const client = new APIClient(serverUrl, token, workspaceId);
16963
17433
  let htmlBody;
16964
17434
  try {
16965
- htmlBody = readFileSync5(opts.bodyFile, "utf-8");
17435
+ htmlBody = readFileSync7(opts.bodyFile, "utf-8");
16966
17436
  } catch (err) {
16967
17437
  console.error(`Error: cannot read body file "${opts.bodyFile}": ${err instanceof Error ? err.message : err}`);
16968
17438
  process.exit(1);
@@ -16978,8 +17448,8 @@ function emailCommand() {
16978
17448
  let bytes;
16979
17449
  let size;
16980
17450
  try {
16981
- bytes = readFileSync5(path);
16982
- size = statSync2(path).size;
17451
+ bytes = readFileSync7(path);
17452
+ size = statSync4(path).size;
16983
17453
  } catch (err) {
16984
17454
  console.error(`Error: cannot read attachment "${path}": ${err instanceof Error ? err.message : err}`);
16985
17455
  process.exit(1);
@@ -17024,6 +17494,73 @@ function emailCommand() {
17024
17494
  process.exit(1);
17025
17495
  }
17026
17496
  });
17497
+ const whitelistCmd = new Command5("whitelist").description("Manage email whitelist (allowed senders)");
17498
+ whitelistCmd.command("list").description("List all whitelisted emails for an agent").requiredOption("--agent_id <id>", "Agent ID").option("--workspace <id>", "Workspace ID").option("--json", "Output as JSON").action(async (opts, command) => {
17499
+ const { serverUrl, token, workspaceId } = resolveClientOpts(command, {
17500
+ workspace: opts.workspace,
17501
+ agentId: opts.agent_id
17502
+ });
17503
+ const client = new APIClient(serverUrl, token, workspaceId);
17504
+ try {
17505
+ const entries = await client.getJSON(`/api/agents/${opts.agent_id}/whitelist`);
17506
+ if (!entries.length) {
17507
+ console.log("No whitelisted emails.");
17508
+ return;
17509
+ }
17510
+ if (opts.json) {
17511
+ printJSON(entries);
17512
+ return;
17513
+ }
17514
+ printTable(["ID", "EMAIL", "CREATED AT"], entries.map((e) => [e.id, e.email, e.created_at]));
17515
+ } catch (err) {
17516
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
17517
+ process.exit(1);
17518
+ }
17519
+ });
17520
+ whitelistCmd.command("add").description("Add an email to the whitelist").requiredOption("--agent_id <id>", "Agent ID").option("--workspace <id>", "Workspace ID").argument("<email>", "Email address to whitelist").action(async (email3, opts, command) => {
17521
+ const { serverUrl, token, workspaceId } = resolveClientOpts(command, {
17522
+ workspace: opts.workspace,
17523
+ agentId: opts.agent_id
17524
+ });
17525
+ const client = new APIClient(serverUrl, token, workspaceId);
17526
+ try {
17527
+ const entry = await client.postJSON(`/api/agents/${opts.agent_id}/whitelist`, { email: email3.toLowerCase() });
17528
+ console.log(`Added ${entry.email} to whitelist (id: ${entry.id})`);
17529
+ } catch (err) {
17530
+ const msg = err instanceof Error ? err.message : String(err);
17531
+ if (msg.includes("409")) {
17532
+ console.error(`Error: ${email3.toLowerCase()} is already whitelisted`);
17533
+ } else {
17534
+ console.error(`Error: ${msg}`);
17535
+ }
17536
+ process.exit(1);
17537
+ }
17538
+ });
17539
+ whitelistCmd.command("delete").description("Remove an email from the whitelist").requiredOption("--agent_id <id>", "Agent ID").option("--workspace <id>", "Workspace ID").argument("<email>", "Email address to remove").action(async (email3, opts, command) => {
17540
+ const { serverUrl, token, workspaceId } = resolveClientOpts(command, {
17541
+ workspace: opts.workspace,
17542
+ agentId: opts.agent_id
17543
+ });
17544
+ const client = new APIClient(serverUrl, token, workspaceId);
17545
+ const normalizedEmail = email3.toLowerCase();
17546
+ try {
17547
+ const entries = await client.getJSON(`/api/agents/${opts.agent_id}/whitelist`);
17548
+ const entry = entries.find((e) => e.email === normalizedEmail);
17549
+ if (!entry) {
17550
+ console.error(`Error: ${normalizedEmail} is not in the whitelist`);
17551
+ process.exit(1);
17552
+ }
17553
+ await client.deleteJSON(`/api/agents/${opts.agent_id}/whitelist/${entry.id}`);
17554
+ console.log(`Removed ${normalizedEmail} from whitelist`);
17555
+ } catch (err) {
17556
+ const msg = err instanceof Error ? err.message : String(err);
17557
+ if (msg === "__exit__")
17558
+ throw err;
17559
+ console.error(`Error: ${msg}`);
17560
+ process.exit(1);
17561
+ }
17562
+ });
17563
+ cmd.addCommand(whitelistCmd);
17027
17564
  return cmd;
17028
17565
  }
17029
17566
 
@@ -17283,7 +17820,7 @@ ${result.output}`);
17283
17820
 
17284
17821
  // commands/sync.ts
17285
17822
  import { Command as Command9 } from "commander";
17286
- import { readFileSync as readFileSync6, statSync as statSync3 } from "fs";
17823
+ import { readFileSync as readFileSync8, statSync as statSync5 } from "fs";
17287
17824
  import { basename as basename2 } from "path";
17288
17825
  var MIME_BY_EXT2 = {
17289
17826
  ".pdf": "application/pdf",
@@ -17332,9 +17869,9 @@ function syncCommand() {
17332
17869
  let bytes;
17333
17870
  let size;
17334
17871
  try {
17335
- const stat = statSync3(opts.file);
17872
+ const stat = statSync5(opts.file);
17336
17873
  size = stat.size;
17337
- bytes = readFileSync6(opts.file);
17874
+ bytes = readFileSync8(opts.file);
17338
17875
  } catch (err) {
17339
17876
  console.error(`Error: cannot read file "${opts.file}": ${err.message}`);
17340
17877
  process.exit(1);