@alook/cli 0.0.119 → 0.0.120
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 +195 -54
- package/dist/session-runner.js +40 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14561,7 +14561,8 @@ var DaemonPushMessageSchema = exports_external.discriminatedUnion("type", [
|
|
|
14561
14561
|
exports_external.object({ type: exports_external.literal("daemon.evict"), workspaceId: exports_external.string() }),
|
|
14562
14562
|
exports_external.object({ type: exports_external.literal("daemon.update"), version: exports_external.string() }),
|
|
14563
14563
|
exports_external.object({ type: exports_external.literal("daemon.rescan") }),
|
|
14564
|
-
exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() })
|
|
14564
|
+
exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() }),
|
|
14565
|
+
exports_external.object({ type: exports_external.literal("daemon.workspace_added"), workspaceId: exports_external.string(), workspaceName: exports_external.string(), token: exports_external.string() })
|
|
14565
14566
|
]);
|
|
14566
14567
|
var RegisterResponseSchema = exports_external.object({
|
|
14567
14568
|
runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
|
|
@@ -14591,6 +14592,9 @@ var RegisterDaemonRequestSchema = exports_external.object({
|
|
|
14591
14592
|
workspaces_root: exports_external.string().optional().default(""),
|
|
14592
14593
|
runtimes: exports_external.array(DaemonRuntimeItemSchema).min(1)
|
|
14593
14594
|
});
|
|
14595
|
+
var BindWorkspaceRequestSchema = exports_external.object({
|
|
14596
|
+
workspace_id: exports_external.string().min(1)
|
|
14597
|
+
});
|
|
14594
14598
|
var DeregisterRequestSchema = exports_external.object({
|
|
14595
14599
|
daemon_id: exports_external.string().min(1)
|
|
14596
14600
|
});
|
|
@@ -14752,7 +14756,8 @@ var CreateConversationRequestSchema = exports_external.object({
|
|
|
14752
14756
|
channel: exports_external.string().optional()
|
|
14753
14757
|
});
|
|
14754
14758
|
var CreateMessageRequestSchema = exports_external.object({
|
|
14755
|
-
content: exports_external.string().min(1, "content is required")
|
|
14759
|
+
content: exports_external.string().min(1, "content is required"),
|
|
14760
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
14756
14761
|
});
|
|
14757
14762
|
var AgentDmRequestSchema = exports_external.object({
|
|
14758
14763
|
content: exports_external.string().min(1, "content is required"),
|
|
@@ -16735,6 +16740,8 @@ var machineToken = sqliteTable("machine_token", {
|
|
|
16735
16740
|
token: text("token").unique().notNull(),
|
|
16736
16741
|
name: text("name").notNull().default(""),
|
|
16737
16742
|
status: text("status").notNull().default("active"),
|
|
16743
|
+
hostname: text("hostname"),
|
|
16744
|
+
runtimesJson: text("runtimes_json"),
|
|
16738
16745
|
lastUsedAt: text("last_used_at"),
|
|
16739
16746
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
16740
16747
|
}, (t) => [index("idx_machine_token").on(t.token)]);
|
|
@@ -16883,7 +16890,24 @@ function isLocalUrl(url2) {
|
|
|
16883
16890
|
return false;
|
|
16884
16891
|
}
|
|
16885
16892
|
}
|
|
16893
|
+
function hasWindow() {
|
|
16894
|
+
return typeof globalThis !== "undefined" && "window" in globalThis;
|
|
16895
|
+
}
|
|
16896
|
+
function isTauri() {
|
|
16897
|
+
return hasWindow() && typeof window !== "undefined" && "__TAURI__" in window;
|
|
16898
|
+
}
|
|
16899
|
+
function isMobile() {
|
|
16900
|
+
if (!isTauri())
|
|
16901
|
+
return false;
|
|
16902
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
16903
|
+
return /(android|iphone|ipad|ipod)/i.test(ua);
|
|
16904
|
+
}
|
|
16886
16905
|
function resolveMode(signals) {
|
|
16906
|
+
if (signals.tauri || isTauri()) {
|
|
16907
|
+
if (signals.tauriPlatform === "mobile" || isMobile())
|
|
16908
|
+
return "mobile";
|
|
16909
|
+
return "desktop";
|
|
16910
|
+
}
|
|
16887
16911
|
if (signals.nodeEnv === "development" && !signals.cmdPrefix)
|
|
16888
16912
|
return "dev";
|
|
16889
16913
|
if (signals.serverUrl && !signals.cmdPrefix && signals.nodeEnv !== "production" && isLocalUrl(signals.serverUrl))
|
|
@@ -16900,6 +16924,8 @@ function cliCommand(mode) {
|
|
|
16900
16924
|
return "pnpm dev:cli";
|
|
16901
16925
|
case "app":
|
|
16902
16926
|
return "npx @alook/app cli";
|
|
16927
|
+
case "desktop":
|
|
16928
|
+
case "mobile":
|
|
16903
16929
|
case "production":
|
|
16904
16930
|
return "npx @alook/cli";
|
|
16905
16931
|
}
|
|
@@ -16958,10 +16984,19 @@ function loadCLIConfigForProfile(profile) {
|
|
|
16958
16984
|
if (profileName && cfg.profiles?.[profileName]) {
|
|
16959
16985
|
return cfg.profiles[profileName];
|
|
16960
16986
|
}
|
|
16961
|
-
|
|
16987
|
+
const result = {
|
|
16962
16988
|
server_url: cfg.server_url || "",
|
|
16963
16989
|
watched_workspaces: cfg.watched_workspaces || []
|
|
16964
16990
|
};
|
|
16991
|
+
const legacy = cfg.machine_token;
|
|
16992
|
+
if (legacy && !result.watched_workspaces.some((w) => w.token === legacy)) {
|
|
16993
|
+
result.watched_workspaces.push({ id: null, name: null, token: legacy, status: "registered", agent_ids: [] });
|
|
16994
|
+
}
|
|
16995
|
+
for (const ws of result.watched_workspaces) {
|
|
16996
|
+
if (!ws.status)
|
|
16997
|
+
ws.status = ws.id ? "active" : "registered";
|
|
16998
|
+
}
|
|
16999
|
+
return result;
|
|
16965
17000
|
}
|
|
16966
17001
|
function saveCLIConfig(cfg) {
|
|
16967
17002
|
mkdirSync(configDir(), { recursive: true, mode: 448 });
|
|
@@ -16976,6 +17011,7 @@ function saveCLIConfigForProfile(profile, profileConfig) {
|
|
|
16976
17011
|
} else {
|
|
16977
17012
|
cfg.server_url = profileConfig.server_url;
|
|
16978
17013
|
cfg.watched_workspaces = profileConfig.watched_workspaces;
|
|
17014
|
+
delete cfg.machine_token;
|
|
16979
17015
|
}
|
|
16980
17016
|
saveCLIConfig(cfg);
|
|
16981
17017
|
}
|
|
@@ -17296,7 +17332,7 @@ async function activateAndSave(opts) {
|
|
|
17296
17332
|
}
|
|
17297
17333
|
console.log(`Found: ${runtimes.map((r) => r.type).join(", ")}`);
|
|
17298
17334
|
const host = hostname4();
|
|
17299
|
-
console.log("Registering
|
|
17335
|
+
console.log("Registering machine...");
|
|
17300
17336
|
let activateResp;
|
|
17301
17337
|
try {
|
|
17302
17338
|
const res = await fetch(`${serverUrl}/api/machine-tokens/activate`, {
|
|
@@ -17314,32 +17350,10 @@ async function activateAndSave(opts) {
|
|
|
17314
17350
|
console.error(`Error: failed to activate: ${err instanceof Error ? err.message : err}`);
|
|
17315
17351
|
process.exit(1);
|
|
17316
17352
|
}
|
|
17317
|
-
const client = new APIClient(serverUrl, token);
|
|
17318
|
-
let workspaces;
|
|
17319
|
-
try {
|
|
17320
|
-
workspaces = await client.getJSON("/api/workspaces");
|
|
17321
|
-
} catch (err) {
|
|
17322
|
-
console.error(`Error: failed to fetch workspaces: ${err instanceof Error ? err.message : err}`);
|
|
17323
|
-
process.exit(1);
|
|
17324
|
-
}
|
|
17325
|
-
if (!workspaces.length) {
|
|
17326
|
-
console.error("Error: no workspaces found for this user");
|
|
17327
|
-
process.exit(1);
|
|
17328
|
-
}
|
|
17329
|
-
const ws = workspaces.find((w) => w.id === activateResp.workspace_id) || workspaces[0];
|
|
17330
|
-
const wsClient = new APIClient(serverUrl, token, ws.id);
|
|
17331
|
-
let agentIds = [];
|
|
17332
|
-
try {
|
|
17333
|
-
const agents = await wsClient.getJSON(`/api/agents?workspace_id=${ws.id}`);
|
|
17334
|
-
agentIds = agents.map((a) => a.id);
|
|
17335
|
-
} catch {}
|
|
17336
17353
|
const existing = loadCLIConfigForProfile(profile);
|
|
17337
17354
|
const watched = existing.watched_workspaces || [];
|
|
17338
|
-
|
|
17339
|
-
|
|
17340
|
-
watched[idx] = { id: ws.id, name: ws.name, token, agent_ids: agentIds };
|
|
17341
|
-
} else {
|
|
17342
|
-
watched.push({ id: ws.id, name: ws.name, token, agent_ids: agentIds });
|
|
17355
|
+
if (!watched.some((w) => w.token === token)) {
|
|
17356
|
+
watched.push({ id: null, name: null, token, status: "registered", agent_ids: [] });
|
|
17343
17357
|
}
|
|
17344
17358
|
saveCLIConfigForProfile(profile, {
|
|
17345
17359
|
server_url: serverUrl,
|
|
@@ -17350,10 +17364,10 @@ async function activateAndSave(opts) {
|
|
|
17350
17364
|
try {
|
|
17351
17365
|
process.kill(daemonPid, "SIGHUP");
|
|
17352
17366
|
console.log(`
|
|
17353
|
-
Daemon (pid ${daemonPid}) notified —
|
|
17367
|
+
Daemon (pid ${daemonPid}) notified — machine registered, awaiting workspace binding.`);
|
|
17354
17368
|
} catch {
|
|
17355
17369
|
console.log(`
|
|
17356
|
-
Daemon is running but could not be notified. Restart it to pick up the new
|
|
17370
|
+
Daemon is running but could not be notified. Restart it to pick up the new token.`);
|
|
17357
17371
|
}
|
|
17358
17372
|
} else {
|
|
17359
17373
|
const startCmd = isDev() ? `${cmdPrefix()} daemon start --foreground` : `${cmdPrefix()} daemon start`;
|
|
@@ -17361,9 +17375,8 @@ Daemon is running but could not be notified. Restart it to pick up the new works
|
|
|
17361
17375
|
console.log(`Run '${startCmd}' to start the daemon.`);
|
|
17362
17376
|
}
|
|
17363
17377
|
return {
|
|
17364
|
-
|
|
17365
|
-
|
|
17366
|
-
runtimeProviders: activateResp.runtimes.map((r) => r.provider)
|
|
17378
|
+
daemonId: activateResp.daemon_id,
|
|
17379
|
+
tokenStatus: activateResp.token_status
|
|
17367
17380
|
};
|
|
17368
17381
|
}
|
|
17369
17382
|
|
|
@@ -17393,8 +17406,8 @@ Usage: ${cmdPrefix()} register --token <token>`);
|
|
|
17393
17406
|
const result = await activateAndSave({ token, serverUrl, profile });
|
|
17394
17407
|
console.log(`
|
|
17395
17408
|
Registered as ${me.email}`);
|
|
17396
|
-
console.log(`
|
|
17397
|
-
console.log(`
|
|
17409
|
+
console.log(`Machine: ${result.daemonId} (status: ${result.tokenStatus})`);
|
|
17410
|
+
console.log(`Workspace binding will happen when you launch a company.`);
|
|
17398
17411
|
});
|
|
17399
17412
|
return cmd;
|
|
17400
17413
|
}
|
|
@@ -17486,8 +17499,8 @@ async function pollAndActivate(opts) {
|
|
|
17486
17499
|
console.log(`
|
|
17487
17500
|
Logged in as ${email3}`);
|
|
17488
17501
|
}
|
|
17489
|
-
console.log(`
|
|
17490
|
-
console.log(`
|
|
17502
|
+
console.log(`Machine: ${result.daemonId} (status: ${result.tokenStatus})`);
|
|
17503
|
+
console.log(`Workspace binding will happen when you launch a company.`);
|
|
17491
17504
|
}
|
|
17492
17505
|
if (process.argv.includes("--__login-poll")) {
|
|
17493
17506
|
const idx = process.argv.indexOf("--__login-poll");
|
|
@@ -17527,7 +17540,7 @@ async function checkExistingAuth(serverUrl, profile) {
|
|
|
17527
17540
|
email3 = me.email;
|
|
17528
17541
|
}
|
|
17529
17542
|
} catch {}
|
|
17530
|
-
return { valid: true, email: email3, workspaceName: ws.name };
|
|
17543
|
+
return { valid: true, email: email3, workspaceName: ws.name ?? undefined };
|
|
17531
17544
|
} catch {
|
|
17532
17545
|
return { valid: false };
|
|
17533
17546
|
}
|
|
@@ -17752,6 +17765,9 @@ class DaemonClient {
|
|
|
17752
17765
|
syncSkills(token, body) {
|
|
17753
17766
|
return this.request("POST", "/api/daemon/skills/sync", token, body);
|
|
17754
17767
|
}
|
|
17768
|
+
async checkStandby(token, body) {
|
|
17769
|
+
return this.request("POST", "/api/daemon/register", token, body);
|
|
17770
|
+
}
|
|
17755
17771
|
}
|
|
17756
17772
|
|
|
17757
17773
|
// daemon/health.ts
|
|
@@ -19199,6 +19215,8 @@ Don't bundle everything into one giant message at the end. The user shouldn't ha
|
|
|
19199
19215
|
|
|
19200
19216
|
\`${cmdPrefix()} sync send-dm --message "…"\` for short messages. For longer or markdown-rich messages, write to a file first and use \`--message-file <path>\` — this preserves formatting and avoids shell escaping issues. The conversation is in $ALOOK_CONVERSATION_ID, so you usually need no flags. You can send several times in one task.
|
|
19201
19217
|
|
|
19218
|
+
Your messages are rendered as **markdown** in the user's app. Use formatting to make your responses clear and scannable — headers, bullet lists, code blocks, bold for key points. Don't send a wall of plain text when structure would help the reader. For anything beyond a one-liner, prefer \`--message-file\` so you can write proper markdown without fighting shell escaping.
|
|
19219
|
+
|
|
19202
19220
|
### Attachments
|
|
19203
19221
|
When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
|
|
19204
19222
|
Use your Read tool to open them. Images and PDFs are read visually.
|
|
@@ -19681,6 +19699,13 @@ function buildTaskObject(task, attachments) {
|
|
|
19681
19699
|
};
|
|
19682
19700
|
if (task.type === "user_dm_message") {
|
|
19683
19701
|
obj.notice = DM_RESPONSE_NOTICE;
|
|
19702
|
+
const ctx = task.context;
|
|
19703
|
+
if (ctx?.message_id) {
|
|
19704
|
+
obj.message_id = ctx.message_id;
|
|
19705
|
+
}
|
|
19706
|
+
if (ctx?.quoted_message) {
|
|
19707
|
+
obj.quoted_message = ctx.quoted_message;
|
|
19708
|
+
}
|
|
19684
19709
|
}
|
|
19685
19710
|
if (task.type === "email_notification") {
|
|
19686
19711
|
const ctx = task.context;
|
|
@@ -21027,11 +21052,14 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21027
21052
|
}
|
|
21028
21053
|
}
|
|
21029
21054
|
const cliConfig = loadCLIConfigForProfile(profile);
|
|
21030
|
-
const
|
|
21031
|
-
|
|
21032
|
-
|
|
21033
|
-
|
|
21034
|
-
|
|
21055
|
+
const allEntries = cliConfig.watched_workspaces || [];
|
|
21056
|
+
const workspaces = allEntries.filter((ws) => ws.status !== "registered" && !!ws.id);
|
|
21057
|
+
const registeredEntries = allEntries.filter((ws) => ws.status === "registered" && !ws.id);
|
|
21058
|
+
const standbyToken = registeredEntries[0]?.token ?? null;
|
|
21059
|
+
if (workspaces.length === 0 && standbyToken) {
|
|
21060
|
+
log10.info("No workspaces configured — daemon starting in standby mode with machine token. Awaiting workspace binding.");
|
|
21061
|
+
} else if (workspaces.length === 0) {
|
|
21062
|
+
log10.info("No workspaces configured — daemon starting in standby mode. Register a workspace to begin.");
|
|
21035
21063
|
}
|
|
21036
21064
|
const hasPerWorkspaceTokens = workspaces.every((ws) => !!ws.token);
|
|
21037
21065
|
if (!hasPerWorkspaceTokens) {
|
|
@@ -21062,6 +21090,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21062
21090
|
log10.info(`Detected providers: ${providers.map((p) => `${p.type}@${p.version}`).join(", ")}`);
|
|
21063
21091
|
const workspaceStates = [];
|
|
21064
21092
|
const runtimeIndex = new Map;
|
|
21093
|
+
let hadWorkspaces = workspaces.length > 0;
|
|
21065
21094
|
for (const ws of workspaces) {
|
|
21066
21095
|
const runtimes = providers.map((p) => ({
|
|
21067
21096
|
type: p.type,
|
|
@@ -21097,7 +21126,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21097
21126
|
});
|
|
21098
21127
|
}
|
|
21099
21128
|
}
|
|
21100
|
-
if (workspaceStates.length === 0) {
|
|
21129
|
+
if (workspaceStates.length === 0 && workspaces.length > 0) {
|
|
21101
21130
|
log10.error("No workspaces registered successfully.");
|
|
21102
21131
|
process.exit(1);
|
|
21103
21132
|
return;
|
|
@@ -21142,6 +21171,50 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21142
21171
|
} catch {}
|
|
21143
21172
|
log10.info(`Workspace ${workspaceId} deleted server-side — removed from config`);
|
|
21144
21173
|
}
|
|
21174
|
+
async function handleWorkspaceAdded(workspaceId, workspaceName, token) {
|
|
21175
|
+
if (workspaceStates.some((ws) => ws.workspaceId === workspaceId)) {
|
|
21176
|
+
log10.info(`Workspace ${workspaceId} already registered — ignoring workspace_added`);
|
|
21177
|
+
return;
|
|
21178
|
+
}
|
|
21179
|
+
log10.info(`Workspace ${workspaceId} bound — registering...`);
|
|
21180
|
+
const runtimes = providers.map((p) => ({ type: p.type, version: p.version }));
|
|
21181
|
+
try {
|
|
21182
|
+
const resp = await client.register(token, {
|
|
21183
|
+
workspace_id: workspaceId,
|
|
21184
|
+
daemon_id: config2.daemonId,
|
|
21185
|
+
device_name: config2.deviceName,
|
|
21186
|
+
cli_version: config2.cliVersion,
|
|
21187
|
+
workspaces_root: config2.workspacesRoot,
|
|
21188
|
+
runtimes
|
|
21189
|
+
});
|
|
21190
|
+
const runtimeIds = resp.runtimes.map((r) => r.id);
|
|
21191
|
+
workspaceStates.push({ workspaceId, token, runtimeIds });
|
|
21192
|
+
for (let i = 0;i < runtimeIds.length; i++) {
|
|
21193
|
+
runtimeIndex.set(runtimeIds[i], {
|
|
21194
|
+
id: runtimeIds[i],
|
|
21195
|
+
workspaceId,
|
|
21196
|
+
provider: providers[i].type
|
|
21197
|
+
});
|
|
21198
|
+
}
|
|
21199
|
+
hadWorkspaces = true;
|
|
21200
|
+
health.setRuntimeCount(workspaceStates.reduce((sum, w) => sum + w.runtimeIds.length, 0));
|
|
21201
|
+
try {
|
|
21202
|
+
const cfg = loadCLIConfigForProfile(profile);
|
|
21203
|
+
const watched = cfg.watched_workspaces || [];
|
|
21204
|
+
const registeredIdx = watched.findIndex((w) => w.token === token && w.status === "registered" && !w.id);
|
|
21205
|
+
if (registeredIdx !== -1) {
|
|
21206
|
+
watched[registeredIdx] = { id: workspaceId, name: workspaceName, token, status: "active", agent_ids: [] };
|
|
21207
|
+
} else if (!watched.some((w) => w.id === workspaceId)) {
|
|
21208
|
+
watched.push({ id: workspaceId, name: workspaceName, token, status: "active" });
|
|
21209
|
+
}
|
|
21210
|
+
cfg.watched_workspaces = watched;
|
|
21211
|
+
saveCLIConfigForProfile(profile, cfg);
|
|
21212
|
+
} catch {}
|
|
21213
|
+
log10.info(`Workspace ${workspaceId} added via WS push — ${runtimeIds.length} runtime(s)`);
|
|
21214
|
+
} catch (e) {
|
|
21215
|
+
log10.error(`Failed to register workspace ${workspaceId} from WS push`, e);
|
|
21216
|
+
}
|
|
21217
|
+
}
|
|
21145
21218
|
const pollCycle = async () => {
|
|
21146
21219
|
let remaining = config2.maxConcurrentTasks - activeTasks.size;
|
|
21147
21220
|
if (remaining <= 0)
|
|
@@ -21217,7 +21290,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21217
21290
|
for (const id of evictedIds) {
|
|
21218
21291
|
evictWorkspace(id);
|
|
21219
21292
|
}
|
|
21220
|
-
if (workspaceStates.length === 0) {
|
|
21293
|
+
if (workspaceStates.length === 0 && hadWorkspaces) {
|
|
21221
21294
|
log10.info("All workspaces evicted — shutting down");
|
|
21222
21295
|
shutdown();
|
|
21223
21296
|
}
|
|
@@ -21330,23 +21403,69 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21330
21403
|
}
|
|
21331
21404
|
break;
|
|
21332
21405
|
}
|
|
21406
|
+
case "daemon.workspace_added": {
|
|
21407
|
+
handleWorkspaceAdded(msg.workspaceId, msg.workspaceName, msg.token);
|
|
21408
|
+
break;
|
|
21409
|
+
}
|
|
21333
21410
|
}
|
|
21334
21411
|
}
|
|
21335
|
-
const
|
|
21412
|
+
const wsToken = firstToken || standbyToken;
|
|
21413
|
+
let wsClient = wsToken ? new DaemonWsClient({
|
|
21336
21414
|
serverURL: config2.serverURL,
|
|
21337
21415
|
daemonId: config2.daemonId,
|
|
21338
|
-
machineToken:
|
|
21416
|
+
machineToken: wsToken,
|
|
21339
21417
|
onMessage: handleWsPush,
|
|
21340
21418
|
onConnected: () => {
|
|
21341
|
-
|
|
21342
|
-
|
|
21419
|
+
if (workspaceStates.length > 0) {
|
|
21420
|
+
log10.info("WS connected — switching to low-frequency poll");
|
|
21421
|
+
updatePollInterval(config2.wsPollInterval);
|
|
21422
|
+
} else {
|
|
21423
|
+
log10.info("WS connected in standby mode — awaiting workspace binding");
|
|
21424
|
+
}
|
|
21343
21425
|
},
|
|
21344
21426
|
onDisconnected: () => {
|
|
21345
|
-
|
|
21346
|
-
|
|
21427
|
+
if (workspaceStates.length > 0) {
|
|
21428
|
+
log10.info("WS disconnected — reverting to high-frequency poll");
|
|
21429
|
+
updatePollInterval(config2.pollInterval);
|
|
21430
|
+
}
|
|
21347
21431
|
}
|
|
21348
21432
|
}) : null;
|
|
21349
21433
|
wsClient?.connect();
|
|
21434
|
+
const STANDBY_POLL_MS = 30000;
|
|
21435
|
+
let standbyPollTimer = null;
|
|
21436
|
+
if (standbyToken && workspaceStates.length === 0) {
|
|
21437
|
+
const standbyPollTick = async () => {
|
|
21438
|
+
if (workspaceStates.length > 0) {
|
|
21439
|
+
if (standbyPollTimer) {
|
|
21440
|
+
clearInterval(standbyPollTimer);
|
|
21441
|
+
standbyPollTimer = null;
|
|
21442
|
+
}
|
|
21443
|
+
return;
|
|
21444
|
+
}
|
|
21445
|
+
try {
|
|
21446
|
+
const runtimes = providers.map((p) => ({ type: p.type, version: p.version }));
|
|
21447
|
+
const resp = await client.checkStandby(standbyToken, {
|
|
21448
|
+
daemon_id: config2.daemonId,
|
|
21449
|
+
device_name: config2.deviceName,
|
|
21450
|
+
cli_version: config2.cliVersion,
|
|
21451
|
+
workspaces_root: config2.workspacesRoot,
|
|
21452
|
+
runtimes
|
|
21453
|
+
});
|
|
21454
|
+
if (!resp.standby && resp.runtimes.length > 0 && resp.workspaceId) {
|
|
21455
|
+
log10.info(`Standby poll: workspace ${resp.workspaceId} discovered via fallback`);
|
|
21456
|
+
await handleWorkspaceAdded(resp.workspaceId, "", standbyToken);
|
|
21457
|
+
if (standbyPollTimer) {
|
|
21458
|
+
clearInterval(standbyPollTimer);
|
|
21459
|
+
standbyPollTimer = null;
|
|
21460
|
+
}
|
|
21461
|
+
}
|
|
21462
|
+
} catch (e) {
|
|
21463
|
+
log10.debug("standby poll failed", { err: e instanceof Error ? e.message : String(e) });
|
|
21464
|
+
}
|
|
21465
|
+
};
|
|
21466
|
+
standbyPollTick();
|
|
21467
|
+
standbyPollTimer = setInterval(standbyPollTick, STANDBY_POLL_MS);
|
|
21468
|
+
}
|
|
21350
21469
|
const sweepTick = async () => {
|
|
21351
21470
|
for (const ws of workspaceStates) {
|
|
21352
21471
|
client.sweep(ws.token, config2.daemonId).catch((e) => {
|
|
@@ -21384,6 +21503,8 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21384
21503
|
clearInterval(pollTimer);
|
|
21385
21504
|
clearInterval(heartbeatTimer);
|
|
21386
21505
|
clearInterval(sweepTimer);
|
|
21506
|
+
if (standbyPollTimer)
|
|
21507
|
+
clearInterval(standbyPollTimer);
|
|
21387
21508
|
stopSkillScanner();
|
|
21388
21509
|
wsClient?.close();
|
|
21389
21510
|
const shutdownMs = restartRequested ? 30000 : Number(process.env.ALOOK_SHUTDOWN_TIMEOUT_MS) || 5000;
|
|
@@ -21432,7 +21553,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21432
21553
|
log10.info("SIGHUP received — reloading config...");
|
|
21433
21554
|
try {
|
|
21434
21555
|
const freshConfig = loadCLIConfigForProfile(profile);
|
|
21435
|
-
const freshWorkspaces = freshConfig.watched_workspaces || [];
|
|
21556
|
+
const freshWorkspaces = (freshConfig.watched_workspaces || []).filter((ws) => ws.status !== "registered" && !!ws.id);
|
|
21436
21557
|
const existingIds = new Set(workspaceStates.map((ws) => ws.workspaceId));
|
|
21437
21558
|
const newWorkspaces = freshWorkspaces.filter((ws) => ws.token && !existingIds.has(ws.id));
|
|
21438
21559
|
for (const ws of newWorkspaces) {
|
|
@@ -21462,7 +21583,27 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21462
21583
|
}
|
|
21463
21584
|
}
|
|
21464
21585
|
if (newWorkspaces.length > 0) {
|
|
21586
|
+
hadWorkspaces = true;
|
|
21465
21587
|
health.setRuntimeCount(workspaceStates.reduce((sum, w) => sum + w.runtimeIds.length, 0));
|
|
21588
|
+
if (!wsClient && workspaceStates.length > 0) {
|
|
21589
|
+
const token = workspaceStates[0].token;
|
|
21590
|
+
wsClient = new DaemonWsClient({
|
|
21591
|
+
serverURL: config2.serverURL,
|
|
21592
|
+
daemonId: config2.daemonId,
|
|
21593
|
+
machineToken: token,
|
|
21594
|
+
onMessage: handleWsPush,
|
|
21595
|
+
onConnected: () => {
|
|
21596
|
+
log10.info("WS connected — switching to low-frequency poll");
|
|
21597
|
+
updatePollInterval(config2.wsPollInterval);
|
|
21598
|
+
},
|
|
21599
|
+
onDisconnected: () => {
|
|
21600
|
+
log10.info("WS disconnected — reverting to high-frequency poll");
|
|
21601
|
+
updatePollInterval(config2.pollInterval);
|
|
21602
|
+
}
|
|
21603
|
+
});
|
|
21604
|
+
wsClient.connect();
|
|
21605
|
+
log10.info("WS push client initialized after SIGHUP reload");
|
|
21606
|
+
}
|
|
21466
21607
|
log10.info(`Reload complete — now polling ${workspaceStates.length} workspace(s)`);
|
|
21467
21608
|
} else {
|
|
21468
21609
|
log10.info("Reload complete — no new workspaces found");
|
|
@@ -22979,7 +23120,7 @@ function versionCommand() {
|
|
|
22979
23120
|
|
|
22980
23121
|
// commands/update.ts
|
|
22981
23122
|
import { Command as Command11 } from "commander";
|
|
22982
|
-
function
|
|
23123
|
+
function updateCommand2() {
|
|
22983
23124
|
const cmd = new Command11("update").description("Update CLI to the latest version").action(async () => {
|
|
22984
23125
|
const current = getCurrentVersion();
|
|
22985
23126
|
console.log(`Current version: ${current}`);
|
|
@@ -23207,7 +23348,7 @@ program.addCommand(issueCommand());
|
|
|
23207
23348
|
program.addCommand(agentCommand());
|
|
23208
23349
|
program.addCommand(configCommand());
|
|
23209
23350
|
program.addCommand(versionCommand());
|
|
23210
|
-
program.addCommand(
|
|
23351
|
+
program.addCommand(updateCommand2());
|
|
23211
23352
|
program.addCommand(syncCommand());
|
|
23212
23353
|
program.addCommand(workspaceCommand());
|
|
23213
23354
|
program.parse();
|
package/dist/session-runner.js
CHANGED
|
@@ -14472,7 +14472,8 @@ var DaemonPushMessageSchema = exports_external.discriminatedUnion("type", [
|
|
|
14472
14472
|
exports_external.object({ type: exports_external.literal("daemon.evict"), workspaceId: exports_external.string() }),
|
|
14473
14473
|
exports_external.object({ type: exports_external.literal("daemon.update"), version: exports_external.string() }),
|
|
14474
14474
|
exports_external.object({ type: exports_external.literal("daemon.rescan") }),
|
|
14475
|
-
exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() })
|
|
14475
|
+
exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() }),
|
|
14476
|
+
exports_external.object({ type: exports_external.literal("daemon.workspace_added"), workspaceId: exports_external.string(), workspaceName: exports_external.string(), token: exports_external.string() })
|
|
14476
14477
|
]);
|
|
14477
14478
|
var RegisterResponseSchema = exports_external.object({
|
|
14478
14479
|
runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
|
|
@@ -14502,6 +14503,9 @@ var RegisterDaemonRequestSchema = exports_external.object({
|
|
|
14502
14503
|
workspaces_root: exports_external.string().optional().default(""),
|
|
14503
14504
|
runtimes: exports_external.array(DaemonRuntimeItemSchema).min(1)
|
|
14504
14505
|
});
|
|
14506
|
+
var BindWorkspaceRequestSchema = exports_external.object({
|
|
14507
|
+
workspace_id: exports_external.string().min(1)
|
|
14508
|
+
});
|
|
14505
14509
|
var DeregisterRequestSchema = exports_external.object({
|
|
14506
14510
|
daemon_id: exports_external.string().min(1)
|
|
14507
14511
|
});
|
|
@@ -14663,7 +14667,8 @@ var CreateConversationRequestSchema = exports_external.object({
|
|
|
14663
14667
|
channel: exports_external.string().optional()
|
|
14664
14668
|
});
|
|
14665
14669
|
var CreateMessageRequestSchema = exports_external.object({
|
|
14666
|
-
content: exports_external.string().min(1, "content is required")
|
|
14670
|
+
content: exports_external.string().min(1, "content is required"),
|
|
14671
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
14667
14672
|
});
|
|
14668
14673
|
var AgentDmRequestSchema = exports_external.object({
|
|
14669
14674
|
content: exports_external.string().min(1, "content is required"),
|
|
@@ -16646,6 +16651,8 @@ var machineToken = sqliteTable("machine_token", {
|
|
|
16646
16651
|
token: text("token").unique().notNull(),
|
|
16647
16652
|
name: text("name").notNull().default(""),
|
|
16648
16653
|
status: text("status").notNull().default("active"),
|
|
16654
|
+
hostname: text("hostname"),
|
|
16655
|
+
runtimesJson: text("runtimes_json"),
|
|
16649
16656
|
lastUsedAt: text("last_used_at"),
|
|
16650
16657
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
16651
16658
|
}, (t) => [index("idx_machine_token").on(t.token)]);
|
|
@@ -16780,7 +16787,24 @@ function isLocalUrl(url2) {
|
|
|
16780
16787
|
return false;
|
|
16781
16788
|
}
|
|
16782
16789
|
}
|
|
16790
|
+
function hasWindow() {
|
|
16791
|
+
return typeof globalThis !== "undefined" && "window" in globalThis;
|
|
16792
|
+
}
|
|
16793
|
+
function isTauri() {
|
|
16794
|
+
return hasWindow() && typeof window !== "undefined" && "__TAURI__" in window;
|
|
16795
|
+
}
|
|
16796
|
+
function isMobile() {
|
|
16797
|
+
if (!isTauri())
|
|
16798
|
+
return false;
|
|
16799
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
16800
|
+
return /(android|iphone|ipad|ipod)/i.test(ua);
|
|
16801
|
+
}
|
|
16783
16802
|
function resolveMode(signals) {
|
|
16803
|
+
if (signals.tauri || isTauri()) {
|
|
16804
|
+
if (signals.tauriPlatform === "mobile" || isMobile())
|
|
16805
|
+
return "mobile";
|
|
16806
|
+
return "desktop";
|
|
16807
|
+
}
|
|
16784
16808
|
if (signals.nodeEnv === "development" && !signals.cmdPrefix)
|
|
16785
16809
|
return "dev";
|
|
16786
16810
|
if (signals.serverUrl && !signals.cmdPrefix && signals.nodeEnv !== "production" && isLocalUrl(signals.serverUrl))
|
|
@@ -16797,6 +16821,8 @@ function cliCommand(mode) {
|
|
|
16797
16821
|
return "pnpm dev:cli";
|
|
16798
16822
|
case "app":
|
|
16799
16823
|
return "npx @alook/app cli";
|
|
16824
|
+
case "desktop":
|
|
16825
|
+
case "mobile":
|
|
16800
16826
|
case "production":
|
|
16801
16827
|
return "npx @alook/cli";
|
|
16802
16828
|
}
|
|
@@ -16921,6 +16947,9 @@ class DaemonClient {
|
|
|
16921
16947
|
syncSkills(token, body) {
|
|
16922
16948
|
return this.request("POST", "/api/daemon/skills/sync", token, body);
|
|
16923
16949
|
}
|
|
16950
|
+
async checkStandby(token, body) {
|
|
16951
|
+
return this.request("POST", "/api/daemon/register", token, body);
|
|
16952
|
+
}
|
|
16924
16953
|
}
|
|
16925
16954
|
|
|
16926
16955
|
// daemon/agent/claude.ts
|
|
@@ -18418,6 +18447,8 @@ Don't bundle everything into one giant message at the end. The user shouldn't ha
|
|
|
18418
18447
|
|
|
18419
18448
|
\`${cmdPrefix()} sync send-dm --message "…"\` for short messages. For longer or markdown-rich messages, write to a file first and use \`--message-file <path>\` — this preserves formatting and avoids shell escaping issues. The conversation is in $ALOOK_CONVERSATION_ID, so you usually need no flags. You can send several times in one task.
|
|
18420
18449
|
|
|
18450
|
+
Your messages are rendered as **markdown** in the user's app. Use formatting to make your responses clear and scannable — headers, bullet lists, code blocks, bold for key points. Don't send a wall of plain text when structure would help the reader. For anything beyond a one-liner, prefer \`--message-file\` so you can write proper markdown without fighting shell escaping.
|
|
18451
|
+
|
|
18421
18452
|
### Attachments
|
|
18422
18453
|
When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
|
|
18423
18454
|
Use your Read tool to open them. Images and PDFs are read visually.
|
|
@@ -18817,6 +18848,13 @@ function buildTaskObject(task, attachments) {
|
|
|
18817
18848
|
};
|
|
18818
18849
|
if (task.type === "user_dm_message") {
|
|
18819
18850
|
obj.notice = DM_RESPONSE_NOTICE;
|
|
18851
|
+
const ctx = task.context;
|
|
18852
|
+
if (ctx?.message_id) {
|
|
18853
|
+
obj.message_id = ctx.message_id;
|
|
18854
|
+
}
|
|
18855
|
+
if (ctx?.quoted_message) {
|
|
18856
|
+
obj.quoted_message = ctx.quoted_message;
|
|
18857
|
+
}
|
|
18820
18858
|
}
|
|
18821
18859
|
if (task.type === "email_notification") {
|
|
18822
18860
|
const ctx = task.context;
|