@alook/cli 0.0.6 → 0.0.8
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 +243 -72
- package/dist/session-runner.js +16 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// src/index.ts
|
|
18
|
-
import { Command as
|
|
18
|
+
import { Command as Command9 } from "commander";
|
|
19
19
|
|
|
20
20
|
// commands/register.ts
|
|
21
21
|
import { Command } from "commander";
|
|
@@ -295,9 +295,9 @@ function statusCommand() {
|
|
|
295
295
|
|
|
296
296
|
// commands/daemon.ts
|
|
297
297
|
import { Command as Command3 } from "commander";
|
|
298
|
-
import { spawn as
|
|
298
|
+
import { spawn as spawn3 } from "child_process";
|
|
299
299
|
import { openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync4 } from "fs";
|
|
300
|
-
import { dirname as
|
|
300
|
+
import { dirname as dirname4 } from "path";
|
|
301
301
|
|
|
302
302
|
// ../shared/src/constants.ts
|
|
303
303
|
var TASK_TYPES = {
|
|
@@ -13900,10 +13900,13 @@ var TaskApiSchema = TaskApiBaseSchema.extend({
|
|
|
13900
13900
|
});
|
|
13901
13901
|
var PollRequestSchema = exports_external.object({
|
|
13902
13902
|
daemon_id: exports_external.string().min(1),
|
|
13903
|
-
max_tasks: exports_external.number().int().min(1).default(1)
|
|
13903
|
+
max_tasks: exports_external.number().int().min(1).default(1),
|
|
13904
|
+
cli_version: exports_external.string().optional()
|
|
13904
13905
|
});
|
|
13905
13906
|
var PollResponseSchema = exports_external.object({
|
|
13906
|
-
tasks: exports_external.array(TaskApiSchema)
|
|
13907
|
+
tasks: exports_external.array(TaskApiSchema),
|
|
13908
|
+
evicted: exports_external.boolean().optional(),
|
|
13909
|
+
pending_update: exports_external.object({ version: exports_external.string() }).optional()
|
|
13907
13910
|
});
|
|
13908
13911
|
var RegisterResponseSchema = exports_external.object({
|
|
13909
13912
|
runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
|
|
@@ -14006,6 +14009,9 @@ var CalendarEventApiSchema = exports_external.object({
|
|
|
14006
14009
|
created_at: exports_external.string(),
|
|
14007
14010
|
updated_at: exports_external.string()
|
|
14008
14011
|
});
|
|
14012
|
+
var AddWhitelistRequestSchema = exports_external.object({
|
|
14013
|
+
email: exports_external.string().email()
|
|
14014
|
+
});
|
|
14009
14015
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@4.20260418.1_@opentelemetry+api@1.9.1_bun-types@1.3.12_kysely@0.28.16/node_modules/drizzle-orm/entity.js
|
|
14010
14016
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
14011
14017
|
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
@@ -15442,6 +15448,7 @@ var machine = sqliteTable("machine", {
|
|
|
15442
15448
|
deviceInfo: text("device_info").notNull().default(""),
|
|
15443
15449
|
lastSeenAt: text("last_seen_at"),
|
|
15444
15450
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
15451
|
+
pendingUpdateVersion: text("pending_update_version"),
|
|
15445
15452
|
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15446
15453
|
}, (t) => [primaryKey({ columns: [t.workspaceId, t.daemonId] })]);
|
|
15447
15454
|
var agentRuntime = sqliteTable("agent_runtime", {
|
|
@@ -15625,6 +15632,20 @@ var RESERVED_HANDLES = new Set([
|
|
|
15625
15632
|
"system",
|
|
15626
15633
|
"alook"
|
|
15627
15634
|
]);
|
|
15635
|
+
// ../shared/src/semver.ts
|
|
15636
|
+
function semverGte(a, b) {
|
|
15637
|
+
const pa = a.split(".").map(Number);
|
|
15638
|
+
const pb = b.split(".").map(Number);
|
|
15639
|
+
for (let i = 0;i < Math.max(pa.length, pb.length); i++) {
|
|
15640
|
+
const sa = pa[i] ?? 0;
|
|
15641
|
+
const sb = pb[i] ?? 0;
|
|
15642
|
+
if (sa > sb)
|
|
15643
|
+
return true;
|
|
15644
|
+
if (sa < sb)
|
|
15645
|
+
return false;
|
|
15646
|
+
}
|
|
15647
|
+
return true;
|
|
15648
|
+
}
|
|
15628
15649
|
// daemon/client.ts
|
|
15629
15650
|
class DaemonClient {
|
|
15630
15651
|
baseURL;
|
|
@@ -15656,10 +15677,14 @@ class DaemonClient {
|
|
|
15656
15677
|
daemon_id: daemonId
|
|
15657
15678
|
});
|
|
15658
15679
|
}
|
|
15659
|
-
async poll(token, daemonId, maxTasks) {
|
|
15660
|
-
const raw = await this.request("POST", "/api/daemon/tasks/poll", token, { daemon_id: daemonId, max_tasks: maxTasks });
|
|
15680
|
+
async poll(token, daemonId, maxTasks, cliVersion) {
|
|
15681
|
+
const raw = await this.request("POST", "/api/daemon/tasks/poll", token, { daemon_id: daemonId, max_tasks: maxTasks, ...cliVersion && { cli_version: cliVersion } });
|
|
15661
15682
|
const resp = PollResponseSchema.parse(raw);
|
|
15662
|
-
return
|
|
15683
|
+
return {
|
|
15684
|
+
tasks: resp.tasks,
|
|
15685
|
+
evicted: resp.evicted ?? false,
|
|
15686
|
+
pending_update: resp.pending_update
|
|
15687
|
+
};
|
|
15663
15688
|
}
|
|
15664
15689
|
startTask(token, taskId) {
|
|
15665
15690
|
return this.request("POST", `/api/daemon/tasks/${taskId}/start`, token);
|
|
@@ -15679,22 +15704,44 @@ class DaemonClient {
|
|
|
15679
15704
|
|
|
15680
15705
|
// daemon/config.ts
|
|
15681
15706
|
import { hostname as hostname4 } from "os";
|
|
15682
|
-
import { join as
|
|
15707
|
+
import { join as join3 } from "path";
|
|
15708
|
+
|
|
15709
|
+
// lib/version.ts
|
|
15710
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
15711
|
+
import { join as join2, dirname } from "path";
|
|
15712
|
+
import { fileURLToPath } from "url";
|
|
15713
|
+
function getCurrentVersion() {
|
|
15714
|
+
const __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
15715
|
+
const candidates = [
|
|
15716
|
+
join2(__dirname2, "..", "package.json"),
|
|
15717
|
+
join2(__dirname2, "..", "..", "package.json")
|
|
15718
|
+
];
|
|
15719
|
+
for (const candidate of candidates) {
|
|
15720
|
+
try {
|
|
15721
|
+
const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
|
|
15722
|
+
if (typeof pkg.version === "string")
|
|
15723
|
+
return pkg.version;
|
|
15724
|
+
} catch {}
|
|
15725
|
+
}
|
|
15726
|
+
return "unknown";
|
|
15727
|
+
}
|
|
15728
|
+
|
|
15729
|
+
// daemon/config.ts
|
|
15683
15730
|
function pidFilePath(profile) {
|
|
15684
15731
|
const name = profile ? `daemon_${profile}.pid` : "daemon.pid";
|
|
15685
|
-
return
|
|
15732
|
+
return join3(configDir(), name);
|
|
15686
15733
|
}
|
|
15687
15734
|
function daemonLogDir() {
|
|
15688
|
-
return
|
|
15735
|
+
return join3(configDir(), "daemon", "logs");
|
|
15689
15736
|
}
|
|
15690
15737
|
function sessionRunnerLogDir() {
|
|
15691
|
-
return
|
|
15738
|
+
return join3(configDir(), "daemon", "session-runners");
|
|
15692
15739
|
}
|
|
15693
15740
|
function daemonLogFilePath(date5 = new Date) {
|
|
15694
15741
|
const y = date5.getFullYear();
|
|
15695
15742
|
const m = String(date5.getMonth() + 1).padStart(2, "0");
|
|
15696
15743
|
const d = String(date5.getDate()).padStart(2, "0");
|
|
15697
|
-
return
|
|
15744
|
+
return join3(daemonLogDir(), `${y}-${m}-${d}.log`);
|
|
15698
15745
|
}
|
|
15699
15746
|
function parseDuration(s) {
|
|
15700
15747
|
if (!s)
|
|
@@ -15734,7 +15781,7 @@ function loadDaemonConfig(profile) {
|
|
|
15734
15781
|
if (profile && !daemonId.endsWith(`-${profile}`)) {
|
|
15735
15782
|
daemonId = `${daemonId}-${profile}`;
|
|
15736
15783
|
}
|
|
15737
|
-
const defaultRoot =
|
|
15784
|
+
const defaultRoot = join3(configDir(), profile ? `workspaces_${profile}` : "workspaces");
|
|
15738
15785
|
const workspacesRoot = process.env.ALOOK_WORKSPACES_ROOT || defaultRoot;
|
|
15739
15786
|
return {
|
|
15740
15787
|
serverURL: normalizeServerBaseURL(process.env.ALOOK_SERVER_URL || "https://alook.ai"),
|
|
@@ -15751,7 +15798,7 @@ function loadDaemonConfig(profile) {
|
|
|
15751
15798
|
deviceName: process.env.ALOOK_DAEMON_DEVICE_NAME || h,
|
|
15752
15799
|
runtimeName: process.env.ALOOK_AGENT_RUNTIME_NAME || "Local Agent",
|
|
15753
15800
|
workspacesRoot,
|
|
15754
|
-
cliVersion:
|
|
15801
|
+
cliVersion: getCurrentVersion()
|
|
15755
15802
|
};
|
|
15756
15803
|
}
|
|
15757
15804
|
function normalizeServerBaseURL(url2) {
|
|
@@ -15917,8 +15964,8 @@ function createLogger2(level) {
|
|
|
15917
15964
|
var log = createLogger2();
|
|
15918
15965
|
|
|
15919
15966
|
// daemon/pidfile.ts
|
|
15920
|
-
import { readFileSync as
|
|
15921
|
-
import { dirname } from "path";
|
|
15967
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync2 } from "fs";
|
|
15968
|
+
import { dirname as dirname2 } from "path";
|
|
15922
15969
|
function isProcessAlive(pid) {
|
|
15923
15970
|
try {
|
|
15924
15971
|
process.kill(pid, 0);
|
|
@@ -15929,7 +15976,7 @@ function isProcessAlive(pid) {
|
|
|
15929
15976
|
}
|
|
15930
15977
|
function readDaemonPid(profile) {
|
|
15931
15978
|
try {
|
|
15932
|
-
const content =
|
|
15979
|
+
const content = readFileSync3(pidFilePath(profile), "utf-8").trim();
|
|
15933
15980
|
const pid = parseInt(content, 10);
|
|
15934
15981
|
return Number.isNaN(pid) ? null : pid;
|
|
15935
15982
|
} catch {
|
|
@@ -15939,14 +15986,14 @@ function readDaemonPid(profile) {
|
|
|
15939
15986
|
function acquireDaemonPid(profile) {
|
|
15940
15987
|
const pidPath = pidFilePath(profile);
|
|
15941
15988
|
try {
|
|
15942
|
-
const content =
|
|
15989
|
+
const content = readFileSync3(pidPath, "utf-8").trim();
|
|
15943
15990
|
const existingPid = parseInt(content, 10);
|
|
15944
15991
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
15945
15992
|
log.error(`Another daemon is already running (PID ${existingPid}). ` + `Remove ${pidPath} if this is stale.`);
|
|
15946
15993
|
return false;
|
|
15947
15994
|
}
|
|
15948
15995
|
} catch {}
|
|
15949
|
-
mkdirSync2(
|
|
15996
|
+
mkdirSync2(dirname2(pidPath), { recursive: true, mode: 448 });
|
|
15950
15997
|
writeFileSync2(pidPath, String(process.pid), { mode: 384 });
|
|
15951
15998
|
return true;
|
|
15952
15999
|
}
|
|
@@ -15963,13 +16010,71 @@ function releaseDaemonPid(profile) {
|
|
|
15963
16010
|
removePidFileIfMatches(process.pid, profile);
|
|
15964
16011
|
}
|
|
15965
16012
|
|
|
16013
|
+
// lib/update.ts
|
|
16014
|
+
import { spawn } from "child_process";
|
|
16015
|
+
function fetchLatestVersion() {
|
|
16016
|
+
return fetch("https://registry.npmjs.org/@alook/cli/latest").then((res) => {
|
|
16017
|
+
if (!res.ok)
|
|
16018
|
+
return null;
|
|
16019
|
+
return res.json();
|
|
16020
|
+
}).then((data) => data?.version ?? null).catch(() => null);
|
|
16021
|
+
}
|
|
16022
|
+
function runNpmUpdate(targetVersion) {
|
|
16023
|
+
return new Promise((resolve) => {
|
|
16024
|
+
const chunks = [];
|
|
16025
|
+
const child = spawn("npm", ["install", "-g", `@alook/cli@${targetVersion}`], {
|
|
16026
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16027
|
+
});
|
|
16028
|
+
child.stdout?.on("data", (d) => chunks.push(d));
|
|
16029
|
+
child.stderr?.on("data", (d) => chunks.push(d));
|
|
16030
|
+
child.on("error", (err) => {
|
|
16031
|
+
resolve({ success: false, output: err.message });
|
|
16032
|
+
});
|
|
16033
|
+
child.on("close", (code) => {
|
|
16034
|
+
const output = Buffer.concat(chunks).toString();
|
|
16035
|
+
resolve({ success: code === 0, output });
|
|
16036
|
+
});
|
|
16037
|
+
});
|
|
16038
|
+
}
|
|
16039
|
+
|
|
16040
|
+
// daemon/update-handler.ts
|
|
16041
|
+
var updating = false;
|
|
16042
|
+
var retryCount = 0;
|
|
16043
|
+
var MAX_RETRIES = 3;
|
|
16044
|
+
function isUpdating() {
|
|
16045
|
+
return updating;
|
|
16046
|
+
}
|
|
16047
|
+
async function handleCliUpdate(version3, onSuccess) {
|
|
16048
|
+
if (updating)
|
|
16049
|
+
return;
|
|
16050
|
+
if (retryCount >= MAX_RETRIES)
|
|
16051
|
+
return;
|
|
16052
|
+
updating = true;
|
|
16053
|
+
try {
|
|
16054
|
+
log.info(`Updating CLI to v${version3}...`);
|
|
16055
|
+
const result = await runNpmUpdate(version3);
|
|
16056
|
+
if (result.success) {
|
|
16057
|
+
log.info(`CLI updated to v${version3} — restarting`);
|
|
16058
|
+
onSuccess();
|
|
16059
|
+
} else {
|
|
16060
|
+
retryCount++;
|
|
16061
|
+
log.error(`CLI update failed (attempt ${retryCount}/${MAX_RETRIES}): ${result.output}`);
|
|
16062
|
+
}
|
|
16063
|
+
} catch (e) {
|
|
16064
|
+
retryCount++;
|
|
16065
|
+
log.error(`CLI update error (attempt ${retryCount}/${MAX_RETRIES})`, e);
|
|
16066
|
+
} finally {
|
|
16067
|
+
updating = false;
|
|
16068
|
+
}
|
|
16069
|
+
}
|
|
16070
|
+
|
|
15966
16071
|
// daemon/daemon.ts
|
|
15967
16072
|
import { existsSync, mkdirSync as mkdirSync3, openSync, closeSync, renameSync, readdirSync, statSync, unlinkSync as unlinkSync2 } from "fs";
|
|
15968
|
-
import { execSync as execSync3, spawn } from "child_process";
|
|
15969
|
-
import { fileURLToPath } from "url";
|
|
15970
|
-
import { dirname as
|
|
15971
|
-
var _dir =
|
|
15972
|
-
var sessionRunnerPath = existsSync(
|
|
16073
|
+
import { execSync as execSync3, spawn as spawn2 } from "child_process";
|
|
16074
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16075
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
16076
|
+
var _dir = dirname3(fileURLToPath2(import.meta.url));
|
|
16077
|
+
var sessionRunnerPath = existsSync(join4(_dir, "session-runner.js")) ? join4(_dir, "session-runner.js") : join4(_dir, "session-runner.ts");
|
|
15973
16078
|
function isCommandAvailable2(cmd) {
|
|
15974
16079
|
try {
|
|
15975
16080
|
execSync3(`which ${cmd}`, { stdio: "ignore" });
|
|
@@ -15990,7 +16095,7 @@ function pruneSessionRunnerLogs() {
|
|
|
15990
16095
|
if (entries.length <= MAX_SESSION_RUNNER_LOGS)
|
|
15991
16096
|
return;
|
|
15992
16097
|
const withMtime = entries.map((name) => {
|
|
15993
|
-
const full =
|
|
16098
|
+
const full = join4(logDir, name);
|
|
15994
16099
|
try {
|
|
15995
16100
|
return { name, mtime: statSync(full).mtimeMs };
|
|
15996
16101
|
} catch {
|
|
@@ -16000,7 +16105,7 @@ function pruneSessionRunnerLogs() {
|
|
|
16000
16105
|
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
16001
16106
|
for (const entry of withMtime.slice(MAX_SESSION_RUNNER_LOGS)) {
|
|
16002
16107
|
try {
|
|
16003
|
-
unlinkSync2(
|
|
16108
|
+
unlinkSync2(join4(logDir, entry.name));
|
|
16004
16109
|
} catch {}
|
|
16005
16110
|
}
|
|
16006
16111
|
}
|
|
@@ -16114,12 +16219,30 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16114
16219
|
}
|
|
16115
16220
|
} catch {}
|
|
16116
16221
|
}
|
|
16222
|
+
function evictWorkspace(workspaceId) {
|
|
16223
|
+
const idx = workspaceStates.findIndex((ws2) => ws2.workspaceId === workspaceId);
|
|
16224
|
+
if (idx === -1)
|
|
16225
|
+
return;
|
|
16226
|
+
const ws = workspaceStates[idx];
|
|
16227
|
+
for (const rid of ws.runtimeIds) {
|
|
16228
|
+
runtimeIndex.delete(rid);
|
|
16229
|
+
}
|
|
16230
|
+
workspaceStates.splice(idx, 1);
|
|
16231
|
+
health.setRuntimeCount(workspaceStates.reduce((sum, w) => sum + w.runtimeIds.length, 0));
|
|
16232
|
+
try {
|
|
16233
|
+
const cfg = loadCLIConfigForProfile(profile);
|
|
16234
|
+
cfg.watched_workspaces = (cfg.watched_workspaces || []).filter((w) => w.id !== workspaceId);
|
|
16235
|
+
saveCLIConfigForProfile(profile, cfg);
|
|
16236
|
+
} catch {}
|
|
16237
|
+
log.info(`Workspace ${workspaceId} evicted — runtimes removed server-side`);
|
|
16238
|
+
}
|
|
16117
16239
|
const pollCycle = async () => {
|
|
16118
16240
|
let remaining = config2.maxConcurrentTasks - activeTasks.size;
|
|
16119
16241
|
if (remaining <= 0)
|
|
16120
16242
|
return;
|
|
16121
16243
|
const N = workspaceStates.length;
|
|
16122
16244
|
const staggerMs = N > 1 ? Math.floor(config2.pollInterval / N) : 0;
|
|
16245
|
+
const evictedIds = [];
|
|
16123
16246
|
for (let i = 0;i < N; i++) {
|
|
16124
16247
|
if (remaining <= 0)
|
|
16125
16248
|
break;
|
|
@@ -16128,8 +16251,15 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16128
16251
|
await new Promise((r) => setTimeout(r, staggerMs));
|
|
16129
16252
|
}
|
|
16130
16253
|
try {
|
|
16131
|
-
const tasks = await client.poll(ws.token, config2.daemonId, remaining);
|
|
16132
|
-
|
|
16254
|
+
const { tasks: apiTasks, evicted, pending_update } = await client.poll(ws.token, config2.daemonId, remaining, config2.cliVersion);
|
|
16255
|
+
if (evicted) {
|
|
16256
|
+
evictedIds.push(ws.workspaceId);
|
|
16257
|
+
continue;
|
|
16258
|
+
}
|
|
16259
|
+
if (pending_update && !isUpdating()) {
|
|
16260
|
+
handleCliUpdate(pending_update.version, () => requestRestart());
|
|
16261
|
+
}
|
|
16262
|
+
for (const apiTask of apiTasks) {
|
|
16133
16263
|
const task = fromApiTask(apiTask);
|
|
16134
16264
|
syncAgentId(task.agentId, ws.workspaceId);
|
|
16135
16265
|
activeTasks.add(task.id);
|
|
@@ -16143,26 +16273,52 @@ async function startDaemon(profile, serverUrl) {
|
|
|
16143
16273
|
log.debug("Poll error", e);
|
|
16144
16274
|
}
|
|
16145
16275
|
}
|
|
16276
|
+
for (const id of evictedIds) {
|
|
16277
|
+
evictWorkspace(id);
|
|
16278
|
+
}
|
|
16279
|
+
if (workspaceStates.length === 0) {
|
|
16280
|
+
log.info("All workspaces evicted — shutting down");
|
|
16281
|
+
shutdown();
|
|
16282
|
+
}
|
|
16146
16283
|
};
|
|
16147
16284
|
const pollTimer = setInterval(pollCycle, config2.pollInterval);
|
|
16148
16285
|
let shuttingDown = false;
|
|
16286
|
+
let restartRequested = false;
|
|
16287
|
+
const requestRestart = () => {
|
|
16288
|
+
restartRequested = true;
|
|
16289
|
+
shutdown();
|
|
16290
|
+
};
|
|
16149
16291
|
const shutdown = async () => {
|
|
16150
16292
|
if (shuttingDown)
|
|
16151
16293
|
return;
|
|
16152
16294
|
shuttingDown = true;
|
|
16153
|
-
log.info("Shutting down...");
|
|
16295
|
+
log.info(restartRequested ? "Restarting..." : "Shutting down...");
|
|
16154
16296
|
clearInterval(pollTimer);
|
|
16155
|
-
const shutdownMs = Number(process.env.ALOOK_SHUTDOWN_TIMEOUT_MS) || 5000;
|
|
16297
|
+
const shutdownMs = restartRequested ? 30000 : Number(process.env.ALOOK_SHUTDOWN_TIMEOUT_MS) || 5000;
|
|
16156
16298
|
const timeout = setTimeout(() => process.exit(1), shutdownMs);
|
|
16157
16299
|
try {
|
|
16158
16300
|
for (const ws of workspaceStates) {
|
|
16159
16301
|
await client.deregister(ws.token, config2.daemonId);
|
|
16160
16302
|
}
|
|
16161
16303
|
} catch {}
|
|
16162
|
-
clearTimeout(timeout);
|
|
16163
16304
|
releaseDaemonPid(profile);
|
|
16164
|
-
health.server.close()
|
|
16165
|
-
|
|
16305
|
+
health.server.close(() => {
|
|
16306
|
+
if (restartRequested) {
|
|
16307
|
+
const args = ["daemon", "start", "--foreground"];
|
|
16308
|
+
if (profile)
|
|
16309
|
+
args.push("--profile", profile);
|
|
16310
|
+
if (serverUrl)
|
|
16311
|
+
args.push("--server", serverUrl);
|
|
16312
|
+
const child = spawn2("alook", args, {
|
|
16313
|
+
detached: true,
|
|
16314
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
16315
|
+
});
|
|
16316
|
+
child.unref();
|
|
16317
|
+
log.info(`Spawned new daemon (pid=${child.pid})`);
|
|
16318
|
+
}
|
|
16319
|
+
clearTimeout(timeout);
|
|
16320
|
+
process.exit(0);
|
|
16321
|
+
});
|
|
16166
16322
|
};
|
|
16167
16323
|
process.on("SIGTERM", shutdown);
|
|
16168
16324
|
process.on("SIGINT", shutdown);
|
|
@@ -16172,16 +16328,16 @@ function spawnSessionRunner(input) {
|
|
|
16172
16328
|
const encoded = Buffer.from(JSON.stringify(input)).toString("base64");
|
|
16173
16329
|
const logDir = sessionRunnerLogDir();
|
|
16174
16330
|
mkdirSync3(logDir, { recursive: true });
|
|
16175
|
-
const tmpLogPath =
|
|
16331
|
+
const tmpLogPath = join4(logDir, `${input.task.id}.log`);
|
|
16176
16332
|
const fd = openSync(tmpLogPath, "a");
|
|
16177
|
-
const child =
|
|
16333
|
+
const child = spawn2(process.execPath, [sessionRunnerPath, encoded], {
|
|
16178
16334
|
detached: true,
|
|
16179
16335
|
stdio: ["ignore", fd, fd]
|
|
16180
16336
|
});
|
|
16181
16337
|
child.unref();
|
|
16182
16338
|
closeSync(fd);
|
|
16183
16339
|
if (child.pid) {
|
|
16184
|
-
const pidLogPath =
|
|
16340
|
+
const pidLogPath = join4(logDir, `${child.pid}.log`);
|
|
16185
16341
|
renameSync(tmpLogPath, pidLogPath);
|
|
16186
16342
|
}
|
|
16187
16343
|
return child;
|
|
@@ -16256,9 +16412,9 @@ async function startInBackground(profile, serverUrl) {
|
|
|
16256
16412
|
return;
|
|
16257
16413
|
}
|
|
16258
16414
|
const logPath = daemonLogFilePath();
|
|
16259
|
-
mkdirSync4(
|
|
16415
|
+
mkdirSync4(dirname4(logPath), { recursive: true, mode: 448 });
|
|
16260
16416
|
const logFd = openSync2(logPath, "a", 384);
|
|
16261
|
-
const child =
|
|
16417
|
+
const child = spawn3(process.execPath, buildChildArgs(profile, serverUrl), {
|
|
16262
16418
|
detached: true,
|
|
16263
16419
|
stdio: ["ignore", logFd, logFd]
|
|
16264
16420
|
});
|
|
@@ -16366,8 +16522,8 @@ function configCommand() {
|
|
|
16366
16522
|
|
|
16367
16523
|
// commands/email.ts
|
|
16368
16524
|
import { Command as Command5 } from "commander";
|
|
16369
|
-
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync5, readFileSync as
|
|
16370
|
-
import { basename, join as
|
|
16525
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
16526
|
+
import { basename, join as join5 } from "path";
|
|
16371
16527
|
import PostalMime from "postal-mime";
|
|
16372
16528
|
var VALID_STATUSES = ["unread", "read", "archived"];
|
|
16373
16529
|
var EMAIL_DIR = "/tmp/alook-emails";
|
|
@@ -16443,7 +16599,7 @@ function emailCommand() {
|
|
|
16443
16599
|
mkdirSync5(EMAIL_DIR, { recursive: true });
|
|
16444
16600
|
const downloadedPaths = [];
|
|
16445
16601
|
for (const email3 of emails2) {
|
|
16446
|
-
const emailDir =
|
|
16602
|
+
const emailDir = join5(EMAIL_DIR, email3.id);
|
|
16447
16603
|
mkdirSync5(emailDir, { recursive: true });
|
|
16448
16604
|
const metadata = {
|
|
16449
16605
|
id: email3.id,
|
|
@@ -16456,7 +16612,7 @@ function emailCommand() {
|
|
|
16456
16612
|
in_reply_to: email3.in_reply_to || "",
|
|
16457
16613
|
references: email3.references || ""
|
|
16458
16614
|
};
|
|
16459
|
-
const metadataPath =
|
|
16615
|
+
const metadataPath = join5(emailDir, "metadata.json");
|
|
16460
16616
|
writeFileSync3(metadataPath, JSON.stringify(metadata, null, 2));
|
|
16461
16617
|
downloadedPaths.push(metadataPath);
|
|
16462
16618
|
let rawMime;
|
|
@@ -16472,17 +16628,17 @@ function emailCommand() {
|
|
|
16472
16628
|
}
|
|
16473
16629
|
const parsed = await new PostalMime().parse(rawMime);
|
|
16474
16630
|
if (parsed.text) {
|
|
16475
|
-
const bodyPath =
|
|
16631
|
+
const bodyPath = join5(emailDir, "body.txt");
|
|
16476
16632
|
writeFileSync3(bodyPath, parsed.text);
|
|
16477
16633
|
downloadedPaths.push(bodyPath);
|
|
16478
16634
|
}
|
|
16479
16635
|
if (parsed.html) {
|
|
16480
|
-
const htmlPath =
|
|
16636
|
+
const htmlPath = join5(emailDir, "body.html");
|
|
16481
16637
|
writeFileSync3(htmlPath, parsed.html);
|
|
16482
16638
|
downloadedPaths.push(htmlPath);
|
|
16483
16639
|
}
|
|
16484
16640
|
if (parsed.attachments && parsed.attachments.length > 0) {
|
|
16485
|
-
const attDir =
|
|
16641
|
+
const attDir = join5(emailDir, "attachments");
|
|
16486
16642
|
mkdirSync5(attDir, { recursive: true });
|
|
16487
16643
|
const usedFilenames = new Set;
|
|
16488
16644
|
for (let i = 0;i < parsed.attachments.length; i++) {
|
|
@@ -16492,7 +16648,7 @@ function emailCommand() {
|
|
|
16492
16648
|
filename = `${i}-${filename}`;
|
|
16493
16649
|
}
|
|
16494
16650
|
usedFilenames.add(filename);
|
|
16495
|
-
const attPath =
|
|
16651
|
+
const attPath = join5(attDir, filename);
|
|
16496
16652
|
const content = att.content;
|
|
16497
16653
|
let buf;
|
|
16498
16654
|
if (typeof content === "string") {
|
|
@@ -16541,7 +16697,7 @@ function emailCommand() {
|
|
|
16541
16697
|
const client = new APIClient(serverUrl, token, workspaceId);
|
|
16542
16698
|
let htmlBody;
|
|
16543
16699
|
try {
|
|
16544
|
-
htmlBody =
|
|
16700
|
+
htmlBody = readFileSync4(opts.bodyFile, "utf-8");
|
|
16545
16701
|
} catch (err) {
|
|
16546
16702
|
console.error(`Error: cannot read body file "${opts.bodyFile}": ${err instanceof Error ? err.message : err}`);
|
|
16547
16703
|
process.exit(1);
|
|
@@ -16557,7 +16713,7 @@ function emailCommand() {
|
|
|
16557
16713
|
let bytes;
|
|
16558
16714
|
let size;
|
|
16559
16715
|
try {
|
|
16560
|
-
bytes =
|
|
16716
|
+
bytes = readFileSync4(path);
|
|
16561
16717
|
size = statSync2(path).size;
|
|
16562
16718
|
} catch (err) {
|
|
16563
16719
|
console.error(`Error: cannot read attachment "${path}": ${err instanceof Error ? err.message : err}`);
|
|
@@ -16816,28 +16972,6 @@ function calendarCommand() {
|
|
|
16816
16972
|
|
|
16817
16973
|
// commands/version.ts
|
|
16818
16974
|
import { Command as Command7 } from "commander";
|
|
16819
|
-
|
|
16820
|
-
// lib/version.ts
|
|
16821
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
16822
|
-
import { join as join5, dirname as dirname4 } from "path";
|
|
16823
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16824
|
-
function getCurrentVersion() {
|
|
16825
|
-
const __dirname2 = dirname4(fileURLToPath2(import.meta.url));
|
|
16826
|
-
const candidates = [
|
|
16827
|
-
join5(__dirname2, "..", "package.json"),
|
|
16828
|
-
join5(__dirname2, "..", "..", "package.json")
|
|
16829
|
-
];
|
|
16830
|
-
for (const candidate of candidates) {
|
|
16831
|
-
try {
|
|
16832
|
-
const pkg = JSON.parse(readFileSync4(candidate, "utf-8"));
|
|
16833
|
-
if (typeof pkg.version === "string")
|
|
16834
|
-
return pkg.version;
|
|
16835
|
-
} catch {}
|
|
16836
|
-
}
|
|
16837
|
-
return "unknown";
|
|
16838
|
-
}
|
|
16839
|
-
|
|
16840
|
-
// commands/version.ts
|
|
16841
16975
|
function versionCommand() {
|
|
16842
16976
|
const cmd = new Command7("version").description("Show CLI version").action(() => {
|
|
16843
16977
|
console.log(`alook version ${getCurrentVersion()}`);
|
|
@@ -16845,8 +16979,44 @@ function versionCommand() {
|
|
|
16845
16979
|
return cmd;
|
|
16846
16980
|
}
|
|
16847
16981
|
|
|
16982
|
+
// commands/update.ts
|
|
16983
|
+
import { Command as Command8 } from "commander";
|
|
16984
|
+
function updateCommand() {
|
|
16985
|
+
const cmd = new Command8("update").description("Update CLI to the latest version").action(async () => {
|
|
16986
|
+
const current = getCurrentVersion();
|
|
16987
|
+
console.log(`Current version: ${current}`);
|
|
16988
|
+
const latest = await fetchLatestVersion();
|
|
16989
|
+
if (!latest) {
|
|
16990
|
+
console.error("Failed to fetch latest version from npm registry.");
|
|
16991
|
+
process.exit(1);
|
|
16992
|
+
return;
|
|
16993
|
+
}
|
|
16994
|
+
if (semverGte(current, latest)) {
|
|
16995
|
+
console.log(`Already up to date (v${current}).`);
|
|
16996
|
+
return;
|
|
16997
|
+
}
|
|
16998
|
+
const healthPort = Number(process.env.ALOOK_HEALTH_PORT) || 19514;
|
|
16999
|
+
try {
|
|
17000
|
+
const res = await fetch(`http://127.0.0.1:${healthPort}/health`);
|
|
17001
|
+
if (res.ok) {
|
|
17002
|
+
console.warn("Warning: daemon is running on the old version. After update, restart with: alook daemon restart");
|
|
17003
|
+
}
|
|
17004
|
+
} catch {}
|
|
17005
|
+
console.log(`Updating to v${latest}...`);
|
|
17006
|
+
const result = await runNpmUpdate(latest);
|
|
17007
|
+
if (result.success) {
|
|
17008
|
+
console.log(`Updated successfully: v${current} → v${latest}`);
|
|
17009
|
+
} else {
|
|
17010
|
+
console.error(`Update failed:
|
|
17011
|
+
${result.output}`);
|
|
17012
|
+
process.exit(1);
|
|
17013
|
+
}
|
|
17014
|
+
});
|
|
17015
|
+
return cmd;
|
|
17016
|
+
}
|
|
17017
|
+
|
|
16848
17018
|
// src/index.ts
|
|
16849
|
-
var program = new
|
|
17019
|
+
var program = new Command9;
|
|
16850
17020
|
program.name("alook").description("Alook CLI").option("--server <url>", "Server URL").option("--profile <name>", "Profile name");
|
|
16851
17021
|
program.addCommand(registerCommand());
|
|
16852
17022
|
program.addCommand(statusCommand());
|
|
@@ -16855,4 +17025,5 @@ program.addCommand(emailCommand());
|
|
|
16855
17025
|
program.addCommand(calendarCommand());
|
|
16856
17026
|
program.addCommand(configCommand());
|
|
16857
17027
|
program.addCommand(versionCommand());
|
|
17028
|
+
program.addCommand(updateCommand());
|
|
16858
17029
|
program.parse();
|
package/dist/session-runner.js
CHANGED
|
@@ -13617,10 +13617,13 @@ var TaskApiSchema = TaskApiBaseSchema.extend({
|
|
|
13617
13617
|
});
|
|
13618
13618
|
var PollRequestSchema = exports_external.object({
|
|
13619
13619
|
daemon_id: exports_external.string().min(1),
|
|
13620
|
-
max_tasks: exports_external.number().int().min(1).default(1)
|
|
13620
|
+
max_tasks: exports_external.number().int().min(1).default(1),
|
|
13621
|
+
cli_version: exports_external.string().optional()
|
|
13621
13622
|
});
|
|
13622
13623
|
var PollResponseSchema = exports_external.object({
|
|
13623
|
-
tasks: exports_external.array(TaskApiSchema)
|
|
13624
|
+
tasks: exports_external.array(TaskApiSchema),
|
|
13625
|
+
evicted: exports_external.boolean().optional(),
|
|
13626
|
+
pending_update: exports_external.object({ version: exports_external.string() }).optional()
|
|
13624
13627
|
});
|
|
13625
13628
|
var RegisterResponseSchema = exports_external.object({
|
|
13626
13629
|
runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
|
|
@@ -13723,6 +13726,9 @@ var CalendarEventApiSchema = exports_external.object({
|
|
|
13723
13726
|
created_at: exports_external.string(),
|
|
13724
13727
|
updated_at: exports_external.string()
|
|
13725
13728
|
});
|
|
13729
|
+
var AddWhitelistRequestSchema = exports_external.object({
|
|
13730
|
+
email: exports_external.string().email()
|
|
13731
|
+
});
|
|
13726
13732
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@4.20260418.1_@opentelemetry+api@1.9.1_bun-types@1.3.12_kysely@0.28.16/node_modules/drizzle-orm/entity.js
|
|
13727
13733
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
13728
13734
|
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
@@ -15159,6 +15165,7 @@ var machine = sqliteTable("machine", {
|
|
|
15159
15165
|
deviceInfo: text("device_info").notNull().default(""),
|
|
15160
15166
|
lastSeenAt: text("last_seen_at"),
|
|
15161
15167
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
15168
|
+
pendingUpdateVersion: text("pending_update_version"),
|
|
15162
15169
|
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
15163
15170
|
}, (t) => [primaryKey({ columns: [t.workspaceId, t.daemonId] })]);
|
|
15164
15171
|
var agentRuntime = sqliteTable("agent_runtime", {
|
|
@@ -15376,10 +15383,14 @@ class DaemonClient {
|
|
|
15376
15383
|
daemon_id: daemonId
|
|
15377
15384
|
});
|
|
15378
15385
|
}
|
|
15379
|
-
async poll(token, daemonId, maxTasks) {
|
|
15380
|
-
const raw = await this.request("POST", "/api/daemon/tasks/poll", token, { daemon_id: daemonId, max_tasks: maxTasks });
|
|
15386
|
+
async poll(token, daemonId, maxTasks, cliVersion) {
|
|
15387
|
+
const raw = await this.request("POST", "/api/daemon/tasks/poll", token, { daemon_id: daemonId, max_tasks: maxTasks, ...cliVersion && { cli_version: cliVersion } });
|
|
15381
15388
|
const resp = PollResponseSchema.parse(raw);
|
|
15382
|
-
return
|
|
15389
|
+
return {
|
|
15390
|
+
tasks: resp.tasks,
|
|
15391
|
+
evicted: resp.evicted ?? false,
|
|
15392
|
+
pending_update: resp.pending_update
|
|
15393
|
+
};
|
|
15383
15394
|
}
|
|
15384
15395
|
startTask(token, taskId) {
|
|
15385
15396
|
return this.request("POST", `/api/daemon/tasks/${taskId}/start`, token);
|