@alook/cli 0.0.3 → 0.0.5
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 +395 -12
- package/dist/session-runner.js +16945 -0
- package/package.json +2 -2
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 Command8 } from "commander";
|
|
19
19
|
|
|
20
20
|
// commands/register.ts
|
|
21
21
|
import { Command } from "commander";
|
|
@@ -64,6 +64,25 @@ class APIClient {
|
|
|
64
64
|
patchJSON(path, body) {
|
|
65
65
|
return this.request("PATCH", path, body);
|
|
66
66
|
}
|
|
67
|
+
async postMultipart(path, form) {
|
|
68
|
+
const headers = {
|
|
69
|
+
Authorization: `Bearer ${this.token}`
|
|
70
|
+
};
|
|
71
|
+
if (this.workspaceId)
|
|
72
|
+
headers["X-Workspace-ID"] = this.workspaceId;
|
|
73
|
+
const res = await fetch(this.baseURL + path, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers,
|
|
76
|
+
body: form
|
|
77
|
+
});
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
const text = await res.text();
|
|
80
|
+
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
81
|
+
}
|
|
82
|
+
if (res.status === 204)
|
|
83
|
+
return;
|
|
84
|
+
return res.json();
|
|
85
|
+
}
|
|
67
86
|
async getText(path) {
|
|
68
87
|
const headers = {
|
|
69
88
|
Authorization: `Bearer ${this.token}`
|
|
@@ -281,6 +300,11 @@ import { openSync, closeSync, mkdirSync as mkdirSync3 } from "fs";
|
|
|
281
300
|
import { dirname as dirname3 } from "path";
|
|
282
301
|
|
|
283
302
|
// ../shared/src/constants.ts
|
|
303
|
+
var TASK_TYPES = {
|
|
304
|
+
USER_DM_MESSAGE: "user_dm_message",
|
|
305
|
+
EMAIL_NOTIFICATION: "email_notification",
|
|
306
|
+
CALENDAR_EVENT: "calendar_event"
|
|
307
|
+
};
|
|
284
308
|
var POLL_INTERVAL_MS = Number(process.env.POLL_INTERVAL_MS) || 3000;
|
|
285
309
|
var OFFLINE_THRESHOLD_MS = Number(process.env.OFFLINE_THRESHOLD_MS) || 9000;
|
|
286
310
|
var EVENT_POLL_INTERVAL_MS = Number(process.env.EVENT_POLL_INTERVAL_MS) || 2000;
|
|
@@ -13839,7 +13863,7 @@ var ClaimedTaskRowSchema = exports_external.object({
|
|
|
13839
13863
|
priority: exports_external.coerce.number(),
|
|
13840
13864
|
result: exports_external.unknown().nullable(),
|
|
13841
13865
|
context: exports_external.unknown().nullable(),
|
|
13842
|
-
type: exports_external.string().default(
|
|
13866
|
+
type: exports_external.string().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
13843
13867
|
sessionId: exports_external.string().nullable(),
|
|
13844
13868
|
createdAt: exports_external.coerce.date(),
|
|
13845
13869
|
dispatchedAt: exports_external.coerce.date().nullable(),
|
|
@@ -13932,6 +13956,50 @@ var MessageItemSchema = exports_external.object({
|
|
|
13932
13956
|
var ReportMessagesRequestSchema = exports_external.object({
|
|
13933
13957
|
messages: exports_external.array(MessageItemSchema)
|
|
13934
13958
|
});
|
|
13959
|
+
var RepeatIntervalSchema = exports_external.string().regex(/^\d+(min|hour|day|week|month)$/, {
|
|
13960
|
+
message: "repeat_interval must match <positive_integer><min|hour|day|week|month>"
|
|
13961
|
+
});
|
|
13962
|
+
var CreateCalendarEventRequestSchema = exports_external.object({
|
|
13963
|
+
agent_id: exports_external.string().min(1),
|
|
13964
|
+
title: exports_external.string().min(1),
|
|
13965
|
+
description: exports_external.string().max(20000).optional(),
|
|
13966
|
+
scheduled_at: exports_external.string().min(1).refine((s) => !Number.isNaN(Date.parse(s)), {
|
|
13967
|
+
message: "scheduled_at must be a valid ISO datetime"
|
|
13968
|
+
}),
|
|
13969
|
+
repeat_interval: RepeatIntervalSchema.optional(),
|
|
13970
|
+
repeat_stop_date: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
13971
|
+
}).refine((data) => !data.repeat_stop_date || !!data.repeat_interval, {
|
|
13972
|
+
message: "repeat_stop_date requires repeat_interval",
|
|
13973
|
+
path: ["repeat_stop_date"]
|
|
13974
|
+
});
|
|
13975
|
+
var UpdateCalendarEventRequestSchema = exports_external.object({
|
|
13976
|
+
title: exports_external.string().min(1).optional(),
|
|
13977
|
+
description: exports_external.string().max(20000).nullable().optional(),
|
|
13978
|
+
agent_id: exports_external.string().min(1).optional(),
|
|
13979
|
+
scheduled_at: exports_external.string().min(1).refine((s) => !Number.isNaN(Date.parse(s)), {
|
|
13980
|
+
message: "scheduled_at must be a valid ISO datetime"
|
|
13981
|
+
}).optional(),
|
|
13982
|
+
repeat_interval: RepeatIntervalSchema.nullable().optional(),
|
|
13983
|
+
repeat_stop_date: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
|
|
13984
|
+
scope: exports_external.enum(["this", "following"]).optional(),
|
|
13985
|
+
occurrence_at: exports_external.string().min(1).refine((s) => !Number.isNaN(Date.parse(s)), {
|
|
13986
|
+
message: "occurrence_at must be a valid ISO datetime"
|
|
13987
|
+
}).optional()
|
|
13988
|
+
}).refine((v) => v.title !== undefined || v.description !== undefined || v.agent_id !== undefined || v.scheduled_at !== undefined || v.repeat_interval !== undefined || v.repeat_stop_date !== undefined, { message: "at least one field is required" });
|
|
13989
|
+
var CalendarEventApiSchema = exports_external.object({
|
|
13990
|
+
id: exports_external.string(),
|
|
13991
|
+
agent_id: exports_external.string(),
|
|
13992
|
+
workspace_id: exports_external.string(),
|
|
13993
|
+
title: exports_external.string(),
|
|
13994
|
+
description: exports_external.string().nullable(),
|
|
13995
|
+
scheduled_at: exports_external.string(),
|
|
13996
|
+
occurrence_at: exports_external.string(),
|
|
13997
|
+
repeat_interval: exports_external.string().nullable(),
|
|
13998
|
+
repeat_stop_at: exports_external.string().nullable(),
|
|
13999
|
+
last_triggered_at: exports_external.string().nullable(),
|
|
14000
|
+
created_at: exports_external.string(),
|
|
14001
|
+
updated_at: exports_external.string()
|
|
14002
|
+
});
|
|
13935
14003
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@4.20260414.1_@opentelemetry+api@1.9.1_bun-types@1.3.12_kysely@0.28.15/node_modules/drizzle-orm/entity.js
|
|
13936
14004
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
13937
14005
|
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
@@ -15424,6 +15492,7 @@ var conversation = sqliteTable("conversation", {
|
|
|
15424
15492
|
agentId: text("agent_id").notNull(),
|
|
15425
15493
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
15426
15494
|
title: text("title").notNull().default(""),
|
|
15495
|
+
type: text("type").notNull().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
15427
15496
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15428
15497
|
}, (t) => [
|
|
15429
15498
|
foreignKey({
|
|
@@ -15446,7 +15515,7 @@ var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
|
15446
15515
|
workspaceId: text("workspace_id").notNull().references(() => workspace.id),
|
|
15447
15516
|
conversationId: text("conversation_id").notNull().references(() => conversation.id),
|
|
15448
15517
|
prompt: text("prompt").notNull(),
|
|
15449
|
-
type: text("type").notNull().default(
|
|
15518
|
+
type: text("type").notNull().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
15450
15519
|
status: text("status").notNull().default("queued"),
|
|
15451
15520
|
priority: integer2("priority").notNull().default(0),
|
|
15452
15521
|
result: text("result", { mode: "json" }),
|
|
@@ -15496,6 +15565,27 @@ var emails = sqliteTable("emails", {
|
|
|
15496
15565
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
15497
15566
|
}).onDelete("cascade")
|
|
15498
15567
|
]);
|
|
15568
|
+
var calendarEvent = sqliteTable("calendar_event", {
|
|
15569
|
+
id: text("id").primaryKey().$defaultFn(() => "ce_" + nanoid3()),
|
|
15570
|
+
agentId: text("agent_id").notNull(),
|
|
15571
|
+
workspaceId: text("workspace_id").notNull(),
|
|
15572
|
+
title: text("title").notNull(),
|
|
15573
|
+
description: text("description"),
|
|
15574
|
+
scheduledAt: text("scheduled_at").notNull(),
|
|
15575
|
+
repeatInterval: text("repeat_interval"),
|
|
15576
|
+
repeatStopAt: text("repeat_stop_at"),
|
|
15577
|
+
lastTriggeredAt: text("last_triggered_at"),
|
|
15578
|
+
exceptions: text("exceptions", { mode: "json" }).$type().notNull().default(sql`'[]'`),
|
|
15579
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
15580
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15581
|
+
}, (t) => [
|
|
15582
|
+
index("idx_calendar_event_agent_ws").on(t.agentId, t.workspaceId),
|
|
15583
|
+
index("idx_calendar_event_ws_scheduled").on(t.workspaceId, t.scheduledAt),
|
|
15584
|
+
foreignKey({
|
|
15585
|
+
columns: [t.agentId, t.workspaceId],
|
|
15586
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
15587
|
+
}).onDelete("cascade")
|
|
15588
|
+
]);
|
|
15499
15589
|
var machineToken = sqliteTable("machine_token", {
|
|
15500
15590
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
15501
15591
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
@@ -15862,9 +15952,12 @@ function releaseDaemonPid(profile) {
|
|
|
15862
15952
|
}
|
|
15863
15953
|
|
|
15864
15954
|
// daemon/daemon.ts
|
|
15955
|
+
import { existsSync } from "fs";
|
|
15865
15956
|
import { execSync as execSync3, spawn } from "child_process";
|
|
15866
15957
|
import { fileURLToPath } from "url";
|
|
15867
15958
|
import { dirname as dirname2, join as join3 } from "path";
|
|
15959
|
+
var _dir = dirname2(fileURLToPath(import.meta.url));
|
|
15960
|
+
var sessionRunnerPath = existsSync(join3(_dir, "session-runner.js")) ? join3(_dir, "session-runner.js") : join3(_dir, "session-runner.ts");
|
|
15868
15961
|
function isCommandAvailable2(cmd) {
|
|
15869
15962
|
try {
|
|
15870
15963
|
execSync3(`which ${cmd}`, { stdio: "ignore" });
|
|
@@ -16036,10 +16129,9 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16036
16129
|
process.on("SIGINT", shutdown);
|
|
16037
16130
|
await pollCycle();
|
|
16038
16131
|
}
|
|
16039
|
-
var SESSION_RUNNER_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "session-runner.ts");
|
|
16040
16132
|
function spawnSessionRunner(input) {
|
|
16041
16133
|
const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
|
|
16042
|
-
const child = spawn(
|
|
16134
|
+
const child = spawn(process.execPath, [sessionRunnerPath, encoded], {
|
|
16043
16135
|
detached: true,
|
|
16044
16136
|
stdio: "ignore"
|
|
16045
16137
|
});
|
|
@@ -16224,11 +16316,37 @@ function configCommand() {
|
|
|
16224
16316
|
|
|
16225
16317
|
// commands/email.ts
|
|
16226
16318
|
import { Command as Command5 } from "commander";
|
|
16227
|
-
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
16228
|
-
import { join as join4 } from "path";
|
|
16319
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3, statSync } from "fs";
|
|
16320
|
+
import { basename, join as join4 } from "path";
|
|
16229
16321
|
import PostalMime from "postal-mime";
|
|
16230
16322
|
var VALID_STATUSES = ["unread", "read", "archived"];
|
|
16231
16323
|
var EMAIL_DIR = "/tmp/alook-emails";
|
|
16324
|
+
var MIME_BY_EXT = {
|
|
16325
|
+
".pdf": "application/pdf",
|
|
16326
|
+
".png": "image/png",
|
|
16327
|
+
".jpg": "image/jpeg",
|
|
16328
|
+
".jpeg": "image/jpeg",
|
|
16329
|
+
".gif": "image/gif",
|
|
16330
|
+
".webp": "image/webp",
|
|
16331
|
+
".svg": "image/svg+xml",
|
|
16332
|
+
".txt": "text/plain",
|
|
16333
|
+
".html": "text/html",
|
|
16334
|
+
".htm": "text/html",
|
|
16335
|
+
".json": "application/json",
|
|
16336
|
+
".csv": "text/csv",
|
|
16337
|
+
".md": "text/markdown",
|
|
16338
|
+
".zip": "application/zip"
|
|
16339
|
+
};
|
|
16340
|
+
function guessContentType(filename) {
|
|
16341
|
+
const idx = filename.lastIndexOf(".");
|
|
16342
|
+
if (idx < 0)
|
|
16343
|
+
return "application/octet-stream";
|
|
16344
|
+
const ext = filename.slice(idx).toLowerCase();
|
|
16345
|
+
return MIME_BY_EXT[ext] ?? "application/octet-stream";
|
|
16346
|
+
}
|
|
16347
|
+
function collectRepeated(value, previous) {
|
|
16348
|
+
return previous.concat([value]);
|
|
16349
|
+
}
|
|
16232
16350
|
function resolveClientOpts(command, opts) {
|
|
16233
16351
|
const parentOpts = command.parent?.parent?.opts() || {};
|
|
16234
16352
|
const profile = parentOpts.profile;
|
|
@@ -16362,14 +16480,278 @@ function emailCommand() {
|
|
|
16362
16480
|
process.exit(1);
|
|
16363
16481
|
}
|
|
16364
16482
|
});
|
|
16483
|
+
cmd.command("send").description("Send an email from the agent").requiredOption("--agent_id <id>", "Agent ID").requiredOption("--to <addr>", "Recipient email address").requiredOption("--subject <s>", "Subject line").requiredOption("--body-file <path>", "Path to HTML body file").option("--attachment <path>", "Path to a file to attach (repeatable)", collectRepeated, []).option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
16484
|
+
const { serverUrl, token, workspaceId } = resolveClientOpts(command, {
|
|
16485
|
+
workspace: opts.workspace,
|
|
16486
|
+
agentId: opts.agent_id
|
|
16487
|
+
});
|
|
16488
|
+
const client = new APIClient(serverUrl, token, workspaceId);
|
|
16489
|
+
let htmlBody;
|
|
16490
|
+
try {
|
|
16491
|
+
htmlBody = readFileSync3(opts.bodyFile, "utf-8");
|
|
16492
|
+
} catch (err) {
|
|
16493
|
+
console.error(`Error: cannot read body file "${opts.bodyFile}": ${err instanceof Error ? err.message : err}`);
|
|
16494
|
+
process.exit(1);
|
|
16495
|
+
}
|
|
16496
|
+
if (!htmlBody) {
|
|
16497
|
+
console.error(`Error: body file "${opts.bodyFile}" is empty`);
|
|
16498
|
+
process.exit(1);
|
|
16499
|
+
}
|
|
16500
|
+
const attachmentPaths = opts.attachment ?? [];
|
|
16501
|
+
const attachments = [];
|
|
16502
|
+
try {
|
|
16503
|
+
for (const path of attachmentPaths) {
|
|
16504
|
+
let bytes;
|
|
16505
|
+
let size;
|
|
16506
|
+
try {
|
|
16507
|
+
bytes = readFileSync3(path);
|
|
16508
|
+
size = statSync(path).size;
|
|
16509
|
+
} catch (err) {
|
|
16510
|
+
console.error(`Error: cannot read attachment "${path}": ${err instanceof Error ? err.message : err}`);
|
|
16511
|
+
process.exit(1);
|
|
16512
|
+
}
|
|
16513
|
+
const filename = basename(path);
|
|
16514
|
+
const contentType = guessContentType(filename);
|
|
16515
|
+
const form = new FormData;
|
|
16516
|
+
form.append("file", new Blob([new Uint8Array(bytes)], { type: contentType }), filename);
|
|
16517
|
+
const uploaded = await client.postMultipart("/api/email/upload", form);
|
|
16518
|
+
attachments.push({
|
|
16519
|
+
key: uploaded.key,
|
|
16520
|
+
filename: uploaded.filename,
|
|
16521
|
+
size: uploaded.size ?? size,
|
|
16522
|
+
contentType: uploaded.contentType ?? contentType
|
|
16523
|
+
});
|
|
16524
|
+
}
|
|
16525
|
+
const res = await client.postJSON("/api/email/send", {
|
|
16526
|
+
agentId: opts.agent_id,
|
|
16527
|
+
to: opts.to,
|
|
16528
|
+
subject: opts.subject,
|
|
16529
|
+
htmlBody,
|
|
16530
|
+
attachments
|
|
16531
|
+
});
|
|
16532
|
+
console.log(`Sent email to ${res.to_email} (id: ${res.id})`);
|
|
16533
|
+
} catch (err) {
|
|
16534
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
16535
|
+
process.exit(1);
|
|
16536
|
+
}
|
|
16537
|
+
});
|
|
16365
16538
|
return cmd;
|
|
16366
16539
|
}
|
|
16367
16540
|
|
|
16368
|
-
// commands/
|
|
16541
|
+
// commands/calendar.ts
|
|
16369
16542
|
import { Command as Command6 } from "commander";
|
|
16543
|
+
function resolveClientOpts2(command, agentId) {
|
|
16544
|
+
const parentOpts = command.parent?.parent?.opts() || {};
|
|
16545
|
+
const profile = parentOpts.profile;
|
|
16546
|
+
const cfg = loadCLIConfigForProfile(profile);
|
|
16547
|
+
const serverUrl = parentOpts.server || cfg.server_url;
|
|
16548
|
+
const workspaces = cfg.watched_workspaces || [];
|
|
16549
|
+
const ws = workspaces.find((w) => w.agent_ids?.includes(agentId));
|
|
16550
|
+
if (!ws || !ws.token) {
|
|
16551
|
+
console.error(`Error: no registered workspace contains agent ${agentId}. Run '${cmdPrefix()} register --token <token>' first.`);
|
|
16552
|
+
process.exit(1);
|
|
16553
|
+
}
|
|
16554
|
+
return { serverUrl, token: ws.token, workspaceId: ws.id };
|
|
16555
|
+
}
|
|
16556
|
+
function parseLocalDatetime(input) {
|
|
16557
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(input);
|
|
16558
|
+
if (!match) {
|
|
16559
|
+
throw new Error(`invalid --datetime "${input}" — expected YYYY-MM-DDTHH:MM`);
|
|
16560
|
+
}
|
|
16561
|
+
const [, y, mo, d, h, mi, s] = match;
|
|
16562
|
+
const date5 = new Date(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), s ? Number(s) : 0);
|
|
16563
|
+
return date5.toISOString();
|
|
16564
|
+
}
|
|
16565
|
+
function formatLocalDatetime(iso) {
|
|
16566
|
+
const d = new Date(iso);
|
|
16567
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
16568
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
16569
|
+
}
|
|
16570
|
+
function printEventDetail(ev) {
|
|
16571
|
+
console.log(`id: ${ev.id}`);
|
|
16572
|
+
console.log(`agent_id: ${ev.agent_id}`);
|
|
16573
|
+
console.log(`title: ${ev.title}`);
|
|
16574
|
+
console.log(`scheduled_at: ${formatLocalDatetime(ev.scheduled_at)}`);
|
|
16575
|
+
if (ev.repeat_interval) {
|
|
16576
|
+
const until = ev.repeat_stop_at ? ` until ${formatLocalDatetime(ev.repeat_stop_at)}` : "";
|
|
16577
|
+
console.log(`repeat: every ${ev.repeat_interval}${until}`);
|
|
16578
|
+
} else {
|
|
16579
|
+
console.log(`repeat: (none)`);
|
|
16580
|
+
}
|
|
16581
|
+
console.log(`last_fired_at: ${ev.last_triggered_at ? formatLocalDatetime(ev.last_triggered_at) : "(never)"}`);
|
|
16582
|
+
console.log("description:");
|
|
16583
|
+
console.log(ev.description ?? "(no description)");
|
|
16584
|
+
}
|
|
16585
|
+
function calendarCommand() {
|
|
16586
|
+
const cmd = new Command6("calendar").description("Manage scheduled agent events");
|
|
16587
|
+
cmd.command("set").description("Create a calendar event").requiredOption("--agent_id <id>", "Agent ID").requiredOption("--event_title <title>", "Event title (used as the task prompt)").requiredOption("--datetime <iso>", "Scheduled datetime (YYYY-MM-DDTHH:MM, local time)").option("--description <text>", "Optional longer-form notes for the event").option("--repeat <interval>", "Repeat interval, e.g. 1day, 2hour, 1month").option("--repeat_stop_date <date>", "Stop repeating on or after this date (YYYY-MM-DD, local time)").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
16588
|
+
const { serverUrl, token, workspaceId } = resolveClientOpts2(command, opts.agent_id);
|
|
16589
|
+
const client = new APIClient(serverUrl, token, workspaceId);
|
|
16590
|
+
let scheduledAt;
|
|
16591
|
+
try {
|
|
16592
|
+
scheduledAt = parseLocalDatetime(opts.datetime);
|
|
16593
|
+
} catch (err) {
|
|
16594
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
16595
|
+
process.exit(1);
|
|
16596
|
+
}
|
|
16597
|
+
if (opts.repeat_stop_date && !opts.repeat) {
|
|
16598
|
+
console.error("Error: --repeat_stop_date requires --repeat");
|
|
16599
|
+
process.exit(1);
|
|
16600
|
+
}
|
|
16601
|
+
if (opts.repeat_stop_date && !/^\d{4}-\d{2}-\d{2}$/.test(opts.repeat_stop_date)) {
|
|
16602
|
+
console.error("Error: --repeat_stop_date must be YYYY-MM-DD");
|
|
16603
|
+
process.exit(1);
|
|
16604
|
+
}
|
|
16605
|
+
const body = {
|
|
16606
|
+
agent_id: opts.agent_id,
|
|
16607
|
+
title: opts.event_title,
|
|
16608
|
+
scheduled_at: scheduledAt
|
|
16609
|
+
};
|
|
16610
|
+
if (opts.description)
|
|
16611
|
+
body.description = opts.description;
|
|
16612
|
+
if (opts.repeat)
|
|
16613
|
+
body.repeat_interval = opts.repeat;
|
|
16614
|
+
if (opts.repeat_stop_date)
|
|
16615
|
+
body.repeat_stop_date = opts.repeat_stop_date;
|
|
16616
|
+
try {
|
|
16617
|
+
const created = await client.postJSON("/api/calendar", body);
|
|
16618
|
+
if (opts.json) {
|
|
16619
|
+
printJSON(created);
|
|
16620
|
+
return;
|
|
16621
|
+
}
|
|
16622
|
+
console.log(`Created ${created.id} — ${created.title} @ ${formatLocalDatetime(created.scheduled_at)}${created.repeat_interval ? ` (every ${created.repeat_interval})` : ""}`);
|
|
16623
|
+
} catch (err) {
|
|
16624
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
16625
|
+
process.exit(1);
|
|
16626
|
+
}
|
|
16627
|
+
});
|
|
16628
|
+
cmd.command("list").description("List calendar events for an agent").requiredOption("--agent_id <id>", "Agent ID").option("--future_days <n>", "Include events scheduled in the next N days", "30").option("--past_days <n>", "Include events scheduled in the past N days", "0").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
16629
|
+
const { serverUrl, token, workspaceId } = resolveClientOpts2(command, opts.agent_id);
|
|
16630
|
+
const client = new APIClient(serverUrl, token, workspaceId);
|
|
16631
|
+
const now = Date.now();
|
|
16632
|
+
const from = new Date(now - Number(opts.past_days) * 86400000).toISOString();
|
|
16633
|
+
const to = new Date(now + Number(opts.future_days) * 86400000).toISOString();
|
|
16634
|
+
try {
|
|
16635
|
+
const events = await client.getJSON(`/api/calendar?agentId=${encodeURIComponent(opts.agent_id)}&from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`);
|
|
16636
|
+
if (opts.json) {
|
|
16637
|
+
printJSON(events);
|
|
16638
|
+
return;
|
|
16639
|
+
}
|
|
16640
|
+
if (events.length === 0) {
|
|
16641
|
+
console.log("No calendar events.");
|
|
16642
|
+
return;
|
|
16643
|
+
}
|
|
16644
|
+
for (const ev of events) {
|
|
16645
|
+
const repeatBadge = ev.repeat_interval ? ` [every ${ev.repeat_interval}${ev.repeat_stop_at ? ` until ${formatLocalDatetime(ev.repeat_stop_at)}` : ""}]` : "";
|
|
16646
|
+
const descBadge = ev.description ? " [has description]" : "";
|
|
16647
|
+
console.log(`${ev.id} ${formatLocalDatetime(ev.scheduled_at)} ${ev.title}${repeatBadge}${descBadge}`);
|
|
16648
|
+
}
|
|
16649
|
+
} catch (err) {
|
|
16650
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
16651
|
+
process.exit(1);
|
|
16652
|
+
}
|
|
16653
|
+
});
|
|
16654
|
+
cmd.command("show").description("Show the full detail of a single calendar event").requiredOption("--agent_id <id>", "Agent ID").requiredOption("--event_id <id>", "Event ID").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
16655
|
+
const { serverUrl, token, workspaceId } = resolveClientOpts2(command, opts.agent_id);
|
|
16656
|
+
const client = new APIClient(serverUrl, token, workspaceId);
|
|
16657
|
+
try {
|
|
16658
|
+
const ev = await client.getJSON(`/api/calendar/${opts.event_id}`);
|
|
16659
|
+
if (ev.agent_id !== opts.agent_id) {
|
|
16660
|
+
console.error(`Error: event ${ev.id} does not belong to agent ${opts.agent_id}`);
|
|
16661
|
+
process.exit(1);
|
|
16662
|
+
}
|
|
16663
|
+
if (opts.json) {
|
|
16664
|
+
printJSON(ev);
|
|
16665
|
+
return;
|
|
16666
|
+
}
|
|
16667
|
+
printEventDetail(ev);
|
|
16668
|
+
} catch (err) {
|
|
16669
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
16670
|
+
process.exit(1);
|
|
16671
|
+
}
|
|
16672
|
+
});
|
|
16673
|
+
cmd.command("update").description("Update fields on an existing calendar event").requiredOption("--agent_id <id>", "Agent ID").requiredOption("--event_id <id>", "Event ID").option("--event_title <title>", "New event title (task prompt)").option("--description <text>", "New description text").option("--clear_description", "Remove the description (sets to null)").option("--datetime <iso>", "New scheduled datetime (YYYY-MM-DDTHH:MM, local time)").option("--repeat <interval>", "New repeat interval, e.g. 1day, 2hour").option("--clear_repeat", "Convert a repeating event into a one-off").option("--repeat_stop_date <date>", "New stop date (YYYY-MM-DD, local time)").option("--clear_repeat_stop_date", "Remove the repeat stop date").option("--json", "Output as JSON").action(async (opts, command) => {
|
|
16674
|
+
if (opts.description && opts.clear_description) {
|
|
16675
|
+
console.error("Error: --description and --clear_description are mutually exclusive");
|
|
16676
|
+
process.exit(1);
|
|
16677
|
+
}
|
|
16678
|
+
if (opts.repeat && opts.clear_repeat) {
|
|
16679
|
+
console.error("Error: --repeat and --clear_repeat are mutually exclusive");
|
|
16680
|
+
process.exit(1);
|
|
16681
|
+
}
|
|
16682
|
+
if (opts.repeat_stop_date && opts.clear_repeat_stop_date) {
|
|
16683
|
+
console.error("Error: --repeat_stop_date and --clear_repeat_stop_date are mutually exclusive");
|
|
16684
|
+
process.exit(1);
|
|
16685
|
+
}
|
|
16686
|
+
const body = {};
|
|
16687
|
+
if (opts.event_title)
|
|
16688
|
+
body.title = opts.event_title;
|
|
16689
|
+
if (opts.description)
|
|
16690
|
+
body.description = opts.description;
|
|
16691
|
+
if (opts.clear_description)
|
|
16692
|
+
body.description = null;
|
|
16693
|
+
if (opts.datetime) {
|
|
16694
|
+
try {
|
|
16695
|
+
body.scheduled_at = parseLocalDatetime(opts.datetime);
|
|
16696
|
+
} catch (err) {
|
|
16697
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
16698
|
+
process.exit(1);
|
|
16699
|
+
}
|
|
16700
|
+
}
|
|
16701
|
+
if (opts.repeat)
|
|
16702
|
+
body.repeat_interval = opts.repeat;
|
|
16703
|
+
if (opts.clear_repeat)
|
|
16704
|
+
body.repeat_interval = null;
|
|
16705
|
+
if (opts.repeat_stop_date) {
|
|
16706
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(opts.repeat_stop_date)) {
|
|
16707
|
+
console.error("Error: --repeat_stop_date must be YYYY-MM-DD");
|
|
16708
|
+
process.exit(1);
|
|
16709
|
+
}
|
|
16710
|
+
body.repeat_stop_date = opts.repeat_stop_date;
|
|
16711
|
+
}
|
|
16712
|
+
if (opts.clear_repeat_stop_date)
|
|
16713
|
+
body.repeat_stop_date = null;
|
|
16714
|
+
if (Object.keys(body).length === 0) {
|
|
16715
|
+
console.error("Error: no fields to update — pass at least one of --event_title, --description, --clear_description, --datetime, --repeat, --clear_repeat, --repeat_stop_date, --clear_repeat_stop_date");
|
|
16716
|
+
process.exit(1);
|
|
16717
|
+
}
|
|
16718
|
+
const { serverUrl, token, workspaceId } = resolveClientOpts2(command, opts.agent_id);
|
|
16719
|
+
const client = new APIClient(serverUrl, token, workspaceId);
|
|
16720
|
+
try {
|
|
16721
|
+
const updated = await client.patchJSON(`/api/calendar/${opts.event_id}`, body);
|
|
16722
|
+
if (updated.agent_id !== opts.agent_id) {
|
|
16723
|
+
console.error(`Error: event ${updated.id} does not belong to agent ${opts.agent_id}`);
|
|
16724
|
+
process.exit(1);
|
|
16725
|
+
}
|
|
16726
|
+
if (opts.json) {
|
|
16727
|
+
printJSON(updated);
|
|
16728
|
+
return;
|
|
16729
|
+
}
|
|
16730
|
+
printEventDetail(updated);
|
|
16731
|
+
} catch (err) {
|
|
16732
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
16733
|
+
process.exit(1);
|
|
16734
|
+
}
|
|
16735
|
+
});
|
|
16736
|
+
cmd.command("delete").description("Delete a calendar event").requiredOption("--agent_id <id>", "Agent ID").requiredOption("--event_id <id>", "Event ID").action(async (opts, command) => {
|
|
16737
|
+
const { serverUrl, token, workspaceId } = resolveClientOpts2(command, opts.agent_id);
|
|
16738
|
+
const client = new APIClient(serverUrl, token, workspaceId);
|
|
16739
|
+
try {
|
|
16740
|
+
await client.deleteJSON(`/api/calendar/${opts.event_id}`);
|
|
16741
|
+
console.log(`Deleted ${opts.event_id}`);
|
|
16742
|
+
} catch (err) {
|
|
16743
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
16744
|
+
process.exit(1);
|
|
16745
|
+
}
|
|
16746
|
+
});
|
|
16747
|
+
return cmd;
|
|
16748
|
+
}
|
|
16749
|
+
|
|
16750
|
+
// commands/version.ts
|
|
16751
|
+
import { Command as Command7 } from "commander";
|
|
16370
16752
|
|
|
16371
16753
|
// lib/version.ts
|
|
16372
|
-
import { readFileSync as
|
|
16754
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
16373
16755
|
import { join as join5, dirname as dirname4 } from "path";
|
|
16374
16756
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16375
16757
|
function getCurrentVersion() {
|
|
@@ -16380,7 +16762,7 @@ function getCurrentVersion() {
|
|
|
16380
16762
|
];
|
|
16381
16763
|
for (const candidate of candidates) {
|
|
16382
16764
|
try {
|
|
16383
|
-
const pkg = JSON.parse(
|
|
16765
|
+
const pkg = JSON.parse(readFileSync4(candidate, "utf-8"));
|
|
16384
16766
|
if (typeof pkg.version === "string")
|
|
16385
16767
|
return pkg.version;
|
|
16386
16768
|
} catch {}
|
|
@@ -16390,19 +16772,20 @@ function getCurrentVersion() {
|
|
|
16390
16772
|
|
|
16391
16773
|
// commands/version.ts
|
|
16392
16774
|
function versionCommand() {
|
|
16393
|
-
const cmd = new
|
|
16775
|
+
const cmd = new Command7("version").description("Show CLI version").action(() => {
|
|
16394
16776
|
console.log(`alook version ${getCurrentVersion()}`);
|
|
16395
16777
|
});
|
|
16396
16778
|
return cmd;
|
|
16397
16779
|
}
|
|
16398
16780
|
|
|
16399
16781
|
// src/index.ts
|
|
16400
|
-
var program = new
|
|
16782
|
+
var program = new Command8;
|
|
16401
16783
|
program.name("alook").description("Alook CLI").option("--server <url>", "Server URL").option("--profile <name>", "Profile name");
|
|
16402
16784
|
program.addCommand(registerCommand());
|
|
16403
16785
|
program.addCommand(statusCommand());
|
|
16404
16786
|
program.addCommand(daemonCommand());
|
|
16405
16787
|
program.addCommand(emailCommand());
|
|
16788
|
+
program.addCommand(calendarCommand());
|
|
16406
16789
|
program.addCommand(configCommand());
|
|
16407
16790
|
program.addCommand(versionCommand());
|
|
16408
16791
|
program.parse();
|