@alook/cli 0.0.11 → 0.0.12

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({
@@ -14052,7 +14053,9 @@ var SendEmailRequestSchema = exports_external.object({
14052
14053
  htmlBody: exports_external.string().default(""),
14053
14054
  inReplyTo: exports_external.string().optional(),
14054
14055
  references: exports_external.string().optional(),
14055
- attachments: exports_external.array(EmailAttachmentSchema).optional()
14056
+ attachments: exports_external.array(EmailAttachmentSchema).optional(),
14057
+ customAccountId: exports_external.string().optional(),
14058
+ from: exports_external.string().email().optional()
14056
14059
  });
14057
14060
  var UpdateEmailStatusRequestSchema = exports_external.object({
14058
14061
  status: exports_external.enum(["unread", "read", "archived"])
@@ -14070,6 +14073,48 @@ var EmailNotifyRequestSchema = exports_external.object({
14070
14073
  inReplyTo: exports_external.string().optional().default(""),
14071
14074
  references: exports_external.string().optional().default("")
14072
14075
  });
14076
+ var CreateEmailAccountSchema = exports_external.object({
14077
+ emailAddress: exports_external.string().email("valid email required"),
14078
+ displayName: exports_external.string().default(""),
14079
+ imapHost: exports_external.string().min(1, "IMAP host is required"),
14080
+ imapPort: exports_external.number().int().min(1).max(65535).default(993),
14081
+ imapUsername: exports_external.string().min(1, "IMAP username is required"),
14082
+ imapPassword: exports_external.string().min(1, "IMAP password is required"),
14083
+ imapTls: exports_external.boolean().default(true),
14084
+ smtpHost: exports_external.string().min(1, "SMTP host is required"),
14085
+ smtpPort: exports_external.number().int().min(1).max(65535).default(587),
14086
+ smtpUsername: exports_external.string().min(1, "SMTP username is required"),
14087
+ smtpPassword: exports_external.string().min(1, "SMTP password is required"),
14088
+ smtpTls: exports_external.number().int().min(0).max(2).default(1),
14089
+ pollIntervalSeconds: exports_external.number().int().min(30).max(3600).default(60)
14090
+ });
14091
+ var UpdateEmailAccountSchema = exports_external.object({
14092
+ emailAddress: exports_external.string().email().optional(),
14093
+ displayName: exports_external.string().optional(),
14094
+ imapHost: exports_external.string().min(1).optional(),
14095
+ imapPort: exports_external.number().int().min(1).max(65535).optional(),
14096
+ imapUsername: exports_external.string().min(1).optional(),
14097
+ imapPassword: exports_external.string().min(1).optional(),
14098
+ imapTls: exports_external.boolean().optional(),
14099
+ smtpHost: exports_external.string().min(1).optional(),
14100
+ smtpPort: exports_external.number().int().min(1).max(65535).optional(),
14101
+ smtpUsername: exports_external.string().min(1).optional(),
14102
+ smtpPassword: exports_external.string().min(1).optional(),
14103
+ smtpTls: exports_external.number().int().min(0).max(2).optional(),
14104
+ pollIntervalSeconds: exports_external.number().int().min(30).max(3600).optional()
14105
+ });
14106
+ var TestEmailConnectionSchema = exports_external.object({
14107
+ imapHost: exports_external.string().min(1),
14108
+ imapPort: exports_external.number().int().min(1).max(65535).default(993),
14109
+ imapUsername: exports_external.string().min(1),
14110
+ imapPassword: exports_external.string().min(1),
14111
+ imapTls: exports_external.boolean().default(true),
14112
+ smtpHost: exports_external.string().min(1),
14113
+ smtpPort: exports_external.number().int().min(1).max(65535).default(587),
14114
+ smtpUsername: exports_external.string().min(1),
14115
+ smtpPassword: exports_external.string().min(1),
14116
+ smtpTls: exports_external.number().int().min(0).max(2).default(1)
14117
+ });
14073
14118
  var CreateWorkspaceRequestSchema = exports_external.object({
14074
14119
  name: exports_external.string().min(1, "name is required"),
14075
14120
  slug: exports_external.string().min(1, "slug is required")
@@ -15683,6 +15728,37 @@ var artifact = sqliteTable("artifact", {
15683
15728
  foreignColumns: [agent.id, agent.workspaceId]
15684
15729
  }).onDelete("cascade")
15685
15730
  ]);
15731
+ var agentEmailAccount = sqliteTable("agent_email_account", {
15732
+ id: text("id").primaryKey().$defaultFn(() => "aea_" + nanoid3()),
15733
+ agentId: text("agent_id").notNull(),
15734
+ workspaceId: text("workspace_id").notNull(),
15735
+ emailAddress: text("email_address").notNull(),
15736
+ displayName: text("display_name").notNull().default(""),
15737
+ imapHost: text("imap_host").notNull(),
15738
+ imapPort: integer2("imap_port").notNull().default(993),
15739
+ imapUsername: text("imap_username").notNull(),
15740
+ imapPassword: text("imap_password").notNull(),
15741
+ imapTls: integer2("imap_tls", { mode: "boolean" }).notNull().default(true),
15742
+ smtpHost: text("smtp_host").notNull(),
15743
+ smtpPort: integer2("smtp_port").notNull().default(587),
15744
+ smtpUsername: text("smtp_username").notNull(),
15745
+ smtpPassword: text("smtp_password").notNull(),
15746
+ smtpTls: integer2("smtp_tls").notNull().default(1),
15747
+ pollIntervalSeconds: integer2("poll_interval_seconds").notNull().default(60),
15748
+ lastSyncedUid: text("last_synced_uid").notNull().default("0"),
15749
+ lastSyncedAt: text("last_synced_at"),
15750
+ status: text("status").notNull().default("active"),
15751
+ errorMessage: text("error_message").notNull().default(""),
15752
+ createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
15753
+ updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
15754
+ }, (t) => [
15755
+ index("idx_email_account_agent_ws").on(t.agentId, t.workspaceId),
15756
+ unique("email_account_agent_email").on(t.agentId, t.emailAddress),
15757
+ foreignKey({
15758
+ columns: [t.agentId, t.workspaceId],
15759
+ foreignColumns: [agent.id, agent.workspaceId]
15760
+ }).onDelete("cascade")
15761
+ ]);
15686
15762
  var machineToken = sqliteTable("machine_token", {
15687
15763
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
15688
15764
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -15888,7 +15964,7 @@ function loadDaemonConfig(profile) {
15888
15964
  codexModel: process.env.ALOOK_CODEX_MODEL || "",
15889
15965
  opencodeModel: process.env.ALOOK_OPENCODE_MODEL || "",
15890
15966
  pollInterval: parseDuration(process.env.ALOOK_DAEMON_POLL_INTERVAL || "3s"),
15891
- agentTimeout: parseDuration(process.env.ALOOK_AGENT_TIMEOUT || "2h"),
15967
+ agentTimeout: parseDuration(process.env.ALOOK_AGENT_TIMEOUT || "12h"),
15892
15968
  maxConcurrentTasks: parseInt(process.env.ALOOK_DAEMON_MAX_CONCURRENT_TASKS || "20"),
15893
15969
  daemonId,
15894
15970
  deviceName: process.env.ALOOK_DAEMON_DEVICE_NAME || h,
@@ -15964,7 +16040,7 @@ function fromApiTask(api2) {
15964
16040
  type: api2.type,
15965
16041
  contextKey: api2.context_key ?? null,
15966
16042
  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,
16043
+ 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
16044
  repos: undefined,
15969
16045
  createdAt: api2.created_at
15970
16046
  };
@@ -16001,10 +16077,13 @@ function useColor() {
16001
16077
  }
16002
16078
  function timestamp() {
16003
16079
  const d = new Date;
16080
+ const Y = d.getFullYear();
16081
+ const M = String(d.getMonth() + 1).padStart(2, "0");
16082
+ const D = String(d.getDate()).padStart(2, "0");
16004
16083
  const h = String(d.getHours()).padStart(2, "0");
16005
16084
  const m = String(d.getMinutes()).padStart(2, "0");
16006
16085
  const s = String(d.getSeconds()).padStart(2, "0");
16007
- return `${h}:${m}:${s}`;
16086
+ return `${Y}-${M}-${D} ${h}:${m}:${s}`;
16008
16087
  }
16009
16088
 
16010
16089
  class Logger2 {
@@ -16193,7 +16272,7 @@ async function handleCliUpdate(version3, onSuccess, profile) {
16193
16272
  }
16194
16273
 
16195
16274
  // daemon/daemon.ts
16196
- import { existsSync, mkdirSync as mkdirSync3, openSync, closeSync, renameSync, readdirSync, statSync, unlinkSync as unlinkSync3 } from "fs";
16275
+ import { existsSync, mkdirSync as mkdirSync3, openSync, closeSync, readdirSync, statSync, unlinkSync as unlinkSync3 } from "fs";
16197
16276
  import { execSync as execSync3, spawn as spawn2 } from "child_process";
16198
16277
  import { fileURLToPath as fileURLToPath2 } from "url";
16199
16278
  import { dirname as dirname3, join as join4 } from "path";
@@ -16468,21 +16547,18 @@ async function startDaemon(profile, serverUrl) {
16468
16547
  await pollCycle();
16469
16548
  }
16470
16549
  function spawnSessionRunner(input) {
16471
- const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
16472
16550
  const logDir = sessionRunnerLogDir();
16473
16551
  mkdirSync3(logDir, { recursive: true });
16474
- const tmpLogPath = join4(logDir, `${input.task.id}.log`);
16475
- const fd = openSync(tmpLogPath, "a");
16552
+ const logFilePath = join4(logDir, `${input.task.id}.log`);
16553
+ input.logFilePath = logFilePath;
16554
+ const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
16555
+ const fd = openSync(logFilePath, "a");
16476
16556
  const child = spawn2(process.execPath, [sessionRunnerPath, encoded], {
16477
16557
  detached: true,
16478
16558
  stdio: ["ignore", fd, fd]
16479
16559
  });
16480
16560
  child.unref();
16481
16561
  closeSync(fd);
16482
- if (child.pid) {
16483
- const pidLogPath = join4(logDir, `${child.pid}.log`);
16484
- renameSync(tmpLogPath, pidLogPath);
16485
- }
16486
16562
  return child;
16487
16563
  }
16488
16564
  async function handleTask(client, config2, runtimeIndex, task, token, activeTasks) {
@@ -16832,7 +16908,7 @@ function emailCommand() {
16832
16908
  process.exit(1);
16833
16909
  }
16834
16910
  });
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) => {
16911
+ 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
16912
  const { serverUrl, token, workspaceId } = resolveClientOpts(command, {
16837
16913
  workspace: opts.workspace,
16838
16914
  agentId: opts.agent_id
@@ -16893,7 +16969,8 @@ function emailCommand() {
16893
16969
  subject: opts.subject,
16894
16970
  htmlBody,
16895
16971
  attachments,
16896
- ...inReplyTo ? { inReplyTo, references } : {}
16972
+ ...inReplyTo ? { inReplyTo, references } : {},
16973
+ ...opts.from ? { from: opts.from } : {}
16897
16974
  });
16898
16975
  console.log(`Sent email to ${res.to_email} (id: ${res.id})`);
16899
16976
  } 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({
@@ -13770,7 +13770,9 @@ var SendEmailRequestSchema = exports_external.object({
13770
13770
  htmlBody: exports_external.string().default(""),
13771
13771
  inReplyTo: exports_external.string().optional(),
13772
13772
  references: exports_external.string().optional(),
13773
- attachments: exports_external.array(EmailAttachmentSchema).optional()
13773
+ attachments: exports_external.array(EmailAttachmentSchema).optional(),
13774
+ customAccountId: exports_external.string().optional(),
13775
+ from: exports_external.string().email().optional()
13774
13776
  });
13775
13777
  var UpdateEmailStatusRequestSchema = exports_external.object({
13776
13778
  status: exports_external.enum(["unread", "read", "archived"])
@@ -13788,6 +13790,48 @@ var EmailNotifyRequestSchema = exports_external.object({
13788
13790
  inReplyTo: exports_external.string().optional().default(""),
13789
13791
  references: exports_external.string().optional().default("")
13790
13792
  });
13793
+ var CreateEmailAccountSchema = exports_external.object({
13794
+ emailAddress: exports_external.string().email("valid email required"),
13795
+ displayName: exports_external.string().default(""),
13796
+ imapHost: exports_external.string().min(1, "IMAP host is required"),
13797
+ imapPort: exports_external.number().int().min(1).max(65535).default(993),
13798
+ imapUsername: exports_external.string().min(1, "IMAP username is required"),
13799
+ imapPassword: exports_external.string().min(1, "IMAP password is required"),
13800
+ imapTls: exports_external.boolean().default(true),
13801
+ smtpHost: exports_external.string().min(1, "SMTP host is required"),
13802
+ smtpPort: exports_external.number().int().min(1).max(65535).default(587),
13803
+ smtpUsername: exports_external.string().min(1, "SMTP username is required"),
13804
+ smtpPassword: exports_external.string().min(1, "SMTP password is required"),
13805
+ smtpTls: exports_external.number().int().min(0).max(2).default(1),
13806
+ pollIntervalSeconds: exports_external.number().int().min(30).max(3600).default(60)
13807
+ });
13808
+ var UpdateEmailAccountSchema = exports_external.object({
13809
+ emailAddress: exports_external.string().email().optional(),
13810
+ displayName: exports_external.string().optional(),
13811
+ imapHost: exports_external.string().min(1).optional(),
13812
+ imapPort: exports_external.number().int().min(1).max(65535).optional(),
13813
+ imapUsername: exports_external.string().min(1).optional(),
13814
+ imapPassword: exports_external.string().min(1).optional(),
13815
+ imapTls: exports_external.boolean().optional(),
13816
+ smtpHost: exports_external.string().min(1).optional(),
13817
+ smtpPort: exports_external.number().int().min(1).max(65535).optional(),
13818
+ smtpUsername: exports_external.string().min(1).optional(),
13819
+ smtpPassword: exports_external.string().min(1).optional(),
13820
+ smtpTls: exports_external.number().int().min(0).max(2).optional(),
13821
+ pollIntervalSeconds: exports_external.number().int().min(30).max(3600).optional()
13822
+ });
13823
+ var TestEmailConnectionSchema = exports_external.object({
13824
+ imapHost: exports_external.string().min(1),
13825
+ imapPort: exports_external.number().int().min(1).max(65535).default(993),
13826
+ imapUsername: exports_external.string().min(1),
13827
+ imapPassword: exports_external.string().min(1),
13828
+ imapTls: exports_external.boolean().default(true),
13829
+ smtpHost: exports_external.string().min(1),
13830
+ smtpPort: exports_external.number().int().min(1).max(65535).default(587),
13831
+ smtpUsername: exports_external.string().min(1),
13832
+ smtpPassword: exports_external.string().min(1),
13833
+ smtpTls: exports_external.number().int().min(0).max(2).default(1)
13834
+ });
13791
13835
  var CreateWorkspaceRequestSchema = exports_external.object({
13792
13836
  name: exports_external.string().min(1, "name is required"),
13793
13837
  slug: exports_external.string().min(1, "slug is required")
@@ -15401,6 +15445,37 @@ var artifact = sqliteTable("artifact", {
15401
15445
  foreignColumns: [agent.id, agent.workspaceId]
15402
15446
  }).onDelete("cascade")
15403
15447
  ]);
15448
+ var agentEmailAccount = sqliteTable("agent_email_account", {
15449
+ id: text("id").primaryKey().$defaultFn(() => "aea_" + nanoid3()),
15450
+ agentId: text("agent_id").notNull(),
15451
+ workspaceId: text("workspace_id").notNull(),
15452
+ emailAddress: text("email_address").notNull(),
15453
+ displayName: text("display_name").notNull().default(""),
15454
+ imapHost: text("imap_host").notNull(),
15455
+ imapPort: integer2("imap_port").notNull().default(993),
15456
+ imapUsername: text("imap_username").notNull(),
15457
+ imapPassword: text("imap_password").notNull(),
15458
+ imapTls: integer2("imap_tls", { mode: "boolean" }).notNull().default(true),
15459
+ smtpHost: text("smtp_host").notNull(),
15460
+ smtpPort: integer2("smtp_port").notNull().default(587),
15461
+ smtpUsername: text("smtp_username").notNull(),
15462
+ smtpPassword: text("smtp_password").notNull(),
15463
+ smtpTls: integer2("smtp_tls").notNull().default(1),
15464
+ pollIntervalSeconds: integer2("poll_interval_seconds").notNull().default(60),
15465
+ lastSyncedUid: text("last_synced_uid").notNull().default("0"),
15466
+ lastSyncedAt: text("last_synced_at"),
15467
+ status: text("status").notNull().default("active"),
15468
+ errorMessage: text("error_message").notNull().default(""),
15469
+ createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
15470
+ updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
15471
+ }, (t) => [
15472
+ index("idx_email_account_agent_ws").on(t.agentId, t.workspaceId),
15473
+ unique("email_account_agent_email").on(t.agentId, t.emailAddress),
15474
+ foreignKey({
15475
+ columns: [t.agentId, t.workspaceId],
15476
+ foreignColumns: [agent.id, agent.workspaceId]
15477
+ }).onDelete("cascade")
15478
+ ]);
15404
15479
  var machineToken = sqliteTable("machine_token", {
15405
15480
  id: text("id").primaryKey().$defaultFn(() => nanoid3()),
15406
15481
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -16421,7 +16496,7 @@ function createBackend(provider, cliPath) {
16421
16496
 
16422
16497
  // daemon/execenv/index.ts
16423
16498
  import { mkdirSync as mkdirSync2 } from "fs";
16424
- import { join as join3, dirname } from "path";
16499
+ import { join as join3 } from "path";
16425
16500
 
16426
16501
  // daemon/execenv/context.ts
16427
16502
  import { createHash } from "crypto";
@@ -16499,9 +16574,18 @@ ${task.agent.instructions}
16499
16574
  You can communicate with the world through Alook CLI.
16500
16575
  Your alook agent id is '${task.agentId}'. remember this, most of alook cli will requires you input your agent id.
16501
16576
  `;
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}'.` : ""}
16577
+ const alookAddr = task.agent?.emailHandle ? toAlookAddress(task.agent.emailHandle) : null;
16578
+ const customAddrs = (task.agent?.emailAddresses ?? []).filter((a) => a !== alookAddr);
16579
+ if (alookAddr || customAddrs.length > 0) {
16580
+ const lines = [];
16581
+ if (alookAddr)
16582
+ lines.push(`- '${alookAddr}' (default, Alook platform address)`);
16583
+ for (const a of customAddrs)
16584
+ lines.push(`- '${a}' (custom IMAP/SMTP mailbox)`);
16585
+ content += `Your email addresses:
16586
+ ${lines.join(`
16587
+ `)}
16588
+ ${task.agent?.userEmail ? `Your owner's email address is '${task.agent.userEmail}'.` : ""}
16505
16589
 
16506
16590
  ### Emails
16507
16591
  ---
@@ -16519,8 +16603,9 @@ Before starting to process an email, mark it as read:
16519
16603
  #### Sending a new email
16520
16604
  Write the HTML body to a file first, then send it. The body is forwarded as-is (HTML).
16521
16605
  - Run 'npx @alook/cli email send --agent_id ${task.agentId} --to <ADDRESS> --subject "<SUBJECT>" --body-file <PATH_TO_HTML>'
16606
+ - To send from a specific mailbox, add '--from <YOUR_EMAIL_ADDRESS>'. Without '--from', the default Alook address is used.
16522
16607
  - 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'
16608
+ - 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
16609
 
16525
16610
  #### Replying to an email
16526
16611
  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 +16793,13 @@ function useColor() {
16708
16793
  }
16709
16794
  function timestamp() {
16710
16795
  const d = new Date;
16796
+ const Y = d.getFullYear();
16797
+ const M = String(d.getMonth() + 1).padStart(2, "0");
16798
+ const D = String(d.getDate()).padStart(2, "0");
16711
16799
  const h = String(d.getHours()).padStart(2, "0");
16712
16800
  const m = String(d.getMinutes()).padStart(2, "0");
16713
16801
  const s = String(d.getSeconds()).padStart(2, "0");
16714
- return `${h}:${m}:${s}`;
16802
+ return `${Y}-${M}-${D} ${h}:${m}:${s}`;
16715
16803
  }
16716
16804
 
16717
16805
  class Logger2 {
@@ -16896,7 +16984,7 @@ function updateEntry(timelineDir, taskId, updater) {
16896
16984
  }
16897
16985
  log.debug(`Timeline updateEntry: task_id ${taskId} not found in last 7 days`);
16898
16986
  }
16899
- function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider, contextKey) {
16987
+ function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider, contextKey, detailedLog) {
16900
16988
  return {
16901
16989
  task_id: taskId,
16902
16990
  context_key: contextKey ?? null,
@@ -16908,7 +16996,8 @@ function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider, con
16908
16996
  prompt,
16909
16997
  agent_responses: [],
16910
16998
  errmsg: null,
16911
- provider: provider ?? null
16999
+ provider: provider ?? null,
17000
+ detailed_log: detailedLog ?? null
16912
17001
  };
16913
17002
  }
16914
17003
  var DEFAULT_RESUME_MAX_AGE_MS = 3 * 60 * 60 * 1000;
@@ -16938,8 +17027,6 @@ function prepare(config2, task) {
16938
17027
  const timelineDir = join3(workDir, ".context_timeline");
16939
17028
  mkdirSync2(timelineDir, { recursive: true });
16940
17029
  writeInstructionFileIfChanged(workDir, task);
16941
- const logFile = join3(config2.workspacesRoot, task.workspaceId, task.agentId, "agent.log");
16942
- mkdirSync2(dirname(logFile), { recursive: true });
16943
17030
  const env = {
16944
17031
  ALOOK_WORKSPACE_ID: task.workspaceId,
16945
17032
  ALOOK_AGENT_ID: task.agentId,
@@ -16947,7 +17034,7 @@ function prepare(config2, task) {
16947
17034
  ALOOK_CONVERSATION_ID: task.conversationId,
16948
17035
  ALOOK_HEALTH_PORT: process.env.ALOOK_HEALTH_PORT || "19514"
16949
17036
  };
16950
- return { workDir, logFile, timelineDir, env };
17037
+ return { workDir, timelineDir, env };
16951
17038
  }
16952
17039
 
16953
17040
  // daemon/prompt.ts
@@ -16993,18 +17080,21 @@ async function downloadAttachments(client, token, workspaceId, taskId, attachmen
16993
17080
  }
16994
17081
  async function runSession(input) {
16995
17082
  const { task, provider, cliPath, model, serverURL, token, workspacesRoot, agentTimeout } = input;
17083
+ log.info(`starting (task=${task.id}, type=${task.type}, agent=${task.agentId}, provider=${provider}, model=${model || "default"})`);
16996
17084
  const client = new DaemonClient(serverURL);
16997
17085
  const backend = createBackend(provider, cliPath);
16998
- const { workDir, logFile, timelineDir, env } = prepare({ workspacesRoot }, task);
17086
+ const { workDir, timelineDir, env } = prepare({ workspacesRoot }, task);
16999
17087
  const attachmentIds = task.context?.attachment_ids ?? [];
17000
17088
  let attachments;
17001
17089
  if (attachmentIds.length > 0) {
17090
+ log.info(`downloading ${attachmentIds.length} attachment(s)`);
17002
17091
  try {
17003
17092
  attachments = await downloadAttachments(client, token, task.workspaceId, task.id, attachmentIds);
17093
+ log.info(`attachments ready (${attachments.length} file(s))`);
17004
17094
  } catch (e) {
17005
17095
  await cleanupAttachments(task.id);
17006
17096
  const errMsg = `failed to download attachments: ${e}`;
17007
- log.error(`Task ${task.id} ${errMsg}`);
17097
+ log.error(errMsg);
17008
17098
  await client.failTask(token, task.id, errMsg);
17009
17099
  return;
17010
17100
  }
@@ -17012,7 +17102,7 @@ async function runSession(input) {
17012
17102
  const prompt = buildPrompt(task, attachments);
17013
17103
  const resumeSessionId = task.contextKey ? findResumableSessionByContextKey(timelineDir, task.contextKey, provider) ?? undefined : undefined;
17014
17104
  if (resumeSessionId) {
17015
- log.info(`Task ${task.id} resuming session ${resumeSessionId} (context_key: ${task.contextKey})`);
17105
+ log.info(`resuming session ${resumeSessionId} (context_key: ${task.contextKey})`);
17016
17106
  }
17017
17107
  const session2 = backend.execute(prompt, {
17018
17108
  cwd: workDir,
@@ -17023,9 +17113,12 @@ async function runSession(input) {
17023
17113
  });
17024
17114
  const agentPid = session2.pid;
17025
17115
  const earlySessionId = await session2.sessionId;
17026
- await initEntryAsync(timelineDir, createTimelineEntry(task.id, task.prompt, task.type, earlySessionId, process.pid, provider, task.contextKey));
17116
+ log.info(`agent started (pid=${agentPid ?? "unknown"}, session=${earlySessionId})`);
17117
+ log.info(JSON.stringify({ role: "user", type: "text", content: prompt }));
17118
+ await initEntryAsync(timelineDir, createTimelineEntry(task.id, task.prompt, task.type, earlySessionId, process.pid, provider, task.contextKey, input.logFilePath));
17027
17119
  const pendingMessages = [];
17028
17120
  let seq = 0;
17121
+ let toolCount = 0;
17029
17122
  const BATCH_SIZE = Number(process.env.ALOOK_MESSAGE_BATCH_SIZE) || 20;
17030
17123
  const FLUSH_INTERVAL_MS = Number(process.env.ALOOK_MESSAGE_FLUSH_INTERVAL_MS) || 100;
17031
17124
  const flushMessages = async () => {
@@ -17035,29 +17128,16 @@ async function runSession(input) {
17035
17128
  try {
17036
17129
  await client.reportMessages(token, task.id, batch);
17037
17130
  } catch (e) {
17038
- log.debug(`Task ${task.id} message report failed`, e);
17131
+ log.debug("message report failed", e);
17039
17132
  }
17040
17133
  };
17041
17134
  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
17135
  let killed = false;
17056
17136
  const onKill = async () => {
17057
17137
  if (killed)
17058
17138
  return;
17059
17139
  killed = true;
17060
- log.info(`Task ${task.id} killed by signal`);
17140
+ log.info(`killed by signal (messages=${seq}, tools=${toolCount})`);
17061
17141
  if (agentPid) {
17062
17142
  try {
17063
17143
  process.kill(agentPid, "SIGTERM");
@@ -17067,7 +17147,6 @@ async function runSession(input) {
17067
17147
  try {
17068
17148
  await flushMessages();
17069
17149
  } catch {}
17070
- logStream?.end();
17071
17150
  await cleanupAttachments(task.id);
17072
17151
  updateEntry(timelineDir, task.id, (entry) => {
17073
17152
  entry.pid = null;
@@ -17095,21 +17174,14 @@ async function runSession(input) {
17095
17174
  input: msg.input,
17096
17175
  output: msg.output
17097
17176
  });
17177
+ if (msg.type === "tool-use")
17178
+ toolCount++;
17179
+ log.info(JSON.stringify({ role: "assistant", ...msg }));
17098
17180
  if (msg.type === "text" && msg.content) {
17099
17181
  updateEntry(timelineDir, task.id, (entry) => {
17100
17182
  entry.agent_responses.push(msg.content);
17101
17183
  });
17102
17184
  }
17103
- if (logStream) {
17104
- try {
17105
- logStream.write(JSON.stringify({
17106
- ts: localISOString(),
17107
- role: "assistant",
17108
- ...msg
17109
- }) + `
17110
- `);
17111
- } catch {}
17112
- }
17113
17185
  if (pendingMessages.length >= BATCH_SIZE) {
17114
17186
  await flushMessages();
17115
17187
  }
@@ -17118,7 +17190,6 @@ async function runSession(input) {
17118
17190
  await flushMessages();
17119
17191
  } finally {
17120
17192
  clearInterval(flushTimer);
17121
- logStream?.end();
17122
17193
  process.removeListener("SIGTERM", onKill);
17123
17194
  process.removeListener("SIGINT", onKill);
17124
17195
  }
@@ -17152,10 +17223,12 @@ async function runSession(input) {
17152
17223
  if (result.sessionId)
17153
17224
  body.session_id = result.sessionId;
17154
17225
  await client.completeTask(token, task.id, body);
17155
- log.info(`Task ${task.id} completed`);
17226
+ const dur = (result.durationMs / 1000).toFixed(1);
17227
+ log.info(`completed (duration=${dur}s, messages=${seq}, tools=${toolCount})`);
17156
17228
  } else {
17157
17229
  await client.failTask(token, task.id, result.error || "unknown error");
17158
- log.info(`Task ${task.id} failed — ${result.error}`);
17230
+ const dur = (result.durationMs / 1000).toFixed(1);
17231
+ log.info(`failed (duration=${dur}s, messages=${seq}, tools=${toolCount}) — ${result.error}`);
17159
17232
  }
17160
17233
  }
17161
17234
  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.12",
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",