@alook/cli 0.0.23 → 0.0.25
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 -38
- package/dist/meeting-runner.js +562 -0
- package/dist/session-runner.js +112 -6
- 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() }))
|
|
@@ -13944,7 +13963,6 @@ var DaemonRuntimeItemSchema = exports_external.object({
|
|
|
13944
13963
|
type: exports_external.string().optional(),
|
|
13945
13964
|
provider: exports_external.string().optional(),
|
|
13946
13965
|
runtime_mode: exports_external.string().optional(),
|
|
13947
|
-
name: exports_external.string().optional(),
|
|
13948
13966
|
version: exports_external.string().optional(),
|
|
13949
13967
|
status: exports_external.string().optional(),
|
|
13950
13968
|
model: exports_external.string().optional()
|
|
@@ -14060,7 +14078,8 @@ var UpdateAgentRequestSchema = exports_external.object({
|
|
|
14060
14078
|
visibility: exports_external.enum(["public", "private"]).optional()
|
|
14061
14079
|
}).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
14080
|
var CreateConversationRequestSchema = exports_external.object({
|
|
14063
|
-
agent_id: exports_external.string().min(1, "agent_id is required")
|
|
14081
|
+
agent_id: exports_external.string().min(1, "agent_id is required"),
|
|
14082
|
+
channel: exports_external.string().optional()
|
|
14064
14083
|
});
|
|
14065
14084
|
var CreateMessageRequestSchema = exports_external.object({
|
|
14066
14085
|
content: exports_external.string().min(1, "content is required")
|
|
@@ -14088,6 +14107,13 @@ var SendEmailRequestSchema = exports_external.object({
|
|
|
14088
14107
|
var UpdateEmailStatusRequestSchema = exports_external.object({
|
|
14089
14108
|
status: exports_external.enum(["unread", "read", "archived"])
|
|
14090
14109
|
});
|
|
14110
|
+
var MeetingInfoSchema = exports_external.object({
|
|
14111
|
+
title: exports_external.string(),
|
|
14112
|
+
meetingUrl: exports_external.string(),
|
|
14113
|
+
startTime: exports_external.string().nullable(),
|
|
14114
|
+
endTime: exports_external.string().nullable(),
|
|
14115
|
+
attendees: exports_external.array(exports_external.object({ name: exports_external.string(), email: exports_external.string() }))
|
|
14116
|
+
});
|
|
14091
14117
|
var EmailNotifyRequestSchema = exports_external.object({
|
|
14092
14118
|
agentId: exports_external.string().min(1),
|
|
14093
14119
|
workspaceId: exports_external.string().min(1),
|
|
@@ -14099,7 +14125,8 @@ var EmailNotifyRequestSchema = exports_external.object({
|
|
|
14099
14125
|
forwarded: exports_external.boolean().optional().default(false),
|
|
14100
14126
|
messageId: exports_external.string().optional().default(""),
|
|
14101
14127
|
inReplyTo: exports_external.string().optional().default(""),
|
|
14102
|
-
references: exports_external.string().optional().default("")
|
|
14128
|
+
references: exports_external.string().optional().default(""),
|
|
14129
|
+
meetingInfo: MeetingInfoSchema.nullable().optional()
|
|
14103
14130
|
});
|
|
14104
14131
|
var CreateEmailAccountSchema = exports_external.object({
|
|
14105
14132
|
emailAddress: exports_external.string().email("valid email required"),
|
|
@@ -14160,6 +14187,25 @@ var DeleteWorkspaceRequestSchema = exports_external.object({
|
|
|
14160
14187
|
var GrantAgentAccessRequestSchema = exports_external.object({
|
|
14161
14188
|
user_id: exports_external.string().min(1, "user_id is required")
|
|
14162
14189
|
});
|
|
14190
|
+
var WorkspaceFileBrowseRequestSchema = exports_external.object({
|
|
14191
|
+
request_type: exports_external.enum(["tree", "read"]),
|
|
14192
|
+
path: exports_external.string().default(".")
|
|
14193
|
+
});
|
|
14194
|
+
var WorkspaceFileEntrySchema = exports_external.object({
|
|
14195
|
+
name: exports_external.string(),
|
|
14196
|
+
path: exports_external.string(),
|
|
14197
|
+
isDirectory: exports_external.boolean(),
|
|
14198
|
+
size: exports_external.number(),
|
|
14199
|
+
modifiedAt: exports_external.string()
|
|
14200
|
+
});
|
|
14201
|
+
var WorkspaceFileReportSchema = exports_external.object({
|
|
14202
|
+
request_id: exports_external.string().min(1),
|
|
14203
|
+
entries: exports_external.array(WorkspaceFileEntrySchema).optional(),
|
|
14204
|
+
content: exports_external.string().nullable().optional(),
|
|
14205
|
+
isBinary: exports_external.boolean().optional(),
|
|
14206
|
+
error: exports_external.string().optional(),
|
|
14207
|
+
path: exports_external.string()
|
|
14208
|
+
});
|
|
14163
14209
|
// ../../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
14210
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
14165
14211
|
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
@@ -15647,7 +15693,6 @@ var agentRuntime = sqliteTable("agent_runtime", {
|
|
|
15647
15693
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
15648
15694
|
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15649
15695
|
daemonId: text("daemon_id").notNull(),
|
|
15650
|
-
name: text("name").notNull().default(""),
|
|
15651
15696
|
runtimeMode: text("runtime_mode").notNull().default("local"),
|
|
15652
15697
|
provider: text("provider").notNull(),
|
|
15653
15698
|
deviceInfo: text("device_info").notNull().default(""),
|
|
@@ -15690,6 +15735,15 @@ var agentWhitelist = sqliteTable("agent_whitelist", {
|
|
|
15690
15735
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
15691
15736
|
}).onDelete("cascade")
|
|
15692
15737
|
]);
|
|
15738
|
+
var channel = sqliteTable("channel", {
|
|
15739
|
+
id: text("id").primaryKey().$defaultFn(() => "ch_" + nanoid3()),
|
|
15740
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15741
|
+
name: text("name").notNull(),
|
|
15742
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15743
|
+
}, (t) => [
|
|
15744
|
+
unique("channel_workspace_name").on(t.workspaceId, t.name),
|
|
15745
|
+
index("idx_channel_workspace").on(t.workspaceId)
|
|
15746
|
+
]);
|
|
15693
15747
|
var conversation = sqliteTable("conversation", {
|
|
15694
15748
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
15695
15749
|
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
@@ -15697,6 +15751,7 @@ var conversation = sqliteTable("conversation", {
|
|
|
15697
15751
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
15698
15752
|
title: text("title").notNull().default(""),
|
|
15699
15753
|
type: text("type").notNull().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
15754
|
+
channel: text("channel").notNull().default("default"),
|
|
15700
15755
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15701
15756
|
}, (t) => [
|
|
15702
15757
|
foreignKey({
|
|
@@ -15850,6 +15905,33 @@ var agentEmailAccount = sqliteTable("agent_email_account", {
|
|
|
15850
15905
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
15851
15906
|
}).onDelete("cascade")
|
|
15852
15907
|
]);
|
|
15908
|
+
var meetingSession = sqliteTable("meeting_session", {
|
|
15909
|
+
id: text("id").primaryKey().$defaultFn(() => "ms_" + nanoid3()),
|
|
15910
|
+
agentId: text("agent_id").notNull(),
|
|
15911
|
+
workspaceId: text("workspace_id").notNull(),
|
|
15912
|
+
title: text("title").notNull().default(""),
|
|
15913
|
+
meetingUrl: text("meeting_url").notNull(),
|
|
15914
|
+
status: text("status").notNull().default("scheduled"),
|
|
15915
|
+
fromEmail: text("from_email"),
|
|
15916
|
+
isWhitelisted: integer2("is_whitelisted", { mode: "boolean" }).notNull().default(true),
|
|
15917
|
+
participants: text("participants", { mode: "json" }).$type().notNull().default([]),
|
|
15918
|
+
scheduledAt: text("scheduled_at"),
|
|
15919
|
+
startedAt: text("started_at"),
|
|
15920
|
+
completedAt: text("completed_at"),
|
|
15921
|
+
transcriptR2Key: text("transcript_r2_key"),
|
|
15922
|
+
summary: text("summary"),
|
|
15923
|
+
error: text("error"),
|
|
15924
|
+
workerSessionId: text("worker_session_id"),
|
|
15925
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
15926
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15927
|
+
}, (t) => [
|
|
15928
|
+
index("idx_meeting_session_agent_ws").on(t.agentId, t.workspaceId),
|
|
15929
|
+
index("idx_meeting_session_status").on(t.status),
|
|
15930
|
+
foreignKey({
|
|
15931
|
+
columns: [t.agentId, t.workspaceId],
|
|
15932
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
15933
|
+
}).onDelete("cascade")
|
|
15934
|
+
]);
|
|
15853
15935
|
var machineToken = sqliteTable("machine_token", {
|
|
15854
15936
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
15855
15937
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
@@ -15860,6 +15942,17 @@ var machineToken = sqliteTable("machine_token", {
|
|
|
15860
15942
|
lastUsedAt: text("last_used_at"),
|
|
15861
15943
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15862
15944
|
}, (t) => [index("idx_machine_token").on(t.token)]);
|
|
15945
|
+
var workspaceFileRequest = sqliteTable("workspace_file_request", {
|
|
15946
|
+
id: text("id").primaryKey().$defaultFn(() => "wfr_" + nanoid3()),
|
|
15947
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
15948
|
+
agentId: text("agent_id").notNull(),
|
|
15949
|
+
requestType: text("request_type").notNull(),
|
|
15950
|
+
path: text("path").notNull().default("."),
|
|
15951
|
+
status: text("status").notNull().default("pending"),
|
|
15952
|
+
result: text("result"),
|
|
15953
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
15954
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15955
|
+
}, (t) => [index("idx_wfr_workspace_status").on(t.workspaceId, t.status)]);
|
|
15863
15956
|
// ../shared/src/db/queries/task.ts
|
|
15864
15957
|
var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
|
|
15865
15958
|
var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
|
|
@@ -15951,7 +16044,8 @@ class DaemonClient {
|
|
|
15951
16044
|
tasks: resp.tasks,
|
|
15952
16045
|
evicted: resp.evicted ?? false,
|
|
15953
16046
|
pending_update: resp.pending_update,
|
|
15954
|
-
pending_rescan: resp.pending_rescan
|
|
16047
|
+
pending_rescan: resp.pending_rescan,
|
|
16048
|
+
file_requests: resp.file_requests
|
|
15955
16049
|
};
|
|
15956
16050
|
}
|
|
15957
16051
|
startTask(token, taskId) {
|
|
@@ -15998,6 +16092,18 @@ class DaemonClient {
|
|
|
15998
16092
|
reportMessages(token, taskId, messages) {
|
|
15999
16093
|
return this.request("POST", `/api/daemon/tasks/${taskId}/messages`, token, { messages });
|
|
16000
16094
|
}
|
|
16095
|
+
reportFileData(token, body) {
|
|
16096
|
+
return this.request("POST", "/api/daemon/workspace/report", token, body);
|
|
16097
|
+
}
|
|
16098
|
+
async claimMeetings(token, daemonId) {
|
|
16099
|
+
const raw = await this.request("POST", "/api/daemon/meetings/claim", token, { daemon_id: daemonId });
|
|
16100
|
+
return raw.map((m) => ({
|
|
16101
|
+
id: m.id,
|
|
16102
|
+
meetingUrl: m.meeting_url,
|
|
16103
|
+
participants: m.participants,
|
|
16104
|
+
workspaceId: m.workspace_id
|
|
16105
|
+
}));
|
|
16106
|
+
}
|
|
16001
16107
|
}
|
|
16002
16108
|
|
|
16003
16109
|
// daemon/config.ts
|
|
@@ -16099,7 +16205,6 @@ function loadDaemonConfig(profile) {
|
|
|
16099
16205
|
maxConcurrentTasks: parseInt(process.env.ALOOK_DAEMON_MAX_CONCURRENT_TASKS || "20"),
|
|
16100
16206
|
daemonId,
|
|
16101
16207
|
deviceName: process.env.ALOOK_DAEMON_DEVICE_NAME || h,
|
|
16102
|
-
runtimeName: process.env.ALOOK_AGENT_RUNTIME_NAME || "Local Agent",
|
|
16103
16208
|
workspacesRoot,
|
|
16104
16209
|
cliVersion: getCurrentVersion()
|
|
16105
16210
|
};
|
|
@@ -16562,14 +16667,108 @@ function releaseSteeringLock(baseDir, contextKey) {
|
|
|
16562
16667
|
releaseLock(lockPath);
|
|
16563
16668
|
}
|
|
16564
16669
|
|
|
16670
|
+
// daemon/workspace-files.ts
|
|
16671
|
+
import { readdir, stat, readFile } from "fs/promises";
|
|
16672
|
+
import { join as join6, resolve, extname, relative } from "path";
|
|
16673
|
+
var SKIP_DIRS = new Set([".git", "node_modules", ".next", ".wrangler", "__pycache__", ".venv"]);
|
|
16674
|
+
var TEXT_EXTENSIONS = new Set([
|
|
16675
|
+
".md",
|
|
16676
|
+
".txt",
|
|
16677
|
+
".json",
|
|
16678
|
+
".js",
|
|
16679
|
+
".ts",
|
|
16680
|
+
".tsx",
|
|
16681
|
+
".jsx",
|
|
16682
|
+
".py",
|
|
16683
|
+
".rb",
|
|
16684
|
+
".go",
|
|
16685
|
+
".rs",
|
|
16686
|
+
".toml",
|
|
16687
|
+
".yaml",
|
|
16688
|
+
".yml",
|
|
16689
|
+
".html",
|
|
16690
|
+
".css",
|
|
16691
|
+
".scss",
|
|
16692
|
+
".sh",
|
|
16693
|
+
".bash",
|
|
16694
|
+
".zsh",
|
|
16695
|
+
".env",
|
|
16696
|
+
".cfg",
|
|
16697
|
+
".ini",
|
|
16698
|
+
".xml",
|
|
16699
|
+
".svg",
|
|
16700
|
+
".sql",
|
|
16701
|
+
".jsonl",
|
|
16702
|
+
".log",
|
|
16703
|
+
".csv"
|
|
16704
|
+
]);
|
|
16705
|
+
var MAX_FILE_SIZE = 1048576;
|
|
16706
|
+
async function readDirectoryTree(dirPath, basePath) {
|
|
16707
|
+
let entries;
|
|
16708
|
+
try {
|
|
16709
|
+
entries = await readdir(dirPath, { withFileTypes: true });
|
|
16710
|
+
} catch {
|
|
16711
|
+
return [];
|
|
16712
|
+
}
|
|
16713
|
+
entries.sort((a, b) => {
|
|
16714
|
+
if (a.isDirectory() && !b.isDirectory())
|
|
16715
|
+
return -1;
|
|
16716
|
+
if (!a.isDirectory() && b.isDirectory())
|
|
16717
|
+
return 1;
|
|
16718
|
+
return a.name.localeCompare(b.name);
|
|
16719
|
+
});
|
|
16720
|
+
const results = [];
|
|
16721
|
+
for (const entry of entries) {
|
|
16722
|
+
if (entry.name.startsWith(".") && entry.name !== ".context_timeline")
|
|
16723
|
+
continue;
|
|
16724
|
+
if (SKIP_DIRS.has(entry.name))
|
|
16725
|
+
continue;
|
|
16726
|
+
const fullPath = join6(dirPath, entry.name);
|
|
16727
|
+
let info;
|
|
16728
|
+
try {
|
|
16729
|
+
info = await stat(fullPath);
|
|
16730
|
+
} catch {
|
|
16731
|
+
continue;
|
|
16732
|
+
}
|
|
16733
|
+
results.push({
|
|
16734
|
+
name: entry.name,
|
|
16735
|
+
path: relative(basePath, fullPath),
|
|
16736
|
+
isDirectory: entry.isDirectory(),
|
|
16737
|
+
size: entry.isDirectory() ? 0 : info.size,
|
|
16738
|
+
modifiedAt: info.mtime.toISOString()
|
|
16739
|
+
});
|
|
16740
|
+
}
|
|
16741
|
+
return results;
|
|
16742
|
+
}
|
|
16743
|
+
async function readFileContent(filePath) {
|
|
16744
|
+
const info = await stat(filePath);
|
|
16745
|
+
if (info.isDirectory())
|
|
16746
|
+
throw new Error("Cannot read a directory");
|
|
16747
|
+
if (info.size > MAX_FILE_SIZE)
|
|
16748
|
+
throw new Error("File too large (>1MB)");
|
|
16749
|
+
const ext = extname(filePath).toLowerCase();
|
|
16750
|
+
if (ext !== "" && !TEXT_EXTENSIONS.has(ext)) {
|
|
16751
|
+
return { content: null, isBinary: true };
|
|
16752
|
+
}
|
|
16753
|
+
const content = await readFile(filePath, "utf-8");
|
|
16754
|
+
return { content, isBinary: false };
|
|
16755
|
+
}
|
|
16756
|
+
function validatePath(agentWorkdir, requestedPath) {
|
|
16757
|
+
const resolved = resolve(agentWorkdir, requestedPath);
|
|
16758
|
+
if (resolved !== agentWorkdir && !resolved.startsWith(agentWorkdir + "/"))
|
|
16759
|
+
return null;
|
|
16760
|
+
return resolved;
|
|
16761
|
+
}
|
|
16762
|
+
|
|
16565
16763
|
// daemon/daemon.ts
|
|
16566
16764
|
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";
|
|
16765
|
+
import { readdir as readdir2, readFile as readFile2, unlink, stat as fsStat } from "fs/promises";
|
|
16568
16766
|
import { execSync as execSync3, spawn as spawn2 } from "child_process";
|
|
16569
16767
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16570
|
-
import { dirname as dirname3, join as
|
|
16768
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
16571
16769
|
var _dir = dirname3(fileURLToPath2(import.meta.url));
|
|
16572
|
-
var sessionRunnerPath = existsSync(
|
|
16770
|
+
var sessionRunnerPath = existsSync(join7(_dir, "session-runner.js")) ? join7(_dir, "session-runner.js") : join7(_dir, "session-runner.ts");
|
|
16771
|
+
var meetingRunnerPath = existsSync(join7(_dir, "meeting-runner.js")) ? join7(_dir, "meeting-runner.js") : join7(_dir, "meeting-runner.ts");
|
|
16573
16772
|
function isCommandAvailable2(cmd) {
|
|
16574
16773
|
try {
|
|
16575
16774
|
const check2 = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
@@ -16591,7 +16790,7 @@ function pruneSessionRunnerLogs() {
|
|
|
16591
16790
|
if (entries.length <= MAX_SESSION_RUNNER_LOGS)
|
|
16592
16791
|
return;
|
|
16593
16792
|
const withMtime = entries.map((name) => {
|
|
16594
|
-
const full =
|
|
16793
|
+
const full = join7(logDir, name);
|
|
16595
16794
|
try {
|
|
16596
16795
|
return { name, mtime: statSync3(full).mtimeMs };
|
|
16597
16796
|
} catch {
|
|
@@ -16601,7 +16800,7 @@ function pruneSessionRunnerLogs() {
|
|
|
16601
16800
|
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
16602
16801
|
for (const entry of withMtime.slice(MAX_SESSION_RUNNER_LOGS)) {
|
|
16603
16802
|
try {
|
|
16604
|
-
unlinkSync4(
|
|
16803
|
+
unlinkSync4(join7(logDir, entry.name));
|
|
16605
16804
|
} catch {}
|
|
16606
16805
|
}
|
|
16607
16806
|
}
|
|
@@ -16642,10 +16841,10 @@ function isValidMarker(data) {
|
|
|
16642
16841
|
var MARKER_STALE_MS = 24 * 60 * 60 * 1000;
|
|
16643
16842
|
var TMP_STALE_MS = 60 * 60 * 1000;
|
|
16644
16843
|
async function reconcilePendingCompletions(workspacesRoot) {
|
|
16645
|
-
const dir =
|
|
16844
|
+
const dir = join7(workspacesRoot, ".pending_completions");
|
|
16646
16845
|
let entries;
|
|
16647
16846
|
try {
|
|
16648
|
-
entries = await
|
|
16847
|
+
entries = await readdir2(dir);
|
|
16649
16848
|
} catch {
|
|
16650
16849
|
return;
|
|
16651
16850
|
}
|
|
@@ -16653,19 +16852,19 @@ async function reconcilePendingCompletions(workspacesRoot) {
|
|
|
16653
16852
|
if (!name.endsWith(".tmp"))
|
|
16654
16853
|
continue;
|
|
16655
16854
|
try {
|
|
16656
|
-
const s = await fsStat(
|
|
16855
|
+
const s = await fsStat(join7(dir, name));
|
|
16657
16856
|
if (Date.now() - s.mtimeMs > TMP_STALE_MS) {
|
|
16658
|
-
await unlink(
|
|
16857
|
+
await unlink(join7(dir, name));
|
|
16659
16858
|
}
|
|
16660
16859
|
} catch {}
|
|
16661
16860
|
}
|
|
16662
16861
|
const jsonFiles = entries.filter((f) => f.endsWith(".json"));
|
|
16663
16862
|
for (const name of jsonFiles) {
|
|
16664
|
-
const filePath =
|
|
16863
|
+
const filePath = join7(dir, name);
|
|
16665
16864
|
try {
|
|
16666
16865
|
let raw;
|
|
16667
16866
|
try {
|
|
16668
|
-
raw = await
|
|
16867
|
+
raw = await readFile2(filePath, "utf-8");
|
|
16669
16868
|
} catch {
|
|
16670
16869
|
continue;
|
|
16671
16870
|
}
|
|
@@ -16780,7 +16979,6 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16780
16979
|
const runtimeIndex = new Map;
|
|
16781
16980
|
for (const ws of workspaces) {
|
|
16782
16981
|
const runtimes = providers.map((p) => ({
|
|
16783
|
-
name: config2.runtimeName || `${p.type} (${config2.deviceName})`,
|
|
16784
16982
|
type: p.type,
|
|
16785
16983
|
version: p.version
|
|
16786
16984
|
}));
|
|
@@ -16872,7 +17070,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16872
17070
|
await new Promise((r) => setTimeout(r, staggerMs));
|
|
16873
17071
|
}
|
|
16874
17072
|
try {
|
|
16875
|
-
const { tasks: apiTasks, evicted, pending_update, pending_rescan } = await client.poll(ws.token, config2.daemonId, remaining, config2.cliVersion);
|
|
17073
|
+
const { tasks: apiTasks, evicted, pending_update, pending_rescan, file_requests } = await client.poll(ws.token, config2.daemonId, remaining, config2.cliVersion);
|
|
16876
17074
|
if (evicted) {
|
|
16877
17075
|
evictedIds.push(ws.workspaceId);
|
|
16878
17076
|
continue;
|
|
@@ -16894,6 +17092,11 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16894
17092
|
activeTasks.delete(task.id);
|
|
16895
17093
|
});
|
|
16896
17094
|
}
|
|
17095
|
+
if (file_requests) {
|
|
17096
|
+
for (const req of file_requests) {
|
|
17097
|
+
handleFileRequest(client, config2, ws.workspaceId, req, ws.token).catch((e) => log.debug("File request error", e));
|
|
17098
|
+
}
|
|
17099
|
+
}
|
|
16897
17100
|
} catch (e) {
|
|
16898
17101
|
if (e instanceof Error && e.message.startsWith("HTTP 401")) {
|
|
16899
17102
|
log.warn(`Workspace ${ws.workspaceId} poll returned 401 — will retry next cycle`);
|
|
@@ -16905,6 +17108,23 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16905
17108
|
for (const id of evictedIds) {
|
|
16906
17109
|
evictWorkspace(id);
|
|
16907
17110
|
}
|
|
17111
|
+
for (const ws of workspaceStates) {
|
|
17112
|
+
try {
|
|
17113
|
+
const meetings = await client.claimMeetings(ws.token, config2.daemonId);
|
|
17114
|
+
for (const m of meetings) {
|
|
17115
|
+
spawnMeetingRunner({
|
|
17116
|
+
meetingId: m.id,
|
|
17117
|
+
meetingUrl: m.meetingUrl,
|
|
17118
|
+
participants: m.participants,
|
|
17119
|
+
workspaceId: m.workspaceId,
|
|
17120
|
+
callbackUrl: config2.serverURL,
|
|
17121
|
+
authToken: ws.token
|
|
17122
|
+
});
|
|
17123
|
+
}
|
|
17124
|
+
} catch (e) {
|
|
17125
|
+
log.debug("Meeting claim error", e);
|
|
17126
|
+
}
|
|
17127
|
+
}
|
|
16908
17128
|
try {
|
|
16909
17129
|
await reconcilePendingCompletions(config2.workspacesRoot);
|
|
16910
17130
|
} catch (e) {
|
|
@@ -16972,7 +17192,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16972
17192
|
function spawnSessionRunner(input) {
|
|
16973
17193
|
const logDir = sessionRunnerLogDir();
|
|
16974
17194
|
mkdirSync5(logDir, { recursive: true });
|
|
16975
|
-
const logFilePath =
|
|
17195
|
+
const logFilePath = join7(logDir, `${input.task.id}.log`);
|
|
16976
17196
|
input.logFilePath = logFilePath;
|
|
16977
17197
|
const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
|
|
16978
17198
|
let fd;
|
|
@@ -16990,6 +17210,50 @@ function spawnSessionRunner(input) {
|
|
|
16990
17210
|
closeSync(fd);
|
|
16991
17211
|
return child;
|
|
16992
17212
|
}
|
|
17213
|
+
function spawnMeetingRunner(input) {
|
|
17214
|
+
const logDir = sessionRunnerLogDir();
|
|
17215
|
+
mkdirSync5(logDir, { recursive: true });
|
|
17216
|
+
const logFilePath = join7(logDir, `meeting-${input.meetingId}.log`);
|
|
17217
|
+
const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
|
|
17218
|
+
let fd;
|
|
17219
|
+
try {
|
|
17220
|
+
fd = openSync(logFilePath, "a");
|
|
17221
|
+
} catch (e) {
|
|
17222
|
+
log.error(`Failed to open meeting log file ${logFilePath}`, e);
|
|
17223
|
+
}
|
|
17224
|
+
const child = spawn2(process.execPath, [meetingRunnerPath, encoded], {
|
|
17225
|
+
detached: true,
|
|
17226
|
+
stdio: fd != null ? ["ignore", fd, fd] : ["ignore", "ignore", "ignore"]
|
|
17227
|
+
});
|
|
17228
|
+
child.unref();
|
|
17229
|
+
if (fd != null)
|
|
17230
|
+
closeSync(fd);
|
|
17231
|
+
log.info(`Spawned meeting runner for ${input.meetingId} (pid=${child.pid})`);
|
|
17232
|
+
return child;
|
|
17233
|
+
}
|
|
17234
|
+
async function handleFileRequest(client, config2, workspaceId, req, token) {
|
|
17235
|
+
const agentWorkdir = join7(config2.workspacesRoot, workspaceId, req.agent_id, "workdir");
|
|
17236
|
+
const resolved = validatePath(agentWorkdir, req.path);
|
|
17237
|
+
if (!resolved) {
|
|
17238
|
+
await client.reportFileData(token, { request_id: req.id, error: "invalid path", path: req.path });
|
|
17239
|
+
return;
|
|
17240
|
+
}
|
|
17241
|
+
try {
|
|
17242
|
+
if (req.request_type === "tree") {
|
|
17243
|
+
const entries = await readDirectoryTree(resolved, agentWorkdir);
|
|
17244
|
+
await client.reportFileData(token, { request_id: req.id, entries, path: req.path });
|
|
17245
|
+
} else {
|
|
17246
|
+
const { content, isBinary } = await readFileContent(resolved);
|
|
17247
|
+
await client.reportFileData(token, { request_id: req.id, content, isBinary, path: req.path });
|
|
17248
|
+
}
|
|
17249
|
+
} catch (e) {
|
|
17250
|
+
await client.reportFileData(token, {
|
|
17251
|
+
request_id: req.id,
|
|
17252
|
+
error: e instanceof Error ? e.message : String(e),
|
|
17253
|
+
path: req.path
|
|
17254
|
+
});
|
|
17255
|
+
}
|
|
17256
|
+
}
|
|
16993
17257
|
async function handleTask(client, config2, runtimeIndex, task, token, activeTasks) {
|
|
16994
17258
|
log.info(`Task ${task.id} claimed agent=${task.agentId}`);
|
|
16995
17259
|
if (task.type === TASK_TYPES.KILL_TASK) {
|
|
@@ -16999,8 +17263,8 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
|
|
|
16999
17263
|
activeTasks.delete(task.id);
|
|
17000
17264
|
return;
|
|
17001
17265
|
}
|
|
17002
|
-
const agentBaseDir =
|
|
17003
|
-
const timelineDir =
|
|
17266
|
+
const agentBaseDir = join7(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir");
|
|
17267
|
+
const timelineDir = join7(agentBaseDir, ".context_timeline");
|
|
17004
17268
|
const pid = findRunningPidByTaskId(timelineDir, targetTaskId);
|
|
17005
17269
|
if (pid != null) {
|
|
17006
17270
|
writeKillIntent(agentBaseDir, {
|
|
@@ -17042,9 +17306,9 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
|
|
|
17042
17306
|
}
|
|
17043
17307
|
const provider = runtimeData.provider;
|
|
17044
17308
|
if (task.contextKey) {
|
|
17045
|
-
const agentBaseDir =
|
|
17309
|
+
const agentBaseDir = join7(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir");
|
|
17046
17310
|
cleanupStaleIntents(agentBaseDir);
|
|
17047
|
-
const timelineDir =
|
|
17311
|
+
const timelineDir = join7(agentBaseDir, ".context_timeline");
|
|
17048
17312
|
const lockAcquired = acquireSteeringLock(agentBaseDir, task.contextKey);
|
|
17049
17313
|
if (!lockAcquired) {
|
|
17050
17314
|
log.warn(`Steering lock contention for context_key=${task.contextKey}, proceeding without steering`);
|
|
@@ -17269,7 +17533,7 @@ function configCommand() {
|
|
|
17269
17533
|
// commands/email.ts
|
|
17270
17534
|
import { Command as Command5 } from "commander";
|
|
17271
17535
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
|
|
17272
|
-
import { basename, join as
|
|
17536
|
+
import { basename, join as join8 } from "path";
|
|
17273
17537
|
import PostalMime from "postal-mime";
|
|
17274
17538
|
var VALID_STATUSES = ["unread", "read", "archived"];
|
|
17275
17539
|
var EMAIL_BASE = "/tmp/alook-emails";
|
|
@@ -17311,11 +17575,26 @@ function resolveClientOpts(command, opts) {
|
|
|
17311
17575
|
let ws;
|
|
17312
17576
|
if (opts.workspace) {
|
|
17313
17577
|
ws = workspaces.find((w) => w.id === opts.workspace);
|
|
17578
|
+
if (!ws) {
|
|
17579
|
+
console.error(`Error: workspace ${opts.workspace} not found in config.`);
|
|
17580
|
+
process.exit(1);
|
|
17581
|
+
}
|
|
17314
17582
|
} else if (opts.agentId) {
|
|
17315
17583
|
ws = workspaces.find((w) => w.agent_ids?.includes(opts.agentId));
|
|
17316
|
-
|
|
17317
|
-
|
|
17584
|
+
if (!ws) {
|
|
17585
|
+
if (workspaces.length === 1) {
|
|
17586
|
+
ws = workspaces[0];
|
|
17587
|
+
} else {
|
|
17588
|
+
console.error(`Error: agent ${opts.agentId} not found in any registered workspace. Use --workspace to specify.`);
|
|
17589
|
+
process.exit(1);
|
|
17590
|
+
}
|
|
17591
|
+
}
|
|
17592
|
+
} else if (workspaces.length === 1) {
|
|
17318
17593
|
ws = workspaces[0];
|
|
17594
|
+
} else {
|
|
17595
|
+
console.error(`Error: multiple workspaces registered. Use --workspace to specify which one.`);
|
|
17596
|
+
process.exit(1);
|
|
17597
|
+
}
|
|
17319
17598
|
const token = ws?.token;
|
|
17320
17599
|
if (!token) {
|
|
17321
17600
|
console.error(`Error: not registered. Run '${cmdPrefix()} register --token <token>' first.`);
|
|
@@ -17332,7 +17611,7 @@ function emailCommand() {
|
|
|
17332
17611
|
console.error(`Error: invalid status "${opts.status}", must be one of: ${VALID_STATUSES.join(", ")}`);
|
|
17333
17612
|
process.exit(1);
|
|
17334
17613
|
}
|
|
17335
|
-
const emailDir_base =
|
|
17614
|
+
const emailDir_base = join8(EMAIL_BASE, workspaceId, opts.agent_id);
|
|
17336
17615
|
try {
|
|
17337
17616
|
let query = `/api/email?agentId=${opts.agent_id}`;
|
|
17338
17617
|
if (opts.status)
|
|
@@ -17349,7 +17628,7 @@ function emailCommand() {
|
|
|
17349
17628
|
mkdirSync7(emailDir_base, { recursive: true });
|
|
17350
17629
|
const downloadedPaths = [];
|
|
17351
17630
|
for (const email3 of emails2) {
|
|
17352
|
-
const emailDir =
|
|
17631
|
+
const emailDir = join8(emailDir_base, email3.id);
|
|
17353
17632
|
mkdirSync7(emailDir, { recursive: true });
|
|
17354
17633
|
const metadata = {
|
|
17355
17634
|
id: email3.id,
|
|
@@ -17362,7 +17641,7 @@ function emailCommand() {
|
|
|
17362
17641
|
in_reply_to: email3.in_reply_to || "",
|
|
17363
17642
|
references: email3.references || ""
|
|
17364
17643
|
};
|
|
17365
|
-
const metadataPath =
|
|
17644
|
+
const metadataPath = join8(emailDir, "metadata.json");
|
|
17366
17645
|
writeFileSync6(metadataPath, JSON.stringify(metadata, null, 2));
|
|
17367
17646
|
downloadedPaths.push(metadataPath);
|
|
17368
17647
|
let rawMime;
|
|
@@ -17378,17 +17657,17 @@ function emailCommand() {
|
|
|
17378
17657
|
}
|
|
17379
17658
|
const parsed = await new PostalMime().parse(rawMime);
|
|
17380
17659
|
if (parsed.text) {
|
|
17381
|
-
const bodyPath =
|
|
17660
|
+
const bodyPath = join8(emailDir, "body.txt");
|
|
17382
17661
|
writeFileSync6(bodyPath, parsed.text);
|
|
17383
17662
|
downloadedPaths.push(bodyPath);
|
|
17384
17663
|
}
|
|
17385
17664
|
if (parsed.html) {
|
|
17386
|
-
const htmlPath =
|
|
17665
|
+
const htmlPath = join8(emailDir, "body.html");
|
|
17387
17666
|
writeFileSync6(htmlPath, parsed.html);
|
|
17388
17667
|
downloadedPaths.push(htmlPath);
|
|
17389
17668
|
}
|
|
17390
17669
|
if (parsed.attachments && parsed.attachments.length > 0) {
|
|
17391
|
-
const attDir =
|
|
17670
|
+
const attDir = join8(emailDir, "attachments");
|
|
17392
17671
|
mkdirSync7(attDir, { recursive: true });
|
|
17393
17672
|
const usedFilenames = new Set;
|
|
17394
17673
|
for (let i = 0;i < parsed.attachments.length; i++) {
|
|
@@ -17398,7 +17677,7 @@ function emailCommand() {
|
|
|
17398
17677
|
filename = `${i}-${filename}`;
|
|
17399
17678
|
}
|
|
17400
17679
|
usedFilenames.add(filename);
|
|
17401
|
-
const attPath =
|
|
17680
|
+
const attPath = join8(attDir, filename);
|
|
17402
17681
|
const content = att.content;
|
|
17403
17682
|
let buf;
|
|
17404
17683
|
if (typeof content === "string") {
|
|
@@ -17884,8 +18163,8 @@ function syncCommand() {
|
|
|
17884
18163
|
let bytes;
|
|
17885
18164
|
let size;
|
|
17886
18165
|
try {
|
|
17887
|
-
const
|
|
17888
|
-
size =
|
|
18166
|
+
const stat2 = statSync5(opts.file);
|
|
18167
|
+
size = stat2.size;
|
|
17889
18168
|
bytes = readFileSync8(opts.file);
|
|
17890
18169
|
} catch (err) {
|
|
17891
18170
|
console.error(`Error: cannot read file "${opts.file}": ${err.message}`);
|