@alook/cli 0.0.8 → 0.0.9
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 +24 -5
- package/dist/session-runner.js +25 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -167,7 +167,8 @@ function saveCLIConfigForProfile(profile, profileConfig) {
|
|
|
167
167
|
// commands/register.ts
|
|
168
168
|
function isCommandAvailable(cmd) {
|
|
169
169
|
try {
|
|
170
|
-
|
|
170
|
+
const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
171
|
+
execSync(check, { stdio: "ignore" });
|
|
171
172
|
return true;
|
|
172
173
|
} catch {
|
|
173
174
|
return false;
|
|
@@ -13864,6 +13865,7 @@ var ClaimedTaskRowSchema = exports_external.object({
|
|
|
13864
13865
|
result: exports_external.unknown().nullable(),
|
|
13865
13866
|
context: exports_external.unknown().nullable(),
|
|
13866
13867
|
type: exports_external.string().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
13868
|
+
contextKey: exports_external.string().nullable().optional(),
|
|
13867
13869
|
sessionId: exports_external.string().nullable(),
|
|
13868
13870
|
createdAt: exports_external.coerce.date(),
|
|
13869
13871
|
dispatchedAt: exports_external.coerce.date().nullable(),
|
|
@@ -13893,7 +13895,8 @@ var TaskApiBaseSchema = exports_external.object({
|
|
|
13893
13895
|
result: exports_external.unknown().nullable(),
|
|
13894
13896
|
error: exports_external.string().nullable(),
|
|
13895
13897
|
created_at: exports_external.string(),
|
|
13896
|
-
type: exports_external.string()
|
|
13898
|
+
type: exports_external.string(),
|
|
13899
|
+
context_key: exports_external.string().nullable().optional()
|
|
13897
13900
|
});
|
|
13898
13901
|
var TaskApiSchema = TaskApiBaseSchema.extend({
|
|
13899
13902
|
agent: TaskAgentDataApiSchema.nullable().optional()
|
|
@@ -15528,6 +15531,7 @@ var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
|
15528
15531
|
conversationId: text("conversation_id").notNull().references(() => conversation.id),
|
|
15529
15532
|
prompt: text("prompt").notNull(),
|
|
15530
15533
|
type: text("type").notNull().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
15534
|
+
contextKey: text("context_key"),
|
|
15531
15535
|
status: text("status").notNull().default("queued"),
|
|
15532
15536
|
priority: integer2("priority").notNull().default(0),
|
|
15533
15537
|
result: text("result", { mode: "json" }),
|
|
@@ -15866,6 +15870,7 @@ function fromApiTask(api2) {
|
|
|
15866
15870
|
status: api2.status,
|
|
15867
15871
|
priority: api2.priority,
|
|
15868
15872
|
type: api2.type,
|
|
15873
|
+
contextKey: api2.context_key ?? null,
|
|
15869
15874
|
agent: api2.agent ? { name: api2.agent.name, instructions: api2.agent.instructions, emailHandle: api2.agent.email_handle ?? undefined, userEmail: api2.agent.user_email ?? undefined, runtimeConfig: api2.agent.runtime_config ?? undefined } : undefined,
|
|
15870
15875
|
repos: undefined,
|
|
15871
15876
|
createdAt: api2.created_at
|
|
@@ -16077,7 +16082,8 @@ var _dir = dirname3(fileURLToPath2(import.meta.url));
|
|
|
16077
16082
|
var sessionRunnerPath = existsSync(join4(_dir, "session-runner.js")) ? join4(_dir, "session-runner.js") : join4(_dir, "session-runner.ts");
|
|
16078
16083
|
function isCommandAvailable2(cmd) {
|
|
16079
16084
|
try {
|
|
16080
|
-
|
|
16085
|
+
const check2 = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
16086
|
+
execSync3(check2, { stdio: "ignore" });
|
|
16081
16087
|
return true;
|
|
16082
16088
|
} catch {
|
|
16083
16089
|
return false;
|
|
@@ -16178,7 +16184,16 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16178
16184
|
runtimes
|
|
16179
16185
|
});
|
|
16180
16186
|
} catch (e) {
|
|
16181
|
-
|
|
16187
|
+
if (e instanceof Error && e.message.startsWith("HTTP 401")) {
|
|
16188
|
+
log.warn(`Workspace ${ws.id} token invalid — removing from config`);
|
|
16189
|
+
try {
|
|
16190
|
+
const cfg = loadCLIConfigForProfile(profile);
|
|
16191
|
+
cfg.watched_workspaces = (cfg.watched_workspaces || []).filter((w) => w.id !== ws.id);
|
|
16192
|
+
saveCLIConfigForProfile(profile, cfg);
|
|
16193
|
+
} catch {}
|
|
16194
|
+
} else {
|
|
16195
|
+
log.error(`Failed to register workspace ${ws.id}, skipping`, e);
|
|
16196
|
+
}
|
|
16182
16197
|
continue;
|
|
16183
16198
|
}
|
|
16184
16199
|
log.info(`Workspace ${ws.id} registered — ${resp.runtimes.length} runtime(s)`);
|
|
@@ -16270,7 +16285,11 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16270
16285
|
});
|
|
16271
16286
|
}
|
|
16272
16287
|
} catch (e) {
|
|
16273
|
-
|
|
16288
|
+
if (e instanceof Error && e.message.startsWith("HTTP 401")) {
|
|
16289
|
+
evictedIds.push(ws.workspaceId);
|
|
16290
|
+
} else {
|
|
16291
|
+
log.debug("Poll error", e);
|
|
16292
|
+
}
|
|
16274
16293
|
}
|
|
16275
16294
|
}
|
|
16276
16295
|
for (const id of evictedIds) {
|
package/dist/session-runner.js
CHANGED
|
@@ -13581,6 +13581,7 @@ var ClaimedTaskRowSchema = exports_external.object({
|
|
|
13581
13581
|
result: exports_external.unknown().nullable(),
|
|
13582
13582
|
context: exports_external.unknown().nullable(),
|
|
13583
13583
|
type: exports_external.string().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
13584
|
+
contextKey: exports_external.string().nullable().optional(),
|
|
13584
13585
|
sessionId: exports_external.string().nullable(),
|
|
13585
13586
|
createdAt: exports_external.coerce.date(),
|
|
13586
13587
|
dispatchedAt: exports_external.coerce.date().nullable(),
|
|
@@ -13610,7 +13611,8 @@ var TaskApiBaseSchema = exports_external.object({
|
|
|
13610
13611
|
result: exports_external.unknown().nullable(),
|
|
13611
13612
|
error: exports_external.string().nullable(),
|
|
13612
13613
|
created_at: exports_external.string(),
|
|
13613
|
-
type: exports_external.string()
|
|
13614
|
+
type: exports_external.string(),
|
|
13615
|
+
context_key: exports_external.string().nullable().optional()
|
|
13614
13616
|
});
|
|
13615
13617
|
var TaskApiSchema = TaskApiBaseSchema.extend({
|
|
13616
13618
|
agent: TaskAgentDataApiSchema.nullable().optional()
|
|
@@ -15245,6 +15247,7 @@ var agentTaskQueue = sqliteTable("agent_task_queue", {
|
|
|
15245
15247
|
conversationId: text("conversation_id").notNull().references(() => conversation.id),
|
|
15246
15248
|
prompt: text("prompt").notNull(),
|
|
15247
15249
|
type: text("type").notNull().default(TASK_TYPES.USER_DM_MESSAGE),
|
|
15250
|
+
contextKey: text("context_key"),
|
|
15248
15251
|
status: text("status").notNull().default("queued"),
|
|
15249
15252
|
priority: integer2("priority").notNull().default(0),
|
|
15250
15253
|
result: text("result", { mode: "json" }),
|
|
@@ -16350,8 +16353,8 @@ var SYSTEM_PROMPT_BODY = `## Memory Management
|
|
|
16350
16353
|
- For SPECIFIC yet LONG rules or pattern, write to experiences/[NAME].md, and add index to ./memory.md for later recall.
|
|
16351
16354
|
### whats is ESSENTIAL and SHORT Memory?
|
|
16352
16355
|
- basic user profile, e.g.:
|
|
16353
|
-
- "user name is
|
|
16354
|
-
- "user is working on
|
|
16356
|
+
- "user name is ..."
|
|
16357
|
+
- "user is working on ..."
|
|
16355
16358
|
- certain local project mapping, e.g.:
|
|
16356
16359
|
- "alook means the project under /user/home/alook/"
|
|
16357
16360
|
- when to read certain stuff, e.g.:
|
|
@@ -16388,6 +16391,7 @@ those json are sorted by datetime in asc order.
|
|
|
16388
16391
|
- When you start a new task, read the last ~10 lines of today's timeline to understand what has been asked and done recently.
|
|
16389
16392
|
- if you don't know the current datetime, obtain the current datetime first.
|
|
16390
16393
|
- When user ask you something you don't have in your current context, try to read the timeline jsonl files for answer (today or previous days).
|
|
16394
|
+
- When access other local projects, make sure you read the CLAUDE.md/AGENTS.md file under the project root dir to understand the requirements.
|
|
16391
16395
|
`;
|
|
16392
16396
|
function buildInstructionContent(task) {
|
|
16393
16397
|
const displayName = task.agent?.name || "Alook Agent";
|
|
@@ -16441,18 +16445,22 @@ To reply to an email, add '--in-reply-to <EMAIL_ID>' to the send command. This s
|
|
|
16441
16445
|
### Calendar
|
|
16442
16446
|
You have your own calendar to setup daily routines and reminders.
|
|
16443
16447
|
Schedule future tasks for yourself. At the scheduled time, a new task is dispatched to you with the event as the prompt (task type 'calendar_event').
|
|
16448
|
+
|
|
16449
|
+
!USE Calendar when you think the tasks are recurring or it should be conducted in the future.
|
|
16450
|
+
!When scheduling calendar events relative to a weekday (e.g. "every Monday"), always run date '+%A' first to confirm today's weekday before calculating the target date
|
|
16444
16451
|
---
|
|
16452
|
+
Keep the event title informative and concise, less than 20 words.
|
|
16453
|
+
Place the event details in description.
|
|
16445
16454
|
Create a one-off event:
|
|
16446
|
-
- Run 'npx @alook/cli calendar set --agent_id ${task.agentId} --event_title "<
|
|
16455
|
+
- Run 'npx @alook/cli calendar set --agent_id ${task.agentId} --event_title "<TASK_TITLE>" --description "<TASK_BODY>" --datetime <YYYY-MM-DDTHH:MM>'
|
|
16447
16456
|
- '--datetime' is LOCAL time, format 'YYYY-MM-DDTHH:MM' (e.g. '2026-04-17T09:30'). Do NOT pass UTC / ISO strings with 'Z'.
|
|
16448
16457
|
- '--event_title' becomes the task prompt when the event fires — write it as the instruction you want future-you to receive.
|
|
16449
|
-
- Optional '--description "<text>"' — longer notes/context shown alongside the event in the web UI. Use it for anything that wouldn't fit cleanly in the title.
|
|
16450
16458
|
|
|
16451
16459
|
Create a repeating event:
|
|
16452
16460
|
- Add '--repeat <interval>' where interval is like '1day', '2hour', '1week', '1month'.
|
|
16453
16461
|
- Optionally add '--repeat_stop_date <YYYY-MM-DD>' to stop the recurrence (local date).
|
|
16454
|
-
- Example: 'npx @alook/cli calendar set --agent_id ${task.agentId} --event_title "
|
|
16455
|
-
|
|
16462
|
+
- Example: 'npx @alook/cli calendar set --agent_id ${task.agentId} --event_title "<REPEAT_TASK_TITLE>" --description "<REPEAT_TASK_BODY>" --datetime 2026-04-18T09:00 --repeat 1day --repeat_stop_date 2026-05-18'
|
|
16463
|
+
---
|
|
16456
16464
|
List upcoming events:
|
|
16457
16465
|
- Run 'npx @alook/cli calendar list --agent_id ${task.agentId}' (defaults: next 30 days, past 0 days).
|
|
16458
16466
|
- Tune the window with '--future_days <N>' and '--past_days <N>'. Add '--json' for machine-readable output.
|
|
@@ -16785,9 +16793,10 @@ function updateEntry(timelineDir, taskId, updater) {
|
|
|
16785
16793
|
}
|
|
16786
16794
|
log.debug(`Timeline updateEntry: task_id ${taskId} not found in last 7 days`);
|
|
16787
16795
|
}
|
|
16788
|
-
function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider) {
|
|
16796
|
+
function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider, contextKey) {
|
|
16789
16797
|
return {
|
|
16790
16798
|
task_id: taskId,
|
|
16799
|
+
context_key: contextKey ?? null,
|
|
16791
16800
|
session_id: sessionId || null,
|
|
16792
16801
|
pid: pid ?? null,
|
|
16793
16802
|
status: "running",
|
|
@@ -16800,7 +16809,9 @@ function createTimelineEntry(taskId, prompt, type, sessionId, pid, provider) {
|
|
|
16800
16809
|
};
|
|
16801
16810
|
}
|
|
16802
16811
|
var DEFAULT_RESUME_MAX_AGE_MS = 3 * 60 * 60 * 1000;
|
|
16803
|
-
|
|
16812
|
+
var EMAIL_RESUME_MAX_AGE_MS = 48 * 60 * 60 * 1000;
|
|
16813
|
+
function findResumableSessionByContextKey(timelineDir, contextKey, provider) {
|
|
16814
|
+
const maxAgeMs = contextKey.startsWith("email:") ? EMAIL_RESUME_MAX_AGE_MS : DEFAULT_RESUME_MAX_AGE_MS;
|
|
16804
16815
|
const now = new Date;
|
|
16805
16816
|
const cutoff = new Date(now.getTime() - maxAgeMs);
|
|
16806
16817
|
const daysToScan = Math.ceil(maxAgeMs / 86400000) + 1;
|
|
@@ -16810,7 +16821,7 @@ function findResumableSessionId(timelineDir, type, provider, maxAgeMs = DEFAULT_
|
|
|
16810
16821
|
}
|
|
16811
16822
|
entries.sort((a, b) => new Date(b.datetime).getTime() - new Date(a.datetime).getTime());
|
|
16812
16823
|
for (const entry of entries) {
|
|
16813
|
-
if (entry.status === "completed" && entry.
|
|
16824
|
+
if (entry.status === "completed" && entry.context_key === contextKey && entry.provider === provider && entry.session_id && new Date(entry.datetime) >= cutoff) {
|
|
16814
16825
|
return entry.session_id;
|
|
16815
16826
|
}
|
|
16816
16827
|
}
|
|
@@ -16848,9 +16859,9 @@ async function runSession(input) {
|
|
|
16848
16859
|
const backend = createBackend(provider, cliPath);
|
|
16849
16860
|
const prompt = buildPrompt(task);
|
|
16850
16861
|
const { workDir, logFile, timelineDir, env } = prepare({ workspacesRoot }, task);
|
|
16851
|
-
const resumeSessionId = task.
|
|
16862
|
+
const resumeSessionId = task.contextKey ? findResumableSessionByContextKey(timelineDir, task.contextKey, provider) ?? undefined : undefined;
|
|
16852
16863
|
if (resumeSessionId) {
|
|
16853
|
-
log.info(`Task ${task.id} resuming session ${resumeSessionId}`);
|
|
16864
|
+
log.info(`Task ${task.id} resuming session ${resumeSessionId} (context_key: ${task.contextKey})`);
|
|
16854
16865
|
}
|
|
16855
16866
|
const session2 = backend.execute(prompt, {
|
|
16856
16867
|
cwd: workDir,
|
|
@@ -16861,11 +16872,11 @@ async function runSession(input) {
|
|
|
16861
16872
|
});
|
|
16862
16873
|
const agentPid = session2.pid;
|
|
16863
16874
|
const earlySessionId = await session2.sessionId;
|
|
16864
|
-
await initEntryAsync(timelineDir, createTimelineEntry(task.id, task.prompt, task.type, earlySessionId, process.pid, provider));
|
|
16875
|
+
await initEntryAsync(timelineDir, createTimelineEntry(task.id, task.prompt, task.type, earlySessionId, process.pid, provider, task.contextKey));
|
|
16865
16876
|
const pendingMessages = [];
|
|
16866
16877
|
let seq = 0;
|
|
16867
16878
|
const BATCH_SIZE = Number(process.env.ALOOK_MESSAGE_BATCH_SIZE) || 20;
|
|
16868
|
-
const FLUSH_INTERVAL_MS = Number(process.env.ALOOK_MESSAGE_FLUSH_INTERVAL_MS) ||
|
|
16879
|
+
const FLUSH_INTERVAL_MS = Number(process.env.ALOOK_MESSAGE_FLUSH_INTERVAL_MS) || 100;
|
|
16869
16880
|
const flushMessages = async () => {
|
|
16870
16881
|
if (pendingMessages.length === 0)
|
|
16871
16882
|
return;
|