@alook/cli 0.0.8 → 0.0.10
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 +191 -7
- package/dist/session-runner.js +182 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// src/index.ts
|
|
18
|
-
import { Command as
|
|
18
|
+
import { Command as Command10 } from "commander";
|
|
19
19
|
|
|
20
20
|
// commands/register.ts
|
|
21
21
|
import { Command } from "commander";
|
|
@@ -167,7 +167,8 @@ function saveCLIConfigForProfile(profile, profileConfig) {
|
|
|
167
167
|
// commands/register.ts
|
|
168
168
|
function isCommandAvailable(cmd) {
|
|
169
169
|
try {
|
|
170
|
-
|
|
170
|
+
const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
171
|
+
execSync(check, { stdio: "ignore" });
|
|
171
172
|
return true;
|
|
172
173
|
} catch {
|
|
173
174
|
return false;
|
|
@@ -13864,6 +13865,7 @@ var ClaimedTaskRowSchema = exports_external.object({
|
|
|
13864
13865
|
result: exports_external.unknown().nullable(),
|
|
13865
13866
|
context: exports_external.unknown().nullable(),
|
|
13866
13867
|
type: exports_external.string().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
13868
|
+
contextKey: exports_external.string().nullable().optional(),
|
|
13867
13869
|
sessionId: exports_external.string().nullable(),
|
|
13868
13870
|
createdAt: exports_external.coerce.date(),
|
|
13869
13871
|
dispatchedAt: exports_external.coerce.date().nullable(),
|
|
@@ -13893,7 +13895,9 @@ var TaskApiBaseSchema = exports_external.object({
|
|
|
13893
13895
|
result: exports_external.unknown().nullable(),
|
|
13894
13896
|
error: exports_external.string().nullable(),
|
|
13895
13897
|
created_at: exports_external.string(),
|
|
13896
|
-
type: exports_external.string()
|
|
13898
|
+
type: exports_external.string(),
|
|
13899
|
+
context_key: exports_external.string().nullable().optional(),
|
|
13900
|
+
context: exports_external.unknown().nullable().optional()
|
|
13897
13901
|
});
|
|
13898
13902
|
var TaskApiSchema = TaskApiBaseSchema.extend({
|
|
13899
13903
|
agent: TaskAgentDataApiSchema.nullable().optional()
|
|
@@ -14012,6 +14016,64 @@ var CalendarEventApiSchema = exports_external.object({
|
|
|
14012
14016
|
var AddWhitelistRequestSchema = exports_external.object({
|
|
14013
14017
|
email: exports_external.string().email()
|
|
14014
14018
|
});
|
|
14019
|
+
var RuntimeConfigSchema = exports_external.object({ model: exports_external.string().max(100).optional() }).passthrough().optional();
|
|
14020
|
+
var CreateAgentRequestSchema = exports_external.object({
|
|
14021
|
+
name: exports_external.string().min(1, "name is required"),
|
|
14022
|
+
description: exports_external.string().optional().default(""),
|
|
14023
|
+
instructions: exports_external.string().optional().default(""),
|
|
14024
|
+
runtime_id: exports_external.string().min(1, "runtime_id is required"),
|
|
14025
|
+
runtime_config: RuntimeConfigSchema,
|
|
14026
|
+
max_concurrent_tasks: exports_external.number().int().optional(),
|
|
14027
|
+
email_handle: exports_external.string().optional()
|
|
14028
|
+
});
|
|
14029
|
+
var UpdateAgentRequestSchema = exports_external.object({
|
|
14030
|
+
name: exports_external.string().min(1).optional(),
|
|
14031
|
+
description: exports_external.string().optional(),
|
|
14032
|
+
instructions: exports_external.string().optional(),
|
|
14033
|
+
runtime_id: exports_external.string().min(1).optional(),
|
|
14034
|
+
runtime_config: RuntimeConfigSchema
|
|
14035
|
+
}).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" });
|
|
14036
|
+
var CreateConversationRequestSchema = exports_external.object({
|
|
14037
|
+
agent_id: exports_external.string().min(1, "agent_id is required")
|
|
14038
|
+
});
|
|
14039
|
+
var CreateMessageRequestSchema = exports_external.object({
|
|
14040
|
+
content: exports_external.string().min(1, "content is required")
|
|
14041
|
+
});
|
|
14042
|
+
var EmailAttachmentSchema = exports_external.object({
|
|
14043
|
+
key: exports_external.string().min(1),
|
|
14044
|
+
filename: exports_external.string().min(1),
|
|
14045
|
+
size: exports_external.number().int().nonnegative().optional(),
|
|
14046
|
+
contentType: exports_external.string().min(1)
|
|
14047
|
+
});
|
|
14048
|
+
var SendEmailRequestSchema = exports_external.object({
|
|
14049
|
+
agentId: exports_external.string().min(1, "agentId is required"),
|
|
14050
|
+
to: exports_external.string().min(1, "to is required"),
|
|
14051
|
+
subject: exports_external.string().min(1, "subject is required"),
|
|
14052
|
+
htmlBody: exports_external.string().default(""),
|
|
14053
|
+
inReplyTo: exports_external.string().optional(),
|
|
14054
|
+
references: exports_external.string().optional(),
|
|
14055
|
+
attachments: exports_external.array(EmailAttachmentSchema).optional()
|
|
14056
|
+
});
|
|
14057
|
+
var UpdateEmailStatusRequestSchema = exports_external.object({
|
|
14058
|
+
status: exports_external.enum(["unread", "read", "archived"])
|
|
14059
|
+
});
|
|
14060
|
+
var EmailNotifyRequestSchema = exports_external.object({
|
|
14061
|
+
agentId: exports_external.string().min(1),
|
|
14062
|
+
workspaceId: exports_external.string().min(1),
|
|
14063
|
+
r2Key: exports_external.string().min(1),
|
|
14064
|
+
from: exports_external.string().min(1),
|
|
14065
|
+
to: exports_external.string().optional(),
|
|
14066
|
+
subject: exports_external.string().min(1),
|
|
14067
|
+
isWhitelisted: exports_external.boolean(),
|
|
14068
|
+
forwarded: exports_external.boolean().optional().default(false),
|
|
14069
|
+
messageId: exports_external.string().optional().default(""),
|
|
14070
|
+
inReplyTo: exports_external.string().optional().default(""),
|
|
14071
|
+
references: exports_external.string().optional().default("")
|
|
14072
|
+
});
|
|
14073
|
+
var CreateWorkspaceRequestSchema = exports_external.object({
|
|
14074
|
+
name: exports_external.string().min(1, "name is required"),
|
|
14075
|
+
slug: exports_external.string().min(1, "slug is required")
|
|
14076
|
+
});
|
|
14015
14077
|
// ../../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
|
|
14016
14078
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
14017
14079
|
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
@@ -15518,6 +15580,7 @@ var message = sqliteTable("message", {
|
|
|
15518
15580
|
role: text("role").notNull(),
|
|
15519
15581
|
content: text("content").notNull().default(""),
|
|
15520
15582
|
taskId: text("task_id"),
|
|
15583
|
+
attachmentIds: text("attachment_ids"),
|
|
15521
15584
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15522
15585
|
});
|
|
15523
15586
|
var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
@@ -15528,6 +15591,7 @@ var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
|
15528
15591
|
conversationId: text("conversation_id").notNull().references(() => conversation.id),
|
|
15529
15592
|
prompt: text("prompt").notNull(),
|
|
15530
15593
|
type: text("type").notNull().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
15594
|
+
contextKey: text("context_key"),
|
|
15531
15595
|
status: text("status").notNull().default("queued"),
|
|
15532
15596
|
priority: integer2("priority").notNull().default(0),
|
|
15533
15597
|
result: text("result", { mode: "json" }),
|
|
@@ -15601,6 +15665,24 @@ var calendarEvent = sqliteTable("calendar_event", {
|
|
|
15601
15665
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
15602
15666
|
}).onDelete("cascade")
|
|
15603
15667
|
]);
|
|
15668
|
+
var artifact = sqliteTable("artifact", {
|
|
15669
|
+
id: text("id").primaryKey().$defaultFn(() => "art_" + nanoid3()),
|
|
15670
|
+
conversationId: text("conversation_id").notNull().references(() => conversation.id, { onDelete: "cascade" }),
|
|
15671
|
+
agentId: text("agent_id").notNull(),
|
|
15672
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15673
|
+
filename: text("filename").notNull(),
|
|
15674
|
+
contentType: text("content_type").notNull().default("application/octet-stream"),
|
|
15675
|
+
size: integer2("size").notNull(),
|
|
15676
|
+
r2Key: text("r2_key").notNull(),
|
|
15677
|
+
source: text("source").notNull().default("agent"),
|
|
15678
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15679
|
+
}, (t) => [
|
|
15680
|
+
index("idx_artifact_conversation").on(t.conversationId),
|
|
15681
|
+
foreignKey({
|
|
15682
|
+
columns: [t.agentId, t.workspaceId],
|
|
15683
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
15684
|
+
}).onDelete("cascade")
|
|
15685
|
+
]);
|
|
15604
15686
|
var machineToken = sqliteTable("machine_token", {
|
|
15605
15687
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
15606
15688
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
@@ -15697,6 +15779,16 @@ class DaemonClient {
|
|
|
15697
15779
|
error: error48
|
|
15698
15780
|
});
|
|
15699
15781
|
}
|
|
15782
|
+
async getArtifactMeta(token, artifactId, workspaceId) {
|
|
15783
|
+
return this.request("GET", `/api/artifacts/${artifactId}?workspace_id=${encodeURIComponent(workspaceId)}`, token);
|
|
15784
|
+
}
|
|
15785
|
+
async downloadArtifact(token, artifactId, workspaceId) {
|
|
15786
|
+
const res = await fetch(`${this.baseURL}/api/artifacts/${artifactId}/content?workspace_id=${encodeURIComponent(workspaceId)}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
15787
|
+
if (!res.ok) {
|
|
15788
|
+
throw new Error(`artifact download failed: HTTP ${res.status}`);
|
|
15789
|
+
}
|
|
15790
|
+
return res.arrayBuffer();
|
|
15791
|
+
}
|
|
15700
15792
|
reportMessages(token, taskId, messages) {
|
|
15701
15793
|
return this.request("POST", `/api/daemon/tasks/${taskId}/messages`, token, { messages });
|
|
15702
15794
|
}
|
|
@@ -15866,6 +15958,8 @@ function fromApiTask(api2) {
|
|
|
15866
15958
|
status: api2.status,
|
|
15867
15959
|
priority: api2.priority,
|
|
15868
15960
|
type: api2.type,
|
|
15961
|
+
contextKey: api2.context_key ?? null,
|
|
15962
|
+
context: api2.context ?? undefined,
|
|
15869
15963
|
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,
|
|
15870
15964
|
repos: undefined,
|
|
15871
15965
|
createdAt: api2.created_at
|
|
@@ -16077,7 +16171,8 @@ var _dir = dirname3(fileURLToPath2(import.meta.url));
|
|
|
16077
16171
|
var sessionRunnerPath = existsSync(join4(_dir, "session-runner.js")) ? join4(_dir, "session-runner.js") : join4(_dir, "session-runner.ts");
|
|
16078
16172
|
function isCommandAvailable2(cmd) {
|
|
16079
16173
|
try {
|
|
16080
|
-
|
|
16174
|
+
const check2 = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
16175
|
+
execSync3(check2, { stdio: "ignore" });
|
|
16081
16176
|
return true;
|
|
16082
16177
|
} catch {
|
|
16083
16178
|
return false;
|
|
@@ -16178,7 +16273,16 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16178
16273
|
runtimes
|
|
16179
16274
|
});
|
|
16180
16275
|
} catch (e) {
|
|
16181
|
-
|
|
16276
|
+
if (e instanceof Error && e.message.startsWith("HTTP 401")) {
|
|
16277
|
+
log.warn(`Workspace ${ws.id} token invalid — removing from config`);
|
|
16278
|
+
try {
|
|
16279
|
+
const cfg = loadCLIConfigForProfile(profile);
|
|
16280
|
+
cfg.watched_workspaces = (cfg.watched_workspaces || []).filter((w) => w.id !== ws.id);
|
|
16281
|
+
saveCLIConfigForProfile(profile, cfg);
|
|
16282
|
+
} catch {}
|
|
16283
|
+
} else {
|
|
16284
|
+
log.error(`Failed to register workspace ${ws.id}, skipping`, e);
|
|
16285
|
+
}
|
|
16182
16286
|
continue;
|
|
16183
16287
|
}
|
|
16184
16288
|
log.info(`Workspace ${ws.id} registered — ${resp.runtimes.length} runtime(s)`);
|
|
@@ -16270,7 +16374,11 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16270
16374
|
});
|
|
16271
16375
|
}
|
|
16272
16376
|
} catch (e) {
|
|
16273
|
-
|
|
16377
|
+
if (e instanceof Error && e.message.startsWith("HTTP 401")) {
|
|
16378
|
+
evictedIds.push(ws.workspaceId);
|
|
16379
|
+
} else {
|
|
16380
|
+
log.debug("Poll error", e);
|
|
16381
|
+
}
|
|
16274
16382
|
}
|
|
16275
16383
|
}
|
|
16276
16384
|
for (const id of evictedIds) {
|
|
@@ -17015,8 +17123,83 @@ ${result.output}`);
|
|
|
17015
17123
|
return cmd;
|
|
17016
17124
|
}
|
|
17017
17125
|
|
|
17126
|
+
// commands/sync.ts
|
|
17127
|
+
import { Command as Command9 } from "commander";
|
|
17128
|
+
import { readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
17129
|
+
import { basename as basename2 } from "path";
|
|
17130
|
+
var MIME_BY_EXT2 = {
|
|
17131
|
+
".pdf": "application/pdf",
|
|
17132
|
+
".png": "image/png",
|
|
17133
|
+
".jpg": "image/jpeg",
|
|
17134
|
+
".jpeg": "image/jpeg",
|
|
17135
|
+
".gif": "image/gif",
|
|
17136
|
+
".txt": "text/plain",
|
|
17137
|
+
".html": "text/html",
|
|
17138
|
+
".json": "application/json",
|
|
17139
|
+
".csv": "text/csv",
|
|
17140
|
+
".md": "text/markdown",
|
|
17141
|
+
".ts": "text/typescript",
|
|
17142
|
+
".js": "text/javascript",
|
|
17143
|
+
".yaml": "text/yaml",
|
|
17144
|
+
".yml": "text/yaml",
|
|
17145
|
+
".svg": "image/svg+xml",
|
|
17146
|
+
".xml": "application/xml",
|
|
17147
|
+
".zip": "application/zip"
|
|
17148
|
+
};
|
|
17149
|
+
function guessContentType2(filename) {
|
|
17150
|
+
const idx = filename.lastIndexOf(".");
|
|
17151
|
+
if (idx < 0)
|
|
17152
|
+
return "application/octet-stream";
|
|
17153
|
+
const ext = filename.slice(idx).toLowerCase();
|
|
17154
|
+
return MIME_BY_EXT2[ext] ?? "application/octet-stream";
|
|
17155
|
+
}
|
|
17156
|
+
function resolveClientOpts3(command, agentId) {
|
|
17157
|
+
const parentOpts = command.parent?.parent?.opts() || {};
|
|
17158
|
+
const profile = parentOpts.profile;
|
|
17159
|
+
const cfg = loadCLIConfigForProfile(profile);
|
|
17160
|
+
const serverUrl = parentOpts.server || cfg.server_url;
|
|
17161
|
+
const workspaces = cfg.watched_workspaces || [];
|
|
17162
|
+
const ws = workspaces.find((w) => w.agent_ids?.includes(agentId));
|
|
17163
|
+
if (!ws || !ws.token) {
|
|
17164
|
+
console.error(`Error: no registered workspace contains agent ${agentId}. Run '${cmdPrefix()} register --token <token>' first.`);
|
|
17165
|
+
process.exit(1);
|
|
17166
|
+
}
|
|
17167
|
+
return { serverUrl, token: ws.token, workspaceId: ws.id };
|
|
17168
|
+
}
|
|
17169
|
+
function syncCommand() {
|
|
17170
|
+
const cmd = new Command9("sync").description("File sync utilities");
|
|
17171
|
+
cmd.command("upload-artifact").description("Upload a file artifact to a conversation").requiredOption("--agent_id <id>", "Agent ID").requiredOption("--conversation_id <id>", "Conversation ID").requiredOption("--file <path>", "Path to file to upload").action(async (opts, command) => {
|
|
17172
|
+
const { serverUrl, token, workspaceId } = resolveClientOpts3(command, opts.agent_id);
|
|
17173
|
+
const client = new APIClient(serverUrl, token, workspaceId);
|
|
17174
|
+
let bytes;
|
|
17175
|
+
let size;
|
|
17176
|
+
try {
|
|
17177
|
+
const stat = statSync3(opts.file);
|
|
17178
|
+
size = stat.size;
|
|
17179
|
+
bytes = readFileSync5(opts.file);
|
|
17180
|
+
} catch (err) {
|
|
17181
|
+
console.error(`Error: cannot read file "${opts.file}": ${err.message}`);
|
|
17182
|
+
process.exit(1);
|
|
17183
|
+
}
|
|
17184
|
+
const filename = basename2(opts.file);
|
|
17185
|
+
const contentType = guessContentType2(filename);
|
|
17186
|
+
const form = new FormData;
|
|
17187
|
+
form.append("file", new Blob([new Uint8Array(bytes)], { type: contentType }), filename);
|
|
17188
|
+
form.append("agent_id", opts.agent_id);
|
|
17189
|
+
form.append("conversation_id", opts.conversation_id);
|
|
17190
|
+
try {
|
|
17191
|
+
const result = await client.postMultipart("/api/artifacts/upload", form);
|
|
17192
|
+
printJSON(result);
|
|
17193
|
+
} catch (err) {
|
|
17194
|
+
console.error(`Error uploading artifact: ${err.message}`);
|
|
17195
|
+
process.exit(1);
|
|
17196
|
+
}
|
|
17197
|
+
});
|
|
17198
|
+
return cmd;
|
|
17199
|
+
}
|
|
17200
|
+
|
|
17018
17201
|
// src/index.ts
|
|
17019
|
-
var program = new
|
|
17202
|
+
var program = new Command10;
|
|
17020
17203
|
program.name("alook").description("Alook CLI").option("--server <url>", "Server URL").option("--profile <name>", "Profile name");
|
|
17021
17204
|
program.addCommand(registerCommand());
|
|
17022
17205
|
program.addCommand(statusCommand());
|
|
@@ -17026,4 +17209,5 @@ program.addCommand(calendarCommand());
|
|
|
17026
17209
|
program.addCommand(configCommand());
|
|
17027
17210
|
program.addCommand(versionCommand());
|
|
17028
17211
|
program.addCommand(updateCommand());
|
|
17212
|
+
program.addCommand(syncCommand());
|
|
17029
17213
|
program.parse();
|
package/dist/session-runner.js
CHANGED
|
@@ -15,6 +15,8 @@ var __export = (target, all) => {
|
|
|
15
15
|
|
|
16
16
|
// daemon/session-runner.ts
|
|
17
17
|
import { createWriteStream } from "fs";
|
|
18
|
+
import { mkdir, writeFile, rm } from "fs/promises";
|
|
19
|
+
import path from "path";
|
|
18
20
|
|
|
19
21
|
// ../shared/src/constants.ts
|
|
20
22
|
var TASK_TYPES = {
|
|
@@ -13581,6 +13583,7 @@ var ClaimedTaskRowSchema = exports_external.object({
|
|
|
13581
13583
|
result: exports_external.unknown().nullable(),
|
|
13582
13584
|
context: exports_external.unknown().nullable(),
|
|
13583
13585
|
type: exports_external.string().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
13586
|
+
contextKey: exports_external.string().nullable().optional(),
|
|
13584
13587
|
sessionId: exports_external.string().nullable(),
|
|
13585
13588
|
createdAt: exports_external.coerce.date(),
|
|
13586
13589
|
dispatchedAt: exports_external.coerce.date().nullable(),
|
|
@@ -13610,7 +13613,9 @@ var TaskApiBaseSchema = exports_external.object({
|
|
|
13610
13613
|
result: exports_external.unknown().nullable(),
|
|
13611
13614
|
error: exports_external.string().nullable(),
|
|
13612
13615
|
created_at: exports_external.string(),
|
|
13613
|
-
type: exports_external.string()
|
|
13616
|
+
type: exports_external.string(),
|
|
13617
|
+
context_key: exports_external.string().nullable().optional(),
|
|
13618
|
+
context: exports_external.unknown().nullable().optional()
|
|
13614
13619
|
});
|
|
13615
13620
|
var TaskApiSchema = TaskApiBaseSchema.extend({
|
|
13616
13621
|
agent: TaskAgentDataApiSchema.nullable().optional()
|
|
@@ -13729,6 +13734,64 @@ var CalendarEventApiSchema = exports_external.object({
|
|
|
13729
13734
|
var AddWhitelistRequestSchema = exports_external.object({
|
|
13730
13735
|
email: exports_external.string().email()
|
|
13731
13736
|
});
|
|
13737
|
+
var RuntimeConfigSchema = exports_external.object({ model: exports_external.string().max(100).optional() }).passthrough().optional();
|
|
13738
|
+
var CreateAgentRequestSchema = exports_external.object({
|
|
13739
|
+
name: exports_external.string().min(1, "name is required"),
|
|
13740
|
+
description: exports_external.string().optional().default(""),
|
|
13741
|
+
instructions: exports_external.string().optional().default(""),
|
|
13742
|
+
runtime_id: exports_external.string().min(1, "runtime_id is required"),
|
|
13743
|
+
runtime_config: RuntimeConfigSchema,
|
|
13744
|
+
max_concurrent_tasks: exports_external.number().int().optional(),
|
|
13745
|
+
email_handle: exports_external.string().optional()
|
|
13746
|
+
});
|
|
13747
|
+
var UpdateAgentRequestSchema = exports_external.object({
|
|
13748
|
+
name: exports_external.string().min(1).optional(),
|
|
13749
|
+
description: exports_external.string().optional(),
|
|
13750
|
+
instructions: exports_external.string().optional(),
|
|
13751
|
+
runtime_id: exports_external.string().min(1).optional(),
|
|
13752
|
+
runtime_config: RuntimeConfigSchema
|
|
13753
|
+
}).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" });
|
|
13754
|
+
var CreateConversationRequestSchema = exports_external.object({
|
|
13755
|
+
agent_id: exports_external.string().min(1, "agent_id is required")
|
|
13756
|
+
});
|
|
13757
|
+
var CreateMessageRequestSchema = exports_external.object({
|
|
13758
|
+
content: exports_external.string().min(1, "content is required")
|
|
13759
|
+
});
|
|
13760
|
+
var EmailAttachmentSchema = exports_external.object({
|
|
13761
|
+
key: exports_external.string().min(1),
|
|
13762
|
+
filename: exports_external.string().min(1),
|
|
13763
|
+
size: exports_external.number().int().nonnegative().optional(),
|
|
13764
|
+
contentType: exports_external.string().min(1)
|
|
13765
|
+
});
|
|
13766
|
+
var SendEmailRequestSchema = exports_external.object({
|
|
13767
|
+
agentId: exports_external.string().min(1, "agentId is required"),
|
|
13768
|
+
to: exports_external.string().min(1, "to is required"),
|
|
13769
|
+
subject: exports_external.string().min(1, "subject is required"),
|
|
13770
|
+
htmlBody: exports_external.string().default(""),
|
|
13771
|
+
inReplyTo: exports_external.string().optional(),
|
|
13772
|
+
references: exports_external.string().optional(),
|
|
13773
|
+
attachments: exports_external.array(EmailAttachmentSchema).optional()
|
|
13774
|
+
});
|
|
13775
|
+
var UpdateEmailStatusRequestSchema = exports_external.object({
|
|
13776
|
+
status: exports_external.enum(["unread", "read", "archived"])
|
|
13777
|
+
});
|
|
13778
|
+
var EmailNotifyRequestSchema = exports_external.object({
|
|
13779
|
+
agentId: exports_external.string().min(1),
|
|
13780
|
+
workspaceId: exports_external.string().min(1),
|
|
13781
|
+
r2Key: exports_external.string().min(1),
|
|
13782
|
+
from: exports_external.string().min(1),
|
|
13783
|
+
to: exports_external.string().optional(),
|
|
13784
|
+
subject: exports_external.string().min(1),
|
|
13785
|
+
isWhitelisted: exports_external.boolean(),
|
|
13786
|
+
forwarded: exports_external.boolean().optional().default(false),
|
|
13787
|
+
messageId: exports_external.string().optional().default(""),
|
|
13788
|
+
inReplyTo: exports_external.string().optional().default(""),
|
|
13789
|
+
references: exports_external.string().optional().default("")
|
|
13790
|
+
});
|
|
13791
|
+
var CreateWorkspaceRequestSchema = exports_external.object({
|
|
13792
|
+
name: exports_external.string().min(1, "name is required"),
|
|
13793
|
+
slug: exports_external.string().min(1, "slug is required")
|
|
13794
|
+
});
|
|
13732
13795
|
// ../../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
|
|
13733
13796
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
13734
13797
|
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
@@ -15235,6 +15298,7 @@ var message = sqliteTable("message", {
|
|
|
15235
15298
|
role: text("role").notNull(),
|
|
15236
15299
|
content: text("content").notNull().default(""),
|
|
15237
15300
|
taskId: text("task_id"),
|
|
15301
|
+
attachmentIds: text("attachment_ids"),
|
|
15238
15302
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15239
15303
|
});
|
|
15240
15304
|
var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
@@ -15245,6 +15309,7 @@ var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
|
15245
15309
|
conversationId: text("conversation_id").notNull().references(() => conversation.id),
|
|
15246
15310
|
prompt: text("prompt").notNull(),
|
|
15247
15311
|
type: text("type").notNull().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
15312
|
+
contextKey: text("context_key"),
|
|
15248
15313
|
status: text("status").notNull().default("queued"),
|
|
15249
15314
|
priority: integer2("priority").notNull().default(0),
|
|
15250
15315
|
result: text("result", { mode: "json" }),
|
|
@@ -15318,6 +15383,24 @@ var calendarEvent = sqliteTable("calendar_event", {
|
|
|
15318
15383
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
15319
15384
|
}).onDelete("cascade")
|
|
15320
15385
|
]);
|
|
15386
|
+
var artifact = sqliteTable("artifact", {
|
|
15387
|
+
id: text("id").primaryKey().$defaultFn(() => "art_" + nanoid3()),
|
|
15388
|
+
conversationId: text("conversation_id").notNull().references(() => conversation.id, { onDelete: "cascade" }),
|
|
15389
|
+
agentId: text("agent_id").notNull(),
|
|
15390
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15391
|
+
filename: text("filename").notNull(),
|
|
15392
|
+
contentType: text("content_type").notNull().default("application/octet-stream"),
|
|
15393
|
+
size: integer2("size").notNull(),
|
|
15394
|
+
r2Key: text("r2_key").notNull(),
|
|
15395
|
+
source: text("source").notNull().default("agent"),
|
|
15396
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15397
|
+
}, (t) => [
|
|
15398
|
+
index("idx_artifact_conversation").on(t.conversationId),
|
|
15399
|
+
foreignKey({
|
|
15400
|
+
columns: [t.agentId, t.workspaceId],
|
|
15401
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
15402
|
+
}).onDelete("cascade")
|
|
15403
|
+
]);
|
|
15321
15404
|
var machineToken = sqliteTable("machine_token", {
|
|
15322
15405
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
15323
15406
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
@@ -15403,6 +15486,16 @@ class DaemonClient {
|
|
|
15403
15486
|
error: error48
|
|
15404
15487
|
});
|
|
15405
15488
|
}
|
|
15489
|
+
async getArtifactMeta(token, artifactId, workspaceId) {
|
|
15490
|
+
return this.request("GET", `/api/artifacts/${artifactId}?workspace_id=${encodeURIComponent(workspaceId)}`, token);
|
|
15491
|
+
}
|
|
15492
|
+
async downloadArtifact(token, artifactId, workspaceId) {
|
|
15493
|
+
const res = await fetch(`${this.baseURL}/api/artifacts/${artifactId}/content?workspace_id=${encodeURIComponent(workspaceId)}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
15494
|
+
if (!res.ok) {
|
|
15495
|
+
throw new Error(`artifact download failed: HTTP ${res.status}`);
|
|
15496
|
+
}
|
|
15497
|
+
return res.arrayBuffer();
|
|
15498
|
+
}
|
|
15406
15499
|
reportMessages(token, taskId, messages) {
|
|
15407
15500
|
return this.request("POST", `/api/daemon/tasks/${taskId}/messages`, token, { messages });
|
|
15408
15501
|
}
|
|
@@ -16350,8 +16443,8 @@ var SYSTEM_PROMPT_BODY = `## Memory Management
|
|
|
16350
16443
|
- For SPECIFIC yet LONG rules or pattern, write to experiences/[NAME].md, and add index to ./memory.md for later recall.
|
|
16351
16444
|
### whats is ESSENTIAL and SHORT Memory?
|
|
16352
16445
|
- basic user profile, e.g.:
|
|
16353
|
-
- "user name is
|
|
16354
|
-
- "user is working on
|
|
16446
|
+
- "user name is ..."
|
|
16447
|
+
- "user is working on ..."
|
|
16355
16448
|
- certain local project mapping, e.g.:
|
|
16356
16449
|
- "alook means the project under /user/home/alook/"
|
|
16357
16450
|
- when to read certain stuff, e.g.:
|
|
@@ -16388,6 +16481,7 @@ those json are sorted by datetime in asc order.
|
|
|
16388
16481
|
- When you start a new task, read the last ~10 lines of today's timeline to understand what has been asked and done recently.
|
|
16389
16482
|
- if you don't know the current datetime, obtain the current datetime first.
|
|
16390
16483
|
- When user ask you something you don't have in your current context, try to read the timeline jsonl files for answer (today or previous days).
|
|
16484
|
+
- When access other local projects, make sure you read the CLAUDE.md/AGENTS.md file under the project root dir to understand the requirements.
|
|
16391
16485
|
`;
|
|
16392
16486
|
function buildInstructionContent(task) {
|
|
16393
16487
|
const displayName = task.agent?.name || "Alook Agent";
|
|
@@ -16438,21 +16532,38 @@ To reply to an email, add '--in-reply-to <EMAIL_ID>' to the send command. This s
|
|
|
16438
16532
|
`;
|
|
16439
16533
|
}
|
|
16440
16534
|
content += `
|
|
16535
|
+
### Artifacts
|
|
16536
|
+
Upload files for your owner to review in the app.
|
|
16537
|
+
- Your current conversation id is available via env var: $ALOOK_CONVERSATION_ID
|
|
16538
|
+
- Run 'npx @alook/cli sync upload-artifact --agent_id ${task.agentId} --conversation_id $ALOOK_CONVERSATION_ID --file <PATH>'
|
|
16539
|
+
- Use this after generating plans, reports, or any file the owner should review.
|
|
16540
|
+
---
|
|
16541
|
+
|
|
16542
|
+
### Attachments
|
|
16543
|
+
When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
|
|
16544
|
+
Use your Read tool to open them. Images and PDFs are read visually.
|
|
16545
|
+
---
|
|
16546
|
+
`;
|
|
16547
|
+
content += `
|
|
16441
16548
|
### Calendar
|
|
16442
16549
|
You have your own calendar to setup daily routines and reminders.
|
|
16443
16550
|
Schedule future tasks for yourself. At the scheduled time, a new task is dispatched to you with the event as the prompt (task type 'calendar_event').
|
|
16551
|
+
|
|
16552
|
+
!USE Calendar when you think the tasks are recurring or it should be conducted in the future.
|
|
16553
|
+
!When scheduling calendar events relative to a weekday (e.g. "every Monday"), always run date '+%A' first to confirm today's weekday before calculating the target date
|
|
16444
16554
|
---
|
|
16555
|
+
Keep the event title informative and concise, less than 20 words.
|
|
16556
|
+
Place the event details in description.
|
|
16445
16557
|
Create a one-off event:
|
|
16446
|
-
- Run 'npx @alook/cli calendar set --agent_id ${task.agentId} --event_title "<
|
|
16558
|
+
- Run 'npx @alook/cli calendar set --agent_id ${task.agentId} --event_title "<TASK_TITLE>" --description "<TASK_BODY>" --datetime <YYYY-MM-DDTHH:MM>'
|
|
16447
16559
|
- '--datetime' is LOCAL time, format 'YYYY-MM-DDTHH:MM' (e.g. '2026-04-17T09:30'). Do NOT pass UTC / ISO strings with 'Z'.
|
|
16448
16560
|
- '--event_title' becomes the task prompt when the event fires — write it as the instruction you want future-you to receive.
|
|
16449
|
-
- Optional '--description "<text>"' — longer notes/context shown alongside the event in the web UI. Use it for anything that wouldn't fit cleanly in the title.
|
|
16450
16561
|
|
|
16451
16562
|
Create a repeating event:
|
|
16452
16563
|
- Add '--repeat <interval>' where interval is like '1day', '2hour', '1week', '1month'.
|
|
16453
16564
|
- Optionally add '--repeat_stop_date <YYYY-MM-DD>' to stop the recurrence (local date).
|
|
16454
|
-
- Example: 'npx @alook/cli calendar set --agent_id ${task.agentId} --event_title "
|
|
16455
|
-
|
|
16565
|
+
- Example: 'npx @alook/cli calendar set --agent_id ${task.agentId} --event_title "<REPEAT_TASK_TITLE>" --description "<REPEAT_TASK_BODY>" --datetime 2026-04-18T09:00 --repeat 1day --repeat_stop_date 2026-05-18'
|
|
16566
|
+
---
|
|
16456
16567
|
List upcoming events:
|
|
16457
16568
|
- Run 'npx @alook/cli calendar list --agent_id ${task.agentId}' (defaults: next 30 days, past 0 days).
|
|
16458
16569
|
- Tune the window with '--future_days <N>' and '--past_days <N>'. Add '--json' for machine-readable output.
|
|
@@ -16785,9 +16896,10 @@ function updateEntry(timelineDir, taskId, updater) {
|
|
|
16785
16896
|
}
|
|
16786
16897
|
log.debug(`Timeline updateEntry: task_id ${taskId} not found in last 7 days`);
|
|
16787
16898
|
}
|
|
16788
|
-
function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider) {
|
|
16899
|
+
function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider, contextKey) {
|
|
16789
16900
|
return {
|
|
16790
16901
|
task_id: taskId,
|
|
16902
|
+
context_key: contextKey ?? null,
|
|
16791
16903
|
session_id: sessionId || null,
|
|
16792
16904
|
pid: pid ?? null,
|
|
16793
16905
|
status: "running",
|
|
@@ -16800,7 +16912,9 @@ function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider) {
|
|
|
16800
16912
|
};
|
|
16801
16913
|
}
|
|
16802
16914
|
var DEFAULT_RESUME_MAX_AGE_MS = 3 * 60 * 60 * 1000;
|
|
16803
|
-
|
|
16915
|
+
var EMAIL_RESUME_MAX_AGE_MS = 48 * 60 * 60 * 1000;
|
|
16916
|
+
function findResumableSessionByContextKey(timelineDir, contextKey, provider) {
|
|
16917
|
+
const maxAgeMs = contextKey.startsWith("email:") ? EMAIL_RESUME_MAX_AGE_MS : DEFAULT_RESUME_MAX_AGE_MS;
|
|
16804
16918
|
const now = new Date;
|
|
16805
16919
|
const cutoff = new Date(now.getTime() - maxAgeMs);
|
|
16806
16920
|
const daysToScan = Math.ceil(maxAgeMs / 86400000) + 1;
|
|
@@ -16810,7 +16924,7 @@ function findResumableSessionId(timelineDir, type, provider, maxAgeMs = DEFAULT_
|
|
|
16810
16924
|
}
|
|
16811
16925
|
entries.sort((a, b) => new Date(b.datetime).getTime() - new Date(a.datetime).getTime());
|
|
16812
16926
|
for (const entry of entries) {
|
|
16813
|
-
if (entry.status === "completed" && entry.
|
|
16927
|
+
if (entry.status === "completed" && entry.context_key === contextKey && entry.provider === provider && entry.session_id && new Date(entry.datetime) >= cutoff) {
|
|
16814
16928
|
return entry.session_id;
|
|
16815
16929
|
}
|
|
16816
16930
|
}
|
|
@@ -16837,20 +16951,68 @@ function prepare(config2, task) {
|
|
|
16837
16951
|
}
|
|
16838
16952
|
|
|
16839
16953
|
// daemon/prompt.ts
|
|
16840
|
-
function buildPrompt(task) {
|
|
16841
|
-
|
|
16954
|
+
function buildPrompt(task, attachments) {
|
|
16955
|
+
const obj = { type: task.type, instruction: task.prompt };
|
|
16956
|
+
if (attachments && attachments.length > 0) {
|
|
16957
|
+
obj.attachments = attachments.map((a) => ({
|
|
16958
|
+
path: a.path,
|
|
16959
|
+
content_type: a.content_type,
|
|
16960
|
+
filename: a.filename
|
|
16961
|
+
}));
|
|
16962
|
+
}
|
|
16963
|
+
return JSON.stringify(obj);
|
|
16842
16964
|
}
|
|
16843
16965
|
|
|
16844
16966
|
// daemon/session-runner.ts
|
|
16967
|
+
var ATTACHMENTS_BASE = "/tmp/alook-attachments";
|
|
16968
|
+
function sanitizeFilename(name) {
|
|
16969
|
+
return path.basename(name).replace(/[/\\]/g, "_").replace(/\.\./g, "_").slice(0, 255) || "file";
|
|
16970
|
+
}
|
|
16971
|
+
async function cleanupAttachments(taskId) {
|
|
16972
|
+
try {
|
|
16973
|
+
await rm(path.join(ATTACHMENTS_BASE, taskId), { recursive: true, force: true });
|
|
16974
|
+
} catch {}
|
|
16975
|
+
}
|
|
16976
|
+
async function downloadAttachments(client, token, workspaceId, taskId, attachmentIds) {
|
|
16977
|
+
const dir = path.join(ATTACHMENTS_BASE, taskId);
|
|
16978
|
+
await mkdir(dir, { recursive: true });
|
|
16979
|
+
const attachments = [];
|
|
16980
|
+
for (const artId of attachmentIds) {
|
|
16981
|
+
const meta3 = await client.getArtifactMeta(token, artId, workspaceId);
|
|
16982
|
+
const content = await client.downloadArtifact(token, artId, workspaceId);
|
|
16983
|
+
const filename = sanitizeFilename(meta3.filename);
|
|
16984
|
+
const localPath = path.join(dir, `${artId}_${filename}`);
|
|
16985
|
+
await writeFile(localPath, Buffer.from(content));
|
|
16986
|
+
attachments.push({
|
|
16987
|
+
path: localPath,
|
|
16988
|
+
content_type: meta3.content_type,
|
|
16989
|
+
filename: meta3.filename
|
|
16990
|
+
});
|
|
16991
|
+
}
|
|
16992
|
+
return attachments;
|
|
16993
|
+
}
|
|
16845
16994
|
async function runSession(input) {
|
|
16846
16995
|
const { task, provider, cliPath, model, serverURL, token, workspacesRoot, agentTimeout } = input;
|
|
16847
16996
|
const client = new DaemonClient(serverURL);
|
|
16848
16997
|
const backend = createBackend(provider, cliPath);
|
|
16849
|
-
const prompt = buildPrompt(task);
|
|
16850
16998
|
const { workDir, logFile, timelineDir, env } = prepare({ workspacesRoot }, task);
|
|
16851
|
-
const
|
|
16999
|
+
const attachmentIds = task.context?.attachment_ids ?? [];
|
|
17000
|
+
let attachments;
|
|
17001
|
+
if (attachmentIds.length > 0) {
|
|
17002
|
+
try {
|
|
17003
|
+
attachments = await downloadAttachments(client, token, task.workspaceId, task.id, attachmentIds);
|
|
17004
|
+
} catch (e) {
|
|
17005
|
+
await cleanupAttachments(task.id);
|
|
17006
|
+
const errMsg = `failed to download attachments: ${e}`;
|
|
17007
|
+
log.error(`Task ${task.id} ${errMsg}`);
|
|
17008
|
+
await client.failTask(token, task.id, errMsg);
|
|
17009
|
+
return;
|
|
17010
|
+
}
|
|
17011
|
+
}
|
|
17012
|
+
const prompt = buildPrompt(task, attachments);
|
|
17013
|
+
const resumeSessionId = task.contextKey ? findResumableSessionByContextKey(timelineDir, task.contextKey, provider) ?? undefined : undefined;
|
|
16852
17014
|
if (resumeSessionId) {
|
|
16853
|
-
log.info(`Task ${task.id} resuming session ${resumeSessionId}`);
|
|
17015
|
+
log.info(`Task ${task.id} resuming session ${resumeSessionId} (context_key: ${task.contextKey})`);
|
|
16854
17016
|
}
|
|
16855
17017
|
const session2 = backend.execute(prompt, {
|
|
16856
17018
|
cwd: workDir,
|
|
@@ -16861,11 +17023,11 @@ async function runSession(input) {
|
|
|
16861
17023
|
});
|
|
16862
17024
|
const agentPid = session2.pid;
|
|
16863
17025
|
const earlySessionId = await session2.sessionId;
|
|
16864
|
-
await initEntryAsync(timelineDir, createTimelineEntry(task.id, task.prompt, task.type, earlySessionId, process.pid, provider));
|
|
17026
|
+
await initEntryAsync(timelineDir, createTimelineEntry(task.id, task.prompt, task.type, earlySessionId, process.pid, provider, task.contextKey));
|
|
16865
17027
|
const pendingMessages = [];
|
|
16866
17028
|
let seq = 0;
|
|
16867
17029
|
const BATCH_SIZE = Number(process.env.ALOOK_MESSAGE_BATCH_SIZE) || 20;
|
|
16868
|
-
const FLUSH_INTERVAL_MS = Number(process.env.ALOOK_MESSAGE_FLUSH_INTERVAL_MS) ||
|
|
17030
|
+
const FLUSH_INTERVAL_MS = Number(process.env.ALOOK_MESSAGE_FLUSH_INTERVAL_MS) || 100;
|
|
16869
17031
|
const flushMessages = async () => {
|
|
16870
17032
|
if (pendingMessages.length === 0)
|
|
16871
17033
|
return;
|
|
@@ -16906,6 +17068,7 @@ async function runSession(input) {
|
|
|
16906
17068
|
await flushMessages();
|
|
16907
17069
|
} catch {}
|
|
16908
17070
|
logStream?.end();
|
|
17071
|
+
await cleanupAttachments(task.id);
|
|
16909
17072
|
updateEntry(timelineDir, task.id, (entry) => {
|
|
16910
17073
|
entry.pid = null;
|
|
16911
17074
|
entry.status = "killed";
|
|
@@ -16968,6 +17131,7 @@ async function runSession(input) {
|
|
|
16968
17131
|
process.removeListener("SIGINT", onKill);
|
|
16969
17132
|
if (killed)
|
|
16970
17133
|
return;
|
|
17134
|
+
await cleanupAttachments(task.id);
|
|
16971
17135
|
if (result.status === "completed") {
|
|
16972
17136
|
updateEntry(timelineDir, task.id, (entry) => {
|
|
16973
17137
|
entry.session_id = result.sessionId || null;
|
|
@@ -17013,6 +17177,7 @@ async function main() {
|
|
|
17013
17177
|
await runSession(input);
|
|
17014
17178
|
} catch (e) {
|
|
17015
17179
|
log.error(`session-runner: unhandled error for task ${input.task.id}`, e);
|
|
17180
|
+
await cleanupAttachments(input.task.id);
|
|
17016
17181
|
try {
|
|
17017
17182
|
await client.failTask(input.token, input.task.id, `session-runner crash: ${e}`);
|
|
17018
17183
|
} catch {}
|