@alook/cli 0.0.11 → 0.0.13

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
@@ -13878,6 +13878,7 @@ var TaskAgentDataApiSchema = exports_external.object({
13878
13878
  name: exports_external.string(),
13879
13879
  runtime_config: exports_external.record(exports_external.string(), exports_external.unknown()).default({}),
13880
13880
  email_handle: exports_external.string().nullable().optional(),
13881
+ email_addresses: exports_external.array(exports_external.string()).default([]),
13881
13882
  user_email: exports_external.string().nullable().optional()
13882
13883
  });
13883
13884
  var TaskApiBaseSchema = exports_external.object({
@@ -14039,6 +14040,9 @@ var CreateConversationRequestSchema = exports_external.object({
14039
14040
  var CreateMessageRequestSchema = exports_external.object({
14040
14041
  content: exports_external.string().min(1, "content is required")
14041
14042
  });
14043
+ var CreateBufferedMessageRequestSchema = exports_external.object({
14044
+ content: exports_external.string().min(1, "content is required")
14045
+ });
14042
14046
  var EmailAttachmentSchema = exports_external.object({
14043
14047
  key: exports_external.string().min(1),
14044
14048
  filename: exports_external.string().min(1),
@@ -14052,7 +14056,9 @@ var SendEmailRequestSchema = exports_external.object({
14052
14056
  htmlBody: exports_external.string().default(""),
14053
14057
  inReplyTo: exports_external.string().optional(),
14054
14058
  references: exports_external.string().optional(),
14055
- attachments: exports_external.array(EmailAttachmentSchema).optional()
14059
+ attachments: exports_external.array(EmailAttachmentSchema).optional(),
14060
+ customAccountId: exports_external.string().optional(),
14061
+ from: exports_external.string().email().optional()
14056
14062
  });
14057
14063
  var UpdateEmailStatusRequestSchema = exports_external.object({
14058
14064
  status: exports_external.enum(["unread", "read", "archived"])
@@ -14070,6 +14076,48 @@ var EmailNotifyRequestSchema = exports_external.object({
14070
14076
  inReplyTo: exports_external.string().optional().default(""),
14071
14077
  references: exports_external.string().optional().default("")
14072
14078
  });
14079
+ var CreateEmailAccountSchema = exports_external.object({
14080
+ emailAddress: exports_external.string().email("valid email required"),
14081
+ displayName: exports_external.string().default(""),
14082
+ imapHost: exports_external.string().min(1, "IMAP host is required"),
14083
+ imapPort: exports_external.number().int().min(1).max(65535).default(993),
14084
+ imapUsername: exports_external.string().min(1, "IMAP username is required"),
14085
+ imapPassword: exports_external.string().min(1, "IMAP password is required"),
14086
+ imapTls: exports_external.boolean().default(true),
14087
+ smtpHost: exports_external.string().min(1, "SMTP host is required"),
14088
+ smtpPort: exports_external.number().int().min(1).max(65535).default(587),
14089
+ smtpUsername: exports_external.string().min(1, "SMTP username is required"),
14090
+ smtpPassword: exports_external.string().min(1, "SMTP password is required"),
14091
+ smtpTls: exports_external.number().int().min(0).max(2).default(1),
14092
+ pollIntervalSeconds: exports_external.number().int().min(30).max(3600).default(60)
14093
+ });
14094
+ var UpdateEmailAccountSchema = exports_external.object({
14095
+ emailAddress: exports_external.string().email().optional(),
14096
+ displayName: exports_external.string().optional(),
14097
+ imapHost: exports_external.string().min(1).optional(),
14098
+ imapPort: exports_external.number().int().min(1).max(65535).optional(),
14099
+ imapUsername: exports_external.string().min(1).optional(),
14100
+ imapPassword: exports_external.string().min(1).optional(),
14101
+ imapTls: exports_external.boolean().optional(),
14102
+ smtpHost: exports_external.string().min(1).optional(),
14103
+ smtpPort: exports_external.number().int().min(1).max(65535).optional(),
14104
+ smtpUsername: exports_external.string().min(1).optional(),
14105
+ smtpPassword: exports_external.string().min(1).optional(),
14106
+ smtpTls: exports_external.number().int().min(0).max(2).optional(),
14107
+ pollIntervalSeconds: exports_external.number().int().min(30).max(3600).optional()
14108
+ });
14109
+ var TestEmailConnectionSchema = exports_external.object({
14110
+ imapHost: exports_external.string().min(1),
14111
+ imapPort: exports_external.number().int().min(1).max(65535).default(993),
14112
+ imapUsername: exports_external.string().min(1),
14113
+ imapPassword: exports_external.string().min(1),
14114
+ imapTls: exports_external.boolean().default(true),
14115
+ smtpHost: exports_external.string().min(1),
14116
+ smtpPort: exports_external.number().int().min(1).max(65535).default(587),
14117
+ smtpUsername: exports_external.string().min(1),
14118
+ smtpPassword: exports_external.string().min(1),
14119
+ smtpTls: exports_external.number().int().min(0).max(2).default(1)
14120
+ });
14073
14121
  var CreateWorkspaceRequestSchema = exports_external.object({
14074
14122
  name: exports_external.string().min(1, "name is required"),
14075
14123
  slug: exports_external.string().min(1, "slug is required")
@@ -15581,8 +15629,11 @@ var message = sqliteTable("message", {
15581
15629
  content: text("content").notNull().default(""),
15582
15630
  taskId: text("task_id"),
15583
15631
  attachmentIds: text("attachment_ids"),
15632
+ status: text("status").notNull().default("active"),
15584
15633
  createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
15585
- });
15634
+ }, (t) => [
15635
+ index("idx_message_conversation_status").on(t.conversationId, t.status)
15636
+ ]);
15586
15637
  var agentTaskQueue = sqliteTable("agent_task_queue", {
15587
15638
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
15588
15639
  agentId: text("agent_id").notNull(),
@@ -15683,6 +15734,37 @@ var artifact = sqliteTable("artifact", {
15683
15734
  foreignColumns: [agent.id, agent.workspaceId]
15684
15735
  }).onDelete("cascade")
15685
15736
  ]);
15737
+ var agentEmailAccount = sqliteTable("agent_email_account", {
15738
+ id: text("id").primaryKey().$defaultFn(() => "aea_" + nanoid3()),
15739
+ agentId: text("agent_id").notNull(),
15740
+ workspaceId: text("workspace_id").notNull(),
15741
+ emailAddress: text("email_address").notNull(),
15742
+ displayName: text("display_name").notNull().default(""),
15743
+ imapHost: text("imap_host").notNull(),
15744
+ imapPort: integer2("imap_port").notNull().default(993),
15745
+ imapUsername: text("imap_username").notNull(),
15746
+ imapPassword: text("imap_password").notNull(),
15747
+ imapTls: integer2("imap_tls", { mode: "boolean" }).notNull().default(true),
15748
+ smtpHost: text("smtp_host").notNull(),
15749
+ smtpPort: integer2("smtp_port").notNull().default(587),
15750
+ smtpUsername: text("smtp_username").notNull(),
15751
+ smtpPassword: text("smtp_password").notNull(),
15752
+ smtpTls: integer2("smtp_tls").notNull().default(1),
15753
+ pollIntervalSeconds: integer2("poll_interval_seconds").notNull().default(60),
15754
+ lastSyncedUid: text("last_synced_uid").notNull().default("0"),
15755
+ lastSyncedAt: text("last_synced_at"),
15756
+ status: text("status").notNull().default("active"),
15757
+ errorMessage: text("error_message").notNull().default(""),
15758
+ createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
15759
+ updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
15760
+ }, (t) => [
15761
+ index("idx_email_account_agent_ws").on(t.agentId, t.workspaceId),
15762
+ unique("email_account_agent_email").on(t.agentId, t.emailAddress),
15763
+ foreignKey({
15764
+ columns: [t.agentId, t.workspaceId],
15765
+ foreignColumns: [agent.id, agent.workspaceId]
15766
+ }).onDelete("cascade")
15767
+ ]);
15686
15768
  var machineToken = sqliteTable("machine_token", {
15687
15769
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
15688
15770
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -15888,7 +15970,7 @@ function loadDaemonConfig(profile) {
15888
15970
  codexModel: process.env.ALOOK_CODEX_MODEL || "",
15889
15971
  opencodeModel: process.env.ALOOK_OPENCODE_MODEL || "",
15890
15972
  pollInterval: parseDuration(process.env.ALOOK_DAEMON_POLL_INTERVAL || "3s"),
15891
- agentTimeout: parseDuration(process.env.ALOOK_AGENT_TIMEOUT || "2h"),
15973
+ agentTimeout: parseDuration(process.env.ALOOK_AGENT_TIMEOUT || "12h"),
15892
15974
  maxConcurrentTasks: parseInt(process.env.ALOOK_DAEMON_MAX_CONCURRENT_TASKS || "20"),
15893
15975
  daemonId,
15894
15976
  deviceName: process.env.ALOOK_DAEMON_DEVICE_NAME || h,
@@ -15964,7 +16046,7 @@ function fromApiTask(api2) {
15964
16046
  type: api2.type,
15965
16047
  contextKey: api2.context_key ?? null,
15966
16048
  context: api2.context ?? undefined,
15967
- agent: api2.agent ? { name: api2.agent.name, instructions: api2.agent.instructions, emailHandle: api2.agent.email_handle ?? undefined, userEmail: api2.agent.user_email ?? undefined, runtimeConfig: api2.agent.runtime_config ?? undefined } : undefined,
16049
+ agent: api2.agent ? { name: api2.agent.name, instructions: api2.agent.instructions, emailHandle: api2.agent.email_handle ?? undefined, emailAddresses: api2.agent.email_addresses ?? [], userEmail: api2.agent.user_email ?? undefined, runtimeConfig: api2.agent.runtime_config ?? undefined } : undefined,
15968
16050
  repos: undefined,
15969
16051
  createdAt: api2.created_at
15970
16052
  };
@@ -16001,10 +16083,13 @@ function useColor() {
16001
16083
  }
16002
16084
  function timestamp() {
16003
16085
  const d = new Date;
16086
+ const Y = d.getFullYear();
16087
+ const M = String(d.getMonth() + 1).padStart(2, "0");
16088
+ const D = String(d.getDate()).padStart(2, "0");
16004
16089
  const h = String(d.getHours()).padStart(2, "0");
16005
16090
  const m = String(d.getMinutes()).padStart(2, "0");
16006
16091
  const s = String(d.getSeconds()).padStart(2, "0");
16007
- return `${h}:${m}:${s}`;
16092
+ return `${Y}-${M}-${D} ${h}:${m}:${s}`;
16008
16093
  }
16009
16094
 
16010
16095
  class Logger2 {
@@ -16193,7 +16278,7 @@ async function handleCliUpdate(version3, onSuccess, profile) {
16193
16278
  }
16194
16279
 
16195
16280
  // daemon/daemon.ts
16196
- import { existsSync, mkdirSync as mkdirSync3, openSync, closeSync, renameSync, readdirSync, statSync, unlinkSync as unlinkSync3 } from "fs";
16281
+ import { existsSync, mkdirSync as mkdirSync3, openSync, closeSync, readdirSync, statSync, unlinkSync as unlinkSync3 } from "fs";
16197
16282
  import { execSync as execSync3, spawn as spawn2 } from "child_process";
16198
16283
  import { fileURLToPath as fileURLToPath2 } from "url";
16199
16284
  import { dirname as dirname3, join as join4 } from "path";
@@ -16468,21 +16553,18 @@ async function startDaemon(profile, serverUrl) {
16468
16553
  await pollCycle();
16469
16554
  }
16470
16555
  function spawnSessionRunner(input) {
16471
- const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
16472
16556
  const logDir = sessionRunnerLogDir();
16473
16557
  mkdirSync3(logDir, { recursive: true });
16474
- const tmpLogPath = join4(logDir, `${input.task.id}.log`);
16475
- const fd = openSync(tmpLogPath, "a");
16558
+ const logFilePath = join4(logDir, `${input.task.id}.log`);
16559
+ input.logFilePath = logFilePath;
16560
+ const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
16561
+ const fd = openSync(logFilePath, "a");
16476
16562
  const child = spawn2(process.execPath, [sessionRunnerPath, encoded], {
16477
16563
  detached: true,
16478
16564
  stdio: ["ignore", fd, fd]
16479
16565
  });
16480
16566
  child.unref();
16481
16567
  closeSync(fd);
16482
- if (child.pid) {
16483
- const pidLogPath = join4(logDir, `${child.pid}.log`);
16484
- renameSync(tmpLogPath, pidLogPath);
16485
- }
16486
16568
  return child;
16487
16569
  }
16488
16570
  async function handleTask(client, config2, runtimeIndex, task, token, activeTasks) {
@@ -16832,7 +16914,7 @@ function emailCommand() {
16832
16914
  process.exit(1);
16833
16915
  }
16834
16916
  });
16835
- cmd.command("send").description("Send an email from the agent").requiredOption("--agent_id <id>", "Agent ID").requiredOption("--to <addr>", "Recipient email address").requiredOption("--subject <s>", "Subject line").requiredOption("--body-file <path>", "Path to HTML body file").option("--in-reply-to <emailId>", "Email ID to reply to (sets threading headers)").option("--attachment <path>", "Path to a file to attach (repeatable)", collectRepeated, []).option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
16917
+ cmd.command("send").description("Send an email from the agent").requiredOption("--agent_id <id>", "Agent ID").requiredOption("--to <addr>", "Recipient email address").requiredOption("--subject <s>", "Subject line").requiredOption("--body-file <path>", "Path to HTML body file").option("--from <addr>", "Send from a specific email address (custom mailbox)").option("--in-reply-to <emailId>", "Email ID to reply to (sets threading headers)").option("--attachment <path>", "Path to a file to attach (repeatable)", collectRepeated, []).option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
16836
16918
  const { serverUrl, token, workspaceId } = resolveClientOpts(command, {
16837
16919
  workspace: opts.workspace,
16838
16920
  agentId: opts.agent_id
@@ -16893,7 +16975,8 @@ function emailCommand() {
16893
16975
  subject: opts.subject,
16894
16976
  htmlBody,
16895
16977
  attachments,
16896
- ...inReplyTo ? { inReplyTo, references } : {}
16978
+ ...inReplyTo ? { inReplyTo, references } : {},
16979
+ ...opts.from ? { from: opts.from } : {}
16897
16980
  });
16898
16981
  console.log(`Sent email to ${res.to_email} (id: ${res.id})`);
16899
16982
  } catch (err) {
@@ -14,7 +14,6 @@ var __export = (target, all) => {
14
14
  };
15
15
 
16
16
  // daemon/session-runner.ts
17
- import { createWriteStream } from "fs";
18
17
  import { mkdir, writeFile, rm } from "fs/promises";
19
18
  import path from "path";
20
19
 
@@ -13596,6 +13595,7 @@ var TaskAgentDataApiSchema = exports_external.object({
13596
13595
  name: exports_external.string(),
13597
13596
  runtime_config: exports_external.record(exports_external.string(), exports_external.unknown()).default({}),
13598
13597
  email_handle: exports_external.string().nullable().optional(),
13598
+ email_addresses: exports_external.array(exports_external.string()).default([]),
13599
13599
  user_email: exports_external.string().nullable().optional()
13600
13600
  });
13601
13601
  var TaskApiBaseSchema = exports_external.object({
@@ -13757,6 +13757,9 @@ var CreateConversationRequestSchema = exports_external.object({
13757
13757
  var CreateMessageRequestSchema = exports_external.object({
13758
13758
  content: exports_external.string().min(1, "content is required")
13759
13759
  });
13760
+ var CreateBufferedMessageRequestSchema = exports_external.object({
13761
+ content: exports_external.string().min(1, "content is required")
13762
+ });
13760
13763
  var EmailAttachmentSchema = exports_external.object({
13761
13764
  key: exports_external.string().min(1),
13762
13765
  filename: exports_external.string().min(1),
@@ -13770,7 +13773,9 @@ var SendEmailRequestSchema = exports_external.object({
13770
13773
  htmlBody: exports_external.string().default(""),
13771
13774
  inReplyTo: exports_external.string().optional(),
13772
13775
  references: exports_external.string().optional(),
13773
- attachments: exports_external.array(EmailAttachmentSchema).optional()
13776
+ attachments: exports_external.array(EmailAttachmentSchema).optional(),
13777
+ customAccountId: exports_external.string().optional(),
13778
+ from: exports_external.string().email().optional()
13774
13779
  });
13775
13780
  var UpdateEmailStatusRequestSchema = exports_external.object({
13776
13781
  status: exports_external.enum(["unread", "read", "archived"])
@@ -13788,6 +13793,48 @@ var EmailNotifyRequestSchema = exports_external.object({
13788
13793
  inReplyTo: exports_external.string().optional().default(""),
13789
13794
  references: exports_external.string().optional().default("")
13790
13795
  });
13796
+ var CreateEmailAccountSchema = exports_external.object({
13797
+ emailAddress: exports_external.string().email("valid email required"),
13798
+ displayName: exports_external.string().default(""),
13799
+ imapHost: exports_external.string().min(1, "IMAP host is required"),
13800
+ imapPort: exports_external.number().int().min(1).max(65535).default(993),
13801
+ imapUsername: exports_external.string().min(1, "IMAP username is required"),
13802
+ imapPassword: exports_external.string().min(1, "IMAP password is required"),
13803
+ imapTls: exports_external.boolean().default(true),
13804
+ smtpHost: exports_external.string().min(1, "SMTP host is required"),
13805
+ smtpPort: exports_external.number().int().min(1).max(65535).default(587),
13806
+ smtpUsername: exports_external.string().min(1, "SMTP username is required"),
13807
+ smtpPassword: exports_external.string().min(1, "SMTP password is required"),
13808
+ smtpTls: exports_external.number().int().min(0).max(2).default(1),
13809
+ pollIntervalSeconds: exports_external.number().int().min(30).max(3600).default(60)
13810
+ });
13811
+ var UpdateEmailAccountSchema = exports_external.object({
13812
+ emailAddress: exports_external.string().email().optional(),
13813
+ displayName: exports_external.string().optional(),
13814
+ imapHost: exports_external.string().min(1).optional(),
13815
+ imapPort: exports_external.number().int().min(1).max(65535).optional(),
13816
+ imapUsername: exports_external.string().min(1).optional(),
13817
+ imapPassword: exports_external.string().min(1).optional(),
13818
+ imapTls: exports_external.boolean().optional(),
13819
+ smtpHost: exports_external.string().min(1).optional(),
13820
+ smtpPort: exports_external.number().int().min(1).max(65535).optional(),
13821
+ smtpUsername: exports_external.string().min(1).optional(),
13822
+ smtpPassword: exports_external.string().min(1).optional(),
13823
+ smtpTls: exports_external.number().int().min(0).max(2).optional(),
13824
+ pollIntervalSeconds: exports_external.number().int().min(30).max(3600).optional()
13825
+ });
13826
+ var TestEmailConnectionSchema = exports_external.object({
13827
+ imapHost: exports_external.string().min(1),
13828
+ imapPort: exports_external.number().int().min(1).max(65535).default(993),
13829
+ imapUsername: exports_external.string().min(1),
13830
+ imapPassword: exports_external.string().min(1),
13831
+ imapTls: exports_external.boolean().default(true),
13832
+ smtpHost: exports_external.string().min(1),
13833
+ smtpPort: exports_external.number().int().min(1).max(65535).default(587),
13834
+ smtpUsername: exports_external.string().min(1),
13835
+ smtpPassword: exports_external.string().min(1),
13836
+ smtpTls: exports_external.number().int().min(0).max(2).default(1)
13837
+ });
13791
13838
  var CreateWorkspaceRequestSchema = exports_external.object({
13792
13839
  name: exports_external.string().min(1, "name is required"),
13793
13840
  slug: exports_external.string().min(1, "slug is required")
@@ -15299,8 +15346,11 @@ var message = sqliteTable("message", {
15299
15346
  content: text("content").notNull().default(""),
15300
15347
  taskId: text("task_id"),
15301
15348
  attachmentIds: text("attachment_ids"),
15349
+ status: text("status").notNull().default("active"),
15302
15350
  createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
15303
- });
15351
+ }, (t) => [
15352
+ index("idx_message_conversation_status").on(t.conversationId, t.status)
15353
+ ]);
15304
15354
  var agentTaskQueue = sqliteTable("agent_task_queue", {
15305
15355
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
15306
15356
  agentId: text("agent_id").notNull(),
@@ -15401,6 +15451,37 @@ var artifact = sqliteTable("artifact", {
15401
15451
  foreignColumns: [agent.id, agent.workspaceId]
15402
15452
  }).onDelete("cascade")
15403
15453
  ]);
15454
+ var agentEmailAccount = sqliteTable("agent_email_account", {
15455
+ id: text("id").primaryKey().$defaultFn(() => "aea_" + nanoid3()),
15456
+ agentId: text("agent_id").notNull(),
15457
+ workspaceId: text("workspace_id").notNull(),
15458
+ emailAddress: text("email_address").notNull(),
15459
+ displayName: text("display_name").notNull().default(""),
15460
+ imapHost: text("imap_host").notNull(),
15461
+ imapPort: integer2("imap_port").notNull().default(993),
15462
+ imapUsername: text("imap_username").notNull(),
15463
+ imapPassword: text("imap_password").notNull(),
15464
+ imapTls: integer2("imap_tls", { mode: "boolean" }).notNull().default(true),
15465
+ smtpHost: text("smtp_host").notNull(),
15466
+ smtpPort: integer2("smtp_port").notNull().default(587),
15467
+ smtpUsername: text("smtp_username").notNull(),
15468
+ smtpPassword: text("smtp_password").notNull(),
15469
+ smtpTls: integer2("smtp_tls").notNull().default(1),
15470
+ pollIntervalSeconds: integer2("poll_interval_seconds").notNull().default(60),
15471
+ lastSyncedUid: text("last_synced_uid").notNull().default("0"),
15472
+ lastSyncedAt: text("last_synced_at"),
15473
+ status: text("status").notNull().default("active"),
15474
+ errorMessage: text("error_message").notNull().default(""),
15475
+ createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
15476
+ updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
15477
+ }, (t) => [
15478
+ index("idx_email_account_agent_ws").on(t.agentId, t.workspaceId),
15479
+ unique("email_account_agent_email").on(t.agentId, t.emailAddress),
15480
+ foreignKey({
15481
+ columns: [t.agentId, t.workspaceId],
15482
+ foreignColumns: [agent.id, agent.workspaceId]
15483
+ }).onDelete("cascade")
15484
+ ]);
15404
15485
  var machineToken = sqliteTable("machine_token", {
15405
15486
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
15406
15487
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -16421,7 +16502,7 @@ function createBackend(provider, cliPath) {
16421
16502
 
16422
16503
  // daemon/execenv/index.ts
16423
16504
  import { mkdirSync as mkdirSync2 } from "fs";
16424
- import { join as join3, dirname } from "path";
16505
+ import { join as join3 } from "path";
16425
16506
 
16426
16507
  // daemon/execenv/context.ts
16427
16508
  import { createHash } from "crypto";
@@ -16499,13 +16580,22 @@ ${task.agent.instructions}
16499
16580
  You can communicate with the world through Alook CLI.
16500
16581
  Your alook agent id is '${task.agentId}'. remember this, most of alook cli will requires you input your agent id.
16501
16582
  `;
16502
- if (task.agent?.emailHandle) {
16503
- content += `Your email address is '${toAlookAddress(task.agent.emailHandle)}'.
16504
- ${task.agent.userEmail ? `Your owner's email address is '${task.agent.userEmail}'.` : ""}
16583
+ const alookAddr = task.agent?.emailHandle ? toAlookAddress(task.agent.emailHandle) : null;
16584
+ const customAddrs = (task.agent?.emailAddresses ?? []).filter((a) => a !== alookAddr);
16585
+ if (alookAddr || customAddrs.length > 0) {
16586
+ const lines = [];
16587
+ if (alookAddr)
16588
+ lines.push(`- '${alookAddr}' (default, Alook platform address)`);
16589
+ for (const a of customAddrs)
16590
+ lines.push(`- '${a}' (custom IMAP/SMTP mailbox)`);
16591
+ content += `Your email addresses:
16592
+ ${lines.join(`
16593
+ `)}
16594
+ ${task.agent?.userEmail ? `Your owner's email address is '${task.agent.userEmail}'.` : ""}
16505
16595
 
16506
16596
  ### Emails
16507
16597
  ---
16508
- Run 'npx @alook/cli pull --agent_id ${task.agentId} --status unread' to download unread emails to '/tmp/alook-emails/'.
16598
+ Run 'npx @alook/cli email pull --agent_id ${task.agentId} --status unread' to download unread emails to '/tmp/alook-emails/'.
16509
16599
  Each email is saved to '/tmp/alook-emails/<emailId>/' with:
16510
16600
  - 'metadata.json' — sender, recipient, subject, date, status, message_id, in_reply_to, references
16511
16601
  - 'body.txt' — plain text body
@@ -16513,14 +16603,15 @@ Each email is saved to '/tmp/alook-emails/<emailId>/' with:
16513
16603
  - 'attachments/' — extracted attachment files (if any)
16514
16604
  ---
16515
16605
  Before starting to process an email, mark it as read:
16516
- - Run 'npx @alook/cli set --agent_id ${task.agentId} --email_id <EMAIL_ID> --status read'
16606
+ - Run 'npx @alook/cli email set --agent_id ${task.agentId} --email_id <EMAIL_ID> --status read'
16517
16607
  ---
16518
16608
 
16519
16609
  #### Sending a new email
16520
16610
  Write the HTML body to a file first, then send it. The body is forwarded as-is (HTML).
16521
16611
  - Run 'npx @alook/cli email send --agent_id ${task.agentId} --to <ADDRESS> --subject "<SUBJECT>" --body-file <PATH_TO_HTML>'
16612
+ - To send from a specific mailbox, add '--from <YOUR_EMAIL_ADDRESS>'. Without '--from', the default Alook address is used.
16522
16613
  - Attach files with '--attachment <PATH>' — repeat the flag for multiple attachments. Each file is uploaded before sending.
16523
- - Example: 'npx @alook/cli email send --agent_id ${task.agentId} --to foo@bar.com --subject "Weekly report" --body-file /tmp/body.html --attachment /tmp/report.pdf --attachment /tmp/chart.png'
16614
+ - Example: 'npx @alook/cli email send --agent_id ${task.agentId} --to foo@bar.com --subject "Weekly report" --body-file /tmp/body.html --from alice@company.com --attachment /tmp/report.pdf'
16524
16615
 
16525
16616
  #### Replying to an email
16526
16617
  To reply to an email, add '--in-reply-to <EMAIL_ID>' to the send command. This sets the correct email threading headers so the recipient's email client groups the reply into the same conversation thread.
@@ -16708,10 +16799,13 @@ function useColor() {
16708
16799
  }
16709
16800
  function timestamp() {
16710
16801
  const d = new Date;
16802
+ const Y = d.getFullYear();
16803
+ const M = String(d.getMonth() + 1).padStart(2, "0");
16804
+ const D = String(d.getDate()).padStart(2, "0");
16711
16805
  const h = String(d.getHours()).padStart(2, "0");
16712
16806
  const m = String(d.getMinutes()).padStart(2, "0");
16713
16807
  const s = String(d.getSeconds()).padStart(2, "0");
16714
- return `${h}:${m}:${s}`;
16808
+ return `${Y}-${M}-${D} ${h}:${m}:${s}`;
16715
16809
  }
16716
16810
 
16717
16811
  class Logger2 {
@@ -16896,7 +16990,7 @@ function updateEntry(timelineDir, taskId, updater) {
16896
16990
  }
16897
16991
  log.debug(`Timeline updateEntry: task_id ${taskId} not found in last 7 days`);
16898
16992
  }
16899
- function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider, contextKey) {
16993
+ function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider, contextKey, detailedLog) {
16900
16994
  return {
16901
16995
  task_id: taskId,
16902
16996
  context_key: contextKey ?? null,
@@ -16908,7 +17002,8 @@ function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider, con
16908
17002
  prompt,
16909
17003
  agent_responses: [],
16910
17004
  errmsg: null,
16911
- provider: provider ?? null
17005
+ provider: provider ?? null,
17006
+ detailed_log: detailedLog ?? null
16912
17007
  };
16913
17008
  }
16914
17009
  var DEFAULT_RESUME_MAX_AGE_MS = 3 * 60 * 60 * 1000;
@@ -16938,8 +17033,6 @@ function prepare(config2, task) {
16938
17033
  const timelineDir = join3(workDir, ".context_timeline");
16939
17034
  mkdirSync2(timelineDir, { recursive: true });
16940
17035
  writeInstructionFileIfChanged(workDir, task);
16941
- const logFile = join3(config2.workspacesRoot, task.workspaceId, task.agentId, "agent.log");
16942
- mkdirSync2(dirname(logFile), { recursive: true });
16943
17036
  const env = {
16944
17037
  ALOOK_WORKSPACE_ID: task.workspaceId,
16945
17038
  ALOOK_AGENT_ID: task.agentId,
@@ -16947,7 +17040,7 @@ function prepare(config2, task) {
16947
17040
  ALOOK_CONVERSATION_ID: task.conversationId,
16948
17041
  ALOOK_HEALTH_PORT: process.env.ALOOK_HEALTH_PORT || "19514"
16949
17042
  };
16950
- return { workDir, logFile, timelineDir, env };
17043
+ return { workDir, timelineDir, env };
16951
17044
  }
16952
17045
 
16953
17046
  // daemon/prompt.ts
@@ -16993,18 +17086,21 @@ async function downloadAttachments(client, token, workspaceId, taskId, attachmen
16993
17086
  }
16994
17087
  async function runSession(input) {
16995
17088
  const { task, provider, cliPath, model, serverURL, token, workspacesRoot, agentTimeout } = input;
17089
+ log.info(`starting (task=${task.id}, type=${task.type}, agent=${task.agentId}, provider=${provider}, model=${model || "default"})`);
16996
17090
  const client = new DaemonClient(serverURL);
16997
17091
  const backend = createBackend(provider, cliPath);
16998
- const { workDir, logFile, timelineDir, env } = prepare({ workspacesRoot }, task);
17092
+ const { workDir, timelineDir, env } = prepare({ workspacesRoot }, task);
16999
17093
  const attachmentIds = task.context?.attachment_ids ?? [];
17000
17094
  let attachments;
17001
17095
  if (attachmentIds.length > 0) {
17096
+ log.info(`downloading ${attachmentIds.length} attachment(s)`);
17002
17097
  try {
17003
17098
  attachments = await downloadAttachments(client, token, task.workspaceId, task.id, attachmentIds);
17099
+ log.info(`attachments ready (${attachments.length} file(s))`);
17004
17100
  } catch (e) {
17005
17101
  await cleanupAttachments(task.id);
17006
17102
  const errMsg = `failed to download attachments: ${e}`;
17007
- log.error(`Task ${task.id} ${errMsg}`);
17103
+ log.error(errMsg);
17008
17104
  await client.failTask(token, task.id, errMsg);
17009
17105
  return;
17010
17106
  }
@@ -17012,7 +17108,7 @@ async function runSession(input) {
17012
17108
  const prompt = buildPrompt(task, attachments);
17013
17109
  const resumeSessionId = task.contextKey ? findResumableSessionByContextKey(timelineDir, task.contextKey, provider) ?? undefined : undefined;
17014
17110
  if (resumeSessionId) {
17015
- log.info(`Task ${task.id} resuming session ${resumeSessionId} (context_key: ${task.contextKey})`);
17111
+ log.info(`resuming session ${resumeSessionId} (context_key: ${task.contextKey})`);
17016
17112
  }
17017
17113
  const session2 = backend.execute(prompt, {
17018
17114
  cwd: workDir,
@@ -17023,9 +17119,12 @@ async function runSession(input) {
17023
17119
  });
17024
17120
  const agentPid = session2.pid;
17025
17121
  const earlySessionId = await session2.sessionId;
17026
- await initEntryAsync(timelineDir, createTimelineEntry(task.id, task.prompt, task.type, earlySessionId, process.pid, provider, task.contextKey));
17122
+ log.info(`agent started (pid=${agentPid ?? "unknown"}, session=${earlySessionId})`);
17123
+ log.info(JSON.stringify({ role: "user", type: "text", content: prompt }));
17124
+ await initEntryAsync(timelineDir, createTimelineEntry(task.id, task.prompt, task.type, earlySessionId, process.pid, provider, task.contextKey, input.logFilePath));
17027
17125
  const pendingMessages = [];
17028
17126
  let seq = 0;
17127
+ let toolCount = 0;
17029
17128
  const BATCH_SIZE = Number(process.env.ALOOK_MESSAGE_BATCH_SIZE) || 20;
17030
17129
  const FLUSH_INTERVAL_MS = Number(process.env.ALOOK_MESSAGE_FLUSH_INTERVAL_MS) || 100;
17031
17130
  const flushMessages = async () => {
@@ -17035,29 +17134,16 @@ async function runSession(input) {
17035
17134
  try {
17036
17135
  await client.reportMessages(token, task.id, batch);
17037
17136
  } catch (e) {
17038
- log.debug(`Task ${task.id} message report failed`, e);
17137
+ log.debug("message report failed", e);
17039
17138
  }
17040
17139
  };
17041
17140
  const flushTimer = setInterval(flushMessages, FLUSH_INTERVAL_MS);
17042
- let logStream;
17043
- try {
17044
- logStream = createWriteStream(logFile, { flags: "a" });
17045
- logStream.write(JSON.stringify({
17046
- ts: localISOString(),
17047
- type: "text",
17048
- role: "user",
17049
- content: prompt
17050
- }) + `
17051
- `);
17052
- } catch {
17053
- logStream = undefined;
17054
- }
17055
17141
  let killed = false;
17056
17142
  const onKill = async () => {
17057
17143
  if (killed)
17058
17144
  return;
17059
17145
  killed = true;
17060
- log.info(`Task ${task.id} killed by signal`);
17146
+ log.info(`killed by signal (messages=${seq}, tools=${toolCount})`);
17061
17147
  if (agentPid) {
17062
17148
  try {
17063
17149
  process.kill(agentPid, "SIGTERM");
@@ -17067,7 +17153,6 @@ async function runSession(input) {
17067
17153
  try {
17068
17154
  await flushMessages();
17069
17155
  } catch {}
17070
- logStream?.end();
17071
17156
  await cleanupAttachments(task.id);
17072
17157
  updateEntry(timelineDir, task.id, (entry) => {
17073
17158
  entry.pid = null;
@@ -17095,21 +17180,14 @@ async function runSession(input) {
17095
17180
  input: msg.input,
17096
17181
  output: msg.output
17097
17182
  });
17183
+ if (msg.type === "tool-use")
17184
+ toolCount++;
17185
+ log.info(JSON.stringify({ role: "assistant", ...msg }));
17098
17186
  if (msg.type === "text" && msg.content) {
17099
17187
  updateEntry(timelineDir, task.id, (entry) => {
17100
17188
  entry.agent_responses.push(msg.content);
17101
17189
  });
17102
17190
  }
17103
- if (logStream) {
17104
- try {
17105
- logStream.write(JSON.stringify({
17106
- ts: localISOString(),
17107
- role: "assistant",
17108
- ...msg
17109
- }) + `
17110
- `);
17111
- } catch {}
17112
- }
17113
17191
  if (pendingMessages.length >= BATCH_SIZE) {
17114
17192
  await flushMessages();
17115
17193
  }
@@ -17118,7 +17196,6 @@ async function runSession(input) {
17118
17196
  await flushMessages();
17119
17197
  } finally {
17120
17198
  clearInterval(flushTimer);
17121
- logStream?.end();
17122
17199
  process.removeListener("SIGTERM", onKill);
17123
17200
  process.removeListener("SIGINT", onKill);
17124
17201
  }
@@ -17152,10 +17229,12 @@ async function runSession(input) {
17152
17229
  if (result.sessionId)
17153
17230
  body.session_id = result.sessionId;
17154
17231
  await client.completeTask(token, task.id, body);
17155
- log.info(`Task ${task.id} completed`);
17232
+ const dur = (result.durationMs / 1000).toFixed(1);
17233
+ log.info(`completed (duration=${dur}s, messages=${seq}, tools=${toolCount})`);
17156
17234
  } else {
17157
17235
  await client.failTask(token, task.id, result.error || "unknown error");
17158
- log.info(`Task ${task.id} failed — ${result.error}`);
17236
+ const dur = (result.durationMs / 1000).toFixed(1);
17237
+ log.info(`failed (duration=${dur}s, messages=${seq}, tools=${toolCount}) — ${result.error}`);
17159
17238
  }
17160
17239
  }
17161
17240
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alook/cli",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Alook CLI — register and run always-on AI coding agents.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/alookai/alook#readme",