@alook/cli 0.0.13 → 0.0.15
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 +59 -19
- package/dist/session-runner.js +59 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14118,6 +14118,9 @@ var TestEmailConnectionSchema = exports_external.object({
|
|
|
14118
14118
|
smtpPassword: exports_external.string().min(1),
|
|
14119
14119
|
smtpTls: exports_external.number().int().min(0).max(2).default(1)
|
|
14120
14120
|
});
|
|
14121
|
+
var UpdateMemberRequestSchema = exports_external.object({
|
|
14122
|
+
global_instruction: exports_external.string().max(50000).trim()
|
|
14123
|
+
});
|
|
14121
14124
|
var CreateWorkspaceRequestSchema = exports_external.object({
|
|
14122
14125
|
name: exports_external.string().min(1, "name is required"),
|
|
14123
14126
|
slug: exports_external.string().min(1, "slug is required")
|
|
@@ -15550,6 +15553,7 @@ var member = sqliteTable("member", {
|
|
|
15550
15553
|
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15551
15554
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
15552
15555
|
role: text("role").notNull().default("member"),
|
|
15556
|
+
globalInstruction: text("global_instruction").notNull().default(""),
|
|
15553
15557
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15554
15558
|
}, (t) => [unique("member_workspace_user").on(t.workspaceId, t.userId)]);
|
|
15555
15559
|
var machine = sqliteTable("machine", {
|
|
@@ -15655,6 +15659,7 @@ var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
|
15655
15659
|
error: text("error")
|
|
15656
15660
|
}, (t) => [
|
|
15657
15661
|
index("idx_task_queue_pending").on(t.agentId, t.status).where(sql`status IN ('queued', 'dispatched')`),
|
|
15662
|
+
index("idx_task_queue_workspace_active").on(t.workspaceId, t.status, t.agentId).where(sql`status IN ('queued', 'dispatched', 'running')`),
|
|
15658
15663
|
foreignKey({
|
|
15659
15664
|
columns: [t.agentId, t.workspaceId],
|
|
15660
15665
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
@@ -15821,16 +15826,33 @@ class DaemonClient {
|
|
|
15821
15826
|
"Content-Type": "application/json",
|
|
15822
15827
|
Authorization: `Bearer ${token}`
|
|
15823
15828
|
};
|
|
15824
|
-
const
|
|
15825
|
-
|
|
15826
|
-
|
|
15827
|
-
|
|
15828
|
-
|
|
15829
|
-
|
|
15830
|
-
|
|
15831
|
-
|
|
15832
|
-
|
|
15833
|
-
|
|
15829
|
+
const MAX_RETRIES = 3;
|
|
15830
|
+
const BASE_DELAY_MS = 500;
|
|
15831
|
+
let lastError;
|
|
15832
|
+
for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
|
|
15833
|
+
try {
|
|
15834
|
+
const res = await fetch(this.baseURL + path, {
|
|
15835
|
+
method,
|
|
15836
|
+
headers,
|
|
15837
|
+
body: body ? JSON.stringify(body) : undefined
|
|
15838
|
+
});
|
|
15839
|
+
if (!res.ok)
|
|
15840
|
+
throw new Error(`HTTP ${res.status}: ${await res.text()}`);
|
|
15841
|
+
if (res.status === 204)
|
|
15842
|
+
return;
|
|
15843
|
+
return res.json();
|
|
15844
|
+
} catch (e) {
|
|
15845
|
+
if (e instanceof TypeError) {
|
|
15846
|
+
lastError = e;
|
|
15847
|
+
if (attempt < MAX_RETRIES) {
|
|
15848
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS * 2 ** attempt));
|
|
15849
|
+
continue;
|
|
15850
|
+
}
|
|
15851
|
+
}
|
|
15852
|
+
throw e;
|
|
15853
|
+
}
|
|
15854
|
+
}
|
|
15855
|
+
throw lastError;
|
|
15834
15856
|
}
|
|
15835
15857
|
async register(token, body) {
|
|
15836
15858
|
const raw = await this.request("POST", "/api/daemon/register", token, body);
|
|
@@ -15865,11 +15887,28 @@ class DaemonClient {
|
|
|
15865
15887
|
return this.request("GET", `/api/artifacts/${artifactId}?workspace_id=${encodeURIComponent(workspaceId)}`, token);
|
|
15866
15888
|
}
|
|
15867
15889
|
async downloadArtifact(token, artifactId, workspaceId) {
|
|
15868
|
-
const
|
|
15869
|
-
|
|
15870
|
-
|
|
15890
|
+
const MAX_RETRIES = 3;
|
|
15891
|
+
const BASE_DELAY_MS = 500;
|
|
15892
|
+
let lastError;
|
|
15893
|
+
for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
|
|
15894
|
+
try {
|
|
15895
|
+
const res = await fetch(`${this.baseURL}/api/artifacts/${artifactId}/content?workspace_id=${encodeURIComponent(workspaceId)}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
15896
|
+
if (!res.ok) {
|
|
15897
|
+
throw new Error(`artifact download failed: HTTP ${res.status}`);
|
|
15898
|
+
}
|
|
15899
|
+
return res.arrayBuffer();
|
|
15900
|
+
} catch (e) {
|
|
15901
|
+
if (e instanceof TypeError) {
|
|
15902
|
+
lastError = e;
|
|
15903
|
+
if (attempt < MAX_RETRIES) {
|
|
15904
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS * 2 ** attempt));
|
|
15905
|
+
continue;
|
|
15906
|
+
}
|
|
15907
|
+
}
|
|
15908
|
+
throw e;
|
|
15909
|
+
}
|
|
15871
15910
|
}
|
|
15872
|
-
|
|
15911
|
+
throw lastError;
|
|
15873
15912
|
}
|
|
15874
15913
|
reportMessages(token, taskId, messages) {
|
|
15875
15914
|
return this.request("POST", `/api/daemon/tasks/${taskId}/messages`, token, { messages });
|
|
@@ -16751,7 +16790,7 @@ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, readFileSync
|
|
|
16751
16790
|
import { basename, join as join5 } from "path";
|
|
16752
16791
|
import PostalMime from "postal-mime";
|
|
16753
16792
|
var VALID_STATUSES = ["unread", "read", "archived"];
|
|
16754
|
-
var
|
|
16793
|
+
var EMAIL_BASE = "/tmp/alook-emails";
|
|
16755
16794
|
var MIME_BY_EXT = {
|
|
16756
16795
|
".pdf": "application/pdf",
|
|
16757
16796
|
".png": "image/png",
|
|
@@ -16801,13 +16840,14 @@ function resolveClientOpts(command, opts) {
|
|
|
16801
16840
|
}
|
|
16802
16841
|
function emailCommand() {
|
|
16803
16842
|
const cmd = new Command5("email").description("Manage agent emails");
|
|
16804
|
-
cmd.command("pull").description("Download and parse emails to /tmp/alook-emails/").requiredOption("--agent_id <id>", "Agent ID").option("--status <status>", "Filter by status (unread, read, archived)").option("--workspace <id>", "Workspace ID").option("--json", "Output as JSON instead of files").action(async (opts, command) => {
|
|
16843
|
+
cmd.command("pull").description("Download and parse emails to /tmp/alook-emails/{workspaceId}/{agentId}/").requiredOption("--agent_id <id>", "Agent ID").option("--status <status>", "Filter by status (unread, read, archived)").option("--workspace <id>", "Workspace ID").option("--json", "Output as JSON instead of files").action(async (opts, command) => {
|
|
16805
16844
|
const { serverUrl, token, workspaceId } = resolveClientOpts(command, { workspace: opts.workspace, agentId: opts.agent_id });
|
|
16806
16845
|
const client = new APIClient(serverUrl, token, workspaceId);
|
|
16807
16846
|
if (opts.status && !VALID_STATUSES.includes(opts.status)) {
|
|
16808
16847
|
console.error(`Error: invalid status "${opts.status}", must be one of: ${VALID_STATUSES.join(", ")}`);
|
|
16809
16848
|
process.exit(1);
|
|
16810
16849
|
}
|
|
16850
|
+
const emailDir_base = join5(EMAIL_BASE, workspaceId, opts.agent_id);
|
|
16811
16851
|
try {
|
|
16812
16852
|
let query = `/api/email?agentId=${opts.agent_id}`;
|
|
16813
16853
|
if (opts.status)
|
|
@@ -16821,10 +16861,10 @@ function emailCommand() {
|
|
|
16821
16861
|
printJSON(emails2);
|
|
16822
16862
|
return;
|
|
16823
16863
|
}
|
|
16824
|
-
mkdirSync5(
|
|
16864
|
+
mkdirSync5(emailDir_base, { recursive: true });
|
|
16825
16865
|
const downloadedPaths = [];
|
|
16826
16866
|
for (const email3 of emails2) {
|
|
16827
|
-
const emailDir = join5(
|
|
16867
|
+
const emailDir = join5(emailDir_base, email3.id);
|
|
16828
16868
|
mkdirSync5(emailDir, { recursive: true });
|
|
16829
16869
|
const metadata = {
|
|
16830
16870
|
id: email3.id,
|
|
@@ -16888,7 +16928,7 @@ function emailCommand() {
|
|
|
16888
16928
|
}
|
|
16889
16929
|
}
|
|
16890
16930
|
}
|
|
16891
|
-
console.log(`Downloaded ${emails2.length} email${emails2.length === 1 ? "" : "s"} to ${
|
|
16931
|
+
console.log(`Downloaded ${emails2.length} email${emails2.length === 1 ? "" : "s"} to ${emailDir_base}/`);
|
|
16892
16932
|
for (const p of downloadedPaths) {
|
|
16893
16933
|
console.log(` ${p}`);
|
|
16894
16934
|
}
|
package/dist/session-runner.js
CHANGED
|
@@ -13835,6 +13835,9 @@ var TestEmailConnectionSchema = exports_external.object({
|
|
|
13835
13835
|
smtpPassword: exports_external.string().min(1),
|
|
13836
13836
|
smtpTls: exports_external.number().int().min(0).max(2).default(1)
|
|
13837
13837
|
});
|
|
13838
|
+
var UpdateMemberRequestSchema = exports_external.object({
|
|
13839
|
+
global_instruction: exports_external.string().max(50000).trim()
|
|
13840
|
+
});
|
|
13838
13841
|
var CreateWorkspaceRequestSchema = exports_external.object({
|
|
13839
13842
|
name: exports_external.string().min(1, "name is required"),
|
|
13840
13843
|
slug: exports_external.string().min(1, "slug is required")
|
|
@@ -15267,6 +15270,7 @@ var member = sqliteTable("member", {
|
|
|
15267
15270
|
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15268
15271
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
15269
15272
|
role: text("role").notNull().default("member"),
|
|
15273
|
+
globalInstruction: text("global_instruction").notNull().default(""),
|
|
15270
15274
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15271
15275
|
}, (t) => [unique("member_workspace_user").on(t.workspaceId, t.userId)]);
|
|
15272
15276
|
var machine = sqliteTable("machine", {
|
|
@@ -15372,6 +15376,7 @@ var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
|
15372
15376
|
error: text("error")
|
|
15373
15377
|
}, (t) => [
|
|
15374
15378
|
index("idx_task_queue_pending").on(t.agentId, t.status).where(sql`status IN ('queued', 'dispatched')`),
|
|
15379
|
+
index("idx_task_queue_workspace_active").on(t.workspaceId, t.status, t.agentId).where(sql`status IN ('queued', 'dispatched', 'running')`),
|
|
15375
15380
|
foreignKey({
|
|
15376
15381
|
columns: [t.agentId, t.workspaceId],
|
|
15377
15382
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
@@ -15527,16 +15532,33 @@ class DaemonClient {
|
|
|
15527
15532
|
"Content-Type": "application/json",
|
|
15528
15533
|
Authorization: `Bearer ${token}`
|
|
15529
15534
|
};
|
|
15530
|
-
const
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15535
|
+
const MAX_RETRIES = 3;
|
|
15536
|
+
const BASE_DELAY_MS = 500;
|
|
15537
|
+
let lastError;
|
|
15538
|
+
for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
|
|
15539
|
+
try {
|
|
15540
|
+
const res = await fetch(this.baseURL + path, {
|
|
15541
|
+
method,
|
|
15542
|
+
headers,
|
|
15543
|
+
body: body ? JSON.stringify(body) : undefined
|
|
15544
|
+
});
|
|
15545
|
+
if (!res.ok)
|
|
15546
|
+
throw new Error(`HTTP ${res.status}: ${await res.text()}`);
|
|
15547
|
+
if (res.status === 204)
|
|
15548
|
+
return;
|
|
15549
|
+
return res.json();
|
|
15550
|
+
} catch (e) {
|
|
15551
|
+
if (e instanceof TypeError) {
|
|
15552
|
+
lastError = e;
|
|
15553
|
+
if (attempt < MAX_RETRIES) {
|
|
15554
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS * 2 ** attempt));
|
|
15555
|
+
continue;
|
|
15556
|
+
}
|
|
15557
|
+
}
|
|
15558
|
+
throw e;
|
|
15559
|
+
}
|
|
15560
|
+
}
|
|
15561
|
+
throw lastError;
|
|
15540
15562
|
}
|
|
15541
15563
|
async register(token, body) {
|
|
15542
15564
|
const raw = await this.request("POST", "/api/daemon/register", token, body);
|
|
@@ -15571,11 +15593,28 @@ class DaemonClient {
|
|
|
15571
15593
|
return this.request("GET", `/api/artifacts/${artifactId}?workspace_id=${encodeURIComponent(workspaceId)}`, token);
|
|
15572
15594
|
}
|
|
15573
15595
|
async downloadArtifact(token, artifactId, workspaceId) {
|
|
15574
|
-
const
|
|
15575
|
-
|
|
15576
|
-
|
|
15596
|
+
const MAX_RETRIES = 3;
|
|
15597
|
+
const BASE_DELAY_MS = 500;
|
|
15598
|
+
let lastError;
|
|
15599
|
+
for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
|
|
15600
|
+
try {
|
|
15601
|
+
const res = await fetch(`${this.baseURL}/api/artifacts/${artifactId}/content?workspace_id=${encodeURIComponent(workspaceId)}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
15602
|
+
if (!res.ok) {
|
|
15603
|
+
throw new Error(`artifact download failed: HTTP ${res.status}`);
|
|
15604
|
+
}
|
|
15605
|
+
return res.arrayBuffer();
|
|
15606
|
+
} catch (e) {
|
|
15607
|
+
if (e instanceof TypeError) {
|
|
15608
|
+
lastError = e;
|
|
15609
|
+
if (attempt < MAX_RETRIES) {
|
|
15610
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS * 2 ** attempt));
|
|
15611
|
+
continue;
|
|
15612
|
+
}
|
|
15613
|
+
}
|
|
15614
|
+
throw e;
|
|
15615
|
+
}
|
|
15577
15616
|
}
|
|
15578
|
-
|
|
15617
|
+
throw lastError;
|
|
15579
15618
|
}
|
|
15580
15619
|
reportMessages(token, taskId, messages) {
|
|
15581
15620
|
return this.request("POST", `/api/daemon/tasks/${taskId}/messages`, token, { messages });
|
|
@@ -16595,30 +16634,30 @@ ${task.agent?.userEmail ? `Your owner's email address is '${task.agent.userEmail
|
|
|
16595
16634
|
|
|
16596
16635
|
### Emails
|
|
16597
16636
|
---
|
|
16598
|
-
Run 'npx @alook/cli email pull --agent_id ${task.agentId} --status unread' to download unread emails to '/tmp/alook-emails/'.
|
|
16599
|
-
Each email is saved to '/tmp/alook-emails/<emailId>/' with:
|
|
16637
|
+
Run 'npx @alook/cli email pull --agent_id ${task.agentId} --workspace ${task.workspaceId} --status unread' to download unread emails to '/tmp/alook-emails/${task.workspaceId}/${task.agentId}/'.
|
|
16638
|
+
Each email is saved to '/tmp/alook-emails/${task.workspaceId}/${task.agentId}/<emailId>/' with:
|
|
16600
16639
|
- 'metadata.json' — sender, recipient, subject, date, status, message_id, in_reply_to, references
|
|
16601
16640
|
- 'body.txt' — plain text body
|
|
16602
16641
|
- 'body.html' — HTML body (if available)
|
|
16603
16642
|
- 'attachments/' — extracted attachment files (if any)
|
|
16604
16643
|
---
|
|
16605
16644
|
Before starting to process an email, mark it as read:
|
|
16606
|
-
- Run 'npx @alook/cli email set --agent_id ${task.agentId} --email_id <EMAIL_ID> --status read'
|
|
16645
|
+
- Run 'npx @alook/cli email set --agent_id ${task.agentId} --workspace ${task.workspaceId} --email_id <EMAIL_ID> --status read'
|
|
16607
16646
|
---
|
|
16608
16647
|
|
|
16609
16648
|
#### Sending a new email
|
|
16610
16649
|
Write the HTML body to a file first, then send it. The body is forwarded as-is (HTML).
|
|
16611
|
-
- Run 'npx @alook/cli email send --agent_id ${task.agentId} --to <ADDRESS> --subject "<SUBJECT>" --body-file <PATH_TO_HTML>'
|
|
16650
|
+
- Run 'npx @alook/cli email send --agent_id ${task.agentId} --workspace ${task.workspaceId} --to <ADDRESS> --subject "<SUBJECT>" --body-file <PATH_TO_HTML>'
|
|
16612
16651
|
- To send from a specific mailbox, add '--from <YOUR_EMAIL_ADDRESS>'. Without '--from', the default Alook address is used.
|
|
16613
16652
|
- Attach files with '--attachment <PATH>' — repeat the flag for multiple attachments. Each file is uploaded before sending.
|
|
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'
|
|
16653
|
+
- Example: 'npx @alook/cli email send --agent_id ${task.agentId} --workspace ${task.workspaceId} --to foo@bar.com --subject "Weekly report" --body-file /tmp/body.html --from alice@company.com --attachment /tmp/report.pdf'
|
|
16615
16654
|
|
|
16616
16655
|
#### Replying to an email
|
|
16617
16656
|
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.
|
|
16618
16657
|
- Use 'Re: <original subject>' as the subject.
|
|
16619
16658
|
- Quote the original email body in your reply (wrap it in a blockquote).
|
|
16620
16659
|
- The <EMAIL_ID> is the Alook email id from metadata.json (not the message_id header).
|
|
16621
|
-
- Example: 'npx @alook/cli email send --agent_id ${task.agentId} --to sender@example.com --subject "Re: Bug report" --body-file /tmp/reply.html --in-reply-to <EMAIL_ID>'
|
|
16660
|
+
- Example: 'npx @alook/cli email send --agent_id ${task.agentId} --workspace ${task.workspaceId} --to sender@example.com --subject "Re: Bug report" --body-file /tmp/reply.html --in-reply-to <EMAIL_ID>'
|
|
16622
16661
|
---
|
|
16623
16662
|
`;
|
|
16624
16663
|
}
|