@alook/cli 0.0.23 → 0.0.24
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 +317 -34
- package/dist/meeting-runner.js +562 -0
- package/dist/session-runner.js +112 -4
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -220,7 +220,6 @@ Usage: ${cmdPrefix()} register --token <token>`);
|
|
|
220
220
|
console.error("Error: no workspaces found for this user");
|
|
221
221
|
process.exit(1);
|
|
222
222
|
}
|
|
223
|
-
const ws = workspaces[0];
|
|
224
223
|
console.log("Scanning for AI runtimes...");
|
|
225
224
|
const runtimes = detectRuntimes();
|
|
226
225
|
if (runtimes.length === 0) {
|
|
@@ -247,6 +246,7 @@ Usage: ${cmdPrefix()} register --token <token>`);
|
|
|
247
246
|
console.error(`Error: failed to activate: ${err instanceof Error ? err.message : err}`);
|
|
248
247
|
process.exit(1);
|
|
249
248
|
}
|
|
249
|
+
const ws = workspaces.find((w) => w.id === activateResp.workspace_id) || workspaces[0];
|
|
250
250
|
const wsClient = new APIClient(serverUrl, token, ws.id);
|
|
251
251
|
let agentIds = [];
|
|
252
252
|
try {
|
|
@@ -325,6 +325,18 @@ var TASK_TYPES = {
|
|
|
325
325
|
var POLL_INTERVAL_MS = Number(process.env.POLL_INTERVAL_MS) || 3000;
|
|
326
326
|
var OFFLINE_THRESHOLD_MS = Number(process.env.OFFLINE_THRESHOLD_MS) || 9000;
|
|
327
327
|
var EVENT_POLL_INTERVAL_MS = Number(process.env.EVENT_POLL_INTERVAL_MS) || 2000;
|
|
328
|
+
var MeetingStatus = {
|
|
329
|
+
PENDING: "pending",
|
|
330
|
+
SCHEDULED: "scheduled",
|
|
331
|
+
JOINING: "joining",
|
|
332
|
+
RECORDING: "recording",
|
|
333
|
+
COMPLETED: "completed",
|
|
334
|
+
FAILED: "failed"
|
|
335
|
+
};
|
|
336
|
+
var TERMINAL_MEETING_STATUSES = [
|
|
337
|
+
MeetingStatus.COMPLETED,
|
|
338
|
+
MeetingStatus.FAILED
|
|
339
|
+
];
|
|
328
340
|
var DEV_WEB_URL = process.env.ALOOK_SERVER_URL || "http://localhost:3000";
|
|
329
341
|
var DEV_WS_DO_URL = process.env.DEV_WS_DO_URL || "http://localhost:8789";
|
|
330
342
|
var DEV_EMAIL_WORKER_URL = process.env.DEV_EMAIL_WORKER_URL || "http://localhost:8787";
|
|
@@ -13931,11 +13943,18 @@ var PollRequestSchema = exports_external.object({
|
|
|
13931
13943
|
max_tasks: exports_external.number().int().min(1).default(1),
|
|
13932
13944
|
cli_version: exports_external.string().optional()
|
|
13933
13945
|
});
|
|
13946
|
+
var FileRequestItemSchema = exports_external.object({
|
|
13947
|
+
id: exports_external.string(),
|
|
13948
|
+
agent_id: exports_external.string(),
|
|
13949
|
+
request_type: exports_external.enum(["tree", "read"]),
|
|
13950
|
+
path: exports_external.string()
|
|
13951
|
+
});
|
|
13934
13952
|
var PollResponseSchema = exports_external.object({
|
|
13935
13953
|
tasks: exports_external.array(TaskApiSchema),
|
|
13936
13954
|
evicted: exports_external.boolean().optional(),
|
|
13937
13955
|
pending_update: exports_external.object({ version: exports_external.string() }).optional(),
|
|
13938
|
-
pending_rescan: exports_external.boolean().optional()
|
|
13956
|
+
pending_rescan: exports_external.boolean().optional(),
|
|
13957
|
+
file_requests: exports_external.array(FileRequestItemSchema).optional()
|
|
13939
13958
|
});
|
|
13940
13959
|
var RegisterResponseSchema = exports_external.object({
|
|
13941
13960
|
runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
|
|
@@ -14060,7 +14079,8 @@ var UpdateAgentRequestSchema = exports_external.object({
|
|
|
14060
14079
|
visibility: exports_external.enum(["public", "private"]).optional()
|
|
14061
14080
|
}).refine((v) => v.name !== undefined || v.description !== undefined || v.instructions !== undefined || v.runtime_id !== undefined || v.runtime_config !== undefined || v.visibility !== undefined, { message: "at least one field is required" });
|
|
14062
14081
|
var CreateConversationRequestSchema = exports_external.object({
|
|
14063
|
-
agent_id: exports_external.string().min(1, "agent_id is required")
|
|
14082
|
+
agent_id: exports_external.string().min(1, "agent_id is required"),
|
|
14083
|
+
channel: exports_external.string().optional()
|
|
14064
14084
|
});
|
|
14065
14085
|
var CreateMessageRequestSchema = exports_external.object({
|
|
14066
14086
|
content: exports_external.string().min(1, "content is required")
|
|
@@ -14088,6 +14108,13 @@ var SendEmailRequestSchema = exports_external.object({
|
|
|
14088
14108
|
var UpdateEmailStatusRequestSchema = exports_external.object({
|
|
14089
14109
|
status: exports_external.enum(["unread", "read", "archived"])
|
|
14090
14110
|
});
|
|
14111
|
+
var MeetingInfoSchema = exports_external.object({
|
|
14112
|
+
title: exports_external.string(),
|
|
14113
|
+
meetingUrl: exports_external.string(),
|
|
14114
|
+
startTime: exports_external.string().nullable(),
|
|
14115
|
+
endTime: exports_external.string().nullable(),
|
|
14116
|
+
attendees: exports_external.array(exports_external.object({ name: exports_external.string(), email: exports_external.string() }))
|
|
14117
|
+
});
|
|
14091
14118
|
var EmailNotifyRequestSchema = exports_external.object({
|
|
14092
14119
|
agentId: exports_external.string().min(1),
|
|
14093
14120
|
workspaceId: exports_external.string().min(1),
|
|
@@ -14099,7 +14126,8 @@ var EmailNotifyRequestSchema = exports_external.object({
|
|
|
14099
14126
|
forwarded: exports_external.boolean().optional().default(false),
|
|
14100
14127
|
messageId: exports_external.string().optional().default(""),
|
|
14101
14128
|
inReplyTo: exports_external.string().optional().default(""),
|
|
14102
|
-
references: exports_external.string().optional().default("")
|
|
14129
|
+
references: exports_external.string().optional().default(""),
|
|
14130
|
+
meetingInfo: MeetingInfoSchema.nullable().optional()
|
|
14103
14131
|
});
|
|
14104
14132
|
var CreateEmailAccountSchema = exports_external.object({
|
|
14105
14133
|
emailAddress: exports_external.string().email("valid email required"),
|
|
@@ -14160,6 +14188,25 @@ var DeleteWorkspaceRequestSchema = exports_external.object({
|
|
|
14160
14188
|
var GrantAgentAccessRequestSchema = exports_external.object({
|
|
14161
14189
|
user_id: exports_external.string().min(1, "user_id is required")
|
|
14162
14190
|
});
|
|
14191
|
+
var WorkspaceFileBrowseRequestSchema = exports_external.object({
|
|
14192
|
+
request_type: exports_external.enum(["tree", "read"]),
|
|
14193
|
+
path: exports_external.string().default(".")
|
|
14194
|
+
});
|
|
14195
|
+
var WorkspaceFileEntrySchema = exports_external.object({
|
|
14196
|
+
name: exports_external.string(),
|
|
14197
|
+
path: exports_external.string(),
|
|
14198
|
+
isDirectory: exports_external.boolean(),
|
|
14199
|
+
size: exports_external.number(),
|
|
14200
|
+
modifiedAt: exports_external.string()
|
|
14201
|
+
});
|
|
14202
|
+
var WorkspaceFileReportSchema = exports_external.object({
|
|
14203
|
+
request_id: exports_external.string().min(1),
|
|
14204
|
+
entries: exports_external.array(WorkspaceFileEntrySchema).optional(),
|
|
14205
|
+
content: exports_external.string().nullable().optional(),
|
|
14206
|
+
isBinary: exports_external.boolean().optional(),
|
|
14207
|
+
error: exports_external.string().optional(),
|
|
14208
|
+
path: exports_external.string()
|
|
14209
|
+
});
|
|
14163
14210
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@4.20260426.1_@opentelemetry+api@1.9.1_bun-types@1.3.13_kysely@0.28.16/node_modules/drizzle-orm/entity.js
|
|
14164
14211
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
14165
14212
|
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
@@ -15690,6 +15737,15 @@ var agentWhitelist = sqliteTable("agent_whitelist", {
|
|
|
15690
15737
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
15691
15738
|
}).onDelete("cascade")
|
|
15692
15739
|
]);
|
|
15740
|
+
var channel = sqliteTable("channel", {
|
|
15741
|
+
id: text("id").primaryKey().$defaultFn(() => "ch_" + nanoid3()),
|
|
15742
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15743
|
+
name: text("name").notNull(),
|
|
15744
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15745
|
+
}, (t) => [
|
|
15746
|
+
unique("channel_workspace_name").on(t.workspaceId, t.name),
|
|
15747
|
+
index("idx_channel_workspace").on(t.workspaceId)
|
|
15748
|
+
]);
|
|
15693
15749
|
var conversation = sqliteTable("conversation", {
|
|
15694
15750
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
15695
15751
|
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
@@ -15697,6 +15753,7 @@ var conversation = sqliteTable("conversation", {
|
|
|
15697
15753
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
15698
15754
|
title: text("title").notNull().default(""),
|
|
15699
15755
|
type: text("type").notNull().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
15756
|
+
channel: text("channel").notNull().default("default"),
|
|
15700
15757
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15701
15758
|
}, (t) => [
|
|
15702
15759
|
foreignKey({
|
|
@@ -15850,6 +15907,33 @@ var agentEmailAccount = sqliteTable("agent_email_account", {
|
|
|
15850
15907
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
15851
15908
|
}).onDelete("cascade")
|
|
15852
15909
|
]);
|
|
15910
|
+
var meetingSession = sqliteTable("meeting_session", {
|
|
15911
|
+
id: text("id").primaryKey().$defaultFn(() => "ms_" + nanoid3()),
|
|
15912
|
+
agentId: text("agent_id").notNull(),
|
|
15913
|
+
workspaceId: text("workspace_id").notNull(),
|
|
15914
|
+
title: text("title").notNull().default(""),
|
|
15915
|
+
meetingUrl: text("meeting_url").notNull(),
|
|
15916
|
+
status: text("status").notNull().default("scheduled"),
|
|
15917
|
+
fromEmail: text("from_email"),
|
|
15918
|
+
isWhitelisted: integer2("is_whitelisted", { mode: "boolean" }).notNull().default(true),
|
|
15919
|
+
participants: text("participants", { mode: "json" }).$type().notNull().default([]),
|
|
15920
|
+
scheduledAt: text("scheduled_at"),
|
|
15921
|
+
startedAt: text("started_at"),
|
|
15922
|
+
completedAt: text("completed_at"),
|
|
15923
|
+
transcriptR2Key: text("transcript_r2_key"),
|
|
15924
|
+
summary: text("summary"),
|
|
15925
|
+
error: text("error"),
|
|
15926
|
+
workerSessionId: text("worker_session_id"),
|
|
15927
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
15928
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15929
|
+
}, (t) => [
|
|
15930
|
+
index("idx_meeting_session_agent_ws").on(t.agentId, t.workspaceId),
|
|
15931
|
+
index("idx_meeting_session_status").on(t.status),
|
|
15932
|
+
foreignKey({
|
|
15933
|
+
columns: [t.agentId, t.workspaceId],
|
|
15934
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
15935
|
+
}).onDelete("cascade")
|
|
15936
|
+
]);
|
|
15853
15937
|
var machineToken = sqliteTable("machine_token", {
|
|
15854
15938
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
15855
15939
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
@@ -15860,6 +15944,17 @@ var machineToken = sqliteTable("machine_token", {
|
|
|
15860
15944
|
lastUsedAt: text("last_used_at"),
|
|
15861
15945
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15862
15946
|
}, (t) => [index("idx_machine_token").on(t.token)]);
|
|
15947
|
+
var workspaceFileRequest = sqliteTable("workspace_file_request", {
|
|
15948
|
+
id: text("id").primaryKey().$defaultFn(() => "wfr_" + nanoid3()),
|
|
15949
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15950
|
+
agentId: text("agent_id").notNull(),
|
|
15951
|
+
requestType: text("request_type").notNull(),
|
|
15952
|
+
path: text("path").notNull().default("."),
|
|
15953
|
+
status: text("status").notNull().default("pending"),
|
|
15954
|
+
result: text("result"),
|
|
15955
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
15956
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15957
|
+
}, (t) => [index("idx_wfr_workspace_status").on(t.workspaceId, t.status)]);
|
|
15863
15958
|
// ../shared/src/db/queries/task.ts
|
|
15864
15959
|
var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
|
|
15865
15960
|
var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
|
|
@@ -15951,7 +16046,8 @@ class DaemonClient {
|
|
|
15951
16046
|
tasks: resp.tasks,
|
|
15952
16047
|
evicted: resp.evicted ?? false,
|
|
15953
16048
|
pending_update: resp.pending_update,
|
|
15954
|
-
pending_rescan: resp.pending_rescan
|
|
16049
|
+
pending_rescan: resp.pending_rescan,
|
|
16050
|
+
file_requests: resp.file_requests
|
|
15955
16051
|
};
|
|
15956
16052
|
}
|
|
15957
16053
|
startTask(token, taskId) {
|
|
@@ -15998,6 +16094,18 @@ class DaemonClient {
|
|
|
15998
16094
|
reportMessages(token, taskId, messages) {
|
|
15999
16095
|
return this.request("POST", `/api/daemon/tasks/${taskId}/messages`, token, { messages });
|
|
16000
16096
|
}
|
|
16097
|
+
reportFileData(token, body) {
|
|
16098
|
+
return this.request("POST", "/api/daemon/workspace/report", token, body);
|
|
16099
|
+
}
|
|
16100
|
+
async claimMeetings(token, daemonId) {
|
|
16101
|
+
const raw = await this.request("POST", "/api/daemon/meetings/claim", token, { daemon_id: daemonId });
|
|
16102
|
+
return raw.map((m) => ({
|
|
16103
|
+
id: m.id,
|
|
16104
|
+
meetingUrl: m.meeting_url,
|
|
16105
|
+
participants: m.participants,
|
|
16106
|
+
workspaceId: m.workspace_id
|
|
16107
|
+
}));
|
|
16108
|
+
}
|
|
16001
16109
|
}
|
|
16002
16110
|
|
|
16003
16111
|
// daemon/config.ts
|
|
@@ -16562,14 +16670,108 @@ function releaseSteeringLock(baseDir, contextKey) {
|
|
|
16562
16670
|
releaseLock(lockPath);
|
|
16563
16671
|
}
|
|
16564
16672
|
|
|
16673
|
+
// daemon/workspace-files.ts
|
|
16674
|
+
import { readdir, stat, readFile } from "fs/promises";
|
|
16675
|
+
import { join as join6, resolve, extname, relative } from "path";
|
|
16676
|
+
var SKIP_DIRS = new Set([".git", "node_modules", ".next", ".wrangler", "__pycache__", ".venv"]);
|
|
16677
|
+
var TEXT_EXTENSIONS = new Set([
|
|
16678
|
+
".md",
|
|
16679
|
+
".txt",
|
|
16680
|
+
".json",
|
|
16681
|
+
".js",
|
|
16682
|
+
".ts",
|
|
16683
|
+
".tsx",
|
|
16684
|
+
".jsx",
|
|
16685
|
+
".py",
|
|
16686
|
+
".rb",
|
|
16687
|
+
".go",
|
|
16688
|
+
".rs",
|
|
16689
|
+
".toml",
|
|
16690
|
+
".yaml",
|
|
16691
|
+
".yml",
|
|
16692
|
+
".html",
|
|
16693
|
+
".css",
|
|
16694
|
+
".scss",
|
|
16695
|
+
".sh",
|
|
16696
|
+
".bash",
|
|
16697
|
+
".zsh",
|
|
16698
|
+
".env",
|
|
16699
|
+
".cfg",
|
|
16700
|
+
".ini",
|
|
16701
|
+
".xml",
|
|
16702
|
+
".svg",
|
|
16703
|
+
".sql",
|
|
16704
|
+
".jsonl",
|
|
16705
|
+
".log",
|
|
16706
|
+
".csv"
|
|
16707
|
+
]);
|
|
16708
|
+
var MAX_FILE_SIZE = 1048576;
|
|
16709
|
+
async function readDirectoryTree(dirPath, basePath) {
|
|
16710
|
+
let entries;
|
|
16711
|
+
try {
|
|
16712
|
+
entries = await readdir(dirPath, { withFileTypes: true });
|
|
16713
|
+
} catch {
|
|
16714
|
+
return [];
|
|
16715
|
+
}
|
|
16716
|
+
entries.sort((a, b) => {
|
|
16717
|
+
if (a.isDirectory() && !b.isDirectory())
|
|
16718
|
+
return -1;
|
|
16719
|
+
if (!a.isDirectory() && b.isDirectory())
|
|
16720
|
+
return 1;
|
|
16721
|
+
return a.name.localeCompare(b.name);
|
|
16722
|
+
});
|
|
16723
|
+
const results = [];
|
|
16724
|
+
for (const entry of entries) {
|
|
16725
|
+
if (entry.name.startsWith(".") && entry.name !== ".context_timeline")
|
|
16726
|
+
continue;
|
|
16727
|
+
if (SKIP_DIRS.has(entry.name))
|
|
16728
|
+
continue;
|
|
16729
|
+
const fullPath = join6(dirPath, entry.name);
|
|
16730
|
+
let info;
|
|
16731
|
+
try {
|
|
16732
|
+
info = await stat(fullPath);
|
|
16733
|
+
} catch {
|
|
16734
|
+
continue;
|
|
16735
|
+
}
|
|
16736
|
+
results.push({
|
|
16737
|
+
name: entry.name,
|
|
16738
|
+
path: relative(basePath, fullPath),
|
|
16739
|
+
isDirectory: entry.isDirectory(),
|
|
16740
|
+
size: entry.isDirectory() ? 0 : info.size,
|
|
16741
|
+
modifiedAt: info.mtime.toISOString()
|
|
16742
|
+
});
|
|
16743
|
+
}
|
|
16744
|
+
return results;
|
|
16745
|
+
}
|
|
16746
|
+
async function readFileContent(filePath) {
|
|
16747
|
+
const info = await stat(filePath);
|
|
16748
|
+
if (info.isDirectory())
|
|
16749
|
+
throw new Error("Cannot read a directory");
|
|
16750
|
+
if (info.size > MAX_FILE_SIZE)
|
|
16751
|
+
throw new Error("File too large (>1MB)");
|
|
16752
|
+
const ext = extname(filePath).toLowerCase();
|
|
16753
|
+
if (ext !== "" && !TEXT_EXTENSIONS.has(ext)) {
|
|
16754
|
+
return { content: null, isBinary: true };
|
|
16755
|
+
}
|
|
16756
|
+
const content = await readFile(filePath, "utf-8");
|
|
16757
|
+
return { content, isBinary: false };
|
|
16758
|
+
}
|
|
16759
|
+
function validatePath(agentWorkdir, requestedPath) {
|
|
16760
|
+
const resolved = resolve(agentWorkdir, requestedPath);
|
|
16761
|
+
if (resolved !== agentWorkdir && !resolved.startsWith(agentWorkdir + "/"))
|
|
16762
|
+
return null;
|
|
16763
|
+
return resolved;
|
|
16764
|
+
}
|
|
16765
|
+
|
|
16565
16766
|
// daemon/daemon.ts
|
|
16566
16767
|
import { existsSync, mkdirSync as mkdirSync5, openSync, closeSync, readdirSync as readdirSync2, statSync as statSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
16567
|
-
import { readdir, readFile, unlink, stat as fsStat } from "fs/promises";
|
|
16768
|
+
import { readdir as readdir2, readFile as readFile2, unlink, stat as fsStat } from "fs/promises";
|
|
16568
16769
|
import { execSync as execSync3, spawn as spawn2 } from "child_process";
|
|
16569
16770
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16570
|
-
import { dirname as dirname3, join as
|
|
16771
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
16571
16772
|
var _dir = dirname3(fileURLToPath2(import.meta.url));
|
|
16572
|
-
var sessionRunnerPath = existsSync(
|
|
16773
|
+
var sessionRunnerPath = existsSync(join7(_dir, "session-runner.js")) ? join7(_dir, "session-runner.js") : join7(_dir, "session-runner.ts");
|
|
16774
|
+
var meetingRunnerPath = existsSync(join7(_dir, "meeting-runner.js")) ? join7(_dir, "meeting-runner.js") : join7(_dir, "meeting-runner.ts");
|
|
16573
16775
|
function isCommandAvailable2(cmd) {
|
|
16574
16776
|
try {
|
|
16575
16777
|
const check2 = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
@@ -16591,7 +16793,7 @@ function pruneSessionRunnerLogs() {
|
|
|
16591
16793
|
if (entries.length <= MAX_SESSION_RUNNER_LOGS)
|
|
16592
16794
|
return;
|
|
16593
16795
|
const withMtime = entries.map((name) => {
|
|
16594
|
-
const full =
|
|
16796
|
+
const full = join7(logDir, name);
|
|
16595
16797
|
try {
|
|
16596
16798
|
return { name, mtime: statSync3(full).mtimeMs };
|
|
16597
16799
|
} catch {
|
|
@@ -16601,7 +16803,7 @@ function pruneSessionRunnerLogs() {
|
|
|
16601
16803
|
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
16602
16804
|
for (const entry of withMtime.slice(MAX_SESSION_RUNNER_LOGS)) {
|
|
16603
16805
|
try {
|
|
16604
|
-
unlinkSync4(
|
|
16806
|
+
unlinkSync4(join7(logDir, entry.name));
|
|
16605
16807
|
} catch {}
|
|
16606
16808
|
}
|
|
16607
16809
|
}
|
|
@@ -16642,10 +16844,10 @@ function isValidMarker(data) {
|
|
|
16642
16844
|
var MARKER_STALE_MS = 24 * 60 * 60 * 1000;
|
|
16643
16845
|
var TMP_STALE_MS = 60 * 60 * 1000;
|
|
16644
16846
|
async function reconcilePendingCompletions(workspacesRoot) {
|
|
16645
|
-
const dir =
|
|
16847
|
+
const dir = join7(workspacesRoot, ".pending_completions");
|
|
16646
16848
|
let entries;
|
|
16647
16849
|
try {
|
|
16648
|
-
entries = await
|
|
16850
|
+
entries = await readdir2(dir);
|
|
16649
16851
|
} catch {
|
|
16650
16852
|
return;
|
|
16651
16853
|
}
|
|
@@ -16653,19 +16855,19 @@ async function reconcilePendingCompletions(workspacesRoot) {
|
|
|
16653
16855
|
if (!name.endsWith(".tmp"))
|
|
16654
16856
|
continue;
|
|
16655
16857
|
try {
|
|
16656
|
-
const s = await fsStat(
|
|
16858
|
+
const s = await fsStat(join7(dir, name));
|
|
16657
16859
|
if (Date.now() - s.mtimeMs > TMP_STALE_MS) {
|
|
16658
|
-
await unlink(
|
|
16860
|
+
await unlink(join7(dir, name));
|
|
16659
16861
|
}
|
|
16660
16862
|
} catch {}
|
|
16661
16863
|
}
|
|
16662
16864
|
const jsonFiles = entries.filter((f) => f.endsWith(".json"));
|
|
16663
16865
|
for (const name of jsonFiles) {
|
|
16664
|
-
const filePath =
|
|
16866
|
+
const filePath = join7(dir, name);
|
|
16665
16867
|
try {
|
|
16666
16868
|
let raw;
|
|
16667
16869
|
try {
|
|
16668
|
-
raw = await
|
|
16870
|
+
raw = await readFile2(filePath, "utf-8");
|
|
16669
16871
|
} catch {
|
|
16670
16872
|
continue;
|
|
16671
16873
|
}
|
|
@@ -16872,7 +17074,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16872
17074
|
await new Promise((r) => setTimeout(r, staggerMs));
|
|
16873
17075
|
}
|
|
16874
17076
|
try {
|
|
16875
|
-
const { tasks: apiTasks, evicted, pending_update, pending_rescan } = await client.poll(ws.token, config2.daemonId, remaining, config2.cliVersion);
|
|
17077
|
+
const { tasks: apiTasks, evicted, pending_update, pending_rescan, file_requests } = await client.poll(ws.token, config2.daemonId, remaining, config2.cliVersion);
|
|
16876
17078
|
if (evicted) {
|
|
16877
17079
|
evictedIds.push(ws.workspaceId);
|
|
16878
17080
|
continue;
|
|
@@ -16894,6 +17096,11 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16894
17096
|
activeTasks.delete(task.id);
|
|
16895
17097
|
});
|
|
16896
17098
|
}
|
|
17099
|
+
if (file_requests) {
|
|
17100
|
+
for (const req of file_requests) {
|
|
17101
|
+
handleFileRequest(client, config2, ws.workspaceId, req, ws.token).catch((e) => log.debug("File request error", e));
|
|
17102
|
+
}
|
|
17103
|
+
}
|
|
16897
17104
|
} catch (e) {
|
|
16898
17105
|
if (e instanceof Error && e.message.startsWith("HTTP 401")) {
|
|
16899
17106
|
log.warn(`Workspace ${ws.workspaceId} poll returned 401 — will retry next cycle`);
|
|
@@ -16905,6 +17112,23 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16905
17112
|
for (const id of evictedIds) {
|
|
16906
17113
|
evictWorkspace(id);
|
|
16907
17114
|
}
|
|
17115
|
+
for (const ws of workspaceStates) {
|
|
17116
|
+
try {
|
|
17117
|
+
const meetings = await client.claimMeetings(ws.token, config2.daemonId);
|
|
17118
|
+
for (const m of meetings) {
|
|
17119
|
+
spawnMeetingRunner({
|
|
17120
|
+
meetingId: m.id,
|
|
17121
|
+
meetingUrl: m.meetingUrl,
|
|
17122
|
+
participants: m.participants,
|
|
17123
|
+
workspaceId: m.workspaceId,
|
|
17124
|
+
callbackUrl: config2.serverURL,
|
|
17125
|
+
authToken: ws.token
|
|
17126
|
+
});
|
|
17127
|
+
}
|
|
17128
|
+
} catch (e) {
|
|
17129
|
+
log.debug("Meeting claim error", e);
|
|
17130
|
+
}
|
|
17131
|
+
}
|
|
16908
17132
|
try {
|
|
16909
17133
|
await reconcilePendingCompletions(config2.workspacesRoot);
|
|
16910
17134
|
} catch (e) {
|
|
@@ -16972,7 +17196,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16972
17196
|
function spawnSessionRunner(input) {
|
|
16973
17197
|
const logDir = sessionRunnerLogDir();
|
|
16974
17198
|
mkdirSync5(logDir, { recursive: true });
|
|
16975
|
-
const logFilePath =
|
|
17199
|
+
const logFilePath = join7(logDir, `${input.task.id}.log`);
|
|
16976
17200
|
input.logFilePath = logFilePath;
|
|
16977
17201
|
const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
|
|
16978
17202
|
let fd;
|
|
@@ -16990,6 +17214,50 @@ function spawnSessionRunner(input) {
|
|
|
16990
17214
|
closeSync(fd);
|
|
16991
17215
|
return child;
|
|
16992
17216
|
}
|
|
17217
|
+
function spawnMeetingRunner(input) {
|
|
17218
|
+
const logDir = sessionRunnerLogDir();
|
|
17219
|
+
mkdirSync5(logDir, { recursive: true });
|
|
17220
|
+
const logFilePath = join7(logDir, `meeting-${input.meetingId}.log`);
|
|
17221
|
+
const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
|
|
17222
|
+
let fd;
|
|
17223
|
+
try {
|
|
17224
|
+
fd = openSync(logFilePath, "a");
|
|
17225
|
+
} catch (e) {
|
|
17226
|
+
log.error(`Failed to open meeting log file ${logFilePath}`, e);
|
|
17227
|
+
}
|
|
17228
|
+
const child = spawn2(process.execPath, [meetingRunnerPath, encoded], {
|
|
17229
|
+
detached: true,
|
|
17230
|
+
stdio: fd != null ? ["ignore", fd, fd] : ["ignore", "ignore", "ignore"]
|
|
17231
|
+
});
|
|
17232
|
+
child.unref();
|
|
17233
|
+
if (fd != null)
|
|
17234
|
+
closeSync(fd);
|
|
17235
|
+
log.info(`Spawned meeting runner for ${input.meetingId} (pid=${child.pid})`);
|
|
17236
|
+
return child;
|
|
17237
|
+
}
|
|
17238
|
+
async function handleFileRequest(client, config2, workspaceId, req, token) {
|
|
17239
|
+
const agentWorkdir = join7(config2.workspacesRoot, workspaceId, req.agent_id, "workdir");
|
|
17240
|
+
const resolved = validatePath(agentWorkdir, req.path);
|
|
17241
|
+
if (!resolved) {
|
|
17242
|
+
await client.reportFileData(token, { request_id: req.id, error: "invalid path", path: req.path });
|
|
17243
|
+
return;
|
|
17244
|
+
}
|
|
17245
|
+
try {
|
|
17246
|
+
if (req.request_type === "tree") {
|
|
17247
|
+
const entries = await readDirectoryTree(resolved, agentWorkdir);
|
|
17248
|
+
await client.reportFileData(token, { request_id: req.id, entries, path: req.path });
|
|
17249
|
+
} else {
|
|
17250
|
+
const { content, isBinary } = await readFileContent(resolved);
|
|
17251
|
+
await client.reportFileData(token, { request_id: req.id, content, isBinary, path: req.path });
|
|
17252
|
+
}
|
|
17253
|
+
} catch (e) {
|
|
17254
|
+
await client.reportFileData(token, {
|
|
17255
|
+
request_id: req.id,
|
|
17256
|
+
error: e instanceof Error ? e.message : String(e),
|
|
17257
|
+
path: req.path
|
|
17258
|
+
});
|
|
17259
|
+
}
|
|
17260
|
+
}
|
|
16993
17261
|
async function handleTask(client, config2, runtimeIndex, task, token, activeTasks) {
|
|
16994
17262
|
log.info(`Task ${task.id} claimed agent=${task.agentId}`);
|
|
16995
17263
|
if (task.type === TASK_TYPES.KILL_TASK) {
|
|
@@ -16999,8 +17267,8 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
|
|
|
16999
17267
|
activeTasks.delete(task.id);
|
|
17000
17268
|
return;
|
|
17001
17269
|
}
|
|
17002
|
-
const agentBaseDir =
|
|
17003
|
-
const timelineDir =
|
|
17270
|
+
const agentBaseDir = join7(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir");
|
|
17271
|
+
const timelineDir = join7(agentBaseDir, ".context_timeline");
|
|
17004
17272
|
const pid = findRunningPidByTaskId(timelineDir, targetTaskId);
|
|
17005
17273
|
if (pid != null) {
|
|
17006
17274
|
writeKillIntent(agentBaseDir, {
|
|
@@ -17042,9 +17310,9 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
|
|
|
17042
17310
|
}
|
|
17043
17311
|
const provider = runtimeData.provider;
|
|
17044
17312
|
if (task.contextKey) {
|
|
17045
|
-
const agentBaseDir =
|
|
17313
|
+
const agentBaseDir = join7(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir");
|
|
17046
17314
|
cleanupStaleIntents(agentBaseDir);
|
|
17047
|
-
const timelineDir =
|
|
17315
|
+
const timelineDir = join7(agentBaseDir, ".context_timeline");
|
|
17048
17316
|
const lockAcquired = acquireSteeringLock(agentBaseDir, task.contextKey);
|
|
17049
17317
|
if (!lockAcquired) {
|
|
17050
17318
|
log.warn(`Steering lock contention for context_key=${task.contextKey}, proceeding without steering`);
|
|
@@ -17269,7 +17537,7 @@ function configCommand() {
|
|
|
17269
17537
|
// commands/email.ts
|
|
17270
17538
|
import { Command as Command5 } from "commander";
|
|
17271
17539
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
|
|
17272
|
-
import { basename, join as
|
|
17540
|
+
import { basename, join as join8 } from "path";
|
|
17273
17541
|
import PostalMime from "postal-mime";
|
|
17274
17542
|
var VALID_STATUSES = ["unread", "read", "archived"];
|
|
17275
17543
|
var EMAIL_BASE = "/tmp/alook-emails";
|
|
@@ -17311,11 +17579,26 @@ function resolveClientOpts(command, opts) {
|
|
|
17311
17579
|
let ws;
|
|
17312
17580
|
if (opts.workspace) {
|
|
17313
17581
|
ws = workspaces.find((w) => w.id === opts.workspace);
|
|
17582
|
+
if (!ws) {
|
|
17583
|
+
console.error(`Error: workspace ${opts.workspace} not found in config.`);
|
|
17584
|
+
process.exit(1);
|
|
17585
|
+
}
|
|
17314
17586
|
} else if (opts.agentId) {
|
|
17315
17587
|
ws = workspaces.find((w) => w.agent_ids?.includes(opts.agentId));
|
|
17316
|
-
|
|
17317
|
-
|
|
17588
|
+
if (!ws) {
|
|
17589
|
+
if (workspaces.length === 1) {
|
|
17590
|
+
ws = workspaces[0];
|
|
17591
|
+
} else {
|
|
17592
|
+
console.error(`Error: agent ${opts.agentId} not found in any registered workspace. Use --workspace to specify.`);
|
|
17593
|
+
process.exit(1);
|
|
17594
|
+
}
|
|
17595
|
+
}
|
|
17596
|
+
} else if (workspaces.length === 1) {
|
|
17318
17597
|
ws = workspaces[0];
|
|
17598
|
+
} else {
|
|
17599
|
+
console.error(`Error: multiple workspaces registered. Use --workspace to specify which one.`);
|
|
17600
|
+
process.exit(1);
|
|
17601
|
+
}
|
|
17319
17602
|
const token = ws?.token;
|
|
17320
17603
|
if (!token) {
|
|
17321
17604
|
console.error(`Error: not registered. Run '${cmdPrefix()} register --token <token>' first.`);
|
|
@@ -17332,7 +17615,7 @@ function emailCommand() {
|
|
|
17332
17615
|
console.error(`Error: invalid status "${opts.status}", must be one of: ${VALID_STATUSES.join(", ")}`);
|
|
17333
17616
|
process.exit(1);
|
|
17334
17617
|
}
|
|
17335
|
-
const emailDir_base =
|
|
17618
|
+
const emailDir_base = join8(EMAIL_BASE, workspaceId, opts.agent_id);
|
|
17336
17619
|
try {
|
|
17337
17620
|
let query = `/api/email?agentId=${opts.agent_id}`;
|
|
17338
17621
|
if (opts.status)
|
|
@@ -17349,7 +17632,7 @@ function emailCommand() {
|
|
|
17349
17632
|
mkdirSync7(emailDir_base, { recursive: true });
|
|
17350
17633
|
const downloadedPaths = [];
|
|
17351
17634
|
for (const email3 of emails2) {
|
|
17352
|
-
const emailDir =
|
|
17635
|
+
const emailDir = join8(emailDir_base, email3.id);
|
|
17353
17636
|
mkdirSync7(emailDir, { recursive: true });
|
|
17354
17637
|
const metadata = {
|
|
17355
17638
|
id: email3.id,
|
|
@@ -17362,7 +17645,7 @@ function emailCommand() {
|
|
|
17362
17645
|
in_reply_to: email3.in_reply_to || "",
|
|
17363
17646
|
references: email3.references || ""
|
|
17364
17647
|
};
|
|
17365
|
-
const metadataPath =
|
|
17648
|
+
const metadataPath = join8(emailDir, "metadata.json");
|
|
17366
17649
|
writeFileSync6(metadataPath, JSON.stringify(metadata, null, 2));
|
|
17367
17650
|
downloadedPaths.push(metadataPath);
|
|
17368
17651
|
let rawMime;
|
|
@@ -17378,17 +17661,17 @@ function emailCommand() {
|
|
|
17378
17661
|
}
|
|
17379
17662
|
const parsed = await new PostalMime().parse(rawMime);
|
|
17380
17663
|
if (parsed.text) {
|
|
17381
|
-
const bodyPath =
|
|
17664
|
+
const bodyPath = join8(emailDir, "body.txt");
|
|
17382
17665
|
writeFileSync6(bodyPath, parsed.text);
|
|
17383
17666
|
downloadedPaths.push(bodyPath);
|
|
17384
17667
|
}
|
|
17385
17668
|
if (parsed.html) {
|
|
17386
|
-
const htmlPath =
|
|
17669
|
+
const htmlPath = join8(emailDir, "body.html");
|
|
17387
17670
|
writeFileSync6(htmlPath, parsed.html);
|
|
17388
17671
|
downloadedPaths.push(htmlPath);
|
|
17389
17672
|
}
|
|
17390
17673
|
if (parsed.attachments && parsed.attachments.length > 0) {
|
|
17391
|
-
const attDir =
|
|
17674
|
+
const attDir = join8(emailDir, "attachments");
|
|
17392
17675
|
mkdirSync7(attDir, { recursive: true });
|
|
17393
17676
|
const usedFilenames = new Set;
|
|
17394
17677
|
for (let i = 0;i < parsed.attachments.length; i++) {
|
|
@@ -17398,7 +17681,7 @@ function emailCommand() {
|
|
|
17398
17681
|
filename = `${i}-${filename}`;
|
|
17399
17682
|
}
|
|
17400
17683
|
usedFilenames.add(filename);
|
|
17401
|
-
const attPath =
|
|
17684
|
+
const attPath = join8(attDir, filename);
|
|
17402
17685
|
const content = att.content;
|
|
17403
17686
|
let buf;
|
|
17404
17687
|
if (typeof content === "string") {
|
|
@@ -17884,8 +18167,8 @@ function syncCommand() {
|
|
|
17884
18167
|
let bytes;
|
|
17885
18168
|
let size;
|
|
17886
18169
|
try {
|
|
17887
|
-
const
|
|
17888
|
-
size =
|
|
18170
|
+
const stat2 = statSync5(opts.file);
|
|
18171
|
+
size = stat2.size;
|
|
17889
18172
|
bytes = readFileSync8(opts.file);
|
|
17890
18173
|
} catch (err) {
|
|
17891
18174
|
console.error(`Error: cannot read file "${opts.file}": ${err.message}`);
|